;-----------------------------------------------------------------------------
; Source for EZEDIT.COM.  COM file is 3.4K code followed by 0.6K fixed data.
;
; Interface here is a little dated, but program has good features considering
; small size.  I've done all my ASM programming with the editor since 1989.
; Main goal with version 2.0 was to reduce COM file size to 4K.
;-----------------------------------------------------------------------------
; 20 Apr 89 V1.0 - Initial release
; 28 Nov 90 V1.1 - Added replace mode, undelete line, color toggle, and shell
;                - Enlarged paste-I/O buffer from 64 to 128 lines
;                - Added wildcard and increased string length for searches
;                - Searches row-center found text
;                - Backspace in insert mode pulls line tail behind
;                - End moves to start of trailing spaces, not column 128
;                - Tab skips to next word when not in trailing spaces
;                - Eliminated shift tab (never used--shift inconvenient)
;                - Color byte made accessible by DEBUG
; 02 Feb 91 V1.2 - Added status color byte, independent of text color byte
;                - Included documentation file in program
;                - Compressed with Katz's PKLITE, adjusted for color DEBUG-edit
; 03 Mar 96 V2.0 - Tightened code/help text to reduce COM to 4K
;                - Removed all variable data from COM image, using BP-reference
;                - PKLITE-compression discarded--no longer needed
;                - Enlarged paste-I/O buffer from 128 to 255 lines
;                - Included three new draw styles
;                - Added upper case/lower case conversion option
;                - Added 25/50 line VGA toggle
;                - Use LF rather than CR for endline (lone CRs discarded)
;                - Allowed wildcard in replace text (not just search text)
;                - Included line-extend character
;                - Allowed raw tabs on read
;                - Changed Ins to act as undo for Del key in insert mode
;                - Accounted for possible ANSI.SYS in exit cursor position
;                - Use DOS 5.0+ UMBs when possible
;                - Allowed case-sensitive search/replace
;-----------------------------------------------------------------------------
Code_Seg   SEGMENT
           ASSUME CS:Code_Seg,DS:Code_Seg,ES:Code_Seg,SS:Code_Seg

MOVE       EQU   xchg           ; Saves byte on some AX moves

INITCOLORS =     1702h          ; Green on black low, white on blue high
STACKSIZE  =     512

PASTEMAX   =     255                 ; Line max for paste buffer (16 - 255)
IOSIZE     =     128 * (PASTEMAX+1)  ; 32K--need 256-multiple

SEG_MAX    =     64             ; Maximum number of work segments (won't
SEG_PARA   =     1024           ;   reach, since only 1MB address space)
SEG_SIZE   =     16384          ; Size of work segments--do not change

WILDCARD   =     '?'            ; String search wildcard character
TAB_PFX    =     '+'            ; Filename prefix for raw tabs

BS         =     08h            ; Miscellaneous control characters
TAB        =     09h
LF         =     0Ah
CR         =     0Dh
CR_LF      =     0A0Dh
EOF_MARK   =     1Ah
ESC_KEY    =     1Bh
MODE_KEY   =     3Dh            ; F3 extended key code
EXIT_KEY   =     41h            ; F7 extended key code

CR_LONG    =     1C0Dh          ; These four are normal key scan codes--will
TAB_LONG   =     0F09h          ;   trap these, but pass along equivalent
ESC_LONG   =     011Bh          ;   Alt key codes as text
BS_LONG    =     0E08h

LF_EDGE    =     1              ; Boundary flags for cursor--assigned values
TOP_EDGE   =     2              ;   also determine order of Bars array
RT_EDGE    =     4
BOT_EDGE   =     8

TYPEOVER   =     0              ; Program modes--must match ModeTbl with
INSERT     =     1              ;   order of first 4 and last 2 assumed--
LINEMARK   =     2              ;   i.e., new ones must be inserted around
BOXMARK    =     3              ;   DRAW or REPEAT
DRAW       =     4
REPEAT     =     5
SEARCH     =     6
REPLACE    =     7

Q_PASTE    =     0              ; Questions for user--must match QuestTbl
Q_SHELL    =     1
Q_SWITCH   =     2
Q_ABORT    =     3
VIDQUEST   =     34 * 2         ; Video offset of status line question

TO_CAPS    =     1              ; Edit flags for user-entered string input
NO_WHITE   =     2
MDEX_OK    =     128            ; Mode or exit key ok

F1F2_WID   =     13             ; Width of entries in F1/F2 table
MODE_WID   =     8              ; Width of entries in mode table
QUEST_WID  =     6              ; Width of entries in question table

STRING_MX  =     24             ; Maximum length of search/replace strings
VIDPROMPT  =     33 * 2         ; Video offset of search/replace prompt

SCRCOLS    =     80             ; Only allowed video mode screen width
NAMEMAX    =     64             ; Maximum filename length, including null
TBLSIZE    =     25             ; Number of dispatch table entries
BYTES      =     15             ; Size of file-specific storage (swapped)
BARBYTES   =     15             ; Length of one draw style list

HELP_LEN   =     10             ; HelpText lines
HELP_TAB   =     19             ; HelpText indent
;-----------------------------------------------------------------------------
; Data assignments in PSP area.  All byte/word variable data is here.  See EOF
; for constant data and buffers.  BP is fixed at 80h throughout program.
;-----------------------------------------------------------------------------
PSP_Env    EQU  <WORD PTR [bp-84]>  ; PSP location of environment segment

VidSeg     EQU  <WORD PTR [bp+0]>   ; Video segment--PSP command-line start
VidHiDB    EQU  <BYTE PTR [bp+1]>
VidHiWD    EQU  <WORD PTR [bp+1]>   ; For initial 0B8h/row byte save
ScrRowsDB  EQU  <BYTE PTR [bp+2]>
ScrRows    EQU  <WORD PTR [bp+2]>   ; Screen rows less one (high byte zero)

CurModeWD  EQU  <WORD PTR [bp+4]>
CurMode    EQU  <BYTE PTR [bp+4]>   ; Current program mode
CaseFlag   EQU  <BYTE PTR [bp+5]>   ; 0/1 flags letter case
StyleFlag  EQU  <BYTE PTR [bp+6]>   ; 0/1/2/3 in low bits flags draw style

TabFlag    EQU  <BYTE PTR [bp+7]>   ; 0/other flags keep/expand tabs on load

BotLnWD    EQU  <WORD PTR [bp+8]>   ; Order fixed through NewFlag
BotLn      EQU  <BYTE PTR [bp+8]>   ; Line offset in last segment
BotSegWD   EQU  <WORD PTR [bp+9]>
BotSeg     EQU  <BYTE PTR [bp+9]>   ; Index to bottom of SegList
CurSeg     EQU  <BYTE PTR [bp+10]>  ; Current index into Seglist
                                    ; 15 bytes for current file variables next
NewFlag    EQU  <BYTE PTR [bp+11]>  ; 255 if new file else 0
CurLine    EQU  <WORD PTR [bp+12]>  ; Current line
CurCol     EQU  <WORD PTR [bp+14]>  ; Current column--must follow CurLine
BotLine    EQU  <WORD PTR [bp+16]>  ; Bottom line number in use
LineMax    EQU  <WORD PTR [bp+18]>  ; Last allocated line number
NameLen    EQU  <WORD PTR [bp+20]>  ; Length of ASCIIZ file name, less null
ScrTop     EQU  <WORD PTR [bp+22]>  ; Line at top of screen (below status line)
ScrLeft    EQU  <WORD PTR [bp+24]>  ; Leftmost screen column

AltVar     EQU  <BYTE PTR [bp+26]>  ; 15 bytes for alternate file variables

FileCnt    EQU  <BYTE PTR [bp+41]>  ; Count of active files (0/1/2)

Strng1Ln   EQU  <WORD PTR [bp+42]>  ; Current length of search string
Strng2Ln   EQU  <WORD PTR [bp+44]>  ; Current length of replace string
PasteLen   EQU  <WORD PTR [bp+46]>  ; Length of current paste buffer data
PasteLenDB EQU  <BYTE PTR [bp+46]>
PasteWid   EQU  <WORD PTR [bp+48]>  ; Width of current paste buffer data
MarkLine   EQU  <WORD PTR [bp+50]>  ; Line and box mark variables
MarkCol    EQU  <WORD PTR [bp+52]>
MarkTop    EQU  <WORD PTR [bp+54]>
MarkLeft   EQU  <WORD PTR [bp+56]>
MarkLen    EQU  <WORD PTR [bp+58]>
MarkLenDB  EQU  <BYTE PTR [bp+58]>
MarkWid    EQU  <WORD PTR [bp+60]>
MarkWidDB  EQU  <BYTE PTR [bp+60]>

Handle     EQU  <WORD PTR [bp+62]>  ; File handle in Read/Write routines
Colors     EQU  <WORD PTR [bp+64]>  ; Color bytes
LoColor    EQU  <BYTE PTR [bp+64]>  ; Main text attributes
HiColorWD  EQU  <WORD PTR [bp+65]>
HiColor    EQU  <BYTE PTR [bp+65]>  ; Status/highlight attributes
                                    ; Shell data next--fixed at initialization
ParmBlk    EQU  <WORD PTR [bp+66]>  ; Zero flags copy of own environment
TailLo     EQU  <WORD PTR [bp+68]>  ; Points to Tail, which holds null/CR--DOS
TailHi     EQU  <WORD PTR [bp+70]>  ;   expects two FCBs, but not needed here
CmdLoDD    EQU <DWORD PTR [bp+72]>
CmdLo      EQU  <WORD PTR [bp+72]>  ; Points to COMSPEC= value, if found, else
CmdHi      EQU  <WORD PTR [bp+74]>  ;   to junk (likely fails)

CircPtrDB  EQU  <BYTE PTR [bp+76]>  ; High byte always zero
CircPtr    EQU  <WORD PTR [bp+76]>  ; Points into CircBuf for Del/Ins
FreeWord   EQU  <WORD PTR [bp+78]>  ; Unused

String1    EQU  <BYTE PTR [bp+80]>  ; 24-byte max search string
String2    EQU  <BYTE PTR [bp+104]> ; 24-byte max replace string
;-----------------------------------------------------------------------------
; Initialization code.  DS is generally CS, but ES varies.  BP data above has
; implicit SS=CS reference--convenient when DS temporarily changed.
;-----------------------------------------------------------------------------
           ORG   100h           ; COM file start
EzEdit:    mov   dx,INITCOLORS  ; Here for easy DEBUG-access
           cld                  ; Program default--could assume from DOS
           mov   bp,80h         ; Fixed til very end

           mov   ax,3000h       ; Insure DOS 3.1 so SS:SP preserved on
           int   21h            ;   Shell and for Fail in CritErr
           xchg  ah,al          ; Swap major/minor--BX/CX ignored
           cmp   ax,030Ah       ; Test minimum DOS version
           jb    Abort          ; Not 3.1+?  Abort

           mov   bx,OFFSET NewStack
           cmp   sp,bx
           jb    Abort          ; No room?  Abort

           pop   cx             ; Pop DOS-pushed-zero--CH zero for getarg
           mov   sp,bx
           push  cx             ; Push zero back for later PSP exit

           mov   cl,4           ; Convert BX to paragraphs
           shr   bx,cl
           mov   ah,4Ah         ; Resize program memory allocation--
           int   21h            ;   should succeed because of SP check

           mov   ax,1A00h
           int   10h            ; Get display codes BL/BH--CX/DX ok
           sub   al,1Ah         ; Test if function supported
           jne   Abort          ; No?  Abort, else AL zero for Int58 call

           shr   bl,1           ; BL 7/8 is active VGA
           sub   bl,cl          ; Now zero if VGA--CL still 4
           je    tryumbs        ; Yes?  Ahead with AL zero

Abort:     mov   dl,LOW OFFSET ErrMsg
DOSDisp:   mov   dh,HIGH OFFSET ErrMsg
           mov   ah,9           ; Display $-terminated string--strings
           int   21h            ;   all have same high offset
           ret                  ; Return or exit via PSP

tryumbs:   call  Int58h         ; Get current fit strategy--AL is 0
           push  ax             ; Save til exit for restore
           mov   al,2           ; If older older DOS, call fails (carry set)
           call  Int58h         ; Get current UMB link state
           cbw                  ; AL is 0/1 if DOS 5.0+
           push  ax             ; Save til exit for restore
           mov   bx,1           ; Flag UMB link to memory chain--BP is 80h
           call  SetUMBs        ; At tail of main loop--CH zero/DX ok after
;-----------------------------------------------------------------------------
; Initialize first filename from command-line argument, if any.
;-----------------------------------------------------------------------------
getarg:    mov   di,bp          ; PSP offset of command tail
           mov   cl,[di]        ; Fetch length--CH zero from above
           mov   bx,cx
           mov   al,' '
           inc   di             ; To start of argument
           mov   [di+bx],al     ; Insert terminating space
           inc   cx             ; Include terminating space in scan
           repe  scasb          ; Skip over leading spaces
           je    noname         ; All spaces?  Then ahead with CX zero

           dec   di             ; Point back to non-space
           mov   cl,NAMEMAX     ; Maximum filename length plus one
           push  di             ; Save momentarily
           repne scasb          ; Scan until space found (or none)
           pop   si             ; Restore pointer to start of filename
           sub   cl,NAMEMAX - 1
           neg   cl             ; CX now holds filename length
noname:    mov   di,OFFSET AltName
           push  cx             ; Save length until PSP bytes zeroed below
           jcxz  clear          ; No length?  Then ahead, else inequality
                                ;   for next call
           lahf                 ; Equality flag to AH for next
           call  CopyCase       ; Copy to AltName, forcing upper case, zero CX
;-----------------------------------------------------------------------------
; Zero 128 PSP bytes, then initialize some non-zero data there.  Also blank
; SaveLine/CircBuf and replace Ctrl-C/critical error handlers.
;-----------------------------------------------------------------------------
clear:     mov   ax,bp
           mov   di,bp          ; Start of PSP command-buffer
           xchg  ax,cx          ; AX/CX gets 0/128
           rep   stosb          ; Zero 128 bytes--can now adjust PSP data

           pop   NameLen + BYTES; Store file name length in alternate area
           mov   Colors,dx      ; DX preserved from program start

           mov   di,OFFSET SaveLine
           mov   ch,2           ; Need at least 128+256 in CX
           call  Spaces         ; Clear SaveLine/CircBuf, zeroing CX

           MOVE  di,ax          ; Zero DI for Shell prep below

           mov   dx,OFFSET CritErr
           mov   ax,2524h
           int   21h            ; Set new int 24h handler to CritErr
           dec   ax             ; AL to 23h
           inc   dx
           inc   dx             ; DX points to iret now
           int   21h            ; Point new int 23h handler to CtrlC

           mov   es,PSP_Env     ; Environment segment--DI zero
           mov   si,OFFSET Comspec
           mov   bh,4           ; Should really get exact environment size
           mov   cl,8           ; Size of search string--CH zero
           call  ScanLine       ; Search should really be case sensitive
           add   di,cx          ; Point after equal sign, if success
           mov   TailLo,OFFSET Tail
           mov   TailHi,cs
           mov   CmdLo,di       ; Garbage if no match--probable beep on Shell
           mov   CmdHi,es

           pop   es             ; Zero
           push  es
           mov   ah,es:[484h]   ; Screen rows less 1 in BIOS memory
           shr   ah,1           ; Convert possible 49 --> 24
           mov   al,0B8h        ; Video segment high byte
           mov   VidHiWD,ax     ; Save both--if AH is 24, next restores 49
           call  SetVid         ; Set video mode and screen rows byte
;-----------------------------------------------------------------------------
; End initialization.  Main program loop next with PSP exit at end.
;-----------------------------------------------------------------------------
donext:    call  SwapAll        ; Swap filenames, variables, and segment lists
           cmp   FileCnt,1
           ja    doswitch       ; Already two files active?  Then just switch

           inc   FileCnt        ; Else bump file count
           call  ClearScr       ; Clear screen, homing cursor--CX zeroed
           mov   al,'$'         ; Help line terminator--24h
           mov   di,((8 * SCRCOLS) + HELP_TAB) * 2
           cmp   ScrRowsDB,al   ; Compare 24/49 to 36
           jb    isnorm         ; 24?  Ahead

           mov   di,((20 * SCRCOLS) + HELP_TAB) * 2
isnorm:    mov   si,OFFSET HelpText
           mov   dx,HELP_LEN    ; Loop counter--lines to display
dohelplp:  push  di
           mov   di,si
           mov   ch,1           ; CL zero from ClearScr/DispBlk
           repne scasb          ; Look for terminator--carry cleared
           not   cl             ; CX now length, excluding terminator
           pop   di             ; Refresh DI
           push  di
           call  DispBlk        ; Display help line--BX preserved, CX zeroed
           pop   di
           add   di,2 * SCRCOLS ; Bump for next line
           inc   si             ; Skip past $-terminator
           dec   dx
           jne   dohelplp

           mov   ax,OFFSET RdPrompt
           call  GetName        ; Get/Edit input file name--output CX length
           jc    doswesc        ; User escaped?  Then abort this file

           mov   di,si          ; SI points to name, CX is length
           lodsb                ;   from GetName call
           sub   al,TAB_PFX     ; Prefix to keep raw tabs
           jne   expndtabs      ; Want expanded tabs?  Ahead

           rep   movsb          ; Discard prefix, with null copied too
           dec   NameLen        ; Adjust length
expndtabs: mov   TabFlag,al     ; Zero means raw tabs
           call  Read           ; Read file, setting BotLine/LineMax/NewFlag/
                                ;   CurLine/CurCol and maybe PasteLen
           jc    doabort        ; Read error?  Then abort this file (but
                                ;   assume new file on open error)
           call  Append         ; Insure at least one line (in case new file)
           jc    doabort        ; No room?  Then abort this file

doswitch:  call  ToTypOvr       ; Switch to other file in RAM, resetting mode
doreedit:  call  Edit           ; Main edit routine--returns BX = 0/1/2/3
           dec   bx
           js    doname         ; Was zero?  Save file

           dec   bx
           je    donext         ; Was two?  Then switch files
           jns   doabort        ; Was three?  Then abort current file

           call  Shell          ; Else was one--shell then return to Edit
           jmp   SHORT doreedit

doname:    mov   ax,OFFSET SvPrompt
           call  GetName        ; Get/edit save name
           jc    doreedit       ; Esc key?  Then continue edit
           jcxz  doreedit       ; No file name?  Also continue edit

           call  Write          ; Write file, given BotLine, zeroing PasteLen
           jc    doname         ; Write error?  Then try new name

doabort:   shl   LineMax,1      ; LineMax segment index (or -1) to high byte
           mov   bx,LineMax+1   ; BL indexes last allocated segment
           jmp   SHORT dofree   ; Free segment list (do nothing on -1)

dofreelp:  push  bx
           call  GetSegBL       ; Set ES to segment indexed by BL
           mov   ah,49h
           int   21h            ; Free block at segment ES
           pop   bx
           dec   bx
dofree:    test  bl,bl
           jns   dofreelp       ; Not -1 yet?  Then loop

doswesc:   call  SwapAll        ; Swap filenames, variables, and segment lists
           dec   FileCnt        ; Decrement count of active files
           jne   doswitch       ; Still one left?  Then switch to it

           mov   dx,ScrRows     ; Cursor to screen bottom
           call  SetCsrX

           pop   bx             ; Restore UMB flag/fit strategy
           pop   bp             ; Can destroy BP now--fall to PSP exit
;-----------------------------------------------------------------------------
; Handle UMBs and DOS allocation strategy.  BX/BP input.
;-----------------------------------------------------------------------------
SetUMBs:   mov   al,3           ; Link or unlink UMBs from memory chain
           call  Int58h         ; BX 0/1 unlinks/links--fails if old DOS
           mov   al,1           ; Set allocation fit strategy--8xh in BX
           mov   bx,bp          ;   flags try high first, then low
Int58h:    mov   ah,58h         ; Will fail some calls if not DOS 5.0+
           int   21h
           ret                  ; Exit via PSP or return
;-----------------------------------------------------------------------------
; Interrupt 24h handler fails errors.  Interrupt 23h handler just does iret.
;-----------------------------------------------------------------------------
CritErr:   mov   al,3           ; Flag Fail--will set int 21h carry
CtrlC:     iret                 ;   if disk error
;-----------------------------------------------------------------------------
; Swap filenames, main variables, and segment lists.  Invert Colors also.
;-----------------------------------------------------------------------------
SwapAll:   mov   di,OFFSET AltList
           mov   cx,SEG_MAX * 2 + NAMEMAX
           call  Swap           ; Swap segment lists and file names
           mov   cl,8
           ror   Colors,cl      ; Swap color bytes
           lea   di,NewFlag
           mov   cl,BYTES       ; Swap main variables and alternates
Swap:      push  cs
           pop   es
           mov   si,di          ; Swap data with block immediately following
           add   di,cx
swaploop:  mov   al,[di]        ; Exchange two bytes
           xchg  al,[si]
           stosb
           inc   si             ; Bump SI also, also setting inequality
           loop  swaploop       ; CX bytes swapped?  If not, loop

           ret
;-----------------------------------------------------------------------------
; Toggle StyleFlag.
;-----------------------------------------------------------------------------
Style:     cmp   cl,DRAW
           jne   sbsout

           inc   StyleFlag      ; Fall harmlessly...
;-----------------------------------------------------------------------------
; Point SI to current Bar data offset and set CL to list length.
;-----------------------------------------------------------------------------
SetBarSI:  push  ax
           mov   al,StyleFlag
           and   al,3
           mov   cl,BARBYTES
           mul   cl
           mov   si,OFFSET BarList
           add   si,ax
           pop   ax
sbsout:    ret
;-----------------------------------------------------------------------------
; Get/edit current filename.  Input AX points to prompt.  Carry output.
;-----------------------------------------------------------------------------
GetName:   call  ClrStat        ; Clear status line, zeroing CX--AX ok
           mov   di,2 * (SCRCOLS - 3)
           call  DispEsc        ; Inverse display Esc at EOL--AX ok
           MOVE  si,ax          ; Prompt offset
           mov   cl,11          ; Length of prompt (CH is zero)
           call  DispBlkXX      ; Inverse display prompt at DI=0
           mov   bx,NAMEMAX - 1 ; Maximum filename length
           mov   cx,NameLen     ; Current name length
           mov   si,OFFSET FileName
           mov   dl,TO_CAPS OR NO_WHITE
           call  GetStr         ; Edit filename, returning carry, CX
           mov   NameLen,cx     ; Update length
           mov   bx,cx          ; CH is zero and SI was preserved
           mov   [si+bx],ch     ; Insure ASCIIZ string
           ret
;-----------------------------------------------------------------------------
; Get free block and add to SegList at index AH.  Carry set on error.  BX not
; preserved.  AH more convenient than AL for AddSeg routine.
;-----------------------------------------------------------------------------
GetBlock:  push  ax             ; Save registers
           mov   al,ah
           cbw                  ; Zero AH if AL under cap
           cmp   al,SEG_MAX     ; SEG_MAX is up to 128
           cmc
           jc    gberror        ; Reached maximum list size?  Then exit

           push  ax
           mov   bx,SEG_PARA
           mov   ah,48h
           int   21h            ; Allocate block, returning segment in AX
           pop   bx
           jc    gberror        ; Allocation error?  Then exit

           shl   bx,1           ; Word offset, clearing carry
           mov   SegList[bx],ax
gberror:   pop   ax
           ret                  ; Return carry, BX destroyed
;-----------------------------------------------------------------------------
; Bump BotSeg/CurSeg, returning ES and zeroing DI.  Carry set if no room.  BX
; not preserved.  Called during Read, so both segment indexes have same value.
;-----------------------------------------------------------------------------
BumpSegs:  push  ax
           xor   di,di
           mov   ax,BotSegWD    ; AL/AH gets BotSeg/CurSeg
           inc   ax             ; Bump both indexes (careful of 0FFFFh)
           mov   ah,al
           call  GetBlock       ; Add new segment to list at index AH
           jc    gberror        ; No room in list or RAM?  Then error

           mov   BotSegWD,ax    ; Update BotSeg/CurSeg
           pop   ax
;-----------------------------------------------------------------------------
; Return current segment in ES.  BX not preserved--BH zeroed.
;-----------------------------------------------------------------------------
GetSeg:    mov   bl,CurSeg
GetSegBL:  xor   bh,bh
           shl   bx,1           ; Word offset, clearing carry
           mov   es,SegList[bx]
           ret
;-----------------------------------------------------------------------------
; Expand AX bytes in file buffer to lines in work area, bumping DI.  Line
; extend has flaw, since extra EOF_MARKs make tab expansion out of synch in
; extended lines (ok through column 128).  Can live with this.  Choice of
; EOF as line extender is least harm, since usually omitted in text anyway.
;-----------------------------------------------------------------------------
Expand:    MOVE  cx,ax          ; Bytes-read
           mov   si,OFFSET FileBuf
           call  GetSeg         ; Set ES to current segment, BH zeroed

exloop:    lodsb                ; Fetch file buffer byte
           cmp   al,CR          ; Check if carriage return
           je    exskip         ; Yes?  Then filter out

           cmp   al,EOF_MARK    ; Check if end-of-file mark
           je    exskip         ; Yes?  Then filter out

           cmp   di,SEG_SIZE    ; Check if need new segment (AFTER exskip's)
           jb    exsegok        ; No?  Ahead

           call  BumpSegs       ; Point ES:DI to next segment--AL ok, BH zeroed
           jc    exabort        ; Allocation problem?  Abort

exsegok:   cmp   TabFlag,bh     ; BH zero
           je    exkeep         ; Raw tabs?  Ahead

           mov   dl,7
           cmp   al,TAB         ; Check if tab character
           je    exclrbnd       ; Yes?  Then clear to boundary

exkeep:    mov   dl,127         ; End-of-line boundary
           cmp   al,LF          ; Check if line feed
           jne   exchk128       ; No?  Then normal character

exclrbnd:  push  cx
           call  BlankBlk       ; Blank to next DL boundary--AL/BH ok
           pop   cx
           cmp   al,LF          ; Recheck LF
           je    exskip         ; yes?  To loop end

           dec   di             ; Else back up to simulate last tab space
           mov   al,' '         ; Will re-store space or store EOF_MARK
exchk128:  mov   dx,di          ; Check if storing 128th byte in line
           inc   dx             ; ..7Fh --> ..80h or ..FFh --> ..00h if EOL
           shl   dl,1           ; Test if at EOL
           jne   exstore        ; No?  Ahead

           dec   si             ; Back up to reprocess current byte
           inc   cx
           mov   [si],al        ; Store space in case was tab character
           mov   al,EOF_MARK    ; Insert special line extend byte
exstore:   stosb                ; Store byte to work area
exskip:    loop  exloop         ; End of file buffer?  If not, loop

           clc                  ; Flag ok
exabort:   ret                  ; Return DI and carry
;-----------------------------------------------------------------------------
; Compress ES:SI lines to DS:DI until SI reaches BX (output carry clear) or
; DI near end of FileBuf (output carry set).  SI and DI updated.
;-----------------------------------------------------------------------------
Shrink:    call  SwapESDS       ; Swap ES and DS
shrloop:   cmp   si,bx          ; Test if done with segment
           jnc   SwapESDS       ; Yes?  Then exit with clear carry,
                                ;   restoring ES and DS
           push  es             ; Save ES:DI
           push  di
           mov   di,si          ; Move DS:SI to ES:DI for GetLen
           push  ds
           pop   es
           call  GetLen         ; Get length CX, less trailing spaces--BX ok
           pop   di
           pop   es             ; Restore ES:DI
           push  si             ; Save work area pointer
           rep   movsb          ; Transfer one line

           dec   di             ; Back up, anticipating line extend mark
           dec   si
           lodsb                ; Fetch last byte moved
           cmp   al,EOF_MARK    ; Test for line extend mark
           je    skipcrlf       ; Yes?  Ahead

           inc   di             ; Else undo back up
           mov   ax,CR_LF       ; Insert CR_LF (cmp below assures room)
           stosw
skipcrlf:  pop   si             ; Restore work area pointer
           add   si,bp          ; Advance to next work area line start
           cmp   di,OFFSET FileBuf + IOSIZE - 130  ; 130 = line plus CRLF
           cmc                  ; Set carry if near end of file buffer
           jnc   shrloop        ; No?  Then loop, else exit with set carry
;-----------------------------------------------------------------------------
; Swap segment registers ES and DS.
;-----------------------------------------------------------------------------
SwapESDS:  push  es
           push  ds
           pop   es
           pop   ds
sedout:    ret
;-----------------------------------------------------------------------------
; Swap ES/DS, move CX bytes, then restore ES/DS.
;-----------------------------------------------------------------------------
SwapMove:  call  SwapESDS       ; Swap segments
           rep   movsb          ; Move CX bytes
           jmp   SHORT SwapESDS ; Restore segments
;-----------------------------------------------------------------------------
; Read file, setting LineMax/BotLine/NewFlag.  Carry set on disk/room error.
; Also zero CurLine/CurCol and possibly PasteLen.
;-----------------------------------------------------------------------------
Read:      lea   di,BotLn
           xor   ax,ax
           mov   CurCol,ax
           dec   ax
           stosw                ; Initialize BotLn/BotSeg/CurSeg/NewFlag each
           stosw                ;   to 255
           stosw                ; Initialize CurLine to -1
           MOVE  di,ax          ; Force new segment on first non-filtered byte
rfnormal:  mov   ax,3D00h       ; Read-open
           call  Open           ; Set Handle, return carry, zeroes CX
           cmc
           jnc   rfout          ; Open error?  Then assume new file and exit

           mov   PasteLen,cx    ; Zero, since paste buffer also I/O buffer
           mov   NewFlag,cl     ; Zero flag, indicating old file
rfloop:    call  SetHnBuf       ; Set handle BX and file buffer DX
           mov   ch,HIGH IOSIZE ; CL zero from Open/Expand
           mov   ah,3Fh         ; DOS read
           int   21h
           jc    rfclose        ; Error?  Then close and exit

           test  ax,ax          ; Check if done reading
           jne   rfxpnd         ; No?  Then expand data

           inc   di             ; Carry is clear
           je    rfclose        ; No bytes?  Then exit

           dec   di             ; Restore DI
           mov   dx,bp          ; 128
           dec   dx
           test  di,dx          ; Check if last line needs space padding
           je    rfup           ; No?  Then ahead

           call  GetSeg         ; DL (127) preserved
           call  BlankBlk       ; Blank out to end of line
rfup:      MOVE  ax,di
           shl   ax,1           ; Lines are 128 bytes (clear carry also)
           dec   ah
           mov   BotLn,ah       ; Set bottom line, using zero origin
           jmp   SHORT rfclose

rfxpnd:    call  Expand         ; Expand AX bytes to space-padded lines
           jnc   rfloop         ; Ok?  Loop with CX zero, else close/exit

rfclose:   call  Close          ; Set carry passes through Close
rfout:     pushf                ; Save carry
           mov   ax,BotLnWD     ; Set AL/AH to BotLn/BotSeg
           cmp   al,-1          ; Check if AL was -1 (if so, AH also -1)
           je    rfnobyte       ; Nothing read?  Then flag with -1 for Append

           shl   al,1           ; 128 lines per segment
           shr   ax,1           ; Convert AL/AH to line equivalent
rfnobyte:  mov   BotLine,ax     ; Set to last line read (or -1)
           or    al,127         ; 128 lines per seqment
           mov   LineMax,ax     ; Set to last line in segment
           popf                 ; Restore carry
           jmp   SHORT wferror  ; Beep if error else exit
;-----------------------------------------------------------------------------
; Write file, given BotLine.  Carry set on error.  Possibly zero PasteLen.
;-----------------------------------------------------------------------------
Write:     mov   ax,BotLine     ; Assume not -1
           shl   ax,1           ; 128 lines per segment
           shr   al,1           ; Convert to segment index/offset in AH/AL
           mov   BotLnWD,ax     ; Set BotLn/BotSeg to AL/AH
           mov   ah,3Ch         ; Create open
           call  Open           ; Set Handle, return carrry, CX zero
           jc    wferror        ; Open error?  Then abort

           mov   di,OFFSET FileBuf
           mov   bx,SEG_SIZE    ; Size of all work areas except last
           mov   PasteLen,cx    ; Zero, since paste buffer killed
wfback:    mov   CurSeg,cl
           cmp   BotSeg,cl      ; Done when AL bumped past BotSeg
           cmc
           jnc   wfflush        ; Done?  Then do last flush and exit
           jne   wfup           ; Not at last segment?  Then ahead

           mov   bh,BotLn       ; Else reduce BX to size of last area
           inc   bh             ; Work area smaller than 64K
           xor   bl,bl
           shr   bx,1           ; Lines are 128 bytes
wfup:      xor   si,si          ; Point to start of work area
           push  bx
           call  GetSeg         ; Get segment of work area in ES
           pop   bx
wfloop:    call  Shrink         ; Compress to buffer, returning carry, DI, SI
           mov   cl,CurSeg      ; Anticipate clear carry
           inc   cx
           jnc   wfback         ; End of work segment?  Then get next segment

wfflush:   pushf                ; Save last-write flag (carry)
           mov   cx,OFFSET FileBuf
           sub   di,cx          ; DI now bytes-to-write
           xchg  di,cx          ; Reset DI for later data, ready CX for write
           push  bx
           call  SetHnBuf       ; Set handle BX and file buffer DX
           mov   ah,40h         ; DOS write
           int   21h
           pop   bx
           jc    wfset          ; Write error?  Ahead

           cmp   ax,cx          ; Set carry if short write
wfset:     pop   ax             ; Restore last-write flag to AX momentarily
           jc    wfclose        ; Write error or short write?  Then exit

           push  ax             ; Put last-write flag back into carry
           popf
           jc    wfloop         ; Not done?  Then loop, else carry clear

wfclose:   call  Close          ; Set carry passes through
wferror:   jnc   shbout         ; No error?  Then exit

           jmp   Beep           ; Attention (passes set carry)
;-----------------------------------------------------------------------------
; Open/create file with AX=3D00h/AH=3Ch.  Return Handle and carry.
;-----------------------------------------------------------------------------
Open:      mov   dx,OFFSET FileName
           xor   cx,cx          ; In case create, normal attributes
           int   21h            ; Open file for read/create
           mov   Handle,ax      ; File handle, if no error
           ret                  ; Carry set on error
;-----------------------------------------------------------------------------
; Close file, passing set carry flag through.  Carry also set on close error.
;-----------------------------------------------------------------------------
Close:     call  SetHnBuf       ; Set handle
           mov   ah,3Eh
           pushf                ; Save input carry
           int   21h
           pop   bx
           jc    shbout         ; Close error?  Then out

           push  bx             ; Else restore input carry
           popf                 ; Fall through setting ignored BX/DX
;-----------------------------------------------------------------------------
; Set file handle and file buffer for Close/WriteBuf/ReadBuf operations.
;-----------------------------------------------------------------------------
SetHnBuf:  mov   bx,Handle
           mov   dx,OFFSET FileBuf
shbout:    ret
;-----------------------------------------------------------------------------
; Forces return to Edit if wrong mode.  Use carefully.
;-----------------------------------------------------------------------------
TestCL:    cmp   cl,INSERT
           ja    popout         ; Not typeover/insert?  Pop return address

           ret                  ; Not fall--flags referenced afterward

TestCL2:   cmp   cl,BOXMARK
           jna   tcout          ; Typeover/insert/linemark/boxmark?  Ok

popout:    pop   ax             ; Pop address so return to Edit follows
tcout:     ret
;-----------------------------------------------------------------------------
; Find/replace successive text until end of file/user quits/replace error.
; Zero length replace string disallowed since would be left edge problem and
; since would be easy for user to invoke inadvertently.
;-----------------------------------------------------------------------------
fmatch:    cmp   CurMode,SEARCH
           je    Find           ; Not replace mode?  Ahead

           xor   di,di          ; CH zero from DispStat
           call  DispEsc        ; Inverse display Esc at BOL
           lea   si,String2
           mov   cx,Strng2Ln
           mov   dl,MDEX_OK
           call  GetFind        ; Get replace string, carry clear
           mov   Strng2Ln,cx
           jnc   sfrepl         ; Not mode, exit, or escape?  Replace

           cmp   al,ESC_KEY
           je    sfskip         ; Escape?  Skip replace, but continue search

sfexit:    mov   cl,CurMode     ; Expected by dispatch routines
           jmp   FindRet        ; Resume Edit at key checks--ES near/CH zero

tofmatch:  jmp   SHORT fmatch   ; Intermediate jump needed

sfrepl:    jcxz  sfskip         ; Zero length replace string?  Disallowed

           mov   di,OFFSET StatLine
           push  cx             ; Save replace parameters
           push  di             ; StatLine used here for temp string

           mov   cl,24          ; Copy replace string to temp string
           rep   movsb          ; SI points to String2
           pop   bx             ; Refresh temp offset
           push  bx

           mov   cx,Strng1Ln    ; Non-zero insured
sfdellp:   push  cx             ; Loop deletes old string and updates ? in temp
           call  DelCh          ; AH gets deleted byte--BX ok, but ES not
           cmp   BYTE PTR [bx],WILDCARD
           jne   notwild        ; Not wildcard?  Ahead

           mov   [bx],ah        ; Else retain deleted character from DelCh
notwild:   inc   bx
           pop   cx
           loop  sfdellp

           pop   si             ; Restore temp string pointer and length
           pop   cx
sfputlp:   lodsb                ; Second loop inserts temp string
           push  cx
           push  si
           call  InsCh          ; Insert character AL
           pushf
           call  ShRight        ; Cursor right (ok if bumping line end, but
           popf                 ;   replacing single character with itself
           pop   si             ;   will 'stick' at line end)
           pop   cx
           jc    fesc           ; Out of room?  Abort search/replace

           loop  sfputlp

           call  ShLeft         ; Cursor left so next search starts correctly
           call  DspCurLn       ; Display replaced text, unhighlighted--CH zero
sfskip:    call  DispStat       ; Clear replace string from status line
Find:      call  DispWild       ; CH zero from DispStat, Edit, FindNext
           lea   si,String1
           mov   cx,Strng1Ln
           mov   dl,TO_CAPS OR MDEX_OK
           xor   dl,CaseFlag    ; 0/1--if 1, undo TO_CAPS, which is 1
           stc
           call  GetFind        ; Let user enter/edit string
           mov   Strng1Ln,cx
           jc    sfexit         ; Mode, exit, or escape key?  Then out
           jcxz  fesc           ; No string?  Then escape

           call  GetPos         ; Set ES:DI to cursor position and BX to
           mov   bx,cx          ;   characters right of cursor
           inc   di             ; Bump past (previously found) start
           mov   dx,CurLine
           call  FindNext       ; Search, setting carry if no match found
           jnc   tofmatch       ; Match?  Then loop

fesc:      jmp   SHORT ToTypOvr ; Set typeover mode and return to Edit
;-----------------------------------------------------------------------------
; Flag save/shell/switch/abort with BX = 0/1/2/3 and pop return address.
;-----------------------------------------------------------------------------
Exit:      mov   bx,Q_ABORT     ; Start from last question
exitloop:  call  GetYN          ; Ask user question
           jc    expop          ; Yes?  Then exit, popping return address
           jne   ToTypOvr       ; Escape?  Then back to Edit--no pop

           dec   bx
           jne   exitloop

expop:     pop   ax             ; Clear address of Edit from stack
;-----------------------------------------------------------------------------
; On Esc key, revert to typeover mode.  Frequently called routine.
;-----------------------------------------------------------------------------
ToTypOvr:  mov   CurMode,TYPEOVER
BadKy:     ret                  ; Returns to top of Edit.
;-----------------------------------------------------------------------------
; Move cursor to top of file.
;-----------------------------------------------------------------------------
CPgUp:     xor   ax,ax          ; Top line
           jmp   SHORT cpdtest  ; Check mode
;-----------------------------------------------------------------------------
; Move cursor to bottom of file.
;-----------------------------------------------------------------------------
CPgDn:     mov   ax,BotLine
cpdtest:   call  TestCL         ; To Edit if not typeover/insert mode
           mov   CurLine,ax     ; Fall harmlessly...
;-----------------------------------------------------------------------------
; Adjust AX if box paste would extend past bottom/right of edit area.
;-----------------------------------------------------------------------------
BoxChk:    sub   bx,cx
           cmp   ax,bx          ; Check if would extend past bottom/right
           jna   bxout          ; No?  Then ahead

           MOVE  ax,bx          ; Else reduce bottom/right
bxout:     ret
;-----------------------------------------------------------------------------
; Main routine processes keystrokes.  On exit, BX = 0/1/2/3 (see Exit).
;-----------------------------------------------------------------------------
Edit:      call  SetMarks       ; Set mark parameters, in case needed, and
                                ;   adjust window coordinates
           call  DispScr        ; Display full screen
           call  DispStat       ; Display status line and cursor--also
           mov   dx,OFFSET Edit ;   zeroes AH/CH and sets ES near
           push  dx             ; Forces return to Edit, unless popped by Exit
           mov   cl,CurMode     ; Pass current mode to all functions in CL
           cmp   cl,INSERT      ;   and CH zero
           jna   edup           ; Typeover or insert mode?  Then ahead

           cmp   cl,SEARCH      ; Test if search or replace mode (highest two)
           jb    edup           ; No?  Ahead

           jmp   Find           ; Handles search/replace modes with jump
                                ;   back to FindRet later possible
edup:      int   16h            ; AH zero--get next keystroke in AX
FindRet:   test  al,al          ; Check if zero (CH zero and ES ok from Find)
           je    edisext        ; Yes?  Then extended key code

           cmp   ax,ESC_LONG    ; Check for Esc key (long codes checked so
           je    ToTypOvr       ;   Alt-key equivalents are normal text)

           cmp   ax,CR_LONG     ; Check for Enter key
           je    Enter

           cmp   ax,TAB_LONG    ; Check for Tab key
           je    TabKy

           cmp   ax,BS_LONG     ; Check for Backspace
           je    BkSpc

           jmp   PutCh          ; Handle other character as normal text

edisext:   xchg  ah,al          ; Zero AH and set AL to extended key code
           cmp   al,118         ; Check for control PgDn
           je    CPgDn

           cmp   al,132         ; Check for control PgUp
           je    CPgUp

           sub   al,3Bh         ; F1 key code
           jc    Badky          ; Lower key code?  Then return to Edit

           cmp   al,TBLSIZE     ; Number of table entries
           jnc   Badky          ; Higher key code?  Then return to Edit

           shl   ax,1           ; Each table entry is two bytes
           MOVE  bx,ax
           jmp   KeyTable[bx]   ; Handle key, then return to Edit, except
                                ;   for Exit, which pops return address
;-----------------------------------------------------------------------------
; Advance to next line with append if at bottom.  Beep if no room.
;-----------------------------------------------------------------------------
Enter:     call  TestCL         ; To Edit if not typeover/insert mode
           jb    entype         ; Typeover mode?  Then ahead
           jmp   Split          ; Else insert mode, so split line

entype:    call  Append         ; Bump CurLine and append blank line if needed
                                ; CL still mode or zeroed (Typeover)
;-----------------------------------------------------------------------------
; Move cursor to leftmost column.
;-----------------------------------------------------------------------------
Home:      push  cx             ; Pop mode CL just before TestCL
           xor   ax,ax          ; Leftmost column
           jmp   SHORT tkput
;-----------------------------------------------------------------------------
; Move cursor to start of trailing white space in line.
;-----------------------------------------------------------------------------
EndKy:     push  cx             ; Pop mode CL just before TestCL
           call  GtLnBgDX
           call  GetLen         ; Set CX to line length, less trailing spaces
           MOVE  ax,cx          ; CX to AX
           jmp   SHORT tkadj    ; Ahead to adjust if at edge
;-----------------------------------------------------------------------------
; Move right to next tabstop if in trailing white space, else to next word.
;-----------------------------------------------------------------------------
TabKy:     push  cx             ; Pop mode CL just before TestCL
           call  GetPos         ; Set ES:DI to cursor, CX to line bytes left
           mov   al,' '
           repne scasb          ; Skip past current word
           jne   tknorm         ; At line end?  Ahead to force column 127

           inc   cx             ; Else back up to found space
           dec   di
           push  cx             ; Save position in case past text
           repe  scasb          ; Skip spaces
           MOVE  ax,cx          ; CX to AX
           pop   cx
           je    tknorm         ; At line end?  Then do normal tab from
                                ;   saved position
           inc   ax
           xor   al,7Fh         ; Else point cursor to found word
           jmp   SHORT tkput

tknorm:    MOVE  ax,cx          ; CX to AX
           xor   al,7Fh         ; Flip bytes remaining to column number
           or    al,7           ; Tab bump
           inc   ax
tkadj:     test  al,al
           jns   tkput          ; Not 128?  Ahead

           dec   ax             ; Else reduce
tkput:     pop   cx             ; Restore mode CL
           call  TestCL2        ; To Edit if not typeover/insert mode
           mov   CurCol,ax
           ret
;-----------------------------------------------------------------------------
; Backspace.
;-----------------------------------------------------------------------------
BkSpc:     call  TestCL         ; To Edit if not typeover/insert mode
           jb    bktype         ; Typeover mode?  Ahead

           call  ShLeft         ; Move cursor left, if not at edge
           jnc   DelCh          ; Not at edge?  Then delete from right
           jmp   SHORT JoinPrev ; Else join to previous line

bktype:    call  ShLeft         ; Move cursor left, if not at edge
           mov   al,' '         ; ES:DI points to byte at cursor now
           jnc   icstore        ; Not at edge?  Store space and exit

           ret                  ; Else done
;-----------------------------------------------------------------------------
; Delete character, pulling rest of line leftward unless wrong mode.
;-----------------------------------------------------------------------------
DelKy:     call  TestCL         ; To Edit if not typeover/insert mode
           jb    dkup           ; Typeover?  Delete, but save for undo

           call  JoinNext       ; Else insert mode, so try join
           jnc   icout          ; Did join?  Then out, else do normal delete

dkup:      call  DelCh          ; Delete character, but save in buffer
           dec   CircPtrDB
           mov   si,CircPtr
           mov   CircBuf[si],ah
           ret
;-----------------------------------------------------------------------------
; Delete character, pulling rest of line leftward.  AH gets deleted byte.
;-----------------------------------------------------------------------------
DelCh:     call  GetPos         ; Get offset DI and count to move CX
           mov   si,di          ; Also, byte to delete in AH
           inc   si
           mov   al,' '
           jmp   SHORT icshift
;-----------------------------------------------------------------------------
; Insert space or last deleted word, pushing rest of line rightward.  No
; action if wrong mode.  Terminates early if no room in line.
;-----------------------------------------------------------------------------
inslp:     inc   CircPtrDB      ; Adjust pointer
insenter:  mov   si,CircPtr
           mov   al,CircBuf[si] ; Get last deleted character
           cmp   al,' '
           jne   insup          ; Not space?  Insert
           jcxz  insout         ; Space, other than first character,
                                ;   terminates loop (or if line full)
insup:     call  InsCh          ; Zeroes CX if ok--initial CL non-zero
           jnc   inslp          ; Ok?  Loop

insout:    ret

InsKy:     call  TestCL         ; To Edit if not typeover/insert mode
           je    insenter       ; Insert mode?  Enter word loop

           mov   al,' '         ; Space to insert, anticipating typeover
;-----------------------------------------------------------------------------
; Insert character AL, pushing rest of line right.  Set carry if no room.
;-----------------------------------------------------------------------------
InsCh:     push  ax             ; Save character
           call  GetPos         ; Get offset DI and count to move CX
           pop   ax             ; Restore character
           add   di,cx
           cmp   BYTE PTR es:[di],' '   ; Check if space at end of line
           je    icok           ; Room?  Then continue (carry clear also)

           jmp   Beep           ; Else beep and abort, setting carry

icok:      mov   si,di
           dec   si
           std
icshift:   push  es             ; ES set by GetPos above
           pop   ds
           rep   movsb          ; Shift bytes rightward (leftward if delete)
           push  cs
           pop   ds
           cld
icstore:   stosb                ; Store character at original position (or
icout:     ret                  ;   space at line end if delete)
;-----------------------------------------------------------------------------
; Join CurLine to previous line, deleting current if blank afterward.
;-----------------------------------------------------------------------------
JoinPrev:  mov   dx,CurLine
           test  dx,dx
           je    jclc           ; At first line?  Then out

           call  SetPtrs        ; Get AX:SI, ES:DI, and CX from DX (now DX-1)
           cmp   cx,bp
           je    jclc           ; Previous line full?  Then do nothing

           mov   CurCol,cx      ; Will be final cursor column
;-----------------------------------------------------------------------------
; Join CurLine (AX:SI) to previous (ES:DI) at column CX, possibly deleting.
;-----------------------------------------------------------------------------
Join:      push  cx             ; For later shift in current line
           push  si             ; Ditto
           add   di,cx          ; Adjust destination offset to start of spaces
           mov   dl,7Fh         ; BlankBlk flag and also used next
           xor   cl,dl
           inc   cx             ; CX now bytes to move
           mov   ds,ax          ; DS:SI is now source
           rep   movsb          ; Do move, advancing SI for use next
           mov   es,ax          ; Now ES and DS both current line
           pop   di
           pop   cx
           rep   movsb          ; Shift within current line, advancing DI
           push  cs
           pop   ds             ; Restore DS to stable state
           call  BlankBlk       ; Blank ES:DI to line end
           mov   cx,bp          ; Set CX to line length (128)
           sub   di,cx          ; Back up DI to line start
           mov   al,' '
           repe  scasb          ; Check if all spaces
           jne   joinup         ; No?  Then skip delete but change cursor line

           call  DelLine        ; Delete current line (input CX zero)
           jc    jclc           ; CurLine already decremented?  Then ahead

joinup:    dec   CurLine
jclc:      clc
jout:      ret
;-----------------------------------------------------------------------------
; Join next line to CurLine at CurCol, deleting next if blank afterward.  Set
; carry and do nothing if there is text at or past CurCol or if no next line.
;-----------------------------------------------------------------------------
JoinNext:  mov   dx,CurLine
           inc   dx
           cmp   BotLine,dx
           jc    jout           ; No next line?  Then out with set carry

           call  SetPtrs        ; Get AX:SI, ES:DI, and CX from DX (now DX-1)
           cmp   CurCol,cx      ; Test if text at or past current column
           jc    jout           ; Yes?  Then out with set carry

           inc   CurLine
           mov   cx,CurCol
           jmp   SHORT Join     ; Back
;-----------------------------------------------------------------------------
; Box paste current contents over work area at cursor, if confirmed.  Set
; typeover mode if successful, or user escapes question.
;-----------------------------------------------------------------------------
BoxPaste:  call  ChkPaste       ; Set AX/CX or pop exit to Edit

           push  ax             ; Save CurLine
           mov   MarkTop,ax     ; Set top mark at cursor
           mov   bx,BotLine
           inc   bx
           xchg  ax,cx
           call  BoxChk         ; Adjust AX if near bottom of edit area
           mov   MarkLen,ax     ; Set as mark length
           push  ax             ; Save adjusted MarkLen
           mov   cx,CurCol
           mov   MarkLeft,cx    ; Set left mark at cursor
           mov   ax,PasteWid
           mov   bx,bp
           call  BoxChk         ; Adjust AX if near right of edit area
           mov   MarkWid,ax     ; Set as mark width
           call  DispScr        ; Display highlighted box
           mov   bl,Q_PASTE
           call  GetYN          ; Ask user if paste
           pop   cx             ; MarkLen
           pop   dx             ; CurLine
           jnc   bpdone         ; No or Esc?  Then revert to typeover and exit

           mov   si,OFFSET Filebuf
bploop:    push  cx
           call  GetLnBeg       ; Point ES:DI to line DX
           add   di,CurCol      ; Bump to paste column
           mov   cx,MarkWid     ; Paste width, as ajusted above
           push  si
           rep   movsb
           pop   si
           add   si,bp          ; Line size in bytes
           inc   dx             ; Bump to next line
           pop   cx
           loop  bploop

bpdone:    jmp   ToTypOvr       ; Set current mode to TYPEOVER and exit
;-----------------------------------------------------------------------------
; Insert blank line, block, or box, depending on mode.  Beep if no room.
;-----------------------------------------------------------------------------
InsLn:     cmp   cl,LINEMARK
           je    Paste          ; Line mark mode?  Then ahead

           cmp   cl,BOXMARK
           je    BoxPaste       ; Box mark mode?  Then back else fall...
;-----------------------------------------------------------------------------
; Insert blank line before CurLine if right mode and room.  Set carry if not.
;-----------------------------------------------------------------------------
InsLnIf:   cmp   cl,INSERT + 1
           cmc
           jc    pstout         ; Not typeover or insert mode?  Do nothing
;-----------------------------------------------------------------------------
; Insert blank line before CurLine.  Set carry and beep if no room.
;-----------------------------------------------------------------------------
InsLine:   mov   cl,1           ; Number of lines to shift--CH zero from Edit
           call  ShftUpBf       ; Shift up one line from CurLine
           jc    pstout         ; No room?  Then abort

           jmp   BlankDX1       ; Blank out CurLine, clearing carry
;-----------------------------------------------------------------------------
; Undelete line, if typeover or insert mode.  Beep if no room.
;-----------------------------------------------------------------------------
UnDelLn:   call  InsLnIf        ; Insert blank line if right mode and room
           jc    pstout         ; No room or not right mode?  Then out

           mov   cl,1           ; CH zero and DX current line from BlankDX1
           mov   si,OFFSET SaveLine
           jmp   SHORT FromNear ; Copy SaveLine to line DX, clearing carry
;-----------------------------------------------------------------------------
; Set AX/CX to CurLine/PasteLen or pop exit to Edit.  Use carefully.
;-----------------------------------------------------------------------------
ChkPaste:  mov   ax,MarkLen
           mul   MarkWid
           test  ax,ax          ; Check if area currently marked
           jne   pstpop         ; Yes?  Pop exit to Edit

           mov   cx,PasteLen
           jcxz  pstpop         ; No paste length?  Pop exit to Edit

           push  CurLine
pstpop:    pop   ax             ; Pop return address or CurLine
pstout:    ret
;-----------------------------------------------------------------------------
; Paste current contents.  Set typeover mode if successful.
;-----------------------------------------------------------------------------
Paste:     call  ChkPaste       ; Set AX/CX or pop exit to Edit

           MOVE  dx,ax          ; CurLine
           push  dx
           push  cx             ; PasteLen
           call  ShftUpBf       ; Move work area up CX lines from CurLine
           pop   cx
           pop   dx
           jc    pstout         ; No room?  Then exit

           call  ToTypOvr             ; Set current mode to TYPEOVER
           mov   si,OFFSET FileBuf    ; Also serves as paste buffer
;-----------------------------------------------------------------------------
; Move CX lines from near data to work area starting at line DX.
;-----------------------------------------------------------------------------
FromNear:  push  cx             ; Save loop counter
           call  GetLnBeg       ; Point ES:DI to line DX
           mov   cx,bp          ; 128
           rep   movsb          ; Move line
           inc   dx
           pop   cx
           loop  FromNear

           ret                  ; Carry clear from GetLnBeg
;-----------------------------------------------------------------------------
; Toggle current mode with cyclic wrap.
;-----------------------------------------------------------------------------
Mode:      inc   cx             ; Mode passed in CL to all dispatch routines
           and   cl,7           ; Wrap back to zero possibly
           mov   CurMode,cl
           mov   ax,CurLine
           mov   MarkLine,ax    ; Set MarkLine and MarkCol in case toggle is
           mov   ax,CurCol      ;   to one of the mark modes
           mov   MarkCol,ax
           ret
;-----------------------------------------------------------------------------
; Move cursor down, performing indicated mode function.
;-----------------------------------------------------------------------------
Down:      cmp   cl,DRAW
           jne   dnup           ; Not draw mode?  Then skip ahead

           mov   bh,BOT_EDGE    ; Direction for drawing
           call  BarDraw
dnup:      call  ShDown         ; Move cursor, returning ES:DI and AX
           jc    dout           ; At edge?  Then done

reptest:   cmp   CurMode,REPEAT
           jne   dout           ; Not repeat mode?  Then done

           mov   al,ah          ; Else fetch previous byte
           stosb                ;   and store under cursor
dout:      ret
;-----------------------------------------------------------------------------
; Move cursor up, performing indicated mode function.
;-----------------------------------------------------------------------------
Up:        cmp   cl,DRAW
           jne   upup           ; Not draw mode?  Then skip ahead

           mov   bh,TOP_EDGE    ; Direction for drawing
           call  BarDraw
upup:      call  ShUp           ; Move cursor, returning ES:DI and AX
uptest:    jnc   reptest        ; Not at edge?  Then check for repeat mode

           ret
;-----------------------------------------------------------------------------
; Move cursor left, performing indicated mode function.
;-----------------------------------------------------------------------------
Left:      cmp   cl,DRAW
           jne   lfup           ; Not draw mode?  Then skip ahead

           mov   bh,LF_EDGE     ; Direction for drawing
           call  BarDraw
lfup:      call  ShLeft         ; Move cursor, returning ES:DI and AX
           jmp   SHORT uptest
;-----------------------------------------------------------------------------
; Move cursor right, performing indicated mode function.
;-----------------------------------------------------------------------------
Right:     cmp   cl,DRAW
           jne   rtup           ; Not draw mode?  Then skip ahead

           mov   bh,RT_EDGE     ; Direction for drawing
           call  BarDraw
rtup:      call  ShRight        ; Move cursor, returning ES:DI and AX
           jmp   SHORT uptest
;-----------------------------------------------------------------------------
; Move cursor up one page.
;-----------------------------------------------------------------------------
PgUp:      call  TestCL2        ; To Edit if not typeover/insert mode
           mov   ax,Curline
           sub   ax,ScrRows     ; Back current line up one screen
           jnb   puup1          ; Not before line zero?  Ahead

           xor   ax,ax          ; Else zero
puup1:     mov   CurLine,ax     ; Save
           mov   ax,ScrTop
           sub   ax,ScrRows     ; Back window up one screen
           jnb   pdup2          ; Not before line zero?  Ahead

           xor   ax,ax          ; Else zero
           jmp   SHORT pdup2    ; Ahead to save
;-----------------------------------------------------------------------------
; Move cursor down one page.
;-----------------------------------------------------------------------------
PgDn:      call  TestCL2        ; To Edit if not typeover/insert mode
           mov   ax,CurLine
           add   ax,ScrRows     ; Bump current line down one screen
           cmp   ax,BotLine
           jna   pdup1          ; In range?  Ahead

           mov   ax,Botline     ; Else set to bottom line
pdup1:     mov   CurLine,ax     ; Save
           mov   ax,ScrTop
           add   ax,ScrRows     ; Bump window down one screen
           cmp   ax,BotLine
           jna   pdup2          ; In range?  Ahead

           mov   ax,BotLine     ; Else set to bottom line
pdup2:     mov   ScrTop,ax      ; Save
           ret
;-----------------------------------------------------------------------------
; Set MarkLeft/MarkWid/MarkTop/MarkLen from CurCol/MarkCol/CurLine/MarkLine
; and fall through to adjust ScrTop/ScrLeft.
;-----------------------------------------------------------------------------
SetMarks:  mov   dx,MarkCol
           mov   ax,CurCol
           sub   ax,dx          ; Plus or minus width
           jnc   smposwid       ; Plus?  Then ahead

           neg   ax             ; Else adjust both
           sub   dx,ax
           inc   dx

smposwid:  mov   MarkLeft,dx    ; Set column marks
           mov   dx,MarkLine
           mov   MarkWid,ax
           mov   ax,CurLine
           sub   ax,dx          ; Plus or minus width, unless too big
           pushf                ; Save carry in case too big
           jnc   smposlen       ; Plus?  Then ahead

           neg   ax             ; Else flip
smposlen:  cmp   ax,PASTEMAX    ; Check if too big
           jna   smlenok        ; No?  Then have mark length

           mov   ax,PASTEMAX
smlenok:   popf                 ; Need carry to get top now
           jnc   smtopok        ; Was cursor below MarkLine?  Then ahead

           sub   dx,ax          ; Else back up from MarkLine to get top
           inc   dx
smtopok:   mov   MarkLen,ax
           mov   MarkTop,dx
;-----------------------------------------------------------------------------
; Adjust window coordinates ScrTop/ScrLeft.
;-----------------------------------------------------------------------------
AdjWin:    mov   ax,ScrTop
           mov   cx,ScrRows
           mov   dx,CurLine
           call  Adjust         ; Adjust ScrTop AX
           mov   ScrTop,ax
           mov   ax,ScrLeft
           mov   cl,SCRCOLS     ; CH zero from ScrRows
           mov   dx,CurCol
           call  Adjust         ; Adjust ScrLeft AX
           mov   ScrLeft,ax
awout:     ret
;-----------------------------------------------------------------------------
; Adjust ScrTop/ScrLeft AX from CurLine/CurCol DX and ScrRows/SCRCOLS CX.
;-----------------------------------------------------------------------------
Adjust:    mov   bx,ax          ; Top/left of screen display (no MOVE)
           cmp   dx,bx          ; Check if currently above/left of screen
           jb    adjchg         ; Yes?  Then make current row/column top/left

           add   bx,cx          ; Just off bottom/right of screen display
           cmp   dx,bx
           jb    awout          ; Currently on screen?  Then no change

           sub   dx,cx          ; Else make current row/column bottom/right
           inc   dx
adjchg:    MOVE  ax,dx
           ret
;-----------------------------------------------------------------------------
; Toggle text (low) color setting if insert mode or toggle 25/50 lines if
; typeover mode.  File switch needed to change other colors.
;-----------------------------------------------------------------------------
Video:     call  TestCL         ; To Edit if not typeover/insert mode
           jne   SetVid         ; Typeover?  Flip 25/50

SetColr:   mov   al,LoColor     ; Else bump low colors
scloop:    inc   ax             ; Bump color
           and   al,7Fh         ; Insure no blink
           mov   dl,al
           mov   cl,4
           ror   dl,cl          ; Exchange nibbles in DL
           cmp   dl,al
           je    scloop         ; Foreground/background match?  Loop once

           mov   LoColor,al     ; Save colors
           ret
;-----------------------------------------------------------------------------
; Flip 25/50 line VGA and insure DOS/ANSI cursor ok on program exit.  Assume
; CH zero on entry.
;-----------------------------------------------------------------------------
SetVid:    mov   ax,3           ; Color text mode 3
           int   10h            ; Set mode, CX ok
           mov   ax,0500h
           int   10h            ; Set page zero, CX ok
           mov   cl,24
           cmp   ScrRowsDB,cl
           jne   svnorm         ; Not 24 previously?  Then want 25-line mode

           mov   ax,1112h
           xor   bx,bx
           int   10h            ; Load ROM 8x8 font, CX ok
           mov   cl,49
svnorm:    mov   ScrRowsDB,cl   ; Current screen height--CX ready for loop

           mov   dl,LOW OFFSET AnsiCls
           call  DOSDisp        ; In case ANSI.SYS, do CLS/HOME

lfloop:    mov   dl,LF          ; DOS LF's now force DOS or ANSI cursor
           mov   ah,2           ;   to bottom for final exit--needed
           int   21h            ;   sometimes if switch to 50-line mode
           loop  lfloop

           ret                  ; Later refresh clears possible ANSI debris
;-----------------------------------------------------------------------------
; Computes buffer offset ES:DI of line DX, assumed in range.  Carry cleared.
;-----------------------------------------------------------------------------
GtLnBgDX:  mov   dx,CurLine
GetLnBeg:  push  cx             ; Save register
           mov   cl,7           ; 128 lines per segment and 128 bytes per
           mov   di,dx          ;   line
           shr   di,cl          ; Shift out line number to get segment index
           shl   di,1           ; Adjust for word offset
           mov   es,SegList[di] ; Set ES to table segment--Case needs CS:
           mov   di,dx
           and   di,127         ; Isolate line number within segment
           shl   di,cl          ; Set DI to offset, clearing carry
           pop   cx             ; Restore register
           ret                  ; Return ES:DI, DX preserved
;-----------------------------------------------------------------------------
; Clear screen and home cursor.  CX zeroed and carry cleared.
;-----------------------------------------------------------------------------
ClearScr:  xor   di,di          ; Screen offset, clearing carry
           call  SetCsrDI       ; Home cursor, preserving carry, AX, DI
           mov   cx,SCRCOLS*50  ; Enough for 50-line and 25-line mode
           jmp   SHORT DispSpcs ; Clears carry
;-----------------------------------------------------------------------------
; Inverse clear status line.  Assume DS points to near data.
;-----------------------------------------------------------------------------
ClrStat:   xor   di,di          ; Video offset
           mov   cx,SCRCOLS
;-----------------------------------------------------------------------------
; Display CX spaces at screen offset DI.  Carry flags attribute.
;-----------------------------------------------------------------------------
DispSpcsX: stc                  ; Inverse video
DispSpcs:  push  ax
           mov   al,' '         ; Flag space display
           jmp   SHORT dbspc
;-----------------------------------------------------------------------------
; Inverse display Esc at video offset DI.  Assume CH zero.
;-----------------------------------------------------------------------------
DispEsc:   mov   si,OFFSET EscText
           mov   cl,3
           jmp   SHORT DispBlkX
;-----------------------------------------------------------------------------
; Inverse display wildcard message at left of status line.  Assume CH zero.
;-----------------------------------------------------------------------------
DispWild:  mov   si,OFFSET WildText
           mov   cl,6
;-----------------------------------------------------------------------------
; Display CX bytes from DS:SI at screen offset DI.  Carry flags attribute.
;-----------------------------------------------------------------------------
DispBlkXX: xor   di,di          ; Start of status line
DispBlkX:  stc                  ; Inverse video
DispBlk:   push  ax
           mov   al,0           ; Flag block display
dbspc:     push  es
           mov   es,VidSeg      ; SS:[BP+...] ok for next three
           mov   ah,LoColor     ; Set for normal attribute
           jnc   dbtest         ; No input carry?  Then skip ahead

           mov   ah,HiColor     ; Else get status attributes
dbtest:    test  al,al          ; Test space or block display, clearing carry
           jne   dbup           ; Spaces?  Ahead
           jcxz  dbup           ; Block, but no bytes?  Ahead

dbloop:    lodsb
           stosw
           loop  dbloop

dbup:      rep   stosw          ; Display spaces, or nothing if block
           pop   es             ; Restore registers
           pop   ax
           ret                  ; Carry clear
;-----------------------------------------------------------------------------
; Display line DX, assumed within ScrRows of ScrTop.  Need CH zero.
;-----------------------------------------------------------------------------
DspCurLn:  mov   dx,CurLine
DispLine:  mov   cl,SCRCOLS     ; Input CH zero
           mov   ax,dx
           sub   ax,ScrTop      ; Get line distance from top
           inc   ax             ; Adjust for top status line
           mul   cl
           shl   ax,1           ; Double screen offset for attribute bytes
           MOVE  di,ax
           cmp   BotLine,dx     ; Check if line in range
           jc    DispSpcs       ; No?  Then display CX inverse spaces

           push  di
           call  GetLnBeg       ; Get ES:DI for line DX
           mov   si,di
           add   si,ScrLeft     ; Adjust for margin offset
           pop   di
                                ; Set widths CX/BX/AX = normal/inverse/normal
           push  cx             ; Still SCRCOLS--default normal display
           xor   bx,bx          ; Default inverse display width
           mov   al,CurMode
           cmp   al,LINEMARK
           je    dlcont         ; Line mark mode?  Then continue check

           cmp   al,BOXMARK
           jne   dldeflts       ; Neither mark mode?  Then use defaults

dlcont:    mov   ax,MarkTop
           cmp   ax,dx
           ja    dldeflts       ; Line DX above marked zone?  Use defaults

           add   ax,MarkLen
           cmp   dx,ax
           jae   dldeflts       ; Line DX below marked zone?  Use defaults

           xchg  bx,cx          ; Set for full inverse display
           cmp   CurMode,LINEMARK
           je    dldeflts       ; Line mark?  Then exit with inverse display

           MOVE  ax,bx          ; Screen columns for below
           mov   bx,MarkWid     ; Usual box inverse display width
           mov   cx,MarkLeft
           sub   cx,ScrLeft     ; Usual left-of-box normal display width
           jb    dlisleft       ; Mark start left of screen?  Then ahead

           sub   ax,cx          ; Screen columns less left-of-box width
           cmp   bx,ax
           jna   dldeflts       ; Mark end on-screen?  Then out

           MOVE  bx,ax          ; Else shorten inverse display width
           jmp   SHORT dldeflts

dlisleft:  add   bx,cx
           xor   cx,cx
dldeflts:  pop   ax             ; Set final normal length--AX now 80
           sub   ax,bx
           sub   ax,cx          ; CX/BX/AX ready--carry clear also

           push  es             ; Move line segment into DS
           pop   ds
           call  DispBlk        ; Write CX bytes to video memory (carry clear)
           mov   cx,bx
           call  DispBlkX       ; Write CX inverse bytes to video memory
           MOVE  cx,ax          ; Carry clear
           call  DispBlk        ; Write CX bytes to video memory (carry clear)
           jmp   SHORT mllout   ; Restore DS to default near data segment
;-----------------------------------------------------------------------------
; Move line DX to line AX, both in work area.  Assume both in range.
;-----------------------------------------------------------------------------
MoveLnLn:  call  GetLnBeg       ; Point ES:DI to source line
           mov   si,di          ; Set SI to source offset
           push  es
           xchg  dx,ax          ; Get ready for destination
           call  GetLnBeg       ; Point ES:DI to destination line
           xchg  dx,ax          ; Restore AX and DX for exit
           pop   ds             ; Set DS to source segment
           push  cx
           mov   cx,bp          ; 128 bytes
           rep   movsb          ; Move line
           pop   cx
mllout:    push  cs             ; Restore default near data segment
           pop   ds
           ret                  ; Carry clear from GetLnBeg
;-----------------------------------------------------------------------------
; Display screen.
;-----------------------------------------------------------------------------
DispScr:   mov   dx,ScrTop      ; First line to display
           mov   cx,ScrRows     ; Number of lines to display
dsloop:    push  cx
           push  dx
           call  DispLine       ; CH zero
           pop   dx
           inc   dx             ; Bump line number
           pop   cx
           loop  dsloop         ; Display ScrRows lines

           ret
;-----------------------------------------------------------------------------
; Set ES:DI to current position, AL/AH to border flags/byte, and CX to count
; of bytes right of position in line (0-127).  Carry cleared.
;-----------------------------------------------------------------------------
GetPos:    push  dx             ; Preserve all but output registers
           call  GtLnBgDX       ; Set ES:DI to DX line start
           mov   cx,CurCol
           add   di,cx          ; Buffer offset of byte under cursor
           mov   ah,es:[di]     ; Buffer byte as output
           mov   al,0           ; Zero before including flags
           neg   cx             ; Stays zero if leftmost column
           jne   gpnotlft       ; No?  Ahead

           or    al,LF_EDGE     ; Else mark
gpnotlft:  add   cx,bp          ; Add 128
           dec   cx             ; CX now number of bytes right in line
           jne   gpnotrht       ; Not rightmost byte?  Ahead

           or    al,RT_EDGE     ; Else mark
gpnotrht:  cmp   BotLine,dx     ; Test if bottom line
           jne   gpnotbot       ; No?  Ahead

           or    al,BOT_EDGE    ; Else mark
gpnotbot:  test  dx,dx          ; Test if top line, clearing carry
           jne   gpnottop       ; No?  Ahead

           or    al,TOP_EDGE    ; Else mark, also clearing carry
gpnottop:  pop   dx             ; Restore DX
           ret
;-----------------------------------------------------------------------------
; Scan ES:DI (length BX) for match to DS:SI (length CX).  Case sensitivity
; depends on CaseFlag (double duty from boxmark mode).  If no match, set carry
; else set AX to distance from match start to line end.
;-----------------------------------------------------------------------------
ScanLine:  sub   bx,cx          ; Reduce outer loop iterations to account
                                ;   for (shorter) string
           jc    slnomtch       ; String longer than scan line?  Then exit

           inc   bx             ; BX greater than zero now
           dec   di
sloutlp:   inc   di             ; Point to next scan line byte
           push  si             ; Save pointers pending substring check
           push  di
           push  cx
slinlp:    mov   ah,es:[di]     ; Fetch next line byte
           inc   di
           lodsb                ; Fetch next string byte (upper case)
           cmp   al,WILDCARD
           je    slmatch        ; Wildcard?  Then continue string check

           cmp   ah,al
           je    slmatch        ; Match?  Then continue string check

           cmp   CaseFlag,ch    ; CH zero
           jne   slnext         ; Case-sensitive?  Exit inner loop

           call  ToLower        ; AL to lower case
           jc    slnext         ; Not letter?  Exit inner loop

           cmp   ah,al
           jne   slnext         ; No match?  Then exit inner loop

slmatch:   loop  slinlp         ; Not end of string?  Then continue inner loop

           pop   cx             ; Else string match found
           pop   di
           pop   si
           mov   ax,bx          ; No MOVE--need BX (??)
           add   ax,cx          ; Also clears carry
           dec   ax             ; AX is distance to line end from match start
           ret                  ; Clear carry flags match

slnext:    pop   cx             ; Restore pointers after unsuccessful
           pop   di             ;   substring check
           pop   si
           dec   bx             ; Check for end of line
           jne   sloutlp        ; No?  Then loop

slnomtch:  stc
           ret                  ; Else return set carry
;-----------------------------------------------------------------------------
; These move cursor and return position ES:DI and old/new byte in AH/AL.  At
; border, carry set, cursor unchanged, and ES:DI is (old) offset.  CH zeroed.
;-----------------------------------------------------------------------------
ShLeft:    call  GetPos         ; All GetPos calls zero CH for exit
           test  al,LF_EDGE     ; Clears carry also
           jne   slnomtch

           dec   di
           dec   CurCol
shok:      mov   al,es:[di]
           ret

ShRight:   call  GetPos
           test  al,RT_EDGE     ; Clears carry also
           jne   slnomtch

           inc   di
           inc   CurCol
           jmp   SHORT shok

ShUp:      call  GetPos
           test  al,TOP_EDGE
           jne   slnomtch

           dec   CurLine
shupok:    MOVE  dx,ax          ; Save old byte in DH
           call  GetPos         ; DX ok--carry cleared too
           mov   al,ah          ; New byte to AL
           mov   ah,dh          ; Old byte to AH
           ret

ShDown:    call  GetPos
           test  al,BOT_EDGE
           jne   slnomtch

           inc   CurLine
           jmp   SHORT shupok
;-----------------------------------------------------------------------------
; Put or insert character and, if not repeat mode, advance cursor.
;-----------------------------------------------------------------------------
PutCh:     cmp   cl,INSERT
           jb    pcnormal       ; Typeover?  Then do normal put
           ja    pcchkrpt       ; Neither typeover nor insert?  Test repeat

           call  InsCh          ; Else insert mode, so insert character
           jnc   ShRight        ; Room?  Move cursor and done

           ret                  ; Else no room

pcchkrpt:  cmp   cl,REPEAT
           jne   pcout          ; Neither typeover nor repeat mode?  Then out

           push  ax             ; Else put character, without advancing
           call  GetPos         ; Set ES:DI to current location
           jmp   SHORT pcup     ; Skip cursor move

pcnormal:  push  ax             ; Save AL
           call  ShRight        ; Move cursor, returning ES:DI
           dec   di             ; Point back to place to put byte
           jnc   pcup           ; Did cursor (and DI) move?  Then ahead

           inc   di             ; Else adjust
pcup:      pop   ax             ; Restore AL
           stosb                ; Put character
pcout:     ret
;-----------------------------------------------------------------------------
; Scan Bars for match to byte AL and with direction link in AH low nibble.
; If both conditions met, update BH with direction link in AH high nibble.
; Assume CH zero from initial and succeeding GetPos calls in BarDraw.  At end,
; get byte AL at CurLine+DX (for two of four DoubScan calls).
;-----------------------------------------------------------------------------
DoubScan:  push  es             ; Save registers
           push  di
           push  cs             ; Set ES to Bars segment
           pop   es
           call  SetBarSI       ; Set SI/CL
           mov   di,si
           inc   cx
           repne scasb          ; Scan for match to AL, getting CL to 0 - 15
           and   cl,ah          ; Check for match to low nibble of AH
           je    dsnomtch       ; No match?  Then no update

           mov   cl,4           ; Else shift high nibble of AH down
           shr   ah,cl
           or    bh,ah          ; Link result to BH
dsnomtch:  add   CurLine,dx
           call  GetPos         ; Interested only in AH, DX/BX preserved
           sub   CurLine,dx
           mov   al,ah          ; Garbage for two of four calls
           pop   di
           pop   es
           ret
;-----------------------------------------------------------------------------
; Given direction BH, draw bar byte at cursor.
;-----------------------------------------------------------------------------
BarDraw:   call  GetPos         ; Get position ES:DI and flags/byte AL/AH
           mov   bl,al          ; Save boundary flags in BL
           mov   al,es:[di-1]   ; Fetch left byte (ok if off edge)
           mov   ah,RT_EDGE OR (LF_EDGE SHL 4)
           cwd                  ; Zero DX (LF_EDGE is under 8)
           call  DoubScan       ; Adjust BH if left byte has right link
           mov   al,es:[di+1]   ; Fetch right byte (ok if off edge)
           mov   ah,LF_EDGE OR (RT_EDGE SHL 4)
           inc   dx             ; Now 1 for DoubScan
           call  DoubScan       ; Adjust BH if right byte has left link then
                                ;   get bottom byte in AL (ok if off edge)
           mov   ah,TOP_EDGE OR (BOT_EDGE SHL 4)
           cwd                  ; Minus 1 (BOT_EDGE is 8) for DoubScan
           call  DoubScan       ; Adjust BH if bottom byte has top link then
                                ;   get top byte in AL (ok if off edge)
           mov   ah,BOT_EDGE OR (TOP_EDGE SHL 4)
           call  DoubScan       ; Adjust BH if top byte has bottom link
           xor   bl,0Fh         ; Flip four boundary bits
           and   bl,bh          ; Undo any boundary link (reason for ok's)
           xor   bl,0Fh         ; Map links to 0 - 15 index
           xor   bh,bh
           mov   al,[si+bx]     ; SI from DoubScan points to bar data
           stosb                ; Store in main buffer
           ret
;-----------------------------------------------------------------------------
; Shift work buffer up CX lines from CurLine.  Beep and carry set if no room.
;-----------------------------------------------------------------------------
ShftUpBf:  mov   ax,BotLine     ; Source
           mov   dx,ax
           add   ax,cx          ; Destination
subretry:  cmp   LineMax,ax     ; Check if in range
           jnc   subok          ; Yes?  Then continue on ahead

           call  AddSeg         ; Else add another segment, updating LineMax
           jnc   subretry       ; Allocation ok?  Then retry range check

           ret                  ; Else abort with set carry

subok:     mov   Botline,ax     ; Update bottom line
           mov   cx,dx
           sub   cx,Curline
           inc   cx             ; Number of lines to move
subloop:   call  MoveLnLn       ; Move line DX to line AX
           dec   ax
           dec   dx
           loop  subloop

           ret                  ; Carry clear from MoveLnLn
;-----------------------------------------------------------------------------
; Move CX lines from work area at line DX to near data.  CX zeroed.
;-----------------------------------------------------------------------------
ToNear:    push  cx             ; Save loop counter
           push  di
           call  GetLnBeg       ; Point ES:DI to line DX
           mov   si,di          ; Will convert to DS:SI
           pop   di
           mov   cx,bp          ; 128
           call  SwapMove       ; Swap ES/DS, move, swap ES/DS
           inc   dx             ; Next line
           pop   cx             ; Restore loop counter
           loop  ToNear

           ret                  ; AX preserved
;-----------------------------------------------------------------------------
; If marked box, set BX/CX/DX/DI, otherwise pop-exit.  Use carefully.
;-----------------------------------------------------------------------------
MarkChk:   cmp   cl,BOXMARK
           jne   mcpop          ; Not box mark mode?  Then pop-exit

           mov   cx,MarkWid
           jcxz  mcpop          ; No box width?  Then pop-exit

           mov   bx,cx
MarkSet:   mov   cx,MarkLen     ; Entry from Cut
           jcxz  mcpop          ; No box length?  Then pop-exit

           mov   di,OFFSET FileBuf
           push  MarkTop
mcpop:     pop   dx             ; MarkTop or pop-exit to Edit
           ret
;-----------------------------------------------------------------------------
; Cut box from work area to paste buffer, blanking box and end of paste lines.
; Set typeover mode if successful.
;-----------------------------------------------------------------------------
BoxCut:    call  MarkChk        ; Set BX/CX/DX/DI if ok--may exit to Edit

           mov   ax,MarkLeft
           mov   PasteLen,cx    ; Set parameters for later paste after exit
           mov   PasteWid,bx
           mov   CurLine,dx     ; Set CurLine for exit
           mov   CurCol,ax      ; Set CurCol for exit
           push  cx             ; Save paste length and buffer offset
           push  di
           xchg  cl,ch          ; PasteLen is less than 256
           shr   cx,1           ; Lines are 128 bytes--ES ok from Edit
           call  Spaces         ; Blank out PasteLen lines in paste buffer
           pop   di             ; AX preserved
           pop   cx

bcloop:    push  cx             ; Save line count
           push  di             ; Save paste buffer pointer
           call  GetLnBeg       ; Point ES:DI to line DX, AX/BX/DX ok
           mov   si,di
           add   si,ax          ; Add offset to left mark
           pop   di             ; Refresh paste buffer pointer
           push  di
           mov   cx,bx          ; Paste width
           push  si             ; Save work area offset
           call  SwapMove       ; Swap ES/DS, move, swap ES/DS
           mov   cx,bx
           pop   di             ; Point to left mark on line again
           call  Spaces         ; Blank out line piece in work area
           pop   di             ; Restore paste buffer offset
           add   di,bp          ; Bump to next paste line start
           inc   dx             ; Bump work area line
           pop   cx             ; Fetch line counter
           loop  bcloop

           jmp   ToTypOvr       ; Set current mode to TYPEOVER and exit
;-----------------------------------------------------------------------------
; Delete line, cut line block, or cut box, depending on mode.
;-----------------------------------------------------------------------------
DelLn:     cmp   cl,LINEMARK
           je    Cut            ; Line mark mode?  Then ahead
           ja    BoxCut         ; Box mark mode or higher?  Then back

           mov   dx,CurLine     ; Else insert/typeover mode
           cmp   BotLine,dx
           ja    dlsvdel        ; Not last line?  Then can delete

           test  dx,dx
           je    dclout         ; Only line?  Then do nothing

dlsvdel:   mov   cl,1           ; First, save line DX in undelete buffer
           mov   di,OFFSET SaveLine
           call  ToNear         ; Zeroes CX
;-----------------------------------------------------------------------------
; Delete current line.  Set carry if CurLine was decremented.  Need CX zero.
;-----------------------------------------------------------------------------
DelLine:   inc   cx             ; Now 1
           jmp   SHORT ShftDnBf ; Shift everything above CurLine down one line
;-----------------------------------------------------------------------------
; Cut marked lines from work area.  Set typeover mode if successful.  Note
; that deallocation does not take place here.  That occurs only on save/abort.
;-----------------------------------------------------------------------------
Cut:       call  MarkSet        ; Set CX/DX/DI--may exit to Edit

           mov   CurLine,dx     ; Will be final position, unless past bottom
           mov   PasteLen,cx    ; Active paste buffer size for later paste
           mov   PasteWid,bp    ; Full width 128
           push  cx
           call  ToNear         ; Move CX lines from DX to paste buffer
           pop   cx
           call  ToTypOvr       ; Set current mode to TYPEOVER and fall
;-----------------------------------------------------------------------------
; Shift down CX lines above CurLine.  Set carry if CurLine adjusted.
;-----------------------------------------------------------------------------
ShftDnBf:  mov   ax,Curline     ; Destination (may adjust CurLine at end)
           mov   dx,ax
           add   dx,cx          ; Source
           mov   cx,BotLine
           sub   cx,dx
           inc   cx             ; Number of lines to move
           jcxz  sdbup          ; Is source one past bottom?  Then ahead

sdbloop:   call  MoveLnLn       ; Move line DX to line AX
           inc   ax
           inc   dx
           loop  sdbloop

sdbup:     dec   ax
           mov   BotLine,ax     ; Update bottom line
           cmp   ax,CurLine
           jnc   dclout         ; CurLine not (one) past Botline?  Then done

deccurln:  dec   CurLine
dclout:    ret
;-----------------------------------------------------------------------------
; Bump CurLine, appending blank line, if needed.  If no room, set carry and
; restore CurLine.  If carry clear and equality flagged, line was appended.
;-----------------------------------------------------------------------------
Append:    mov   dx,CurLine
           inc   CurLine        ; Bump CurLine, returning old line in DX
           mov   ax,BotLine     ; Assume CurLine at most one past BotLine
           cmp   ax,dx
           clc                  ; Needed, since DX may be -1 on first call
           jne   dclout         ; CurLine not past Botline?  Then do nothing

           cmp   LineMax,ax     ; Assume LineMax greater equal BotLine
           ja    apok           ; Room in current segment?  Ahead

           call  AddSeg         ; Else add segment, adjusting LineMax
           jc    deccurln       ; No room?  Adjust CurLine and exit
                                ;   with set carry
apok:      inc   BotLine        ; Adjust BotLine to match CurLine
;-----------------------------------------------------------------------------
; Blank out line DX+1, clearing carry and setting equality.
;-----------------------------------------------------------------------------
BlankDX1:  inc   dx             ; Restore DX to CurLine
           call  GetLnBeg       ; Point ES:DI to start of line DX
;-----------------------------------------------------------------------------
; Store 128 spaces at ES:DI, clearing carry, setting equality, zeroing CX.
;-----------------------------------------------------------------------------
Space128:  mov   cx,bp          ; Byte-size of line--128
           jmp   SHORT SpacesCL ; Blank line, clearing carry, setting equality
;-----------------------------------------------------------------------------
; Split CurLine at CurCol, adjusting cursor.  Beep if no room.
;-----------------------------------------------------------------------------
Split:     call  Append         ; Bump CurLine and append blank line if needed
           jc    dclout         ; At end and no room?  Then out
           je    splitup        ; Line was appended?  Then up

           call  InsLine        ; Else insert blank line before CurLine
           jc    deccurln       ; No room?  Then adjust CurLine and exit

splitup:   dec   dx             ; Point DX to line before blanked CurLine
           call  GetLnBeg       ; Point ES:DI to its start
           xchg  cx,CurCol      ; Zero CurCol for exit (from Append/InsLine)
           add   di,cx
           mov   si,di          ; Source offset
           neg   cx             ; 0 to -127
           add   cx,bp          ; CX holds move length 1 to 128
           push  es             ; Save for DS later
           inc   dx
           call  GetLnBeg       ; Point ES:DI to blank line (destination)
           pop   ds             ; Now DS:SI points to source
           push  cx             ; Save three registers for blanking later
           push  si
           push  ds
           rep   movsb          ; Move tail of previous line to blank line
           pop   es             ; Restore three registers
           pop   di
           pop   cx
           push  cs             ; Bring DS back to stable program value
           pop   ds
           jmp   SHORT Spaces   ; Blank tail of previous line and exit
;-----------------------------------------------------------------------------
; Blank out ES:DI to next DL (7/127) boundary, advancing DI and zeroing CX.
;-----------------------------------------------------------------------------
BlankBlk:  mov   cx,di
           and   cl,dl
           neg   cl
           add   cl,dl
           inc   cx             ; Fall through
;-----------------------------------------------------------------------------
; Store CX spaces at ES:DI, advancing DI and zeroing CX.  SpacesCL also
; clears carry and sets equality.
;-----------------------------------------------------------------------------
SpacesCL:  xor   ch,ch          ; Clear carry/equality too
Spaces:    push  ax
           mov   al,' '
           rep   stosb
           pop   ax
           ret
;-----------------------------------------------------------------------------
; Get yes/no response to status line question BL.  Set carry if yes.  Clear
; carry and not equal flags escape key.  BL preserved.
;-----------------------------------------------------------------------------
GetYN:     mov   dx,ScrRows
           call  SetCsrXX       ; Hide cursor
           call  ClrStat        ; Clear status line, zeroing CX
gynloop:   mov   al,bl          ; Question number, preserved in BL
           or    HiColorWD,bp   ; Turn on blink--80h--next byte unchanged
           mov   si,OFFSET QuestTbl
           mov   cl,QUEST_WID   ; CH zero
           mul   cl             ; Also zeroes AH for int 16h
           add   si,ax
           mov   di,VIDQUEST    ; Video offset into top screen line
           call  DispBlkX       ; Inverse display
           mov   si,OFFSET NoPrompt
           mov   cl,3
           call  DispBlkX
           xor   HiColorWD,bp   ; Turn off blink--next byte unchanged
           int   16h            ; BIOS get keyboard character--AH zero
           cmp   al,CR
           je    isno

           and   al,NOT 20h     ; Forces upper case
           cmp   al,'Y'
           je    beepstc        ; Set carry and out

           cmp   al,'N'
           je    isno

           cmp   al,ESC_KEY     ; Semicolon converted to Esc above--not
           jne   gynloop        ;   worth detecting

           inc   ax             ; Flag escape by clear carry and not equal
isno:      ret                  ; Flag no by clear carry and equal
;-----------------------------------------------------------------------------
; Add new segment to SegList, updating LineMax.  Beep and set carry on error.
;-----------------------------------------------------------------------------
AddSeg:    push  ax
           mov   ax,LineMax
           shl   ax,1           ; 128 lines per segment
           shr   al,1           ; AH/AL gets LineMax segment index/line offset
           inc   ah             ; AH is next segment index, 0 if LineMax is -1
           call  GetBlock       ; SegList extended, with set carry on error
           jc    bpnopush       ; Allocation error or list size exceeded?
                                ;   Beep and exit with set carry
           shl   al,1           ; 128 lines per segment
           shr   ax,1           ; Pack AH/AL back to line AX, clearing carry
           mov   LineMax,ax
           pop   ax
           ret                  ; Carry clear, BX destroyed
;-----------------------------------------------------------------------------
; Shell to DOS, beeping on error.  Video mode NOT checked afterward.
;-----------------------------------------------------------------------------
Shell:     call  ClearScr       ; Clear screen, homing cursor
           mov   dl,LOW OFFSET ExitMsg
           call  DOSDisp        ; Display message
           lea   bx,ParmBlk     ; ES:BX points to ParmBlk--ES near from Edit
           lds   dx,CmdLoDD     ; DS:DX points to COMMAND.COM usually
           mov   ax,4B00h
           int   21h            ; Shell to DOS--sets carry if problem
           push  cs             ; Restore DS--stack ok since DOS 3.1+
           pop   ds
           jnc   beepout        ; Ok?  Beep else exit
;-----------------------------------------------------------------------------
; Beep and set carry bit to indicate an error.
;-----------------------------------------------------------------------------
Beep:      push  ax
bpnopush:  mov   ax,0E07h       ; Beep
           int   10h            ; BIOS service
           pop   ax
beepstc:   stc
beepout:   ret
;-----------------------------------------------------------------------------
; Point AX:SI to line DX and ES:DI to line DX-1, setting CX to length of DX-1,
; less trailing spaces.
;-----------------------------------------------------------------------------
SetPtrs:   call  GetLnBeg       ; Point ES:DI to line DX
           mov   si,di          ; Move to AX:SI
           mov   ax,es
           dec   dx
           call  GetLnBeg       ; Point ES:DI to line DX-1
;-----------------------------------------------------------------------------
; Compute length CX of line at ES:DI, less trailing spaces.
;-----------------------------------------------------------------------------
GetLen:    push  ax
           push  di
           mov   cx,bp          ; Line length
           add   di,cx
           dec   di             ; Point to line end
           mov   al,' '         ; Comparison value
           std
           repe  scasb          ; Scan backwards over spaces
           cld
           je    glup           ; All spaces?  Then ahead

           inc   cx             ; Else bump length to account for non-space
glup:      pop   di             ; Restore line start offset
           pop   ax             ; Restore AX
           ret
;-----------------------------------------------------------------------------
; Preliminary display of prompt character, flagged by carry.
;-----------------------------------------------------------------------------
GetFind:   push  si
           push  cx
           mov   cl,2           ; CH zero
           mov   si,OFFSET QPrompts + 2
           sbb   si,cx
           mov   di,VIDPROMPT
           call  DispBlkX       ; Inverse display prompt character--DL ok
           pop   cx             ; Restore parameters, with DI also ready
           pop   si
           mov   bx,STRING_MX
;-----------------------------------------------------------------------------
; Get/edit string at DS:SI, displaying at top line video offset DI.  Initial
; length is CX, max length is BX, edit flags in DL.  Return final length CX
; and last key AX.  Carry (and equality) set if escape, mode, or exit key.
; Other registers preserved.  Screen cursor moved to string start at end.
;-----------------------------------------------------------------------------
GetStr:    push  si             ; Save start pointers and string length
           push  di             ;   for redisplays and eventual exit
           push  cx
           call  DispBlkX       ; Inverse display current text, advancing DI
           call  SetCsrDI       ; Set cursor after string
           pop   cx             ; Refresh CX
           push  cx
           neg   cx
           add   cx,bx          ; Spaces remaining
           call  DispSpcsX      ; Inverse display trailing spaces
           pop   cx             ; Restore start pointers and length
           pop   di
           pop   si
           xor   ah,ah
           int   16h            ; BIOS get character
           test  dl,dl          ; MDEX_OK = 128 test
           jns   gsup1          ; Mode/exit key disallowed?  Then ahead

           cmp   ax,MODE_KEY SHL 8
           je    gsabort        ; Mode key?  Then kickout

           cmp   ax,EXIT_KEY SHL 8
           je    gsabort        ; Exit key?  Then kickout

gsup1:     test  al,al
           je    GetStr         ; Other extended key code?  Then ignore

           cmp   ax,ESC_LONG
           je    gsabort        ; Escape key?  Then kickout

           cmp   ax,BS_LONG
           jne   gsup2          ; Not backspace?  Then ahead

           jcxz  GetStr         ; Zero length?  Then ignore backspace

           dec   cx
           jmp   SHORT GetStr

gsup2:     cmp   ax,CR_LONG
           je    SetCsrDI       ; Carriage return?  Then exit with clear carry

           cmp   cx,bx
           je    GetStr         ; End of string buffer?  Then ignore and loop

           cmp   al,' '
           ja    gsup3          ; Not whitespace?  Then ahead

           test  dl,NO_WHITE
           jne   GetStr         ; No whitespace allowed?  Then ignore

gsup3:     test  dl,TO_CAPS
           je    gsstore        ; Not forcing upper case?  Then ahead

           call  IsLower        ; Clear carry if AL is lower case letter
           jc    gsstore        ; No?  Ahead

           and   al,NOT 20h     ; Else force upper case
gsstore:   add   si,cx
           mov   [si],al        ; Store byte in string
           sub   si,cx
           inc   cx
           jmp   SHORT GetStr

gsabort:   stc                  ; Flag MODE_KEY, EXIT_KEY, or ESC_KEY
;-----------------------------------------------------------------------------
; Set cursor to status line video offset DI (0 - 159).  Preserve all.
;-----------------------------------------------------------------------------
SetCsrDI:  pushf
           push  dx
           mov   dx,di
           shr   dx,1
           call  SetCsr         ; Set screen cursor back to string start on
           pop   dx             ;   exit or string end on entry
           popf
fnnomatch: ret
;-----------------------------------------------------------------------------
; Find next match and highlight, advancing cursor.  Set carry if no match.
;-----------------------------------------------------------------------------
fnloop:    inc   dx
           cmp   BotLine,dx     ; Test if past bottom
           jc    fnnomatch      ; Past?  Then return set carry--ES high here,
                                ;   but returning to Edit, not FindRet
           mov   bx,bp          ; 128
           call  GetLnBeg       ; Set ES:DI to line DX, length BX
FindNext:  lea   si,String1     ; Set DS:SI to scan string, length CX
           mov   cx,Strng1Ln
           call  ScanLine       ; Scan for match, returning AX
           jc    fnloop         ; No match?  Loop, else got match

           dec   ax             ; AX was distance from line end to match start
           xor   al,7Fh         ; Convert to cursor column
           mov   CurCol,ax      ; Point cursor column and mark column to
           mov   MarkLeft,ax    ;   start of match column
           mov   CurLine,dx     ; Point cursor line and mark line to line
           mov   MarkTop,dx     ;   where match found
           mov   ax,ScrRows     ; Adjust relative screen offset to
           shr   ax,1           ;   keep match row near middle
           sub   dx,ax
           jnc   fntopok        ; Match not near top of file?  Ahead

           cwd                  ; Else will set screen top to line 0
fntopok:   mov   ScrTop,dx
           mov   MarkWid,cx     ; Set match width and length
           mov   MarkLenDB,1
           call  AdjWin         ; Adjust screen window (ScrLeft may change)
           mov   al,BOXMARK     ; Fake out DispScr with boxmark mode
           xchg  ax,CurModeWD   ; Junk to CaseFlag temporarily
           push  ax
           call  DispScr        ; Highlight found text
           pop   CurModeWD      ; Restore CurMode and CaseFlag
;-----------------------------------------------------------------------------
; Display status line/cursor.  On exit, carry clear, AH/CH zero, and ES near.
;-----------------------------------------------------------------------------
DispStat:  push  cs             ; Set ES to near data segment
           pop   es
           mov   di,OFFSET StatLine
           push  di             ; Save til near end after line constructed

           push  di
           call  Space128       ; Clear status buffer, zeroing CX, equality
           pop   di
           lahf                 ; Save equality for F6
           mov   si,OFFSET F1F2Tbl
           mov   cl,F1F2_WID    ; CH is zero
           mov   al,CurMode
           cmp   al,LINEMARK
           jb    dsnorm         ; Typeover or insert mode?  Normal display
           je    dsline         ; Linemark mode?  Then ahead

           cmp   al,BOXMARK
           jne   dsnof1f2       ; Not boxmark mode?  Then no display

           cmp   MarkWidDB,ch
           je    dsnocut        ; Nothing marked?  Then test paste buffer

dsline:    cmp   MarkLenDB,ch
           lahf                 ; Inequality possibly for F6
           jne   dsiscut        ; Something marked?  Then F2 active as cut key

dsnocut:   cmp   PasteLenDB,ch
           je    dsnof1f2       ; Empty paste buffer?  Then F1 and F2 inactive

           add   si,cx          ; Else bump twice to paste display
dsnorm:    add   si,cx          ; Bump once to normal display
dsiscut:   rep   movsb          ; Copy F1/F2 text to status buffer
dsnof1f2:  mov   cl,8           ; CH is zero
           mov   di,OFFSET StatLine + 14
           mov   si,OFFSET F3Stat
           rep   movsb
           push  ax             ; Save CurMode a moment
           mov   cl,MODE_WID    ; CH is zero
           mul   cl
           mov   si,OFFSET ModeTbl
           add   si,ax
           rep   movsb          ; Move mode string into status buffer
           pop   ax             ; Restore CurMode AL
           inc   di

           call  SetBarSI       ; Set SI (CL ignored)
           mov   dl,[si]        ; First bar byte will do as style byte
           mov   si,OFFSET F8Stat
           mov   cl,9
           cmp   al,DRAW
           je    dsf6f8         ; F8 if equality

           mov   dl,''         ; Up/down arrow for case byte
           add   dl,CaseFlag    ; Add 0/1
           mov   si,OFFSET F6Stat
           mov   cl,7           ; CH is zero
           cmp   al,BOXMARK
           jne   chkins

           sahf
           jne   dsf6f8         ; F6 if inequality

chkins:    mov   dl,' '         ; Blank style/case byte
           cmp   al,INSERT
           ja    dsnof4

           mov   si,OFFSET F4F5Stat
           mov   cl,11          ; CH is zero
           rep   movsb

           mov   cl,3
           jb    dsf6f8

           mov   si,OFFSET F5Stat
dsf6f8:    rep   movsb
           mov   [di],dl        ; Style byte or blank

dsnof4:    mov   di,OFFSET StatLine + 47
           mov   si,OFFSET FileName
           mov   cx,NameLen
           cmp   NewFlag,ch
           je    dsnotnew

           mov   si,OFFSET NewFile
           mov   cl,8           ; Length of text (CH is zero)
dsnotnew:  jcxz  dsnoname

           add   si,cx          ; Next no harm if NewFile
           dec   si             ; Point to end of name
           push  si             ; Save pointer
dslp:      mov   al,[si]        ; Scan backwards for ':' or '\'
           cmp   al,'\'
           je    dsfound
           cmp   al,':'
           je    dsfound

           dec   si
           loop  dslp           ; On any loop exit SI+1 points to short name

dsfound:   pop   ax             ; Restore pointer to end of name
           sub   ax,si          ; Now length of filename.ext
           cmp   al,12          ; Maximum length allowed
           jna   dsup           ; Ok?  Then ahead

           mov   al,12          ; Else truncate (e.g., filename.extt)
dsup:      MOVE  cx,ax
           inc   si             ; Source is filename.ext
           rep   movsb          ; Move name to status line buffer

dsnoname:  lea   si,CurLine     ; CurCol follows CurLine in PSP
           push  si
           mov   di,OFFSET Statline + 60
           mov   al,'L'
           call  BinToDec       ; Store row decimals to status buffer
           mov   di,OFFSET Statline + 67
           mov   al,'C'
           call  BinToDec       ; Store column decimals to status buffer
           mov   di,OFFSET Statline + 73
           mov   si,OFFSET F7Stat
           mov   cl,7           ; CH is zero
           rep   movsb
           pop   si             ; Restore CurLine offset
           lodsw
           MOVE  dx,ax          ; CurLine to DX
           lodsw                ; CurCol to AX--both preserved through next

           pop   si             ; StatLine offset from top of call
           mov   cl,SCRCOLS     ; CH is zero here and on exit from DispStat
           call  DispBlkXX      ; Inverse display buffer at video offset DI=0

           sub   dx,ScrTop      ; Get row offset from top of screen
           sub   ax,ScrLeft     ; Get screen column offset--AH zero for Edit
           mov   dh,al          ; DH/DL ready
;-----------------------------------------------------------------------------
; Set cursor to screen row/column DH/DL.  Entry SetCsrX is for initial swap.
; and entry SetCsrXX is for row bump then swap.
;-----------------------------------------------------------------------------
SetCsrXX:  inc   dx             ; Adjust for status line
SetCsrX:   xchg  dl,dh
SetCsr:    push  ax             ; DH/DL is row/column with 0/0 origin
           push  bx
           mov   ah,2           ; Set cursor function
           xor   bh,bh          ; Page zero, clearing carry
           int   10h            ; BIOS service
           pop   bx
           pop   ax
           ret                  ; Clear carry passed through int 10h
;-----------------------------------------------------------------------------
; Convert [SI]+1 to left-adjusted decimal number string at ES:DI with prefix
; AL and equal sign.  Assume CL zero and number high bit clear.
;-----------------------------------------------------------------------------
BinToDec:  mov   ah,"="
           stosw                ; Store L= or C=
           lodsw                ; Fetch CurLine or CurCol
           inc   ax
           mov   ch,1           ; 100h to CX--need CL zero initially
           mov   bx,10          ; Divisor
btdloop1:  cwd                  ; Zero DX--needs AX high bit clear
           div   bx
           push  dx             ; Save remainder 0-9 on stack
           test  ax,ax          ; Test if dividend zero yet
           loopnz btdloop1      ; Dividend zero yet?  Then quit

           neg   cl             ; Set CX to number of pushed characters
btdloop2:  pop   ax             ; Retrieve characters in reverse order
           add   al,'0'         ; Make ASCII digit
           stosb
           loop  btdloop2       ; Done?  If not loop

           ret
;-----------------------------------------------------------------------------
; If boxmark mode and anything highlighted, force upper or lower case and
; toggle case flag.  Area remains highlighted.
;-----------------------------------------------------------------------------
Case:      call  MarkChk        ; Set BX/CX/DX if ok--may exit to Edit

           xor   CaseFlag,1     ; Flip 0/1 flag
           lahf                 ; AH preserved through loop
csloop:    push  cx             ; Save line count
           call  GetLnBeg       ; Point ES:DI to line DX, AX/BX preserved
           add   di,MarkLeft    ; Add offset to left mark
           mov   si,di
           mov   cx,bx          ; Paste width
           push  es
           pop   ds             ; Synch DS
           call  CopyCase       ; AH input too--AX/BX/DX ok
           push  cs
           pop   ds             ; Restore default DS
           inc   dx             ; Bump work area line
           pop   cx             ; Fetch line counter
           loop  csloop

caseout:   ret
;-----------------------------------------------------------------------------
; Copy CX bytes from SI to DI, forcing case based on equality flag in AH.
;-----------------------------------------------------------------------------
CopyCase:  lodsb
           stosb                ; Anticipate non-letter
           call  ToLower        ; To lower case, if letter
           jc    notlet         ; Not letter?  Ahead

           sahf                 ; Restore equality setting
           je    toup           ; Want lower?  Save AL

           and   al,NOT 20h     ; Else force upper case
toup:      dec   di
           stosb
notlet:    loop  CopyCase       ; Fall harmlessly when done...
;-----------------------------------------------------------------------------
; Converts AL to lower case, setting carry if not letter.
;-----------------------------------------------------------------------------
ToLower:   or    al,20h         ; To lower case
IsLower:   cmp   al,'a'
           jb    tlout          ; Not letter?  Ahead

           cmp   al,'z'+1
           cmc                  ; Set carry if not letter
tlout:     ret
;-----------------------------------------------------------------------------
; Program fixed data, read-only, referenced by offset.
;-----------------------------------------------------------------------------
BarList    DB    "ÅÃÂÚ´³¿³ÁÀÄÄÙ³Ä"
           DB    "ØÆÑÕµ³¸³ÏÔÍÍ¾³Í"
           DB    "×ÇÒÖ¶º·ºÐÓÄÄ½ºÄ"
           DB    "ÎÌËÉ¹º»ºÊÈÍÍ¼ºÍ"

KeyTable   DW    OFFSET InsLn, OFFSET DelLn, OFFSET Mode,  OFFSET UnDelLn
           DW    OFFSET Video, OFFSET Case,  OFFSET Exit,  OFFSET Style
           DW    OFFSET BadKy, OFFSET BadKy, OFFSET BadKy, OFFSET BadKy
           DW    OFFSET Home,  OFFSET Up,    OFFSET PgUp,  OFFSET BadKy
           DW    OFFSET Left,  OFFSET BadKy, OFFSET Right, OFFSET BadKy
           DW    OFFSET EndKy, OFFSET Down,  OFFSET PgDn,  OFFSET InsKy
           DW    OFFSET DelKy

Comspec    DB    "COMSPEC="
RdPrompt   DB    "Text file:"   ; Follows HelpText, includes following space
QuestTbl   DB    " Paste ShellSwitch Abort"
WildText   DB    "Wild="        ; Includes following question mark
NoPrompt   DB    "? N"
EscText    DB    "Esc"
NewFile    DB    "New file"
ModeTbl    DB    "TypeoverInsert  LinemarkBoxmark "
           DB    "Draw    Repeat  Search  Replace "
QPrompts   DB    ""           ; Includes preceding space
SvPrompt   DB    "Save as:"     ; Includes three following spaces
F1F2Tbl    DB    "       F2:CUT"
           DB    "F1:INS F2:DEL"
           DB    "F1:PASTE     "
F3Stat     DB    "F3:MODE="
F4F5Stat   DB    "F4:UDEL F5:VID"
F5Stat     DB    "SET"
F6Stat     DB    "F6:CASE"
F8Stat     DB    "F8:STYLE="
F7Stat     DB    "F7:"          ; Includes EXIT next
ExitMsg    DB    "EXIT later."  ; ExitMsg/AnsiCls/ErrMsg in same page
Tail       DB    0,CR,LF,"$"    ; First two bytes double as command-tail
AnsiCls    DB    1Bh,"[2J$"     ; Needed only for final cursor position

HelpText   DB    "EZEDIT 2.0  Free 4K Editor  16 Mar 96"   ,"$"
ErrMsg     DB    "Need DOS 3.1+/VGA and 38+(lines/8)K RAM"   ,"$"
           DB    "$"
           DB    "Load raw tabs with +filename.  Alt-26 as"  ,"$"
           DB    "last non-space in line omits CrLf on save.","$"
           DB    "Navigate with Home/End/Tab/PgUp/PgDn/Ctrl-","$"
           DB    "PgUp/Ctrl-PgDn.  Ins key inserts space or" ,"$"
           DB    "deleted word, per mode."                   ,"$"
           DB    "$"
           DB    "Craig.Hessel@mailgw.er.doe.gov"            ,"$"
;-----------------------------------------------------------------------------
; Allocation lists, filename storage, near buffers, and stack follow.  Edit
; buffers containing expanded file image are allocated to own segments after
; near segment allocation is shrunk to end of stack.
;-----------------------------------------------------------------------------
SLACK      =     (OFFSET $ - OFFSET EzEdit) MOD 16
SLACK      =     (16 - SLACK) MOD 16                    ; 16-align next

AltList    =     $ + SLACK                              ; 128 bytes
AltName    =     OFFSET AltList  + 2 * SEG_MAX          ; 64 bytes
SegList    =     OFFSET AltName  + NAMEMAX              ; 128 bytes
FileName   =     OFFSET SegList  + 2 * SEG_MAX          ; 64 bytes
SaveLine   =     OFFSET FileName + NAMEMAX              ; 128 bytes
CircBuf    =     OFFSET SaveLine + 128                  ; 256 bytes
FileBuf    =     OFFSET CircBuf  + 256                  ; 32K bytes
StatLine   =     OFFSET FileBuf  + IOSIZE - 128         ; Overlaps buffer tail

NewStack   =     OFFSET StatLine + 128 + STACKSIZE      ; 512 bytes

Code_Seg   ENDS
           END EzEdit
