;Programmer's editor. Assemble with TASM. (C) David Nye, 1989. ; changed for PortFolio + PC Bernhard Konze, 1994. ; PC : tasm /dtcnt= e =TabCount ; PortFolio: tasm /dpf e ( Test with PC: tasm /dpf /dpc e ) ; finish : tlink e IDEAL DOSSEG MODEL TINY MACRO String Name, Text LOCAL L1 Name db L1-Name-1, Text L1: ENDM ;Constants ifdef pf LINELENGTH EQU 80 ;Length of string storage for line LINESEGS EQU 5 SCREENLENGTH EQU 7 ;Number of rows in display window RAM_TOP EQU 08000h ;Portfolio 512kB MAXLINES EQU 2000 ;Size of array of line pointers BUFFERLENGTH EQU 1800h ;Length of file buffer else LINELENGTH EQU 128 ;Length of string storage for line LINESEGS EQU 8 SCREENLENGTH EQU 24 ;Number of rows in display window RAM_TOP EQU 0A000h ;PC 640kB MAXLINES EQU 5000 ;Size of array of line pointers BUFFERLENGTH EQU 1800h ;Length of file buffer endif T_LEFT EQU 75 T_RIGHT EQU 77 T_UP EQU 72 T_DOWN EQU 80 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 TEXTMARK EQU 1 FINDMARK EQU 2 DATASEG ;Strings String cantOpenMsg, "Can't open file." String rdErrorMsg, 'Error reading file.' String fileErrorMsg, 'File error.' String noRoomMsg, 'Out of memory.' String noLinesMsg, 'To much lines.' String noInsertMsg, 'Cannot insert - line is full.' String noInsertMsg2, 'Cannot insert text - line is full.' String notMarkingMsg, 'Not marking.' String readFileMsg, 'Reading File ...' String setLabelMsg, 'Label (0-9): ' String gotoMsg 'Jump to what line? ' String setTabsMsg, 'Tab width: ' String newFileMsg, 'File name: ' ifdef pf String editingMsg <'F1',186,''> else String editingMsg <' F1 ',186,' '> endif String textMsg 'Text: ' String findMsg 'Find: ' String replaceMsg 'Replace with: ' String doReplaceMsg 'Replace (y/n/a/q).' String notFoundMsg 'No more matching strings found.' String endHelpMsg 'HELP - Press any key to continue.' String endEscMsg 'EXIT - Save with (Y,CR).' String ctrlCMsg '*Break*' String cancelledMsg 'Cancelled.' String copyright1 ' (C) David Nye, 1989.' String copyright2 ' (C) Bernhard Konze, 1994, V1.0 ' BAK db '.BAK', 0 tempFile db 'e.tmp', 0 ifdef pf helpPf1 db 'begin blk @b find @f again ^f ' , 40 dup ( ) db 'unmark blk @u text @t insert ^t ' , 40 dup ( ) db 'copy blk buf @c * find & replace @r ' , 40 dup ( ) db 'del blk buf @d * graphics insert @g ' , 40 dup ( ) db 'ins blk b @v,@i * s(ave) and cont @s ' , 40 dup ( ) db 'kill blk buf @e * s and exit ESC,@x ' , 40 dup ( ) db 'dup line @z kill s on exit @k ' , 40 dup ( ) helpPf2 db 'autoindent @a jump to line # @j ' , 40 dup ( ) db 'another file @o set tab width F2 ' , 40 dup ( ) db 'set label @l0-9 go to label @0-9' , 40 dup ( ) db 'start of file ^PgUp word< ^left arrow ' , 40 dup ( ) db 'end of file ^PgDn word> ^right arrow ' , 40 dup ( ) db 'delete: line ^d,^y undo ^u,^x bs del ' , 40 dup ( ) db '@ = Alt, ^ = Ctrl, * = file if shifted ' , 40 dup ( ) else helpMsg db 'left left arrow ' db ' begin block @b ' db 'right right arrow ' db ' copy block to buffer @c * ' db 'word left ^left arrow ' db ' delete block to buffer @d * ' db 'word right ^right arrow ' db ' insert from buf @v, @i * ' db 'start of line Home ' db ' empty block buffer @e * ' db 'end of line End ' db ' unmark @u ' db 'up up arrow ' db ' graphics insert @g ' db 'down down arrow ' db ' find @f ' db 'page up PgUp ' db ' find again ^f ' db 'page down PgDn ' db ' find/replace all @r ' db 'start of file ^PgUp ' db ' text @t ' db 'end of file ^PgDn ' db ' insert text ^t ' db 40 dup ( ) db ' save and continue @s ' db 'backspace Backspace ' db ' save and exit ESC, @x ' db 'delete Del ' db ' kill save on exit @k ' db 40 dup ( ) db ' label (0-9) @l ' db 40 dup ( ) db ' go to label (0-9) @0-9 ' db 'delete line ^y, ^d ' db ' jump to line # @j ' db 'undelete line ^x, ^u ' db ' set tab width F2 ' db 'duplicate line @z ' db ' toggle autoindent @a ' db 'toggle insert mode Ins ' db ' open another file @o ' db 80 dup ( ) db '@ = Alt, ^ = Ctrl, * = to/from file if s' db 'hifted. ' db 'Status line flags: Insert Overwrite C' db 'hanged AutoIndent ' endif ;Variables attribNl db 1eh ;Default video attributes attribInv db 70h ifdef pf tabSize db 2 ;Tab increment else tabSize db TCNT ;Tab increment endif newFile? db ? ;True if new file inserting? db -1 ;True if in insert mode autoIndent? db -1 ;True if in autoindent mode marking? db 0 ;True if marking block tMarking? db 0 ;True if marking text tMarkAct? db ? ;True if actual line 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 skipGetStr? db 0 ;True if string already exists doReplace? db 0 ;True if find + replace active allReplace? db 0 ;True if all, 0 = ask for replace fName? db -1 ;True if file name given on command line redrawAll? db 1 ;True if screen and status both are refreshed lines24? db 0 ;True if previous screen had had 24 lines vgaDeSelect? db 1 ;True if no vga-mode selected fInsert? db 0 ;True if file is inserted row db ? ;Current row column db ? ;Current column fHandle dw ? ;File handle lastLine dw ? ;Index of last line top dw ? ;Index of first line on screen bottom dw ? ;Index of last line on screen bufferPtr dw ? ;Multipurpose buffer pointer mark dw ? ;Start of marking for block command here dw ? ;Temporary hereCol dw ? ;Temporary tMark dw ? ;Temporary tMarkStart dw ? ;Temporary tMarkEnd dw ? ;Temporary screenLines dw ? ;Actual screenlength screenLOffs dw ? ;Actual (screenlength-1)*2 pfOffs dw ? ;Absolut-col for Tab-Expand lineOffs dw ? ;Line-Offset for Tab-Expand colOffs dw 0 ;Column-Offset for Draw-Window colOffsCnt dw ? ;Column-Offset for Draw-Window byteCnt dw ? ;Temporary byte count videoSegment dw ? ;Segment of system's video memory fName db 20 dup (?) ;File name in ASCIIZ format fNameBAK db 20 dup (?) ;Current file with .BAK extension added fNameBlock db 20 dup (?) ;File to be used in block read/writes pad db 20 dup (?) ;Scratch buffer tempLine dw LINELENGTH dup (?) ;Scratch line buffer textString dw LINELENGTH dup (?) ;Search string for Find command findString dw LINELENGTH dup (?) ;Search string for Find command replaceString dw LINELENGTH dup (?) ;New string for Replace command heapStart dw ? ;Segment of start of heap heapPtr dw ? ;Segment pointer to next free paragraph in heap labelTable dw 10 dup (0) ;Table of line pointers assigned to labels linePtrs dw MAXLINES dup (?) ;List of line pointers blockPtrs dw MAXLINES dup (?) ;Line pointers for block commands blockPtrsLast dw OFFSET blockPtrs delLinePtrs dw MAXLINES dup (?) ;Line pointers for del/undel lines delLinePtrsLast dw OFFSET delLinePtrs buffer db BUFFERLENGTH dup (?) ;File and delete buffer (dual purpose) bufferEnd: ;Jump tables: ^ = Ctrl, @ = Alt, # = Shift. ctrlTable dw na ;^@ dw na ;^A dw na ;^B dw na ;^C dw DeleteLine ;^D dw na ;^E dw FindAgain ;^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 na ;^R or DC2 dw na ;^S or DC3 dw InsertText ;^T or DC4 dw UndeleteLine ;^U dw na ;^V dw na ;^W dw UndeleteLine ;^X or CAN dw DeleteLine ;^Y dw na ;^Z dw ExitEsc ;^[ or ESC dw na ;^\ dw na ;^] dw na ;^^ dw na ;^- auxTable dw 15 DUP (na) ;Undefined (except for NULL = 3) dw na ;#Tab dw na ;@Q dw na ;@W dw EmptyBuffer ;@E dw AutoReplace ;@R dw BeginText ;@T dw na ;@Y dw Unmark ;@U dw InsertBlock ;@I dw OtherFile ;@O dw na ;@P dw 4 DUP (na) ;Undefined dw AutoIndent ;@A dw Save ;@S dw DeleteBlock ;@D dw Find ;@F dw Graphic ;@G dw Help ;@H dw Jump ;@J dw Kill ;@K dw SetLabel ;@L dw 5 DUP (na) ;Undefined dw DupLine ;@Z dw Exit ;@X dw Copy ;@C dw InsertBlock ;@V dw BeginBlock ;@B dw na ;@N dw na ;@M dw 8 DUP (na) ;Undefined dw Help ;F1 dw SetTabs ;F2 dw na ;F3 dw na ;F4 dw na ;F5 dw na ;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 30 DUP (na) ;[#Fn, ^Fn, @Fn] dw na ;^PrtSc dw WordLeft ;^Left arrow dw WordRight ;^Right arrow dw na ;^End dw BottomFile ;^PgDn dw na ;^Home dw Goto1 ;@1 dw Goto2 ;@2 dw Goto3 ;@3 dw Goto4 ;@4 dw Goto5 ;@5 dw Goto6 ;@6 dw Goto7 ;@7 dw Goto8 ;@8 dw Goto9 ;@9 dw Goto0 ;@0 dw na ;@- dw na ;@= dw TopFile ;^PgUp ;****************************************************************************** CODESEG mov ax, cs mov es, ax mov ah, 0Fh ;Get display segment int 10h mov bx, 0B000h ;B000h for video mode 7 (= MDA or Herc) cmp al, 7 je @@L0 mov bx, 0B800h ;B800h for the rest @@L0: mov [cs:videoSegment], bx mov si, 80h ;Make pointer to command tail mov cl, [si] ;Get filename length sub ch, ch push cx ;Save a copy mov al, ' ' ;Skip leading blanks @@L1: inc si cmp al, [si] loope @@L1 inc cx ;Plus CR mov al, [si] ;Get first parameter cmp al, '-' jne @@L3 inc si dec cx mov al, [si] and al, 0dfh cmp al, 'V' jne @@L2 mov [cs:vgaDeSelect?], 0 @@L2: add si, 2 ;Next argument sub cx, 2 @@L3: 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, SEG Cancel mov ds, dx mov dx, OFFSET Cancel int 21h InitFile: mov ax, cs ;Compute starting segment of heap mov ds, ax add ax, 1000h mov [heapStart], ax mov dx, OFFSET fName ;Open file and set up list of line pointers pop ax ;If no file name specified on command line, or ax, ax jne @@L0 call GetFileName ;Prompt for it @@L0: call OpenFile mov [screenLines], SCREENLENGTH ;Set screen ifndef pf push es push bx push di test [vgaDeSelect?], 1 jnz @@L2a mov ax, 1c00h mov cx, 1 int 10h cmp al, 1ch jne @@L2a mov ax, 40h mov es, ax mov bx, 84h mov al, [es:bx] push ax mov ax, 1112h mov bl, 0 int 10h mov [screenLines], 49 pop ax cmp al, 18h jne @@L2a mov [lines24?],1 @@L2a: pop di pop bx pop es endif mov ax, [cs:screenLines] dec ax shl ax,1 mov [cs:screenLOffs], ax ifdef pf ifndef pc mov ah,0 int 61h endif endif ; mov [changed?], -1 ; jmp exit 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 OtherFile: ;Close current file and open another test [changed?], -1 ;If previous file was changed, jz @@L0 call Save ;Save file and close it, jmp @@L1 @@L0: test [newFile?], -1 ;If unchanged but an old file, just close it jnz @@L2 @@L1: mov bx, [fHandle] mov ah, 3Eh int 21h @@L2: mov ax, [blockPtrsLast] ;If block buffer is empty, sub ax, OFFSET blockPtrs jne @@L2b @@L2a: mov dx, OFFSET fName ; prompt for new file name call GetFileName jmp OpenFile ; read it in @@L2b: 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, LINESEGS mul dl add ax, [heapStart] ;Calculate upper limit of target zone cmp ax, RAM_TOP ;Out of room? jae @@L2a ; yes - delete buffer mov [heapPtr], ax ; which will also be new value of heap pointer mov bx, OFFSET blockPtrs ;For each pointer in block buffer, mov bp, OFFSET delLinePtrs @@L3: cmp [bx], ax ;If its line is already within target zone, jae @@L4 mov dx, [bx] mov [bp], dx ; save pointer of existing line inc bp inc bp @@L4: inc bx ;Next pointer inc bx loop @@L3 mov [changed?], 1 ;Use as temporary flag mov [delLinePtrsLast], bp cmp bp, OFFSET delLinePtrs jne @@L41 mov [changed?], 0 ;No lines within saveblock @@L41: push ds mov bx, OFFSET blockPtrs ;For each pointer in block buffer: mov es, [heapStart] @@L42: test [changed?], 1 jz @@L5 mov bp, OFFSET delLinePtrs ;Now look if this line exists mov cx, [delLinePtrsLast] sub cx, OFFSET delLinePtrs shr cx, 1 @@L43: mov ax, [bx] cmp ax, [bp] jne @@L44 ;Line already in use, try next target line inc bx inc bx jmp @@L8 @@L44: inc bp inc bp loop @@L43 @@L5: mov ds, [cs:bx] ;Move one Line mov cx, LINELENGTH shr cx, 1 ;Words are moved sub si, si sub di, di rep movsw @@L5a: mov [cs:bx], es ;Update pointer @@L6: inc bx ;Next block pointer inc bx mov ax, es ;Next target line add ax, LINESEGS mov es, ax @@L8: cmp bx, [cs:blockPtrsLast] ;Loop until all lines moved jb @@L42 pop ds mov dx, OFFSET fName ;Prompt for new file name call GetFileName jmp OpenFile1 ;Read in new file above block buffer lines OpenFile: ;Open file, load if found. 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 ax, OFFSET delLinePtrs ;Reset pointers mov [delLinePtrsLast], ax mov [newFile?], 0 mov [changed?], 0 mov ax, 3D02h ;Try to open file int 21h jnc OldFile mov [newFile?], -1 ;If no such file, create a new one call NewFile jmp XOpenFile OldFile: mov [fHandle], ax ;Else save file handle mov bx, OFFSET linePtrs ;Read file in mov dx, [fHandle] mov [fInsert?], 0 call ReadFile dec bx dec bx mov [lastLine], bx ;Save index of last line XOpenFile: mov bx, OFFSET linePtrs ;Reset row, screen pointers mov [top], bx mov es, [bx] sub di, di Ret0: 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 @@Lx: pop si Ret5: 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 mov dx, OFFSET pad ;Get string mov ah, 3Fh sub bx, bx mov cx, 20 int 21h dec ax ;Strip CR/LF dec ax jz @@La ;Abort if null string 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 ret @@La: mov si, OFFSET cancelledMsg ;Abort if null string jmp Abort ReadFile: ;Load file with handle in DX into memory, assigning segment pointers from BX push es mov si, OFFSET readFileMsg ;Print prompt call Print0 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 NextChar inc si NextChar: mov al, [si] ;Get next char cmp al, CR ;If char is CR, end of line jne @@L1c inc si ; move past CR cmp [byte si], LF ; and LF if present jne @@L1b @@L1a: inc si @@L1b: call EndOfLine ;Pad out line with spaces and save it jmp @@L3 @@L1c: cmp al, LF ;Or LF if present je @@L1a cmp di, LINELENGTH ja @@L1b inc si cmp al, HT ;Else if a tab, expand it jne @@L2c push dx ;Save file handle mov ax, di ;Calculate character count mov dl, [tabSize] sub dh, dh div dl mov dl, [tabSize] sub dl, ah mov ax, di ;Look for lineoverflow add ax, dx cmp ax, LINELENGTH jb @@L1d pop dx ;Retore file handle jmp @@L1b @@L1d: mov al, 0 ;$00 = 1st space @@L2a: stosb dec cx dec dx jnz @@L2b pop dx ;Retore file handle jz @@L3 @@L2b: mov al, 0ffh ;$ff = other spaces jmp @@L2a @@L2c: stosb ;Else add char to line dec cx @@L3: cmp si, [bufferPtr] ;Loop until end of buffer jb NextChar cmp si, OFFSET bufferEnd ;If buffer less than full, indicates end of file jb EndOfFile jmp FillBuffer EndOfFile: cmp bx, OFFSET linePtrs ;If an empty file, jne @@L0 call NewFile ;Set up as a new file (single blank line) jmp @@L1 @@L0: or di, di ;Finish up present line if anything is on it je @@L1 call EndOfLine @@L1: mov [heapPtr], es ;Update pointer to start of free heap space pop es ret EndOfLine: add cx, LINELENGTH ;Pad to end with spaces jle @@L1 ;Truncate lines longer than 80 chars mov al, ' ' rep stosb @@L1: mov [bx], es ;Store segment of this line mov ax, es ;Next line add ax, LINESEGS cmp ax, RAM_TOP ;Out of room? jae TooBig mov es, ax inc bx inc bx push bx ;Test line overflow test [fInsert?], 1 jne @@L1a sub bx, OFFSET linePtrs jmp @@L1b @@L1a: sub bx, OFFSET blockPtrs @@L1b: cmp bx, MAXLINES * 2 jb @@L2 mov ah, 3Eh ;Close file mov bx, [fHandle] int 21h mov si, OFFSET noLinesMsg call Print0 ;Print error message ... call Beep ;Beep sub ah, ah ;Get keypress int 16h ifndef pf test [lines24?], 1 je @@LL24 mov ax, 1114h mov bl, 0 int 10h @@LL24: endif mov ax, 4C00h ;Bye! int 21h @@L2: pop bx sub di, di sub cx, cx Ret3: ret TooBig: mov si, OFFSET noRoomMsg jmp Abort Redraw: ;Redraw screen and status line mov [redrawAll?], 1 jmp ReDraw1 RedrawScreen: ;Redraw only screen mov [redrawAll?], 0 ReDraw1: mov [here], bx mov [hereCol], di push bx push di push es push ds mov es, [videoSegment] ;Get segment for display mov di, 160 ;Move to next display line mov ax, [top] ;Compute bottom of screen mov bx, ax add ax, [screenLOffs] cmp ax, [lastLine] ;If at end of file, jle @@L0 mov ax, [lastLine] ; stop at lastLine @@L0: mov [bottom], ax mov [pfOffs], 0 ;For each row @@L1: mov cx, [cs:colOffs] mov [cs:colOffsCnt], cx mov cx, 80 ;Count of chars per row mov ds, [cs:bx] ;Get pointer to screen line sub si, si ;Initialize column counter mov ah, [cs:attribNl] ;Attribute = inverse video if Marked test [cs:tMarking?], TEXTMARK+FINDMARK je @@L1a0 mov [cs:tMarkAct?], 0 cmp bx, [cs:tMark] jne @@L2 cmp bx, [cs:here] jne @@L2 mov [cs:tMarkAct?], 1 jmp @@L2 @@L1a0: test [cs:marking?], -1 je @@L2 cmp bx, [cs:mark] je @@L1b jb @@L1a cmp bx, [cs:here] jbe @@L1b jmp @@L2 @@L1a: cmp bx, [cs:here] jb @@L2 @@L1b: ifdef pf push ax mov al,16 stosw dec cx inc [cs:pfOffs] pop ax else mov ah, [cs:attribInv] endif @@L2: lodsb ;For each char, write char and attribute test [cs:tMarkAct?], 1 je @@M9 ifdef pf dec si cmp si, [cs:tMarkStart] jne @@PF1 push ax mov al,16 stosw inc [cs:pfOffs] dec cx or cx, cx ;Last byte in line ? jne @@PF0 dec [cs:pfOffs] inc cx ;Yes - set to 1 for LOOP @@PF0: pop ax @@PF1: inc si endif mov ah, [cs:attribNl] ;Attribute = inverse video if Marked cmp si, [cs:tMarkStart] jb @@M9 je @@M9 cmp si, [cs:hereCol] je @@M1 jnb @@M9 @@M1: ifndef pf mov ah, [cs:attribInv] endif mov [cs:tMarkEnd], si @@M9: test [cs:colOffsCnt], -1 jz @@P1 dec [cs:colOffsCnt] inc cx jmp @@L2c @@L1h: jmp @@L1 @@P1: stosw @@L2c: loop @@L2 inc bx ;Next row inc bx cmp bx, [cs:bottom] ;Stop if screen full jle @@L1h mov cx, [cs:screenLines] ;Fill out screen with blanks inc cx mov ax, 160 mul cl mov cx, ax sub cx, di shr cx, 1 mov ah, [cs:attribNl] mov al, ' ' rep stosw @@L3: pop ds ;Restore ds test [redrawAll?], 1 je @@S7 mov si, OFFSET editingMsg ;Refresh status line call Prompt ifdef pf mov di, 6 ;Tab to column 3 else mov di, 12 ;Tab to column 3 endif mov si, OFFSET fName ; mov ah, [attribInv] @@S1: lodsb or al, al je @@S2 stosw jmp @@S1 @@S2: ifdef pf add di, 2 ;1 spaces else add di, 4 ;2 spaces endif mov al, 'L' ;"L" stosw ifndef pf inc di inc di endif mov ax, [here] sub ax, OFFSET linePtrs shr ax, 1 inc ax call PrintInt ifdef pf add di, 2 ;1 spaces else add di, 4 ;2 spaces endif mov al, 'C' ;"C" mov ah, [attribInv] stosw ifndef pf inc di inc di endif mov ax, [hereCol] add ax, [pfOffs] inc ax call PrintInt ifdef pf add di, 2 ;1 spaces else add di, 4 ;2 spaces endif 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' test [autoIndent?], -1 jne @@S6 mov al, ' ' @@S6: stosw @@S7: ifdef pf ifndef pc push ax mov ah,12h int 61h pop ax endif endif pop es pop di pop bx ret DrawCursor: ;Set cursor shape and place it on screen mov ax, di sub ax, [colOffs] ifdef pf cmp ax, 40 else cmp ax, 80 endif jb @@L0 ifdef pf cmp [colOffs], LINELENGTH-40 else cmp [colOffs], LINELENGTH-80 endif jae @@L0 add [colOffs], 8 call Redraw jmp DrawCursor @@L0: push bx mov cl, 13 ;Set cursor shape depending on Inserting? mov ch, 12 ;Line for insert mode, test [inserting?], -1 je @@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 sub ax, [colOffs] mov dl, al mov ah, 2 mov bh, 0 int 10h pop bx mov ax, di sub ax, [colOffs] ifdef pf cmp ax, 32 else cmp ax, 72 endif jae @@L2 test [colOffs], -1 jz @@L2 sub [colOffs], 8 @@L2: ret RebuildLine: push bx push es push di push cx push es ;XChange es <-> ds push ds pop es pop ds mov di, OFFSET tempLine mov cx, LINELENGTH mov al, ' ' rep stosb mov di, OFFSET tempLine sub si, si mov cx, LINELENGTH @@L1: cmp si, LINELENGTH jae @@L3 lodsb @@L1a: cmp al, 0 ;Tab indicator ? jne @@L2c ; no copy char mov ax, di ;Calculate character count sub ax, OFFSET tempLine mov dl, [cs:tabSize] sub dh, dh div dl mov dl, [cs:tabSize] sub dl, ah push ax mov ax, di sub ax, OFFSET tempLine cmp ax, LINELENGTH pop ax jae @@L2b mov al, 0 ;$00 = 1st space @@L2a: stosb dec dx jz @@L2b push ax mov ax, di sub ax, OFFSET tempLine cmp ax, LINELENGTH pop ax jae @@L2b mov al, 0ffh ;$ff = other spaces jmp @@L2a @@L2b: cmp si, LINELENGTH jae @@L3 lodsb cmp al, 0ffh jne @@L1a @@L2b1: dec cx je @@L3 jmp @@L2b @@L2c: cmp al, 0ffh je @@L2b1 stosb loop @@L1 @@L3: push es ;XChange es <-> ds push ds pop es pop ds sub di, di ;Copy line back mov si, OFFSET tempLine mov cx, LINELENGTH rep movsb pop cx pop di pop es pop bx @@L6: mov al, [es:di] cmp al, 0ffh jne @@L7 dec di jmp @@L6 @@L7: 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 ifdef pf ifndef pc push ax mov ah,12h int 61h pop ax endif endif 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 ax, -2 ;Reset stack pointer to top mov sp, ax mov bx, [here] ;Retrieve cursor position in case it was trashed mov di, [hereCol] 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 push bx ;Position cursor at row, column (DL, DH) 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 or ax, ax ;If integer = 0, jne @@L0 push dx ; push another 0 and skip divisions jmp @@L2 @@L0: 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 @@L2: pop ax ;Pop and print remainders in reversed order @@L3: stosw pop ax or ax, ax jne @@L3 ret NewLine: ;Jump here if cursor row is changed by a command mov [tMarking?], 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, [screenLOffs] cmp bx, ax jle @@L4 mov ax, bx sub ax, [screenLOffs] mov [top], ax @@L4: cmp di, 0 jnz @@L5 mov [colOffs], 0 @@L5: mov es, [bx] ;Adjust ES to point to new line @@L6: mov al, [es:di] cmp al, 0ffh jne @@L7 dec di jmp @@L6 @@L7: ret Left: test [tMarking?], TEXTMARK+FINDMARK jne LeftTMark 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 [tMarking?], 0 CursorMoved2: @@L6: mov al, [es:di] cmp al, 0ffh jne @@L7 dec di jmp @@L6 @@L7: ret LeftTMark: cmp di, [tMarkStart] je CursorMoved2 dec di jmp CursorMoved2 Right: test [tMarking?], TEXTMARK+FINDMARK jne RightTMark push di call RScanE pop di je @@L0 cmp di, LINELENGTH-1 ;If at end of line, jne @@L1 @@L0: sub di, di ; move to start of line below jmp Down @@L1: inc di ; else just increment cursor RightTab: mov al, [es:di] cmp al, 0ffh jne @@L7 inc di cmp di, LINELENGTH-1 jz @@L7 jmp RightTab @@L7: jmp CursorMoved RightTMark: cmp di, LINELENGTH-1 ;If at end of line, je @@L2 inc di @@L1: mov al, [es:di] cmp al, 0ffh jne @@L2 inc di cmp di, LINELENGTH-1 jz @@L2 jmp @@L1 @@L2: jmp CursorMoved2 LScanE: ;Scan left past first non-space or start of line mov al, ' ' mov cx, di inc cx std ; repe scasb @@L0: mov ah, [es:di] dec di cmp al, ah je @@L0a cmp ah, 0 je @@L0a cmp ah, 0ffh jne @@L1 @@L0a: dec cx jne @@L0 mov ah, 0 ;Set zero or ah, ah @@L1: cld ret LScanNE: ;Scan left past first space or start of line mov al, ' ' mov cx, di inc cx std ; repne scasb @@L0: mov ah, [es:di] dec di cmp al, ah je @@L1 cmp ah, 0 je @@L1 cmp ah, 0ffh je @@L1 dec cx jne @@L0 mov ah, 1 ;Set non-zero or ah, ah @@L1: cld ret RScanE: ;Scan right past first non-space or end of line mov al, ' ' mov cx, LINELENGTH sub cx, di ; repe scasb @@L0: mov ah, [es:di] inc di cmp al, ah je @@L0a cmp ah, 0 je @@L0a cmp ah, 0ffh jne @@L1 @@L0a: dec cx jne @@L0 mov ah, 0 ;Set zero or ah, ah @@L1: ret RScanNE: ;Scan right past first space or end of line mov al, ' ' mov cx, LINELENGTH sub cx, di ; repne scasb @@L0: mov ah, [es:di] inc di cmp al, ah je @@L1 cmp ah, 0 je @@L1 cmp ah, 0ffh je @@L1 dec cx jne @@L0 mov ah, 1 ;Set non-zero or ah, ah @@L1: ret WordLeft: ;Move left one word or di, di ;Do nothing if at start of line je @@Lx mov [tMarking?], 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, LINELENGTH - 1 ;Do nothing if at end of line jb @@L0 call LScanE inc di jmp @@Lx @@L0: mov [tMarking?], 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 jne @@L1 jmp EndLine @@L1: dec di @@Lx: ret HomeLine: ;Move cursor to column zero sub di, di mov [colOffs], 0 mov [tMarking?], 0 ret EndLine: ;Move cursor to end of line push cx mov [tMarking?], 0 mov di, LINELENGTH - 1 ;Start at end of line call LScanE ;Skip all spaces until non-space je @@L1 ; or beginning of line inc di cmp di, LINELENGTH - 1 je @@L2 @@L1: inc di @@L2: 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, [screenLOffs] mov ax, [top] sub ax, [screenLOffs] cmp ax, OFFSET linePtrs jge @@L1 mov ax, OFFSET linePtrs @@L1: mov [top], ax jmp NewLine PageDown: ;Move cursor down one page add bx, [screenLOffs] mov ax, [top] add ax, [screenLOffs] add ax, [screenLOffs] cmp ax, [lastline] jle @@L1 mov ax, [lastline] @@L1: sub ax, [screenLOffs] cmp ax, OFFSET linePtrs jae @@L2 mov ax, OFFSET linePtrs @@L2: mov [top], ax jmp NewLine TopFile: ;Move cursor to top of file mov bx, OFFSET linePtrs mov [top], bx call HomeLine mov [colOffs], 0 jmp NewLine BottomFile: ;Move cursor to bottom of file mov bx, [lastLine] mov es, [bx] mov ax, bx sub ax, [screenLOffs] cmp ax, OFFSET linePtrs ja @@L1 mov ax, OFFSET linePtrs @@L1: mov [top], ax call EndLine jmp NewLine Tab: ;Tab right mov al, 0 ;Handle as a character call Insert cmp di, LINELENGTH-1 ;If at end of line, jae @@L1 inc di cmp [byte es:di], 0ffh je @@L1 dec di @@L1: jmp RightTab CRet: ;Split line at cursor test [tMarking?], TEXTMARK+FINDMARK je @@L0 jmp CopyMarkedText @@L0: push ds push es push di push es call InsertLine ;Start a new line below current one, ->ES:DI pop ds ;DS:SI := current cursor position pop si push di mov cx, LINELENGTH ;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 mov [changed?], -1 ;Mark file as changed call NewLine jmp RebuildLine CopyMarkedText: push di mov cx, [tMarkEnd] sub cx, [tMarkStart] je @@L9 or ch, ch jnz @@L9 mov si, [tMarkStart] mov di, OFFSET textString test [tMarking?], TEXTMARK jnz @@L1 mov di, OFFSET findString @@L1: mov [ds:di], cl inc di @@L2: mov al, [es:si] inc si mov [ds:di], al inc di dec cx jnz @@L2 pop di test [tMarking?], FINDMARK jz @@L3 mov [tMarking?], 0 jmp FindAgain @@L3: mov [tMarking?], 0 call RebuildLine ret @@L9: pop di mov [tMarking?], 0 ret InsertLine: ;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 BlankLine NewFile: ;Set up initial blank line of a new file mov ax, [heapStart] ;Set ES and [bx] to available heap mov bx, OFFSET linePtrs mov [lastLine], bx BlankLine: mov [bx], ax mov es, ax add ax, LINESEGS cmp ax, RAM_TOP ;Out of room? jb @@L0a jmp TooBig @@L0a: mov [heapPtr], ax ;Update heap pointer (segment value only) sub di, di ;Blank new line mov cx, LINELENGTH mov al, ' ' rep stosb sub di, di ;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 cmp [cs:hereCol], 0 je @@Lx @@L0: mov es, [bx - 2] ; line up with first char of line above mov al, [es:di] cmp al, ' ' je @@L1 cmp al, 0 je @@L1 cmp al, 0ffh je @@L1 jmp @@Lx @@L1: mov es, [bx] mov [es:di], al inc di cmp di, LINELENGTH - 1 ; unless above line is blank jb @@L0 sub di, di mov cx, LINELENGTH mov al, ' ' rep stosb sub di, di ;Home cursor on new line @@Lx: mov es, [bx] ret DupLine: ;Copy Line to new line push di call InsertLine sub di, di @@L0: mov es, [bx - 2] ;Line up with first char of line above mov al, [es:di] mov es, [bx] mov [es:di],al inc di cmp di, LINELENGTH ; unless above line is blank jb @@L0 pop di mov [changed?], -1 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 push di sub di, OFFSET linePtrs cmp di, MAXLINES * 2 jb @@L1 mov si, OFFSET noLinesMsg jmp Abort @@L1: pop di 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 [tMarking?], 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 ptr es:LINELENGTH-1], ' ' ;If a space at end of line, jne Delete0 call EndLine xchg di, dx cmp di, dx jge Join ; join to line below Delete0: push di ; else slide text left push cx push ds mov cx, LINELENGTH - 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 mov [changed?], -1 Ret1: call RebuildLine ret UndeleteLine: mov bp, [delLinePtrsLast] ;Abort if no lines are in buffer cmp bp, OFFSET delLinePtrs ja @@L0 jmp Beep @@L0: dec bp ;Else move pointer to top line of delete buffer dec bp mov [delLinePtrsLast], bp or di, di ;If cursor is at start of line, jne @@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, LINELENGTH ;Cursor not at 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: ;(verbinden) Join lower line to current line at cursor cmp bx, [lastLine] ;Abort if this is the last line of the file je @@Lx push di ;Save registers push ds push di push es mov es, [bx + 2] ;Get next line's segment push es ;Save a copy mov dx, di ;Get length of lower line: call EndLine ;Find first non-space char from end add dx, di ;If concatenated line is too long, abort. cmp dx, LINELENGTH jbe @@L0 call Beep pop ax pop es pop di pop ds pop ax jmp Ret1 @@L0: mov cx, di ;Count = lower line length sub si, si ;Source = start of 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 cmp bx, [lastLine] je @@L1 dec bx dec bx @@L1: pop di ;Restore pointers and return @@Lx: jmp NewLine Insert: ;Insert or overwrite at cursor mov [tMarking?], 0 test [inserting?], -1 ;If inserting, open up space for new character jz Insert1 cmp [byte es:LINELENGTH - 1], ' ' ;If line is full, do not do it je Insert0 mov si, OFFSET noInsertMsg jmp Abort Insert0: push ax push cx push ds mov ax, es mov ds, ax mov si, LINELENGTH - 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, LINELENGTH ;Don't advance DI if at end of line jb @@L1 dec di @@L1: call RebuildLine ret DeleteLine: ;Delete cursor line and append to buffer mov bp, [delLinePtrsLast] ;Save segment of current line in delete buffer mov [bp], es inc bp inc bp mov [delLinePtrsLast], 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 [changed?], -1 sub di, di ;Home cursor on new line jmp NewLine 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 [tMarking?], 0 jmp 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 ;Goto label with ALT-number Goto1: mov cx, 2 jmp GotoX Goto2: mov cx, 4 jmp GotoX Goto3: mov cx, 6 jmp GotoX Goto4: mov cx, 8 jmp GotoX Goto5: mov cx, 10 jmp GotoX Goto6: mov cx, 12 jmp GotoX Goto7: mov cx, 14 jmp GotoX Goto8: mov cx, 16 jmp GotoX Goto9: mov cx, 18 jmp GotoX Goto0: mov cx, 0 ; fall through GotoX GotoX: mov si, OFFSET LabelTable ;Form index into LabelTable add si, cx ;Return address of label storage in SI mov bx, [si] ;Retrieve address mov [tMarking?], 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: ;Clear changed? flag so file changes will be discarded on exit mov [changed?], 0 ret Save: ;Write lines to file, renaming old version with .BAK extension push dx push di push es push bx mov al, [changed?] ;If no changes, done. or al, al jnz @@L0 jmp XSave @@L0: mov al, [newFile?] ;If a new file, create it first or al, [isBAKed?] ;If already BAKed up, no .BAK needed jnz DoSave mov ah, 3Eh ;Else close file mov bx, [fHandle] int 21h mov ax, ds mov es, ax mov si, OFFSET fName ;Make new ASCIIZ string with .BAK extension mov di, OFFSET fNameBAK @@L1: lodsb cmp al, '.' je @@L2 or al, al je @@L2 stosb jmp @@L1 @@L2: mov cx, 4 mov si, OFFSET BAK rep movsb mov ah, 41h ;Delete old back-up copy 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 XSave: pop bx pop es pop di pop dx ret WriteFile: ;Write lines out to file 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, LINELENGTH mov di, LINELENGTH - 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 ; jmp @@L1e or cx, cx jz @@L1e @@L1b: lodsb @@L1b2: cmp al, 0 jne @@L1d mov al, HT stosb dec cx jz @@L1e @@L1c: lodsb cmp al, 0ffh jne @@L1b2 dec cx jnz @@L1c jmp @@L1e @@L1d: stosb dec cx jnz @@L1b @@L1e: 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 - LINELENGTH - 2 ;Buffer is 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 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 WriteBuffer: ;Write text in buffer to disk push bx mov ah, 40h mov bx, [fHandle] mov cx, di mov dx, OFFSET Buffer sub cx, dx jz @@L1 int 21h jc FileError mov di, OFFSET Buffer @@L1: pop bx ret ExitEsc: push di mov si, OFFSET endEscMsg call Print0 pop di sub ah, ah int 16h cmp al, CR je Exit cmp al, 'y' je Exit cmp al, 'Y' je Exit cmp al, ESCAPE jne @@L1 ret @@L1: mov [changed?], 0 Exit: call Save ;Save file if changed mov cx, 0C0Dh ;Restore standard cursor size mov ah, 1 int 10h mov dx, [screenLines] ;Put cursor at bottom of screen xchg dl, dh sub bh, bh mov ah, 2 int 10h ifndef pf test [lines24?], 1 je @@L1 mov ax, 1114h mov bl, 0 int 10h @@L1: endif mov ax, 4C00h ;Bye! int 21h 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 fNameBlock ;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 mov [fInsert?], 1 call ReadFile mov cx, bx mov bx, dx ;Close file mov ah, 3Eh int 21h jmp 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, LINELENGTH rep movsb add dx, LINESEGS cmp dx, RAM_TOP jb @@L1a jmp TooBig @@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 mov [changed?], -1 sub di, di jmp NewLine FileOrBuffer?: ;Prompt for file name if Shift is down, put it in fNameBlock. ;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 fNameBlock call GetFileName jnz Ret4 ;If null string returned, abort mov si, OFFSET cancelledMsg jmp Abort Shifted?: mov ah, 2 ;Get shift key status 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 fNameBlock 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 fNameBlock 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 [mark], bx @@L1: push bx push di push es mov si, [mark] cmp bx, si ;If mark comes after here, exchange jae @@L2 xchg bx, si @@L2: mov [mark], si ; save in this order for possible delete call FileOrBuffer? ;If Shift key was down when command entered, je @@L4 call WriteBlock mov di, OFFSET blockPtrs jmp @@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 mov [lastLine], di ;Store index of new last line sub di, di ;Point cursor to start of old marked line mov bx, [mark] mov [needCopies?], 0 mov [changed?], -1 jmp NewLine PromptMsg: ;Print prompt string at [SI] push di call Prompt ;Display prompt pop di ret GetCountedString: ;Read counted string into [DX]. ;Returns count (minus CR/LF) in AL. DX is advanced one to start of string. push di mov di, dx inc dx call GetString ;Get input string mov [di], al ;Store count in front of string pop di ret BeginText: ;Prompt for mark . mov [tMarking?], 0 push bx push es push di mov si, OFFSET textMsg ;Get text string call PromptMsg @@L0: mov ah, 1 ;Have a look to first key int 16h jz @@L0 or al, al jnz @@L1 cmp ah, T_RIGHT jne @@L1 mov [tMarking?], TEXTMARK pop di mov [tMarkStart], di pop es pop bx mov [tMark], bx ret @@L1: mov dx, OFFSET textString call GetCountedString pop di pop es pop bx ret InsertText: ;Insert textstring at cusrsorposition push di mov si, OFFSET textString lodsb or al ,al ;Anything to copy ? je @@L9 push ax ;Look for room to insert push bx mov bx, LINELENGTH - 1 @@L0: cmp [byte es:bx], ' ' je @@L0a cmp [byte es:bx], 0 je @@L0a cmp [byte es:bx], 0ffh je @@L0a mov si, OFFSET noInsertMsg2 jmp Abort @@L0a: dec bx dec al jne @@L0 pop bx pop ax mov cl, al ;Ok - now insert sub ch, ch @@L1: lodsb cmp al, 0ffh je @@L8 push ax push si push cx call Insert pop cx pop si pop ax cmp al, 0 jne @@L8 push si push cx call Right pop cx pop si @@L8: loop @@L1 @@L9: pop di ret UpConvert: cmp al, 'a' jb @@L1 cmp al, 'z' jg @@L1 and al, 0dfh @@L1: ret UpConvert2: xchg al, ah call UpConvert xchg al, ah ret FindAgain: mov [skipGetStr?], 1 Find: ;Prompt for and find next . If Shifted, reuse last . mov [tMarking?], 0 mov [colOffs], 0 push bx push es push di call Right test [doReplace?], 1 ;If doing replace, bypass getstring jnz @@L1 test [skipGetStr?], 1 ;If find again, bypass getstring jnz @@L1 mov si, OFFSET findMsg ;Get search string call PromptMsg @@L0: mov ah, 1 ;Have a look to first key int 16h jz @@L0 or al, al jnz @@L0a cmp ah, T_RIGHT jne @@L0a mov [tMarking?], FINDMARK pop di mov [tMarkStart], di pop es pop bx mov [tMark], bx ret @@L0a: mov dx, OFFSET findString call GetCountedString @@L1: mov si, OFFSET findString lodsw dec al mov dl, al mov al, ah call UpConvert mov cx, LINELENGTH sub cx, di FindLoop: ; repne scasb ;Scan for first char of Find string ; jmp @@L1b ;Case sensitive !!! @@L0: ;** NOT ** case sentive !!! mov ah, [es:di] inc di call UpConvert2 cmp al, ah je @@L1 dec cx jne @@L0 jmp FindNextLine @@L1: push di ;Once found, compare rest of string dec cx ;One -dec- lost if found mov dh, cl mov cl, dl sub ch, ch mov si, OFFSET findString + 2 push ax ;Save first char in -al- ; repe cmpsb ;Case sensitive !!! ; jmp @@L3 @@L2: ;** NOT ** case sentive !!! mov al, [ds:si] inc si call UpConvert mov ah, [es:di] inc di call UpConvert2 cmp al, ah jne @@L3 dec cx jne @@L2 @@L3: pop ax ;Restore first char je Found pop di ;Match failed. Scan again for 1st char. mov cl, dh sub ch,ch jmp FindLoop FindNextLine: inc bx ;Search next line (until EOF) inc bx mov es, [bx] sub di, di mov cx, LINELENGTH cmp bx, [lastLine] jbe FindLoop mov si, OFFSET notFoundMsg ;Not found test [doReplace?], 1 ;If doing auto-replace, return jz @@L1 mov [doReplace?], 0 mov [skipGetStr?], 0 pop di pop es pop bx ret @@L1: mov [skipGetStr?], 0 call Print0 ;Else restore cursor, abort with error message pop di pop es pop bx jmp na Found: mov [skipGetStr?], 0 pop di dec di add sp, 6 mov ax, bx ;Show found line 5 below top of screen jmp JL1 Replace: ;Replace founded string push di 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, LINELENGTH ja @@LE @@L3: call Insert0 ; then insert extra characters loop @@L3 jmp @@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 mov [changed?], -1 jmp NewLine @@LE: pop di jmp na AutoReplace: ;Find and replace. mov [tMarking?], 0 mov [doReplace?], 1 ;Set flag for -find- doing replace mov [allReplace?], 0 ;Set flag: 1 = all, 0 = ask mov si, OFFSET findMsg ;Get search string call PromptMsg mov dx, OFFSET findString call GetCountedString mov si, OFFSET replaceMsg call PromptMsg mov dx, OFFSET replaceString call GetCountedString @@L1: call Find ;Find/replace until flag reset by not foun test [doReplace?], 1 jz @@Lx test [allReplace?], 1 jne @@L2 push di call ReDrawScreen ;Show position call DrawCursor pop di @@L1a: push di mov si, OFFSET doReplaceMsg call Print0 pop di sub ah, ah int 16h and al, 0dfh cmp al, 'Y' je @@L2 cmp al, 'N' je @@L1 cmp al, 'A' je @@L3 cmp al, 'Q' je @@Lx call Beep jmp @@L1a @@L2: call Replace jmp @@L1 @@L3: mov [allReplace?], 1 jmp @@L2 @@Lx: push si call ReDraw ;Refresh screen with any changes pop si push di call Print0 ;Show notFoundMsg and exit pop di jmp NextNoRedraw ifdef pf Help: push es push di mov es, [videoSegment] mov di, 160 mov si, OFFSET helpPf1 mov cx, 80 * 7 mov ah, [attribNl] @@L1: lodsb stosw loop @@L1 mov si, OFFSET endHelpMsg call Print0 sub ah, ah int 16h mov di, 160 mov si, OFFSET helpPf2 mov cx, 80 * 7 mov ah, [attribNl] @@L2: lodsb stosw loop @@L2 mov si, OFFSET endHelpMsg call Print0 sub ah, ah int 16h pop di pop es ret else 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 endHelpMsg call Print0 sub ah, ah int 16h pop di pop es ret endif Graphic: ;Special character insert push es push bx push di mov es, [videoSegment] ;1st line mov di, 160 mov ah, [attribNl] mov al, 0c9h stosw mov cx, 32 mov al, 0cdh rep stosw mov al, 0bbh stosw ;2nd line mov di, 2*160 mov al, 0bah stosw mov cx, 32 mov al, 0 @@L0: stosw inc al dec cx jnz @@L0 mov al, 0bah stosw ;3rd-6th line mov di, 480 mov dx, 4 mov al, 80h @@L1: push ax mov al, 0bah stosw pop ax mov cx, 32 @@L2: stosw inc al dec cx jnz @@L2 push ax mov al, 0bah stosw pop ax add di, 160-68 dec dx jnz @@L1 ;7th line mov di, 7*160 mov ah, [attribNl] mov al, 0c8h stosw mov cx, 32 mov al, 0cdh rep stosw mov al, 0bch stosw mov cl, 13 ;Blockcursor mov ch, 0 mov ah, 1 int 10h mov dh, 2 mov dl, 1 mov ah, 2 mov bh, 0 int 10h ifdef pf ifndef pc push ax mov ah,12h int 61h pop ax endif endif ;choose char @@S1: sub ah, ah int 16h or al, al jz @@S1b cmp al, ESCAPE jne @@S1a jmp @@L9 @@S1a: cmp al, CR jne @@S1 jmp @@L5 @@S1b: cmp ah, T_RIGHT jne @@S2 cmp dl, 32 je @@S1 inc dl jmp @@S9 @@S2: cmp ah, T_LEFT jne @@S3 cmp dl, 1 je @@S1 dec dl jmp @@S9 @@S3: cmp ah, T_UP jne @@S4 cmp dh, 2 je @@S1 dec dh jmp @@S9 @@S4: cmp ah, T_DOWN jne @@S1 cmp dh, 6 je @@S1 inc dh @@S9: mov ah, 2 mov bh, 0 int 10h jmp @@S1 @@L5: sub dl, 1 sub dh, 2 or dh, dh jnz @@L6 mov al, dl jmp @@L8 @@L6: dec dh mov al, dh ror al, 1 ror al, 1 ror al, 1 or al, 80h or al, dl @@L8: cmp al, 0 je @@L9 cmp al, 09h je @@L9 cmp al, 0ah je @@L9 cmp al, 0dh je @@L9 cmp al, 0ffh je @@L9 pop di pop bx pop es jmp Insert @@L9: pop di pop bx pop es ret END