home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Best Objectech Shareware Selections
/
UNTITLED.iso
/
boss
/
word
/
text
/
015
/
e.asm
next >
Wrap
Assembly Source File
|
1991-12-15
|
65KB
|
2,501 lines
;E 1.4. Copyright (C) David Nye, 1990, 1991, all rights reserved.
IDEAL
;Constants
LINEWIDTH EQU 80 ;Length of string storage for line
SCREENLENGTH EQU 24 ;Number of rows in display window
MAXLINES EQU 8096 ;Size of arrays of line pointers
BUFFERLENGTH EQU 1200h ;Length of file buffer
STACKSIZE EQU 50h ;Length of stack
BELL EQU 7 ;Some non-printing chars
BS EQU 8
HT EQU 9
LF EQU 10
CR EQU 13
CRLF EQU 0A0Dh
CTRL_Z EQU 26
ESCAPE EQU 27
DEL EQU 127
PROGLENGTH = EndOfProgram - Orig + 100h ;Length of .COM file
;Create a named string
MACRO String Name, Text
LOCAL LL
Name db LL-Name-1, Text
LL:
ENDM
;Make <Name> a byte ptr to <Length> bytes of storage after end of code.
;Like a .DATA? segment for .COM file, so uninitialized data doesn't take up
;room in the .COM file.
MACRO B? Name, Length
LOCAL where
where = EndOfProgram + BuffersCount
Name EQU BYTE PTR where
BuffersCount = BuffersCount + Length
ENDM
;Like B? but for word storage
MACRO W? Name, Length
LOCAL where
where = EndOfProgram + BuffersCount
Name EQU WORD PTR where
BuffersCount = BuffersCount + Length + Length
ENDM
BuffersCount = 0
MODEL TINY
CODESEG
ORG 100h
Orig:
jmp Start
String copyRight 'E 1.3. Copyright (C) David Nye, 1991. All rights reserved.'
;Some defaults, use ECONFIG to change
attribNl db 17h ;Default text attributes
attribInv db 70h ;Default status line attributes
tabSize db 4 ;Tab increment
inserting? db -1 ;True if in insert mode
autoIndent? db -1 ;True if in autoindent mode
startInText? db 0 ;Set to true to start up in text mode
zLMargin db 8 ;Left margin setting for Alt Z
zRMargin db LINEWIDTH-8 ;Right margin setting for Alt Z
;Strings
String cantOpenMsg, "Can't open file."
String rdErrorMsg, 'Error reading file.'
String fileErrorMsg, "Can't save file."
String diskFullMsg, 'Disk full.'
String noRoomMsg, 'Out of memory.'
String sysErrMsg, 'System error.'
String notMarkingMsg, 'Not marking.'
String setLabelMsg, 'Label (0-9): '
String setTabsMsg, 'Tab width: '
String newFileMsg, 'File name: '
String gotoMsg 'Jump to what line? '
String editingMsg <'Help F1',186,'Editing: '>
String findMsg 'Find: '
String replaceMsg 'Replace with: '
String notFoundMsg 'No more matching strings found.'
String anyKeyMsg 'Press any key to continue.'
String ctrlCMsg '*Break*'
String cancelledMsg 'Cancelled.'
BAK db '.BAK', 0
comspec$ db 'COMSPEC='
helpMsg db 'CURSOR left left arrow, ^S '
db 'BLOCK begin @B '
db ' right right arrow, ^D '
db ' copy block to buffer @C * '
db ' word left ^left arrow, ^A '
db ' delete block to buffer @D * '
db ' word right ^right arrow, ^F '
db ' insert block from buffer @I * '
db ' tab right, left Tab, Shift Tab '
db ' empty block buffer @E * '
db ' start, end of line Home, End '
db ' unmark @U '
db ' line up up arrow, ^E '
db 'FIND @F + '
db ' line down down arrow, ^X '
db ' replace @R + '
db ' page up PgUp, ^R '
db ' find/replace all @A + '
db ' page down PgDn, ^C '
db 'SAVE and continue @S '
db ' start of file ^PgUp '
db ' save and exit @X, ESC '
db ' end of file ^PgDn '
db ' toggle kill save at exit @K '
db 'DELETE Del '
db ' open another file @O+ '
db ' backspace Backspace '
db 'JUMP to line # @J '
db ' delete word left ^[ '
db ' set, goto label (0-9) @L, @G '
db ' delete word right ^], ^T '
db 'MARGIN set L, R ^Home, ^End '
db ' delete rest of line ^\ '
db ' wrap paragraph @W '
db ' delete line ^-, ^Y '
db ' set tabs @T '
db ' undelete line ^^ '
db ' toggle autoindent ^@ '
db 'INSERT mode toggle Ins '
db ' toggle text/prog mode @Z '
db ' insert raw char @= (+80h w shift) '
db 'SHELL to DOS, run EFn.BAT F2, F3-F6 * '
db 80 dup (' ')
db '@ = Alt, ^ = Ctrl, * = to/from file if s'
db 'hifted, + = use last string if shifted. '
db 'Status line flags: Insert Overwrite C'
db 'hanged AutoIndent [ LMargin ] RMargin'
;EXEC function parameter block
EXECParams dw 0
EXECCmdLineOff dw 0
ProgramSegment dw 0, -1, -1 , -1 , -1
EXECBAT db 0, '/c EF'
EXECFnumber db 'x.BAT '
EXECFileName db 20 dup (0)
;Variables
newFile? db 0 ;True if new file
marking? db 0 ;True if marking text
changed? db 0 ;True if original file has been changed
isBAKed? db 0 ;True if .BAK file already written
needCopies? db -1 ;True unless lines in buffer were just deleted
autoReplace? db 0 ;-1 if auto-replace with shift, 1 without shift
noEscape? db 0 ;True if prompt demands response
labelTable dw 10 dup (0) ;Table of line pointers assigned to labels
;These variables and buffers are allocated space following .COM file
W? sstack, STACKSIZE ;Stack goes here
STACKTOP = EndOfProgram + BuffersCount
W? cursorShape, 1 ;Line cursor parameters for color or mono
B? fName?, 1 ;True if file name given on command line
B? justFound?, 1 ;True if no other commands since last Find
B? swapped?, 1 ;True if edited file swapped out during EXEC
W? lMargin, 1 ;Current margins
W? rMargin, 1
W? fHandle, 1 ;File handle
W? lastLine, 1 ;Index of last line in file
W? blockPtrsLast, 1 ;Index of last line in block buffer
W? top, 1 ;Index of first line on screen
W? bottom, 1 ;Index of last line on screen
W? mark, 1 ;Start of marking for block command
W? here, 1 ;Temporaries
W? spTemp, 1
W? comspecPtrOff, 1 ;Pointer to COMSPEC value: offset, segment
W? comspecPtrSeg, 1
W? bufferPtr, 1 ;Multipurpose buffer pointer
W? hereCol, 1
W? topSegPtr, 1
W? topSeg, 1
W? topIndex, 1
W? videoSegment, 1 ;Segment of system's video memory
W? heapStart, 1 ;Segment of start of heap
W? heapPtr, 1 ;Segment pointer to next free paragraph in heap
B? fName, 50 ;File name in ASCIIZ format
B? otherFName, 50 ;Name of second file
W? otherLine, 1 ;Line of cursor in second file
ESSENTIALS = BuffersCount ;Buffers above here not saved with shell + swap
W? temp, 1 ;Temporary storage
B? fNameBAK, LINEWIDTH ;Current file with .BAK extension added
B? fNameTemp, LINEWIDTH ;File name for block read/writes, shell
B? pad, LINEWIDTH ;Scratch buffer
B? findString, LINEWIDTH ;Search string for Find command
B? replaceString, LINEWIDTH ;New string for Replace command
W? linePtrs, MAXLINES ;List of line pointers
W? blockPtrs, MAXLINES ;Line pointers for block or line deletes
B? buffer, BUFFERLENGTH ;File buffer
ENDFBUFFER = BuffersCount
;Jump tables: ^ = Ctrl, @ = Alt, # = Shift.
ctrlTable dw na ;Undefined
dw WordLeft ;^A
dw na ;^B
dw PageDown ;^C
dw Right ;^D
dw Up ;^E
dw WordRight ;^F
dw na ;^G or BEL
dw BackSpace ;^H or BS
dw Tab ;^I or HT
dw na ;^J or LF
dw na ;^K or VT
dw na ;^L or FF
dw CRet ;^M or CR
dw na ;^N or SO
dw na ;^O or SI
dw na ;^P
dw na ;^Q or DC1
dw PageUp ;^R or DC2
dw Left ;^S or DC3
dw DeleteWordR ;^T or DC4
dw na ;^U
dw na ;^V
dw na ;^W
dw Down ;^X or CAN
dw DeleteLine ;^Y
dw na ;^Z
dw DeleteWordL ;^[
dw DeleteToEOL ;^\
dw DeleteWordR ;^]
dw UndeleteLine ;^^
dw DeleteLine ;^-
auxTable dw 3 DUP (na) ;Undefined
dw AutoIndent ;^@ or NUL
dw 11 DUP (na) ;Undefined
dw ReverseTab ;#Tab
dw na ;@Q
dw Wrap ;@W
dw EmptyBuffer ;@E
dw Replace ;@R
dw SetTabs ;@T
dw na ;@Y
dw Unmark ;@U
dw InsertBlock ;@I
dw OtherFile ;@O
dw na ;@P
dw 4 DUP (na) ;Undefined
dw ReplaceAll ;@A
dw Save ;@S
dw DeleteBlock ;@D
dw Find ;@F
dw GotoLabel ;@G
dw Help ;@H
dw Jump ;@J
dw Kill ;@K
dw SetLabel ;@L
dw 5 DUP (na) ;Undefined
dw ToggleWPMode ;@Z
dw Exit ;@X
dw Copy ;@C
dw na ;@V
dw BeginBlock ;@B
dw na ;@N
dw na ;@M
dw 8 DUP (na) ;Undefined
dw Help ;F1
dw Shell ;F2
dw F3BAT ;F3
dw F4BAT ;F4
dw F5BAT ;F5
dw F6BAT ;F6
dw na ;F7
dw na ;F8
dw na ;F9
dw na ;F10
dw 2 DUP (na) ;Undefined
dw HomeLine ;Home
dw Up ;Up arrow
dw PageUp ;PgUp
dw na ;Undefined
dw Left ;Left arrow
dw na ;Undefined
dw Right ;Right arrow
dw na ;Undefined
dw EndLine ;End
dw Down ;Down arrow
dw PageDown ;PgDn
dw ToggleIns ;Ins
dw Delete ;Del
dw na ;#F1
dw Shell ;#F2
dw F3BAT ;#F3
dw F4BAT ;#F4
dw F5BAT ;#F5
dw F6BAT ;#F6
dw na ;#F7
dw na ;#F8
dw na ;#F9
dw na ;#F10
dw 20 DUP (na) ;[^Fn, @Fn]
dw na ;^PrtSc
dw WordLeft ;^Left arrow
dw WordRight ;^Right arrow
dw SetRMargin ;^End
dw BottomFile ;^PgDn
dw SetLMargin ;^Home
dw 10 DUP (na) ;[Alt numbers]
dw na ;@-
dw InsertRaw ;@=
dw TopFile ;^PgUp
;******************************************************************************
Start:
mov ax, cs
mov [ProgramSegment], ax ;Store current segment for EXEC function
add ax, ((PROGLENGTH + ENDFBUFFER) SHR 4) + 1
mov [heapStart], ax ;Compute start of free memory in paragraphs
mov [heapPtr], ax
mov sp, OFFSET STACKTOP
mov si, 80h ;Make pointer to command tail
mov cl, [si] ;Get filename length
sub ch, ch
mov [fName?], cl ;Save a copy
mov al, ' ' ;Skip leading blanks
@@L1:
inc si
cmp al, [si]
loope @@L1
inc cx
mov di, OFFSET fName ;Move command tail to FName
rep movsb
sub al, al ;Make ASCIIZ string
stosb
mov ax, 2523h ;Redirect Ctrl C handler
mov dx, OFFSET Cancel
int 21h
mov [byte OtherFName], 0 ;Prevent Alt Shift O crash without prev. file
mov ah, 0Fh ;Set defaults for color or mono adapter
int 10h
mov bx, 0B000h ;If mode 7 (= MDA or Herc), video seg=B000h,
mov dx, 0C0Dh ;Set cursor shapes for mono
cmp al, 7
je @@L2
mov bx, 0B800h ;Otherwise video seg=B800h,
mov dx, 0607h ;Color cursor size
@@L2:
mov es,bx ;Improved DesqView-compatible code, furnished
push bx ; by gchicares on BIX
push dx
mov cx,'DE'
mov dx,'SQ'
mov ax,2b01h
int 21h
pop dx
pop bx
cmp al,0ffh
je @@L3a
mov ah,0feh
int 10h
mov bx,es
@@L3a:
mov [videoSegment], bx
mov [cursorShape], dx
mov es, [2Ch] ;Find COMSPEC
sub di, di
@@L3:
mov si, OFFSET comspec$
mov cx, 8
repe cmpsb
jne @@L3
mov [comspecPtrOff], di
mov [comspecPtrSeg], es
mov es, [programSegment]
InitFile:
mov dx, OFFSET fName ;Open file and set up list of line pointers
cmp [fName?], 0 ;If no file name specified on command line,
jne @@L0
mov [noEscape?], -1 ;Prompt for it
call GetFileName
@@L0:
call OpenFile
mov [lMargin], 0 ;Set initial margins
mov [rMargin], LINEWIDTH - 1
cmp [startInText?], 0
je NextKey
call ToggleWPMode
NextKey:
call Redraw ;Redraw screen, status line
NextNoRedraw:
call DrawCursor ;Place cursor
sub ah, ah ;Get keypress to AL
int 16h
or al, al ;Check for control codes
je IsAux
cmp al, ' '
jb IsCtrl
call Insert ;Insert or overwrite if none
jmp NextKey
IsAux:
xchg al, ah ;Get aux code
cmp al, 132
ja NextKey
mov si, OFFSET auxTable ;Jump indirect to appropriate routine
DoTableJump:
shl ax, 1
add si, ax
call [WORD si]
jmp NextKey
IsCtrl:
mov si, OFFSET ctrlTable ;Jump to routine through table
sub ah, ah
jmp DoTableJump
InsertRaw:
call Shifted? ;Set flag if shifted
mov dl, al
sub ah, ah ;Get keypress to AL
int 16h
or al, al ;Check for aux code, ignore InsertRaw if found
je IsAux
cmp dl, 0 ;If shift was down set high bit
jz @@L1
or al, 80h
@@L1:
call Insert ;Insert
jmp NextKey
SwapNames:
;Swap old and new file names prior to opening new file
mov si, OFFSET fName
mov di, OFFSET otherFName
mov cx, 50
@@L1:
mov al, [si]
xchg al, [di]
mov [si], al
inc si
inc di
loop @@L1
ret
OtherFile:
;Open another file
call Shifted? ;Set flag if shifted
mov dh, [byte OtherFName] ;Ignore it if no prior file
or dh, dh
jz @@L1
mov dh, al
@@L1:
call Save ;Save current file if altered
mov ax, [blockPtrsLast] ;If block buffer is empty,
sub ax, OFFSET blockPtrs
jne @@L2
mov ax, [heapStart] ; reset heap pointer to start
mov [heapPtr], ax
jmp SHORT GetOther ; prompt for new file name
@@L2:
shr ax, 1 ;Else move lines with pointers in block buffer
mov cx, ax ; to start of heap (load new file above them)
mov dl, 5
mul dl
add ax, [heapStart] ;Calculate upper limit of target zone
mov [heapPtr], ax ; which will also be new value of heap pointer
mov bx, OFFSET blockPtrs ;For each pointer in block buffer,
@@L3:
cmp [bx], ax ;If its line is already within target zone,
jae @@L4
mov es, [bx] ;Set high bit of first char in line to mark it
or [byte es:0], 80h ; (we won't need to move these lines)
@@L4:
inc bx ;Next pointer
inc bx
loop @@L3
push ds
mov bx, OFFSET blockPtrs ;For each pointer in block buffer:
mov es, [heapStart]
@@L4a:
test [byte es:0], 80h ;If high bit set in target line,
jne @@L7 ; line already in use, try next target line
mov ds, [cs:bx] ;If high bit set in source line,
test [byte 0], 80h
je @@L5 ; don't need to move it (already there)
inc bx ; Next source line, same target line
inc bx
jmp SHORT @@L8
@@L5:
mov cx, 40 ;Else move one line
sub si, si
sub di, di
rep movsw
mov [cs:bx], es ;Update pointer
@@L6:
inc bx ;Next block pointer
inc bx
@@L7:
mov ax, es ;Next target line
add ax, 5
mov es, ax
@@L8:
segcs ;Loop until all lines moved
cmp bx, [blockPtrsLast]
jb @@L4a
pop ds
mov bx, OFFSET blockPtrs ;Reset high bits of all first chars set high
@@L9:
mov es, [bx]
and [byte es:0], 7Fh
inc bx
inc bx
cmp bx, [blockPtrsLast]
jb @@L9
GetOther:
call SwapNames
cmp dh, 0 ;If Shifted, use last file
je @@L1
mov dx, OFFSET fName
call OpenFile1
mov bx, [otherLine] ;Restore cursor to previous position
mov ax, [here]
mov [otherLine], ax
jmp NewLine
@@L1:
mov [noEscape?], -1
mov ax, [here] ;Save current file name and cursor line
mov [otherLine], ax
mov dx, OFFSET fName ;Prompt for new file name
call GetFileName
jmp SHORT OpenFile1 ;Read in new file above block buffer lines
OpenFile:
;Open file, load if found, then close. Call with dx -> ASCIIZ file name.
mov ax, OFFSET blockPtrs ;Reset block buffer pointer
mov [blockPtrsLast], ax
mov ax, [heapStart] ;Begin loading at start of heap
mov [heapPtr], ax
OpenFile1:
mov [newFile?], 0
mov [changed?], 0
mov ax, 3D00h ;Try to open file
int 21h
jnc OldFile
mov [newFile?], -1 ;If no such file, remind me to create a new one
OpenNewFile:
call NewFile
jmp SHORT XOpenFile
OldFile:
mov [fHandle], ax ;Else save file handle
mov bx, OFFSET linePtrs ;Read file in
mov dx, ax
call ReadFile
dec bx
dec bx
mov [lastLine], bx ;Save index of last line
mov bx, [fHandle] ;Close file
mov ah, 3Eh
int 21h
XOpenFile:
mov bx, OFFSET linePtrs ;Reset row, screen pointers
mov [top], bx
mov es, [bx]
sub di, di
ret
GetFileName:
;Prompt for file name. Abort if null name. Call with buffer address in DX.
push si
push dx
mov si, OFFSET newFileMsg ;Print prompt
call Prompt
pop dx
call GetString ;Get file name
mov si, dx ;Convert to ASCIIZ
add si, ax
mov [BYTE si], 0
pop si
ret
GetString:
;Get string to [DX], return count (minus CR/LF) in AX. Abort if null string.
push bx
push cx
push si
push di
push es
push dx
@@L2:
mov dx, OFFSET pad ;Get string
mov ah, 3Fh
sub bx, bx
mov cx, 20
int 21h
dec ax ;Strip CR/LF
dec ax
jnz @@L1 ;Abort if null string
cmp [noEscape?], 0 ; unless escape precluded
je @@L0
call Beep
mov si, OFFSET newFileMsg
call Prompt
jmp @@L2
@@L0:
mov si, OFFSET cancelledMsg
jmp Abort
@@L1:
mov cx, ax ;Copy temporary copy of string to [DX]
pop dx
push ax
mov ax, ds
mov es, ax
mov si, OFFSET pad
mov di, dx
rep movsb
pop ax
pop es
pop di
pop si
pop cx
pop bx
mov [noEscape?], 0
ret
ReadFile:
;Load file with handle in DX, assigning segment pointers starting at BX
push es
mov ax, [heapPtr]
mov es, ax
sub cx, cx
sub di, di
FillBuffer:
push bx ; Fill buffer
push cx
push dx
mov bx, dx
mov ah, 3Fh
mov cx, BUFFERLENGTH
mov dx, OFFSET buffer
int 21h
jnc @@L1 ; Check for read error
jmp ReadError
@@L1:
pop dx
pop cx
pop bx
mov si, OFFSET buffer ; Set pointers
add ax, si
mov [bufferPtr], ax
cmp ax, OFFSET buffer ;Exit if empty buffer
je EndOfFile
cmp [byte si], LF ;Skip LF if first char in buffer
jne SHORT NextLine
inc si
NextLine:
mov al, [si] ;Get next char
cmp al, CR ;If char is CR, end of line
jne @@L2
inc si ; move past CR
cmp [byte si], LF ; and LF if present
jne @@L1
inc si
@@L1:
call EndOfLine ; pad out line with spaces and save it
jmp SHORT @@L3
@@L2:
cmp al, HT ;Else if a tab, expand it
jne @@L2a
push cx
mov al, ' '
mov cl, [tabSize]
sub ch, ch
rep stosb
pop cx
sub cl, [tabSize]
sbb ch, 0
inc si
jmp SHORT @@L3
@@L2a:
movsb ;Else add char to line
dec cx
@@L3:
cmp si, [bufferPtr] ;Loop until end of buffer
jb NextLine
cmp si, OFFSET ENDFBUFFER ;If buffer less than full, indicates end of file
jae FillBuffer
EndOfFile:
call EndOfLine ;Finish up present line
mov [heapPtr], es ;Update pointer to start of free heap space
pop es
ret
EndOfLine:
add cx, LINEWIDTH ;Pad to end with spaces
jle @@L1 ;Truncate lines longer than LINEWIDTH chars
mov al, ' '
rep stosb
@@L1:
mov [bx], es ;Store segment of this line
mov ax, es ;Next line
add ax, 5
cmp ax, 0A000h ;Out of room?
jb SHORT @@L2
jmp NoRoom
@@L2:
mov es, ax
inc bx
inc bx
sub di, di
sub cx, cx
Ret3:
ret
Redraw:
;Redraw screen and status line
mov [here], bx
mov [hereCol], di
push bx
push di
push ds
push es
push di
mov es, [videoSegment] ;Get segment for display
mov si, OFFSET editingMsg ;Refresh status line: "Editing ..."
call Prompt
mov di, 34 ;Tab to column 17
mov si, OFFSET fName ; <file name>
mov ah, [attribInv]
@@S1:
lodsb
or al, al
je @@S2
stosw
jmp @@S1
@@S2:
add di, 6 ;3 spaces
mov al, 'L' ;"L" <line #>
stosw
inc di
inc di
mov ax, bx
sub ax, OFFSET linePtrs
shr ax, 1
inc ax
call PrintInt
add di, 4 ;2 spaces
mov al, 'C' ;"C" <column number>
mov ah, [attribInv]
stosw
inc di
inc di
pop ax ;Get copy of DI as cursor row
inc ax
call PrintInt
mov di, LINEWIDTH * 2 - 12 ;Tab to start of status chars display
mov al, 'I' ;Insert/Overwrite status
mov ah, [attribInv]
test [inserting?], -1
jne @@S3
mov al, 'O'
@@S3:
stosw
mov al, 'C' ;Changed status
test [changed?], -1
jne @@S5
mov al, ' '
@@S5:
stosw
mov al, 'A' ;Autoinsert status
test [autoIndent?], -1
jne @@S6
mov al, ' '
@@S6:
stosw
mov al, ' ' ;L margin set?
cmp [lMargin], 0
je @@S7
mov al, '['
@@S7:
stosw
mov al, ' ' ;R margin set?
cmp [rMargin], LINEWIDTH - 1
je @@S8
mov al, ']'
stosw
@@S8:
segcs
mov al, [attribNl] ;Mark margins as non-inv chars in status line
mov di, [lMargin]
add di, di
je @@S8a
inc di
stosb
@@S8a:
mov di, [rMargin]
cmp di, LINEWIDTH - 1
je @@S8b
add di, di
inc di
stosb
@@S8b:
mov di, LINEWIDTH * 2 ;Move to next display line
mov ax, [top] ;Compute bottom of screen
mov bx, ax
add ax, (SCREENLENGTH - 1) * 2
cmp ax, [lastLine] ;If at end of file,
jle @@L0
mov ax, [lastLine] ; stop at lastLine
@@L0:
mov [bottom], ax
@@L1: ;For each row
mov cx, LINEWIDTH ;Count of chars per row
mov ds, [cs:bx] ;Get pointer to screen line
sub si, si ;Initialize column counter
segcs
mov ah, [attribNl] ;Attribute = inverse video if Marked
test [cs:marking?], -1
je @@L2
segcs
cmp bx, [mark]
je @@L1b
jb @@L1a
segcs
cmp bx, [here]
jbe @@L1b
jmp SHORT @@L2
@@L1a:
segcs
cmp bx, [here]
jb @@L2
@@L1b:
segcs
mov ah, [attribInv]
@@L2: ;For each char, write char and attribute
lodsb
stosw
loop @@L2 ;Next char
inc bx ;Next row
inc bx
segcs
cmp bx, [bottom] ;Stop if screen full
jle @@L1
mov cx, LINEWIDTH * 2 * (SCREENLENGTH + 1) ;Fill out screen with blanks
sub cx, di
shr cx, 1
segcs
mov ah, [attribNl]
mov al, ''
rep stosw
@@L3:
pop es
pop ds
pop di
pop bx
ret
DrawCursor:
;Set cursor shape and place it on screen
push bx
mov cx, [cursorShape] ;Set cursor shape: line for insert mode,
test [Inserting?], -1
jne @@L1
sub ch, ch ;Block for overwrite
@@L1:
mov ah, 1
int 10h
sub bx, [top] ;Show cursor at current row, column
shr bx, 1
inc bx
mov dh, bl
mov ax, di
mov dl, al
mov ah, 2
mov bh, 0
int 10h
pop bx
ret
Print0:
call ClearStatus ;Blank status line
sub di, di ;Starting at beginning of line ...
Print:
;Print string pointed to by SI on status line in inverse video, starting at DI
push es
mov es, [videoSegment]
lodsb ;Get count of string to be printed
mov cl, al
sub ch, ch
mov ah, [attribInv] ;Attribute = inverse video
@@L1:
lodsb
stosw
loop @@L1
pop es
ret
ClearStatus:
;Inverse-blank status line
push di
push es
mov es, [videoSegment]
mov ah, [attribInv]
mov al, ' '
mov cx, 80
sub di, di
rep stosw
pop es
pop di
ret
Cancel:
;Ctrl C routine
mov si, OFFSET ctrlCMsg ;Abort with message ...
Abort:
;Print counted string pointed to by SI on status line and abort
call Print0 ;Print error message ...
na:
;Unassigned key or other error. Beep and abort.
call Beep ;Beep
mov sp, OFFSET STACKTOP ;Reset stack pointer to top
mov bx, [here] ;Retrieve cursor position in case it was trashed
mov di, [hereCol]
call DrawCursor
jmp NextNoRedraw ;Restart main editing loop
Beep:
mov ah, 2 ;Output a BELL
mov dl, BELL
int 21h
ret
Prompt:
push di
call Print0 ;Print string at start of line
mov dx, di ;Set cursor to end of printed string ...
shr dl, 1
sub dh, dh
pop di
GotoXY:
;Position cursor at row, column given by DL, DH
push bx
mov ah, 2
sub bx, bx
int 10h
pop bx
ret
PrintInt:
;Print to ES:DI in inverse video the unsigned decimal integer in AX
sub dx, dx ;Start stack with a null
push dx
mov cx, 10 ;Get remainders of successive divisions by 10
@@L1:
div cx
add dl, '0' ;Convert to ASCII
mov dh, [attribInv] ;Attribute is reverse video
push dx
sub dx, dx
or ax, ax
jne @@L1
pop ax ;Pop and print remainders in reverse order
@@L2:
stosw
pop ax
or ax, ax
jne @@L2
ret
NewLineC:
;Jump here if file is changed by a command
mov [changed?], -1
NewLine:
mov [justFound?], 0
NewLine0:
cmp bx, OFFSET linePtrs ;Check bounds, adjust if necessary
jge @@L1
mov bx, OFFSET linePtrs
@@L1:
cmp bx, [lastLine]
jle @@L2
mov bx, [lastLine]
@@L2:
mov ax, [top]
cmp bx, ax
jge @@L3
mov [top], bx
@@L3:
add ax, (SCREENLENGTH-1)*2
cmp bx, ax
jle @@L4
mov ax, bx
sub ax, (SCREENLENGTH-1)*2
mov [top], ax
@@L4:
mov es, [bx] ;Adjust ES to point to new line
ret
Left:
or di, di ;If at start of line,
jne @@L1
call Up ; move to end of line above
jmp EndLine
@@L1:
dec di ; else just decrement cursor
CursorMoved:
mov [justFound?], 0
ret
Right:
cmp di, LINEWIDTH - 1 ;If at end of line,
jne @@L1
sub di, di ; move to start of line below
jmp Down
@@L1:
inc di ; else just increment cursor
jmp SHORT CursorMoved
LScanE:
;Scan left past first non-space or start of line
mov al, ' '
mov cx, di
inc cx
std
repe scasb
cld
ret
LScanNE:
;Scan left past first space or start of line
mov al, ' '
mov cx, di
inc cx
std
repne scasb
cld
ret
RScanE:
;Scan right past first non-space or end of line
mov al, ' '
mov cx, LINEWIDTH
sub cx, di
repe scasb
ret
RScanNE:
;Scan right past first space or end of line
mov al, ' '
mov cx, LINEWIDTH
sub cx, di
repne scasb
ret
WordLeft:
;Move left one word
or di, di ;Do nothing if at start of line
je @@Lx
mov [justFound?], 0
dec di ;Else starting at char to left,
call LScanE ; skip spaces until non-space
inc di
je @@Lx ; or start of line,
call LScanNE ; then scan to next space or start of line
jne @@L1
inc di
@@L1:
inc di
@@Lx:
ret
WordRight:
;Move right one word
cmp di, LINEWIDTH - 1 ;Do nothing if at end of line
je @@Lx
mov [justFound?], 0
call RScanNE ;Skip non-spaces until space
jne @@L1 ; or end of line,
dec di
call RScanE ; then scan to next non-space or end of line
@@L1:
dec di
@@Lx:
ret
HomeLine:
;Move cursor to L margin
mov di, [lMargin]
mov [justFound?], 0
ret
EndLine:
;Move cursor to R margin or end of text on line (but not left of L margin)
push cx
mov [justFound?], 0
mov di, [rMargin] ;Start at R margin
cmp [BYTE es:di], ' ' ;If non-blank, stop here
jne @@L2
call LScanE ;Skip all spaces until non-space
je @@L1 ; or beginning of line
inc di
@@L1:
inc di
@@L2:
cmp di, [lMargin] ;If left of L margin, set to L margin
jae @@L3
mov di, [lMargin]
@@L3:
pop cx
Ret2:
ret
Up:
;Move cursor up one line
cmp bx, OFFSET linePtrs ;If at top of file already, do nothing
je Ret2
dec bx
dec bx
jmp NewLine
Down:
;Move cursor down one line
cmp bx, [lastLine] ;If at last line already, do nothing
je Ret2
inc bx
inc bx
jmp NewLine
PageUp:
;Move cursor up one page
sub bx, (SCREENLENGTH-1)*2
jmp NewLine
PageDown:
;Move cursor down one page
add bx, (SCREENLENGTH-1)*2
jmp NewLine
TopFile:
;Move cursor to top of file
mov bx, OFFSET linePtrs
mov [top], bx
call HomeLine
jmp NewLine
BottomFile:
;Move cursor to bottom of file
mov bx, [lastLine]
mov es, [bx]
mov ax, bx
sub ax, (SCREENLENGTH-1)*2
cmp ax, OFFSET linePtrs
ja @@L1
mov ax, OFFSET linePtrs
@@L1:
mov [top], ax
call EndLine
jmp NewLine
Tab:
;Tab right
mov [justFound?], 0
mov ax, di ;Advance di to next tab stop
mov cl, [tabSize]
div cl
sub cl, ah
sub ch, ch
add di, cx
cmp di, LINEWIDTH ;If past end of line,
jl @@L1
mov di, LINEWIDTH - 1 ;Set cursor at end of line
@@L1:
ret
ReverseTab:
;Tab left
mov [justFound?], 0
mov ax, di ;Decrement di to nearest tab stop
dec al
div [tabSize]
mov al, ah
sub ah, ah
inc al
sub di, ax
jnc @@L1 ;Set to start of line if past start
sub di, di
@@L1:
ret
SetLMargin:
;Toggle left margin between 0 and present cursor setting
cmp [lMargin], 0
je @@L1
mov [lMargin], 0
ret
@@L1:
mov [lMargin], di
ret
SetRMargin:
;Toggle right margin between LINEWIDTH-1 and present cursor setting
cmp [rMargin], LINEWIDTH-1
je @@L1
mov [rMargin], LINEWIDTH-1
ret
@@L1:
mov [rMargin], di
ret
ToggleWPMode:
;Toggle word processing and programming defaults
cmp [lMargin], 0
jne @@L1
cmp [rMargin], LINEWIDTH-1
jne @@L1
sub ah, ah
mov al, [zLMargin]
mov [lMargin], ax
mov al, [zRMargin]
mov [rMargin], ax
mov [autoIndent?], 0
jmp HomeLine
@@L1:
mov [lMargin], 0
mov [rMargin], LINEWIDTH-1
mov [autoIndent?], -1
jmp HomeLine
CRet:
;Split line at cursor
push ds
push es
push di
push es
call AddBlankLine ;Start a new line below current one, ->ES:DI
pop ds ;DS:SI := current cursor position
pop si
push di
mov cx, LINEWIDTH ;CX := # chars left on line
sub cx, si
je @@L2
@@L1:
movsb ;Split line,
mov [byte si - 1], ' ' ; blank original to end from cursor
loop @@L1
@@L2:
pop di
pop es
pop ds
jmp NewLineC
AddBlankLine:
;Insert a new blank line below current one
mov cx, 1 ;Make room for new entry in linePtr
call OpenRow
inc bx
inc bx
mov ax, [heapPtr]
jmp SHORT BlankLine
NewFile:
;Set up initial blank line of a new file
mov ax, [heapPtr] ;Set ES and [bx] to available heap
mov bx, OFFSET linePtrs
mov [lastLine], bx
BlankLine:
mov [bx], ax
mov es, ax
add ax, 5
mov [heapPtr], ax ;Update heap pointer (segment value only)
BlankThisLine:
sub di, di ;Blank new line
mov cx, LINEWIDTH
mov al, ' '
rep stosb
mov di, [lMargin] ;Home cursor on new line
test [autoIndent?], -1 ; or if in autoindent mode,
je @@Lx
cmp bx, OFFSET linePtrs ; and this is not first line in file,
je @@Lx
mov es, [bx - 2] ; line up with first char of line above
call RScanE
mov es, [bx]
je @@L1 ; unless above line is blank
dec di
cmp di, [lMargin] ; or first char is left of L margin
jae @@Lx
@@L1:
mov di, [lMargin] ; in which case use L margin
@@Lx:
ret
OpenRow:
;Open CX lines at BX in linePtrs
push cx
push di
push es
mov ax, ds ;DS, ES -> data segment (for linePtr)
mov es, ax
mov si, [lastLine] ;SI points to last line's segment pointer
mov di, si ;DI points CX lines beyond that
add di, cx
add di, cx
mov [lastLine], di ;Point lastLine to new last line
mov cx, si ;Count = # lines from here to end
sub cx, bx
shr cx, 1
inc cx
std
rep movsw ;Move array elements up
cld
pop es
pop di
pop cx
ret
BackSpace:
;Delete char to left of cursor
mov ax, di ;Unless at first character of file,
add ax, bx
sub ax, OFFSET linePtrs
jz Ret1 ; do Left then Delete
mov [justFound?], 0
push di
call Left
pop ax ;Don't do Join if already at end of line ...
or ax, ax
jne Delete0
Delete:
;Delete char at cursor
mov dx, di ;Save cursor column
cmp [BYTE es:di], ' ' ;If deleting a space at end of line,
jne Delete0
call RScanE
mov di, dx ; join line below
je Join
Delete0:
mov [changed?], -1
push di ; else slide text left
push cx
push ds
mov cx, LINEWIDTH - 1
sub cx, di
mov si, di
inc si
mov ax, es
mov ds, ax
rep movsb
mov [BYTE di], ' ' ;Blank last character on line
pop ds
pop cx
pop di
Ret1:
ret
UndeleteLine:
mov bp, [blockPtrsLast] ;Abort if no lines are in buffer
cmp bp, OFFSET blockPtrs
ja @@L0
jmp Beep
@@L0:
dec bp ;Else move pointer to top line of delete buffer
dec bp
mov [blockPtrsLast], bp
cmp di, [lMargin] ;If cursor is at or before L margin,
ja @@L1
mov cx, 1
call OpenRow ;Start new row below current one
mov [bx+2], es ;Swap rows to insert undeleted above current
mov ax, [bp] ;Retrieve and store pointer to undeleted line
mov [bx], ax
jmp NewLine
@@L1:
mov cx, LINEWIDTH ;Cursor past start of line
sub cx, di ;Copy popped line over current one
push di
push ds
mov ds, [bp]
sub si, si
rep movsb
pop ds
pop di
ret
Join:
;Join lower line to current line at cursor
cmp bx, [lastLine] ;Abort if this is the last line of the file
jne @@L1
jmp Beep
@@L1:
push bx ;Save registers
push di
push ds
push di
push es
mov es, [bx + 2] ;Get next line's segment
push es ;Save a copy
mov dx, di
sub di, di ;Remove leading spaces
call RScanE
jne @@L2
mov di, 1
@@L2:
dec di
mov [temp], di
call EndLine ;Find first non-space char from end
add dx, di ;If concatenated line is too long, abort.
sub dx, [temp]
cmp dx, LINEWIDTH
jle @@L3
call Beep
pop ax
pop es
pop di
pop ds
pop ax
pop ax
jmp Ret1
@@L3:
mov cx, di ;Count = lower line length minus leading spaces
sub cx, [temp]
mov si, [temp] ;Source = start of text on lower line
pop ds
pop es ;Destination = present cursor location
pop di
rep movsb ;Concatenate lines
pop ds
inc bx ;Delete lower line
inc bx
call DeleteLineNS
pop di ;Restore pointers and return
pop bx
jmp NewLineC
Insert:
;Insert or overwrite at cursor
mov [justFound?], 0
test [inserting?], -1 ;If inserting, open up space for new character
jz Insert1
mov si, [RMargin] ;If line is full, split it
cmp [BYTE es:si], ' '
je Insert0
push ax
push bx
push di
call CRet
pop di
pop bx
call NewLine
pop ax
jmp SHORT Insert1
Insert0:
push ax
push cx
push ds
mov ax, es
mov ds, ax
mov si, LINEWIDTH - 1
mov cx, si
sub cx, di
mov di, si
dec si
std
rep movsb
cld
pop ds
pop cx
pop ax
Insert1:
stosb ;Add character
mov [changed?], -1
cmp di, [rMargin] ;Wrap if at R margin
ja WrapLine
ret
WrapLine:
;Wrap last word on current line
cmp [BYTE es:di-1], ' '
je @@L1
call WordLeft
@@L1:
call CRet
mov es, [bx]
cmp [BYTE es:di], ' '
jne @@L2
jmp DeleteWordR
@@L2:
jmp EndLine
Wrap:
;Wrap paragraph starting at present line
mov di, [rMargin] ;Start at R margin of present line
cmp bx, [lastLine] ;Quit if we're at end of file
je @@Lx
@@L1:
mov dx, di
call RScanE ;Is there text between here and end of line?
cmp di, LINEWIDTH
mov di, dx
je @@L2
call LScanNE ;Yes. Is there a space at which to split line?
or di, di
mov di, dx
jle @@Lx ;No, stop wrapping
call FindBreak ;Yes, split this line, drop to lower line
call CRet
jmp Wrap
@@L2:
call EndLine ;Put cursor after end of text on line.
mov al, [es:di-1] ;If last char is in {.;:!?}, add two spaces
cmp al, 40h
jae @@L2b
cmp al, '.'
je @@L2a
cmp al, ';'
je @@L2a
cmp al, ':'
je @@L2a
cmp al, '!'
je @@L2a
cmp al, '?'
jne @@L2b
@@L2a:
inc di
@@L2b:
inc di ;After other chars, just add one space
mov dx, [rMargin] ;Calculate spaces remaining on upper line ->DX
sub dx, di
ja @@L3 ;If no room for more text, next line
call Down
call KeepWrapping?
jnc @@Lx
jmp Wrap
@@L3:
mov [topSegPtr], bx ;Save pointers for this line
mov [topSeg], es
mov [topIndex], di
call Down ;Drop down to next line
call KeepWrapping? ;Does text start at L margin?
jc @@L4
@@Lx:
jmp NewLine ;No, we're done
@@L4:
add di, dx ;Yes, move words up from lower line
call FindBreak ;Find right place to break line
mov dx, di ;Set up pointers and count for move
mov si, [lMargin]
sub bp, si
jnz @@L4a ;If nothing will fit, next line
jmp Wrap
@@L4a:
mov ax, es
mov di, [topIndex]
mov es, [topSeg]
mov bx, [topSegPtr]
mov ds, ax
mov cx, bp
rep movsb ;Move part of lower line up
mov cx, cs
mov ds, cx
cmp dx, LINEWIDTH - 1 ;If nothing left on lower line,
jne @@L5
call Down
call DeleteLine ;Delete it
cmp bx, [lastLine] ;Done if that was last line
je @@Lx
mov es, [topSeg] ;Get top line back and keep wrapping
mov bx, [topSegPtr]
jmp Wrap
@@L5:
call Down ;Else slide lower line left
mov si, dx
mov di, [lMargin]
call CloseGap
jmp Wrap ;Continue wrapping with lower line
KeepWrapping?:
;Set DI to L margin. Return C = 1 if line <- BX has text starting at L margin.
mov di, [lMargin]
cmp [BYTE es:di], ' ' ;Fail if space at margin
je @@Lx
or di, di
je @@L1
push di
dec di ; or chars before margin
call LScanE
pop di
jne @@Lx
@@L1:
stc ; else return C = 1
db 3Ch ;'cmp al, ?' trick to skip one byte
@@Lx:
clc
ret
FindBreak:
;Find place to break line while wrapping. Return BP = last char to move.
cmp [BYTE es:di], ' '
je @@L1
inc di
call WordLeft
mov bp, di
ret
@@L1:
mov bp, di
jmp WordRight
DeleteToEOL:
;Delete from cursor to end of line
mov [justFound?], 0
push bx ;Save regs to return to current cursor position
push di
push es
push [word autoIndent?] ;Turn autoIndent off
mov [autoIndent?], 0
call Cret ;Do Enter then delete lower line
call DeleteWordL
call DeleteLine
pop [word autoIndent?]
pop es
pop di
pop bx
jmp NewLine
DeleteLine:
;Delete cursor line and append to buffer
mov [changed?], -1
cmp bx, [lastLine] ;If last line of file, just blank it
jne @@L1
jmp BlankThisLine
@@L1:
mov bp, [blockPtrsLast] ;Save segment of current line in delete buffer
mov [bp], es
inc bp
inc bp
mov [blockPtrsLast], bp
DeleteLineNS: ;Enter here if we don't want to save line
mov di, bx ;Delete line: destination = this line
mov si, di ;Source = next line
inc si
inc si
mov cx, [lastLine] ;Count = number of lines from here to end
mov ax, cx
dec ax
dec ax
mov [lastLine], ax
sub cx, bx
shr cx, 1
mov ax, ds ;Move line segment values above cursor down
mov es, ax
rep movsw
mov di, [lMargin] ;Home cursor on new line
jmp NewLine
DeleteWordL:
;Delete left to space or column zero
mov ah, 2 ;Check for Ctrl key down. ^[ or ESC?
int 16h ;If ^[, delete word left. If ESC, quit.
and al, 4
je DWLx
mov si, di ;Save cursor column
call WordLeft ;Tab left one word
CloseGap:
push di ;Close gap between di and si cursor positions
mov cx, LINEWIDTH
sub cx, si
push ds
mov ax, es
mov ds, ax
rep movsb
mov cx, LINEWIDTH ;Pad end of line with spaces
sub cx, di
mov al, ' '
rep stosb
pop ds
pop di
mov [changed?], -1
ret
DWLx:
jmp Exit
DeleteWordR:
;Delete right to space or end of line
mov si, di ;Save cursor
push di
call WordRight ;Tab right one word
xchg si, di ;Close up space between si and di
pop di
jmp CloseGap
ToggleIns:
not [inserting?]
ret
Jump:
;Jump to line number n
mov si, OFFSET gotoMsg
call Prompt
call GetInt
dec ax
shl ax, 1
mov bx, ax
add bx, OFFSET linePtrs
mov [justFound?], 0
jmp SHORT JL1 ;Jump to address
GetInt:
;Get a decimal integer from keyboard to AX. Carry set on improper input.
;Abort if null input.
push bx
push cx
push dx
push si
mov dx, OFFSET buffer
call GetString ;Input a string
mov cx, ax ;Construct integer a digit at a time
mov si, OFFSET buffer
sub ax, ax
mov bh, 10
@@L1:
mov bl, [si] ;Get next char
inc si
sub bl, '0'
jc @@Lx ;Exit with carry set if not a digit
cmp bl, '9'
cmc
jc @@Lx
mul bh ;AX := AX + (new digit - '0')
add al, bl
adc ah, 0
jc @@Lx ;Check for overflow
loop @@L1 ;Next char
@@Lx:
pop si ;Return with int in AX, carry set if error
pop dx
pop cx
pop bx
ret
@@La:
mov si, OFFSET cancelledMsg
jmp Abort
SetLabel:
;Set label 0-9 at current line
call GetLabel
mov [si], bx
ret
GotoLabel:
;Goto label 0-9 previously set by SetLabel
call GetLabel
cmp [WORD si], 0 ;Cancel if label not assigned
je JLx
mov bx, [si] ;Retrieve address
mov [justFound?], 0
JL1:
mov ax, bx
sub ax, 8 ;Make cursor line fifth from top
cmp ax, OFFSET linePtrs
jge @@L1
mov ax, OFFSET linePtrs
@@L1:
mov [top], ax
JLx:
jmp NewLine0
GetLabel:
mov si, OFFSET setLabelMsg
call Prompt
mov ah, 8 ;Get char from keyboard
int 21h
mov dl, al ;Save copy to echo
sub al, '0' ;Don't accept input if not a digit
jl GetLabel
cmp al, 9
jg GetLabel
mov ah, 2
mov cl, al
int 21h
mov si, OFFSET LabelTable ;Form index into LabelTable
shl cl, 1
sub ch, ch
add si, cx ;Return address of label storage in SI
ret
SetTabs:
;Set tab width
mov si, OFFSET setTabsMsg
call Prompt
call GetInt
mov [tabSize], al
ret
AutoIndent:
;Toggle autoindent mode
not [autoIndent?]
ret
Kill:
;Toggle changed? flag for file changes to be discarded on exit
not [changed?]
ret
Save:
;Write lines to file, renaming old version with .BAK extension
cmp [changed?], 0 ;If no changes, done.
je XSave
push dx
push di
push es
push bx
mov al, [newFile?] ;If a new file, create it first
or al, [isBAKed?] ;If already BAKed up, no .BAK needed
jnz DoSave
mov ax, ds ;Else make new ASCIIZ str with .BAK extension
mov es, ax
mov si, OFFSET fName
mov di, OFFSET fNameBAK
@@L1:
lodsb
cmp al, '.'
je @@L2
or al, al
je @@L2
stosb
jmp SHORT @@L1
@@L2:
mov cx, 5
mov si, OFFSET BAK
rep movsb
mov ah, 41h ;Delete old back-up copy if present
mov dx, OFFSET fNameBAK
int 21h
mov dx, OFFSET fName ;Rename current file to file.BAK
mov di, OFFSET fNameBAK
mov ah, 56h
int 21h
DoSave:
mov ah, 3Ch ;CREATe new file with old name
sub cx, cx
mov dx, OFFSET fName
int 21h
jc CantOpen
mov [fHandle], ax
mov [isBAKed?], -1 ;Set flag so we only make .BAK file once
mov bx, OFFSET linePtrs ;Write file
call WriteFile
mov ah, 3Eh ;Close file
mov bx, [fHandle]
int 21h
pop bx
pop es
pop di
pop dx
mov [changed?], 0
XSave:
ret
WriteFile:
;Write lines out to file [fHandle] starting at BX and ending at [lastLine]
push es
push di
mov di, OFFSET buffer
@@L1:
mov si, di ;Preserve file buffer pointer
mov es, [bx] ;Strip trailing blanks
mov cx, LINEWIDTH
mov di, LINEWIDTH - 1
mov al, ' '
std
repe scasb
cld
je @@L1a
inc cx
@@L1a:
mov ax, ds ;Copy line to file buffer
mov dx, es
mov es, ax
mov ds, dx
mov di, si
sub si, si
rep movsb
mov ax, CRLF ;Stick a CRLF on the end
stosw
mov ax, ds
mov dx, es
mov es, ax
mov ds, dx
cmp di, OFFSET Buffer + BUFFERLENGTH - 80 ;If buffer is almost full,
jl @@L2
call WriteBuffer ; write it
@@L2:
inc bx ;Next line, loop until all lines are written
inc bx
cmp bx, [lastLine]
jle @@L1
dec di ;Delete last CR, LF
dec di
call WriteBuffer ;Write final partial buffer to file and exit
pop di
pop es
ret
FileError:
mov si, OFFSET fileErrorMsg
jmp Abort
CantOpen:
mov si, OFFSET cantOpenMsg
jmp Abort
NoRoom:
mov si, OFFSET noRoomMsg
jmp Abort
ReadError:
mov si, OFFSET rdErrorMsg
jmp Abort
DiskFull:
mov dx, OFFSET fName ;Attempt to delete file first
mov ah, 41h
int 21h
mov si, OFFSET diskFullMsg
jmp Abort
WriteBuffer:
;Write text in buffer to disk
push bx
mov bx, [fHandle]
mov cx, di
mov dx, OFFSET Buffer
sub cx, dx
jle @@L1
push cx
mov ah, 40h
int 21h
jc FileError
pop cx
cmp cx, ax
jne DiskFull
mov di, OFFSET Buffer
@@L1:
pop bx
ret
Exit:
call Save ;Save file if changed
call RestoreCursor
mov ax, 4C00h ;Bye!
int 21h
RestoreCursor:
mov cx, [cursorShape] ;Restore standard cursor size
mov ah, 1
int 10h
mov dx, 1800h ;Put cursor at bottom of screen
sub bh, bh
mov ah, 2
int 10h
ret
BeginBlock:
;Start marking block for block operation
mov [marking?], -1
mov [mark], bx
ret
Unmark:
;Clear marking
mov [marking?], 0
ret
InsertBlock:
;Insert from buffer or named file
push bx
call FileOrBuffer? ;From file or buffer?
je InsertBuffer
mov dx, OFFSET fNameTemp ;If file, open it
mov ax, 3D00h
int 21h
jnc @@L1
jmp CantOpen
@@L1:
mov dx, ax ;Load file
mov bx, OFFSET blockPtrs
mov di, bx
call ReadFile
mov cx, bx
mov bx, dx ;Close file
mov ah, 3Eh
int 21h
jmp SHORT DoInsert
InsertBuffer: ;Insert from buffer
mov bp, [blockPtrsLast] ;Abort if empty
cmp bp, OFFSET blockPtrs
jne @@L0
pop bx
jmp na
@@L0:
test [needCopies?], -1 ;If not just moving lines, need to duplicate
jz @@L2
push bx
push es
push ds
mov bx, bp
mov dx, [heapPtr]
@@L1:
dec bx ;Copy contents of buffered lines to new ones
dec bx
mov ds, [cs:bx]
sub si, si
mov es, dx
sub di, di
mov [cs:bx], es
mov cx, LINEWIDTH
rep movsb
add dx, 5
cmp dx, 0A000h
jb @@L1a
jmp NoRoom
@@L1a:
cmp bx, OFFSET blockPtrs
ja @@L1
pop ds
pop es
pop bx
mov [heapPtr], dx
@@L2:
mov [needCopies?], -1
mov cx, bp
DoInsert:
pop bx
sub cx, OFFSET blockPtrs ;Get count of lines to move
shr cx, 1
call OpenRow ;Open that much room in array of seg pointers
mov si, OFFSET blockPtrs ;Copy new lines into opening
mov di, bx
mov ax, ds
mov es, ax
rep movsw
sub di, di
jmp NewLineC
FileOrBuffer?:
;Prompt for file name if Shift is down, put it in fNameTemp.
;If return with Z set, Shift was up, indicating buffer is to be used.
call Shifted? ;If shift is down, prompt for file name
jz Ret4
mov dx, OFFSET fNameTemp
call GetFileName
jnz Ret4 ;If null string returned, abort
mov si, OFFSET cancelledMsg
jmp Abort
Shifted?:
;Get shift key status, returned in AL. Z set if no shift.
mov ah, 2
int 16h
and al, 3
Ret4:
ret
EmptyBuffer:
;If Shifted, write block buffer to file, else discard
mov bp, [blockPtrsLast] ;Abort if buffer is empty
cmp bp, OFFSET blockPtrs
jne @@L0
jmp Beep
@@L0:
call Shifted? ;If shifted, write to file
je @@L1
push bx
mov dx, OFFSET fNameTemp
call GetFileName
mov si, OFFSET blockPtrs
mov bx, bp
dec bx
dec bx
call WriteBlock
pop bx
@@L1:
mov ax, OFFSET blockPtrs ;Else just reset buffer pointer
mov [blockPtrsLast], ax
@@Lx:
ret
WriteBlock:
;Write block to file. SI -> starting seg pointer, BX -> ending seg pointer.
push [lastLine] ;Copy block buffered lines to file:
push [fHandle]
mov ah, 3Ch ;CREATe file
sub cx, cx
mov dx, OFFSET fNameTemp
int 21h
jnc @@L1
jmp CantOpen
@@L1:
mov [fHandle], ax ;Write it
mov [lastLine], bx
mov bx, si
call WriteFile
mov bx, [fHandle] ;Close it
mov ah, 3Eh
int 21h
pop [fHandle]
pop [lastLine]
ret
Copy:
;Copy marked lines, to file if shift is down, otherwise to buffer.
test [marking?], -1 ;Abort with a beep if not already marking,
jnz @@L1
mov si, OFFSET notMarkingMsg
jmp Abort
@@L1:
mov si, [mark]
cmp bx, si ;If mark comes after here, exchange
jae @@L2
xchg bx, si
mov [mark], si ; save in this order for possible delete
@@L2:
push bx
push di
push es
call FileOrBuffer? ;If Shift key was down when command entered,
je @@L4
call WriteBlock
mov di, OFFSET blockPtrs
jmp SHORT @@Lx
@@L4:
mov cx, bx ;If no Shift, move marked lines to buffer
sub cx, si
shr cx, 1
inc cx
mov di, OFFSET blockPtrs
mov ax, ds
mov es, ax
rep movsw
@@Lx:
mov [blockPtrsLast], di ;Save pointer to last line
pop es
pop di
pop bx
mov [marking?], 0
ret
DeleteBlock:
;Do a Copy, then delete copied lines
call Copy ;Copy block to file or buffer
mov si, bx ;Close up copied lines:
inc si ;SI = cursor line + 2
inc si
mov di, [mark] ;DI = start of marking
mov cx, [lastLine] ;CX = number of lines from here to end
sub cx, bx
push es
mov ax, ds
mov es, ax
rep movsb
pop es
dec di
dec di
cmp di, OFFSET linePtrs ;If whole file deleted, start new file
jae @@L1
call NewFile
jmp NewLineC
@@L1:
mov [lastLine], di ;Else store index of new last line
mov di, [lMargin] ;Point cursor to start of old marked line
mov bx, [mark]
mov [needCopies?], 0
jmp NewLineC
Find:
;Prompt for and find next <string>. If Shifted, reuse last <string>.
push bx
push es
push di
test [autoReplace?], -1 ;If doing replace all, bypass shift check
js @@L1
jnz @@L4
call Shifted? ;If Shifted,
jnz @@L4
@@L1:
mov si, OFFSET findMsg ;Get search string
mov dx, OFFSET findString
call GetCountedString
sub ah, ah
mov cx, ax
mov si, OFFSET findString+1 ;Convert to all upper case
@@L2:
lodsb
cmp al, 'a'
jb @@L3
cmp al, 'z'
ja @@L3
add al, 'A' - 'a'
mov [si-1], al
@@L3:
loop @@L2
@@L4:
mov dl, [byte findString] ;Save copy of string length
mov cx, LINEWIDTH-2
sub cx, di
inc di
mov si, di
FindLoop:
mov di, OFFSET findString+1 ;Get pointer to find string
mov ah, [di] ;AH = first char of string to find
@@L1:
seges ;Quick match for first char
lodsb
xor al, ah
and al, 11011111b ;(case-insensitive)
loopne @@L1
jcxz FindNextLine
mov [temp], si ;First char matched, save place, check rest
mov [SPTemp], cx
dec si
mov dh, dl
@@L2:
seges ;Get a char of find string
lodsb
mov ah, [di] ;Convert text char to upper case
inc di
cmp al, 'a'
jb @@L3
cmp al, 'z'
ja @@L3
add al, 'A' - 'a'
@@L3:
cmp al, ah ;Compare
jne @@L4
dec dh
loopnz @@L2
jnz FindNextLine
mov di, si ;Match. Point cursor at start of found string.
sub di, dx
add sp, 6 ;Discard old line index, segment
mov [justFound?], -1
mov ax, bx ;Show found line 5 below top of screen
jmp JL1
@@L4:
mov si, [temp] ;Match failed. Keep looking.
mov cx, [SPTemp]
jmp FindLoop
FindNextLine:
sub si, si ;Search next line (until EOF)
mov cl, LINEWIDTH-1
inc bx
inc bx
mov es, [bx]
cmp bx, [lastLine]
jbe FindLoop
mov si, OFFSET notFoundMsg ;Not found
test [autoReplace?], -1 ;If doing auto-replace, return
jz @@L1
mov [autoReplace?], 0
pop di
pop es
pop bx
ret
@@L1:
call Print0 ;Else restore cursor, abort with error message
pop di
pop es
pop bx
jmp na
GetCountedString:
;Print prompt string at [SI], read counted string into [DX].
;Returns count (minus CR/LF) in AL. DX is advanced one to start of string.
push di
push dx
call Prompt ;Display prompt
pop dx
mov di, dx
inc dx
call GetString ;Get input string
mov [di], al ;Store count in front of string
pop di
ret
Replace:
push di
test [autoReplace?], -1 ;If using auto-replace command,
jz @@L0
jns @@L2 ;Skip shift check
mov [autoReplace?], 1
jmp SHORT @@L1a ;Get replace string if shifted and first pass
@@L0:
test [justFound?], -1 ;Beep if not immediately preceded by a Find
jnz @@L1
@@LE:
pop di
jmp na
@@L1:
call Shifted? ;If not Shifted, prompt for replace string
jnz @@L2
@@L1a:
mov si, OFFSET replaceMsg
mov dx, OFFSET replaceString
call GetCountedString
@@L2:
mov si, OFFSET replaceString
lodsb
sub ah, ah
mov dx, si
push ax
push dx
sub ch, ch ;Compare lengths of find and replace strings
mov cl, al
sub cl, [byte findString] ;If replace string is longer,
je @@L6
jb @@L4
xchg dx, di
call EndLine ; make sure there will be enough room on line
xchg dx, di
add dl, cl
cmp dl, LINEWIDTH
ja @@LE
@@L3:
call Insert0 ; then insert extra characters
loop @@L3
jmp SHORT @@L6
@@L4:
neg cl ;If shorter, delete difference
@@L5:
call Delete0
loop @@L5
@@L6:
pop si ;Now copy new string over old
pop cx
sub ch, ch
pop di
rep movsb
jmp NewLineC
ReplaceAll:
;Find and replace all. If shift not down, prompt for strings.
push [top]
push es
push di
push bx
mov [autoReplace?], 1 ;Set flag: 1 = no shift, -1 = shift
call Shifted?
jne @@L1
mov [autoReplace?], -1
@@L1:
call Find ;Find/replace until flag reset by not found
test [autoReplace?], -1
jz @@Lx
call Replace
jmp @@L1
@@Lx:
pop bx ;Restore original position
pop di
pop es
pop [top]
push si
call ReDraw ;Refresh screen with any changes
pop si
push di
call Print0 ;Show notFoundMsg and exit
pop di
jmp NextNoRedraw
Help:
push es
push di
mov es, [videoSegment]
mov di, 160
mov si, OFFSET helpMsg
mov cx, 80 * 24
mov ah, [attribNl]
@@L1:
lodsb
stosw
loop @@L1
mov si, OFFSET anyKeyMsg
call Print0
sub ah, ah
int 16h
pop di
pop es
ret
F3BAT:
mov dl, '3'
jmp SHORT FnBAT
F4BAT:
mov dl, '4'
jmp SHORT FnBAT
F5BAT:
mov dl, '5'
jmp SHORT FnBAT
F6BAT:
mov dl, '6'
FnBAT:
;Execute 'EFn.BAT f' where n = DL and f = current file name minus extension
push es
push di
mov [EXECFNumber], dl ;Which .BAT file?
mov ax, cs ;Copy file name to command line string
mov es, ax
mov di, OFFSET EXECFileName
mov si, OFFSET fName
sub cl, cl
@@L1:
lodsb
or al, al ; up to '.' or null (ie, omit extension)
je @@L2
cmp al, '.'
je @@L2
stosb
inc cl
jmp @@L1
@@L2:
mov al, CR
stosb
add cl, 11
mov [EXECBAT], cl ;Store count to include file name
pop di
pop es
mov ax, OFFSET EXECBAT
jmp SHORT Shell1
Shell:
;Shell to DOS
mov ax, OFFSET EXECParams ;Point to null command line tail
Shell1:
mov [EXECCmdLineOff], ax
push bx ;Save regs
push es
push di
call Shifted? ;Get and save shift status
pushf
call Save ;Save file being edited
popf ;If Shift was down, release file, buffers
jz @@L1
mov bx, ((PROGLENGTH + ESSENTIALS) SHR 4) + 1
mov [swapped?], -1
jmp SHORT @@L2
@@L1:
mov bx, [heapPtr] ; else keep file and buffers in memory
mov ax, cs
sub bx, ax
mov [swapped?], 0
@@L2:
mov ax, cs ;Free all unneeded memory
mov es, ax
mov ah, 4Ah
int 21h
jc SysErr
call RestoreCursor ;Restore cursor
mov [SPTemp], sp ;Do EXEC fn
mov dx, [comspecPtrOff]
mov ds, [comspecPtrSeg]
mov bx, OFFSET EXECParams
mov ax, 4B00h
int 21h
mov ax, cs ;Reclaim memory
mov ds, ax
mov ss, ax
mov sp, [spTemp]
jc SysErr
mov ah, 4Ah
mov bx, -1
int 21h
mov ah, 4Ah
int 21h
jc SysErr
cmp [swapped?], 0 ;If edited file was swapped out,
je XEXEC
mov dx, OFFSET fName ;Read it back in
call OpenFile
XEXEC:
pop di ;Restore regs and return
pop es
pop bx
jmp NewLine
SysErr:
mov si, OFFSET sysErrMsg
jmp Abort
EndOfProgram:
END Orig