4
\$\begingroup\$

This procedure is the natural continuation of the Example 4 that I wrote for How buffered input works, a Stack Overflow Q/A.

Features include:

  • freely positioning the cursor.
    • Left Moves cursor left.
    • Right Moves cursor right.
    • Home Moves cursor to the far left.
    • End Moves cursor to the far right.
    • CtrlLeft Move cursor to word on the left.
    • CtrlRight Move cursor to word on the right.
  • deleting text in numerous ways.
    • Delete Removes the current character or the selected text.
    • Backspace Removes the character to the left of the cursor or the selected text.
    • CtrlHome Removes all characters to the left.
    • CtrlEnd Removes all characters to the right.
  • cutting, copying and pasting on a clipboard.
    • ShiftLeft Extend selection to the left.
    • ShiftRight Extend selection to the right.
    • ShiftHome Extend selection to the far left.
    • ShiftEnd Extend selection to the far right.
    • ShiftCtrlLeft Extend selection to word on the left.
    • ShiftCtrlRight Extend selection to word on the right.
    • CtrlX Cut to clipboard.
    • CtrlC Copy to clipboard.
    • CtrlV Paste from clipboard.
  • undo/redo of the most recently deleted text.
    • CtrlZ Undo deleted text.
  • easy character case flipping.
    • CtrlO Other case of selection.
  • using an optional input history.
    • Up Fetch previous history item.
    • Down Fetch next history item.
  • ending input in several manners allowing for complex dialogs.
    • Return Ends input altering the input buffer and the history.
    • Escape Ends input altering the input buffer.
    • Tab Ends input altering the input buffer.
    • ShiftTab Ends input altering the input buffer.

If the 8th byte of the input buffer holds a non-zero value then the storage space is supposed to contain an old string (perhaps from a previous input). The old string is not required to be zero terminated and is immediately shown on screen.

While the input is in progress, the input is confined to staying within the user specified input box on the current row. Longer texts will scroll horizontally.
When the input is finally done, either the complete text or at least the right hand part of it is shown in the input box, leaving the cursor beyond the last character.

The first key to process and the last key processed are of particular interest in building complex dialogs. They are passed through the AX register.

This procedure does no ctrlC checking.

This procedure supports input redirection.

; Example 5, Rich Edit Form Input. (FASM 1.54)
; IN (ds:dx,es:bx,ax) OUT (ax)
FormInput:
; Silently quits on parameter errors, just like DOS
; Entry DS:DX Buffer of max 8+128 bytes
; 1st dword is far pointer to optional history
; (History is collection of ASCIIZ strings plus a final zero)
; 5th byte is size of history in 256 bytes pages
; 6th byte is size of input box
; 7th byte is size of storage space starting at 9th byte
; 8th byte is size of old ASCII(Z) string, 0 if none
; Storage space can contain old ASCII(Z) string
; ES:BX Clipboard (minimum 128 bytes)
; AX First key to process, 0 if none
; Exit DS:DX Nothing changed if any parameter was invalid
; 8th byte is size of new ASCIIZ string
; Storage space contains new ASCIIZ string
; New ASCIIZ string uniquely appended to history
; AX Last key processed
; Local [bp-1] PAGE Display page
; [bp-2] STORE Size of storage space
; [bp-3] ROW Row of input box
; [bp-4] COL Column of input box
; [bp-5] SHIFT Number of characters shifted out on leftside
; [bp-6] INBOX Size of input box
; [bp-7] LIX Number of characters in current input string
; [bp-8] CIX Position of cursor in current input string
; [bp-10] FLAGS Bit[0] is ON for normal keyboard input
; Bit[1] is ON if selection exists
; [bp-11] HISEL Position of high end of selection
; [bp-12] LOSEL Position of low end of selection
; [bp-14] HISLIX Number of strings in history
; [bp-16] HISCIX Index of current string in history
; [bp-18] OLDKEY Key previously processed
 pusha
 sub sp, 268 ;EditBuffer (128) and
 mov bp, sp ; UndeleteBuffer (12+128)
 pusha ;'sub sp, 16' Local variables
 push 0 ;OLDKEY
 mov si, dx
 add si, 6
 lodsw ; -> SI points at storage space
 dec al ;AL is size of storage space
 js .Quit ;Storage space not in [1,128]!
 inc al
 cmp ah, al ;AH is size of old string
 jnb .Quit ;Old string too long!
 xor di, di
 push es ;(1)
 les bx, [si-8] ;FarPtrOfHistory
 mov ch, [si-4] ;SizeOfHistory (in pages)
 xor cl, cl
 jcxz .c ;It's fine to not use an history
.a: cmp byte [es:bx], 0
 je .c ;Found final zero
 inc di ;Counts strings
.b: inc bx
 dec cx ;Room for terminating zero ?
 jz .Quit_ ;No
 cmp byte [es:bx], 0
 jne .b
 inc bx
 dec cx ;Room left for final zero ?
 jnz .a ;Yes, go check
.Quit_: pop es ;(1a)
.Quit: lea sp, [bp+268] ;Free Locals, EditBuffer,
 popa ; and UndeleteBuffer
 ret
.c: pop es ;(1b)
 mov [bp-14], di ;HISLIX
 mov [bp-16], di ;HISCIX
 mov bl, al
 mov ah, 0Fh ;BIOS.GetVideoMode
 int 10h ; -> AL=Mode AH=Cols BH=Page
 mov [bp-2], bx ;STORE and PAGE
 mov bl, ah
 mov ah, 03h ;BIOS.GetCursor
 int 10h ; -> CX=Shape DL=Col DH=Row
 mov [bp-4], dx ;COL and ROW
 sub bl, dl ;Size of the widest inbox
 movzx cx, byte [si-3] ;Requested size inbox
 jcxz .d ;It's fine to not want to set the
 cmp bl, cl ; size for the input box
 jb .d
 mov bl, cl
.d: mov [bp-6], bl ;INBOX
 call .Wipe ; -> CIX=0 LIX=0 SHIFT=0
 xor bx, bx ;STDIN
 mov ax, 4400h ;DOS.GetDeviceInformation
 int 21h ; -> AX DX CF
 jc .e ;Go default to keyboard
 test dl, dl
 jns .f ;Block device, not keyboard
 shr dl, 1
.e: adc bx, bx ; -> BX=1 if Keyboard
.f: mov [bp-10], bx ;FLAGS
 mov cl, [si-1] ;CH=0 Size of old string
 jmp .h
.g: lodsb ;Storage space gives old string
 push cx si ;(2)
 call .Asc ; -> CF Input old string
 pop si cx ;(2)
 jc .i
.h: dec cx
 jns .g
.i: mov ax, [bp+268+14] ;pusha.AX is NEWKEY
 test ax, ax
 jnz .Main_
.Main: call .Show
 mov ax, [bp+268+14] ;pusha.AX is NEWKEY
 mov [bp-18], ax ;OLDKEY
 call .Key ; -> AX (DL SI)
 mov [bp+268+14], ax ;pusha.AX is NEWKEY
.Main_: mov bx, .Scan
 test ah, ah
 jz .l ;Not an extended ASCII
 mov [cs:.Fail], ax ;Sentinel
.j: lea bx, [bx+4]
 mov cx, [cs:bx-2]
 mov dx, cx
 and cx, 0FF01h ;Strip flag bits
 cmp ax, cx
 jne .j
 mov [bp+268+14], dx ;pusha.AX is NEWKEY Update with
 shl dl, 1 ;Flag bit 7 extra flags
 jc .k ;Don't undo selection
 call .NoSel
.k: shl dl, 1 ;Flag bit 6
 jnc .l ;Current key is not a deletion key
 call .Backup
.l: call word [cs:bx]
 jmp .Main
; - - - - - - - - - - - - - - - - - - -
; Bit[0] is ON if <SHIFT> has to be pressed
; Bit[6] is ON if this is a deletion key
; Bit[7] is ON if selection should not be un-done automatically
.Scan: dw .Asc
 dw 4B00h, .s4B ;<LEFT>
 dw 4D00h, .s4D ;<RIGHT>
 dw 4700h, .s47 ;<HOME>
 dw 4F00h, .s4F ;<END>
 dw 7740h, .s77 ;<CTRL-HOME>
 dw 7540h, .s75 ;<CTRL-END>
 dw 7300h, .s73 ;<CTRL-LEFT>
 dw 7400h, .s74 ;<CTRL-RIGHT>
 dw 4800h, .s48 ;<UP>
 dw 5000h, .s50 ;<DOWN>
 dw 1C00h, .s1C ;<RETURN> or <ENTER>
 dw 0100h, .s1C ;<ESCAPE>
 dw 0F00h, .s1C ;<TAB>
 dw 0F01h, .s1C ;<SHIFT-TAB>
 dw 1100h, .s11 ;<CTRL-Z> undo/redo
 dw 53C0h, .s53 ;<DELETE>
 dw 0EC0h, .s0E ;<BACKSPACE>
 dw 4B81h, .s4Bs ;<SHIFT-LEFT>
 dw 4D81h, .s4Ds ;<SHIFT-RIGHT>
 dw 4781h, .s47s ;<SHIFT-HOME>
 dw 4F81h, .s4Fs ;<SHIFT-END>
 dw 7381h, .s73s ;<SHIFT-CTRL-LEFT>
 dw 7481h, .s74s ;<SHIFT-CTRL-RIGHT>
 dw 1880h, .s18 ;<CTRL-O> other case of selection
 dw 2DC0h, .s2D ;<CTRL-X> cut to clipboard
 dw 2E80h, .s2E ;<CTRL-C> copy to clipboard
 dw 2F80h, .s2F ;<CTRL-V> paste from clipboard
.Fail: dw ?, .STC
; - - - - - - - - - - - - - - - - - - -
; Most of the complexity herein is due to getting the key from DOS,
; but for now I want input redirection capability.
.Key: mov ah, 07h ;DOS.STDINInput
 int 21h ; -> AL
 test al, al ;Extended ASCII requires 2 calls
 jz .Key_2
 mov si, .Key_List ;Translate ASCII into scancode
 mov [cs:.Key_Fail], al ;Sentinel
 mov dl, al
.Key_1: lods word [cs:si]
 cmp al, dl
 jne .Key_1
 test ah, ah
 jnz .Key_3 ;Got scancode we can use
 cmp al, 10 ;Remains an ASCII
 je .Key ;Silently ignoring linefeed
 ret ; in favor of input redirection
.Key_2: mov ah, 07h ;DOS.STDINInput
 int 21h ; -> AL
 shl ax, 8
 jz .Key ;Don't allow embedded zeroes
.Key_3: xor al, al
 test word [bp-10], 1 ;FLAGS.Keyboard ?
 jz .Key_4 ;No, input is redirected
 push ax ;(3)
 mov ah, 02h ;BIOS.GetKeyboardFlags
 int 16h ; -> AL
 test al, 00000011b ;Either SHIFT key is depressed?
 pop ax ;(3)
 setnz al
.Key_4: ret
.Key_List:
 dw 1C0Dh ;<RETURN> or <ENTER>
 dw 011Bh ;<ESCAPE>
 dw 0F09h ;<TAB>
 dw 0E08h ;<BACKSPACE>
 dw 180Fh ;<CTRL-O> other case of selection
 dw 2D18h ;<CTRL-X> cut to clipboard
 dw 2E03h ;<CTRL-C> copy to clipboard
 dw 2F16h ;<CTRL-V> paste from clipboard
 dw 111Ah ;<CTRL-Z> undo/redo
.Key_Fail:
 db ?, 0
; - - - - - - - - - - - - - - - - - - -
.Show: test word [bp-10], 1 ;FLAGS.Keyboard ?
 jz .Show_Ready ;No, input is redirected
 movzx di, byte [bp-6] ;INBOX
 movzx si, byte [bp-5] ;SHIFT
 mov dx, [bp-4] ;COL and ROW
 mov cx, 1 ;Replication count
 mov bh, [bp-1] ;PAGE
.Show_Next:
 mov ah, 02h ;BIOS.SetCursor
 int 10h
 mov bl, 07h ;WhiteOnBlack for normal text
 test word [bp-10], 2 ;FLAGS.Selection ?
 jz .Show_None
 mov ax, si ;Current position
 cmp al, [bp-12] ;LOSEL
 jb .Show_None
 cmp al, [bp-11] ;HISEL
 jae .Show_None
 mov bl, 1Fh ;BrightWhiteOnBlue for selection
.Show_None:
 mov al, [bp+si] ;Current character
 mov ah, 09h ;BIOS.WriteCharacterAndAttribute
 int 10h
 inc dl ;Next column
 inc si ;Next character
 dec di
 jnz .Show_Next ;Process all of the input box
 mov dx, [bp-4] ;COL and ROW
 add dl, [bp-8] ;CIX
 sub dl, [bp-5] ;SHIFT
 mov ah, 02h ;BIOS.SetCursor
 int 10h
.Show_Ready:
 ret
; - - - - - - - - - - - - - - - - - - -
; Will backup only once for a series of deletion keys
.Backup:mov dx, [bp-18] ;OLDKEY
 test dh, dh ;DH is zero for a character key
 jz .Backup_First ;Previous key is not a deletion key
 test dl, 64 ;DL has flags for an action key
 jnz .Backup_Once ;Previous key was also deletion key
.Backup_First:
 mov si, 128 ;Undelete buffer is at BP+128
@@: sub si, 2
 mov dx, [bp+si]
 mov [bp+128+12+si], dx
 cmp si, -12
 jg @b
.Backup_Once:
 ret
; - - - - - - - - - - - - - - - - - - -
; <CTRL-Z>
.s11: mov ax, [bp-18] ;OLDKEY
 cmp ax, 1100h
 je .s11_Undo ;Previous key was also <CTRL-Z>
 test ah, ah ;AH is zero for a character key
 jz .s11_Bad ;Previous key is not a deletion key
 test al, 64 ;AL has flags for an action key
 jz .s11_Bad ;Previous key is not a deletion key
.s11_Undo:
 mov si, 128 ;Undelete buffer is at BP+128
@@: sub si, 2
 mov ax, [bp+si]
 mov dx, [bp+128+12+si]
 mov [bp+128+12+si], ax
 mov [bp+si], dx
 cmp si, -12
 jg @b
 ret
.s11_Bad:
 mov word [bp+268+14], 0 ;pusha.AX is NEWKEY Throw away
 ret ; invalid key
; - - - - - - - - - - - - - - - - - - -
; AH=01h <ESCAPE>
; AH=0Fh <TAB> or <SHIFT-TAB>
; AH=1Ch <RETURN> or <ENTER>
.s1C: xor si, si
 mov bx, [bp+268+10] ;pusha.DX -> DS:BX
 mov al, [bp-7] ;LIX
 mov [bx+7], al ;8th byte is size of new string
 add bx, 8
 jmp .s1C_2
.s1C_1: mov dl, [bp+si]
 mov [bx+si], dl ;Storage space receives new string
 inc si
.s1C_2: sub al, 1
 jnb .s1C_1
 mov byte [bx+si], 0 ;Terminating zero
 cmp ah, 1Ch ;Don't touch history for
 jne .s1C_12 ; <TAB> or <ESCAPE>
 mov dh, [bx-4] ;Size of history (in pages)
 xor dl, dl
 test dx, dx
 jz .s1C_12 ;No history
 movzx cx, byte [bp-7] ;LIX
 jcxz .s1C_12 ;Don't add empty item
 push es ;(4)
 les di, [bx-8] ; -> ES:DI History
 mov si, di
 jmp .s1C_7
.s1C_3: cmp al, [bx] ;DS:BX points at storage space
 jne .s1C_6 ;Not same first character
 push bx si ;(5)
 inc si ;Don't re-test first characters
.s1C_4: inc bx
 lods byte [es:si] ;Compare following characters
 cmp al, [bx]
 jne .s1C_5
 test al, al
 jnz .s1C_4
 pop bx bx ;(5a) Duplicate found
 jmp .s1C_7 ;Don't keep it
.s1C_5: pop si bx ;(5b) Not a duplicate
.s1C_6: lods byte [es:si] ;Keep this item
 stosb
 test al, al
 jnz .s1C_6
.s1C_7: mov al, [es:si] ;Next byte in history
 test al, al
 jnz .s1C_3 ;Not yet the final zero
 mov [es:di], al ;Temporary final zero
 inc cx ;Space needed for new item
 mov ax, di ;DI points beyond last item
 sub ax, [bx-8] ;[BX-8] points at first item
 sub dx, ax ;Free=Size-(Last-First)
 cmp cx, dx
 jb .s1C_11 ;Plenty space available
 mov di, [bx-8] ; -> ES:DI History
 mov si, di
.s1C_8: lods byte [es:si] ;Discard oldest item
 inc dx ;One more free byte
 test al, al
 jnz .s1C_8
 cmp cx, dx
 jnb .s1C_8 ;Not enough, go discard another one
 jmp .s1C_10 ;Go keep all remaining items
.s1C_9: lods byte [es:si] ;Keep this item
 stosb
 test al, al
 jnz .s1C_9
.s1C_10:cmp [es:si], al ;AL=0
 jne .s1C_9 ;More items to keep
.s1C_11:mov al, [bx] ;Copy new ASCIIZ item
 stosb
 inc bx
 test al, al
 jnz .s1C_11
 stosb ;New final zero
 pop es ;(4)
.s1C_12:call .s47 ;<HOME>
 call .s4F ;<END>
 or word [bp-10], 1 ;FLAGS.Keyboard, Force display
 call .Show ;Show all or last part of result
 jmp .Quit
; - - - - - - - - - - - - - - - - - - -
; <BACKSPACE>
.s0E: call .DelSel ; -> CF
 jc .s0E_1
 cmp byte [bp-8], 0 ;CIX
 jne .s0E_2
.s0E_1: ret
.s0E_2: call .s4B ;<LEFT>
; --- --- --- --- --- --- --
.Del: movzx di, byte [bp-8] ;CIX
 movzx cx, byte [bp-7] ;LIX
 sub cx, di
 jz .Del_No ;Cursor behind the current input
@@: mov dl, [bp+di+1] ;Move down in edit buffer
 mov [bp+di], dl
 inc di
 dec cx
 jnz @b
 dec byte [bp-7] ;LIX
.Del_No:ret
; - - - - - - - - - - - - - - - - - - -
; <DELETE>
.s53: call .DelSel ; -> CF
 jnc .Del
 ret
; - - - - - - - - - - - - - - - - - - -
.Beep: mov ax, 0E07h ;BIOS.TeletypeBeep
 int 10h
.STC: stc
 ret
; - - - - - - - - - - - - - - - - - - -
.Asc: cmp al, 32
 jb .STC
 call .DelSel ; -> CF
 movzx di, byte [bp-8] ;CIX
 movzx si, byte [bp-7] ;LIX
 lea dx, [si+1]
 cmp dl, [bp-2] ;STORE
 jnb .Beep ;Not enough storage capacity
 jmp .Asc_2
.Asc_1: mov dl, [bp+si-1] ;Move up in edit buffer
 mov [bp+si], dl
 dec si
.Asc_2: cmp si, di
 ja .Asc_1
 mov [bp+si], al ;Add newest character
 inc byte [bp-7] ;LIX
; --- --- --- --- --- --- --
; <RIGHT>
.s4D: inc byte [bp-8] ;CIX
 mov al, [bp-7] ;LIX
 cmp [bp-8], al ;CIX
 jbe .Shift
; --- --- --- --- --- --- --
; <LEFT>
.s4B: sub byte [bp-8], 1 ;CIX
 jnb .Shift
; --- --- --- --- --- --- --
; <HOME>
.s47: xor bl, bl
 jmp .Shift_
; - - - - - - - - - - - - - - - - - - -
; <END>
.s4F: mov bl, [bp-7] ;LIX
; --- --- --- --- --- --- --
.Shift_:mov [bp-8], bl ;CIX
.Shift: mov dl, [bp-5] ;SHIFT
 mov dh, [bp-8] ;CIX
 cmp dh, dl
 jb .Shi_1
 add dl, [bp-6] ;INBOX
 sub dh, dl
 jb .Shi_2
 inc dh
 add dh, [bp-5] ;SHIFT
.Shi_1: mov [bp-5], dh ;SHIFT
.Shi_2: clc
 ret
; - - - - - - - - - - - - - - - - - - -
; <CTRL-HOME>
.s77: call .s0E ;<BACKSPACE>
 cmp byte [bp-8], 0 ;CIX
 ja .s77
 ret
; - - - - - - - - - - - - - - - - - - -
; <CTRL-END>
.s75: call .Del
 mov al, [bp-8] ;CIX
 cmp al, [bp-7] ;LIX
 jb .s75
 ret
; - - - - - - - - - - - - - - - - - - -
; <CTRL-LEFT>
.s73: movzx bx, byte [bp-8] ;CIX
 dec bx
 call .wChar_ ; -> AL
@@: shr al, 1 ;Make room for 2nd leftside signal
 dec bx
 call .wChar ; -> AL
 inc bx
 and al, 11000000b ;Only keep both left signals
 cmp al, 01000000b ;Found the start of a word ?
 je .Shift_ ;Yes
 dec bx
 jns @b
 ret
; - - - - - - - - - - - - - - - - - - -
; <CTRL-RIGHT>
.s74: movzx bx, byte [bp-8] ;CIX
 call .wChar_ ; -> AL
@@: shl al, 1 ;Make room for 2nd rightside signal
 inc bx
 call .wChar ; -> AL
 and al, 00000011b ;Only keep both right signals
 cmp al, 00000010b ;Found the end of a word ?
 je .Shift_ ;Yes
 cmp bl, [bp-7] ;LIX
 jb @b
 ret
; - - - - - - - - - - - - - - - - - - -
.wChar_:xor al, al
.wChar: test bx, bx
 js @f
 cmp bl, [bp-7] ;LIX
 jae @f
 mov si, bx
 cmp byte [bp+si], " " ;Current character
 je @f ;Whitespace
 or al, 10000001b ;Signal at both sides
@@: ret
; - - - - - - - - - - - - - - - - - - -
; <UP>
.s48: mov cx, [bp-16] ;HISCIX
 dec cx
 jns .s48_
 ret
.s48_: mov [bp-16], cx ;HISCIX
 mov bx, [bp+268+10] ;pusha.DX points at InputBuffer
 push es ;(6)
 les bx, [bx] ;FarPtrOfHistory
 jmp .s48_2
.s48_1: inc bx ;Skip an item in the history
 cmp byte [es:bx-1], 0
 jne .s48_1
.s48_2: dec cx
 jns .s48_1
 call .Wipe ; -> CIX=0 LIX=0 SHIFT=0
 jmp .s48_4
.s48_3: push bx ;(7)
 call .Asc ; -> CF Input an history string
 pop bx ;(7)
 jc .s48_5
 inc bx
.s48_4: mov al, [es:bx]
 test al, al
 jnz .s48_3
.s48_5: pop es ;(6)
 ret
; - - - - - - - - - - - - - - - - - - -
; <DOWN>
.s50: mov cx, [bp-16] ;HISCIX
 inc cx
 cmp cx, [bp-14] ;HISLIX
 jbe .s48_
; --- --- --- --- --- --- --
.Wipe: mov di, 128 ;Fill edit buffer with spaces
@@: sub di, 2
 mov word [bp+di], " "
 jnz @b
 mov [bp-8], di ;DI=0 -> CIX=0 LIX=0
 mov byte [bp-5], 0 ;SHIFT=0
 ret
; - - - - - - - - - - - - - - - - - - -
; Other case of selection
; <CTRL-O>
.s18: bt word [bp-10], 1 ;FLAGS.Selection ?
 jnc .s18_None
 mov cx, [bp-12] ;LOSEL and HISEL
.s18_1: movzx si, cl
 mov al, [bp+si] ;From edit buffer
 cmp al, "A"
 jb .s18_3
 cmp al, "Z"
 jbe .s18_2
 cmp al, "a"
 jb .s18_3
 cmp al, "z"
 ja .s18_3
.s18_2: xor al, 32 ;Change the case
 mov [bp+si], al
.s18_3: inc cl
 cmp cl, ch
 jb .s18_1
.s18_None:
 ret
; - - - - - - - - - - - - - - - - - - -
; <SHIFT-LEFT>
.s4Bs: mov ax, .s4B ;<LEFT>
 jmp .Sel
; - - - - - - - - - - - - - - - - - - -
; <SHIFT-RIGHT>
.s4Ds: mov ax, .s4D ;<RIGHT>
 jmp .Sel
; - - - - - - - - - - - - - - - - - - -
; <SHIFT-HOME>
.s47s: mov ax, .s47 ;<HOME>
 jmp .Sel
; - - - - - - - - - - - - - - - - - - -
; <SHIFT-END>
.s4Fs: mov ax, .s4F ;<END>
 jmp .Sel
; - - - - - - - - - - - - - - - - - - -
; <SHIFT-CTRL-LEFT>
.s73s: mov ax, .s73 ;<CTRL-LEFT>
 jmp .Sel
; - - - - - - - - - - - - - - - - - - -
; <SHIFT-CTRL-RIGHT>
.s74s: mov ax, .s74 ;<CTRL-RIGHT>
; --- --- --- --- --- --- --
.Sel: mov ch, [bp-8] ;CIX
 call ax ;One of the cursor movement ops
 mov cl, [bp-8] ;CIX
 cmp cl, ch ;Current vs previous position
 je .Sel_Nop ;Cursor didn't move at all
 bts word [bp-10], 1 ;FLAGS.Selection ?
 jc .Sel_Old
.Sel_New:
 cmp cl, ch ;Normalize the indexes
 jb @f
 xchg cl, ch
@@: mov [bp-12], cx ;LOSEL and HISEL
.Sel_Nop:
 ret
.Sel_Old:
 mov dx, [bp-12] ;LOSEL and HISEL
 cmp ch, dl ;Previous cursor was
 je @f ; either at LOSEL or at HISEL
 mov dh, cl ;Update HISEL
 mov cl, dl ;Replaces a 'jmp'
@@: mov dl, cl ;Update LOSEL
 mov cx, dx
 cmp dl, dh ;Compare new borders
 jne .Sel_New ;Selection continues to exist
 mov [bp-12], cx ;Selection dies with LOSEL=HISEL
; --- --- --- --- --- --- --
.NoSel: and word [bp-10], -3 ;FLAGS.Selection OFF
 ret
; - - - - - - - - - - - - - - - - - - -
; Cut to clipboard
; <CTRL-X>
.s2D: call .s2E ;<CTRL-C>
; --- --- --- --- --- --- --
.DelSel:btr word [bp-10], 1 ;FLAGS.Selection ?
 jnc .DelSel_None
 mov bx, [bp-12] ;LOSEL and HISEL
 call .Shift_
@@: call .Del
 dec bh
 cmp bh, bl
 ja @b
 mov [bp-12], bx ;LOSEL and HISEL
 stc ;CF=1 if selection existed
.DelSel_None:
 ret
; - - - - - - - - - - - - - - - - - - -
; Copy to clipboard
; <CTRL-C>
.s2E: bt word [bp-10], 1 ;FLAGS.Selection ?
 jnc .s2E_None
 mov di, [bp+268+8] ;pusha.BX -> ES:DI is Clipboard
 mov cx, [bp-12] ;LOSEL and HISEL
@@: movzx si, cl
 mov al, [bp+si] ;From edit buffer
 stosb ; to clipboard
 inc cl
 cmp cl, ch
 jb @b
 xor al, al
 stosb
.s2E_None:
 ret
; - - - - - - - - - - - - - - - - - - -
; Paste from clipboard
; <CTRL-V>
.s2F: call .DelSel ; -> CF
 mov si, [bp+268+8] ;pusha.BX -> ES:SI is Clipboard
 jmp .s2F_2
.s2F_1: push si ;(8)
 call .Asc ; -> CF Input the clipboard string
 pop si ;(8)
 jc .s2F_3
.s2F_2: lods byte [es:si] ;From clipboard
 test al, al
 jnz .s2F_1
.s2F_3: ret
; --------------------------------------
  • The idea behind this all new input procedure (input as in a dialog window) is to pimp my DOS programs with input capabilities that rival those found on Windows. It's possible that my observations on what Windows has to offer is missing a thing or two. Hence my question:

    Did I include enough (Windows style) features to rightfully call this procedure "Rich Edit" ?

  • Since I program for DOS, I can't afford to waste memory. I tried to write the shortest code possible, but perhaps someone else could suggest additional code size reductions?

  • The current code uses a lot of memory transfers (buffer to buffer). I would like these to be as fast as possible, but I don't want the code size to increase. Kind of a dilemma but maybe a seasoned programmer can find a way out?

200_success
146k22 gold badges190 silver badges479 bronze badges
asked Jan 21, 2018 at 14:40
\$\endgroup\$
3
  • 3
    \$\begingroup\$ What kind of assembly? It looks to my untrained eye like x86 of some kind; the linked question says x86-16 and dos - is that the case here? Consider additional tags. \$\endgroup\$ Commented Jan 22, 2018 at 9:48
  • 3
    \$\begingroup\$ Please give some idea what the intended general use is. I get the idea it's for field entry for database items as in your Buffered input example. In that case, it is over capable, but for text like word processing, it could use some other functionality. \$\endgroup\$ Commented Jan 22, 2018 at 13:32
  • \$\begingroup\$ @TobySpeight Code Review has very few tags available. I had tried all of these: x86, DOS, input, form, dialog. I finished by putting (x86-16) in the title. Not sure if that's OK since I seldom see this. \$\endgroup\$ Commented Jan 28, 2018 at 16:11

1 Answer 1

2
\$\begingroup\$

Silently quits on parameter errors, just like DOS

Why would you do that? Please don't follow DOS' bad example. At the very least report parameter errors by setting the carry flag.


Passing too many parameters via registers IMO is never a good thing. I think you should extend the input buffer's header to 12 bytes, and move that clipboard ES:BX far pointer in there. Passing the first key to process and returning the last key processed through AX is a good choice though.


 call .Asc ; -> CF Input old string
 pop si cx ;(2)
 jc .i ???
 .s0E: call .DelSel ; -> CF
 jc .s0E_1 ???
 .s53: call .DelSel ; -> CF
 jnc .Del
 ret ???
 call .Asc ; -> CF Input an history string
 pop bx ;(7)
 jc .s48_5 ???
 call .Asc ; -> CF Input the clipboard string
 pop si ;(8)
 jc .s2F_3 ???

There seems to be a recurring problem of not stating what the CF stands for upon returning from a few subroutines. To understand the code I had to find and examine those subroutines when simple comments like "Abort on invalid key or out of space" and "Existing selection got deleted" would have made my reviewing a lot easier.


 and cx, 0FF01h ;Strip flag bits

This comment suggests that all of the flag bits are to be removed, yet you keep Bit[0]. Perhaps better write "Strip some flag bits" or "Strip the extra flag bits". I'd prefer the latter because it corresponds best with the comment you wrote a few lines after.


.NoSel: and word [bp-10], -3 ;FLAGS.Selection OFF

I think not too many people would instantly recognize this -3 as the correct value to clear the second bit. Clearer choices include:

and word [bp-10], 1111'1111'1111'1101b
and word [bp-10], 0FFFDh
and word [bp-10], not 2

Answering your questions

Did I include enough (Windows style) features to rightfully call this procedure "Rich Edit" ?

I could definitely say this is indeed "Rich Edit". There's but one feature that I feel is missing. Why didn't you implement the insert/overwrite mode for adding characters?


Since I program for DOS, I can't afford to waste memory. I tried to write the shortest code possible, but perhaps someone else could suggest additional code size reductions?

You can shave off 1 byte in the sequence mov al, [bp-8] cmp al, [bp-7].
Read both variables in AX (mov ax, [bp-8]), then compare both register halves (cmp al, ah).


The current code uses a lot of memory transfers (buffer to buffer). I would like these to be as fast as possible, but I don't want the code size to increase. Kind of a dilemma but maybe a seasoned programmer can find a way out?

Speeding up the many loops might be futile since in the context of inputting the user at the keyboard is the real bottleneck. This said, the loops at .Backup_First, .s11_Undo, and .Wipe can easily be sped up by using dwords instead of words. This of course comes at the cost of adding several Operand Size Prefixes to the code.

rolfl
98.1k17 gold badges219 silver badges419 bronze badges
answered Apr 14, 2018 at 11:21
\$\endgroup\$
0

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.