1
\$\begingroup\$

This console application uses the Rich Edit Form Input procedure for which you can find the source here.

It presents the user with a dialog that has 5 input boxes and 2 buttons.
The dialog is drawn differently depending on screen width. You use TAB or SHIFTTAB to switch between fields. ENTER and ESCAPE have their usual meaning.

The program supports both input redirection and output redirection.

; A dialog using Rich Edit Form Input. Assembled with FASM 1.54
 ORG 256
 cld
; Fix far pointers
 mov ax, cs
 mov [InBufs+2], ax
 mov [InBufs+40+2], ax
 mov [InBufs+160+2], ax
; Program supports 'current' display page and
; adapts itself to the number of columns on screen
 mov ah, 0Fh ;BIOS.GetVideoMode
 int 10h ; -> AL=Mode AH=Cols BH=Page
 mov bl, ah
 mov [VCols], bx ;VCols and VPage
; Program supports i/o redirection
 xor bx, bx ;STDIN
 mov ax, 4400h ;DOS.GetDeviceInformation
 int 21h ; -> AX DX CF
 jc .a ;Go default to keyboard
 test dl, dl
 jns .b ;Block device, not keyboard
 shr dl, 1
.a: adc bl, bl ; -> BL=1 if keyboard
.b: mov [IsKeyb], bl ;[0,1]
; Draw a new dialog
New: call Form
 mov di, 200 ;Current inbox DI={160,120,80,40,0}
.a: sub di, 40
 mov byte [InBufs+di+7], 0 ;No preset string
 jnz .a
; Indicate which field has focus
Cont: mov al, ":"
 call Focus
; Call REFI for input boxes
 cmp di, 4 ;5 input boxes and 2 buttons
 ja .a
 mov cx, di
 xor ax, ax ;No special priority key
 call Input ; -> AX
 jmp .b
; Just wait for a key for buttons
.a: call Key ; -> AX
; Hide indication of focus
.b: push ax
 mov al, " "
 call Focus
 pop ax
; Move around in dialog
 cmp ax, 0F00h ;<TAB> ?
 jne .c
 inc di ;Go to next field
 cmp di, 7
 jb Cont
 xor di, di
 jmp Cont
.c: cmp ax, 0F01h ;<SHIFT-TAB> ?
 jne .d
 dec di ;Go to previous field
 jns Cont
 mov di, 6
 jmp Cont
.d: cmp ax, 0100h ;<ESCAPE> ?
 je Exit
 cmp ax, 1C00h ;<RETURN> or <ENTER> ?
 jne Cont ;Ignore invalid key
 cmp di, 6 ;"Cancel" ?
 je Exit
; "OK" on an empty dialog equals "Cancel"
 mov si, InBufs+7
.e: or al, [si] ;Length of current ASCIIZ
 add si, 40
 cmp si, InBufs+7+40*5 ;5 input boxes
 jb .e
 test al, al
 jz Exit ;End program when all boxes empty
; Re-visit all input boxes so that REFI can update their histories
 mov cx, 4 ;Indexes of 5 input boxes [0,4]
.f: cmp cx, di ;DI is field with last focus,
 je .g ; then history is already updated
 mov ax, 1C00h ;Force update history
 call Input ;Returns quickly! -> AX=1C00h
.g: dec cx
 jns .f
; Print contents as Tab Separated Values
 mov dx, [BaseXY]
 call SetCursor ; -> BH (AH)
 mov di, InBufs+8 ;Storage space holds an ASCIIZ
 jmp .i
.h: mov si, TAB ;Summarize in .TSV format
 call WriteString
.i: mov si, di
 call WriteString
 add di, 40
 cmp di, InBufs+8+40*5 ;5 input boxes
 jb .h
 mov si, CRLF
 call WriteString
; Present a new dialog
 jmp New
; Remove the last (unused) dialog
Exit: movzx cx, byte [VCols] ;Replication count (full row)
 mov di, 7 ;Lines to wipe
 cmp cl, 80
 jnb .a
 mov di, 11 ;The narrow dialog is higher
.a: mov dx, [BaseXY]
 dec dh
.b: dec dh ;Move 1 row up
 call SetCursor ; -> BH (AH)
 mov ah, 08h ;BIOS.GetCharacterAndAttribute
 int 10h ; -> AL=Character AH=Attribute
 mov bl, ah
 mov ax, 0920h ;BIOS.WriteCharacterAndAttribute
 int 10h
 dec di
 jnz .b
; Program supports i/o redirection
 mov bx, 1 ;STDOUT
 mov ax, 4400h ;DOS.GetDeviceInformation
 int 21h ; -> AX DX CF
 jc .d ;Go default to screen
 test dl, dl
 jns .c ;Block device, output is redirected
 test dl, 00000010b
 jnz .d
.c: mov si, CRLF ;Facilitates using redirected
 call WriteString ; output as next redirected input
; Exit the program
.d: mov ax, 4C00h ;DOS.TerminateWithExitcode
 int 21h
; --------------------------------------
; IN (ax,cx) OUT (ax) MOD ()
Input: push bx dx
 push ax ;AX is priority key
 imul bx, cx, 4 ;CX is index of input box [0,4]
 add bx, [Tags]
 mov dx, [BaseXY]
 add dx, [bx] ;dCol and dRow
 add dx, 00FEh ;Col-2 and Row+1
 call SetCursor ; -> BH (AH)
 imul bx, cx, 40
 lea dx, [InBufs+bx] ;DS:DX One of the 5 input buffers
 mov bx, ClipB ;ES:BX Clipboard
 pop ax ;AX Priority key
 call FormInput ; -> AX
 pop dx bx
 ret
; --------------------------------------
; IN () OUT (ax) MOD ()
; 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 .b
 push si ;(1)
 mov si, .List ;Translate ASCII into scancode
 mov [.Fail], al ;Sentinel
.a: lodsw
 cmp al, [.Fail]
 jne .a
 pop si ;(1)
 test ah, ah
 jnz .c ;Got scancode we can use
 cmp al, 10 ;Remains an ASCII
 je Key ;Silently ignoring linefeed
 ret ; in favor of input redirection
.b: mov ah, 07h ;DOS.STDINInput
 int 21h ; -> AL
 shl ax, 8
 jz Key ;Don't allow embedded zeroes
.c: xor al, al
 cmp [IsKeyb], al ;[0,1]
 je .d ;Input is redirected
 push ax ;(2)
 mov ah, 02h ;BIOS.GetKeyboardFlags
 int 16h ; -> AL
 test al, 00000011b ;Either SHIFT key is depressed?
 pop ax ;(2)
 setnz al
.d: ret
 ALIGN 2
.List: dw 1C0Dh ;<RETURN> or <ENTER>
 dw 011Bh ;<ESCAPE>
 dw 0F09h ;<TAB>
 dw 0E08h ;<BACKSPACE>
.Fail: db ?, 0
; --------------------------------------
; IN (ds:si) OUT () MOD ()
WriteString:
 pusha
 mov bx, 1 ;STDOUT
 mov cx, bx
 jmp .b
.a: lea dx, [si-1]
 mov ah, 40h ;DOS.WriteToDevice
 int 21h ; -> AX CF
.b: lodsb
 test al, al
 jnz .a
 popa
 ret
; --------------------------------------
; IN (dx) OUT (bh) MOD (ah)
SetCursor:
 mov bh, [VPage]
 mov ah, 02h ;BIOS.SetCursor
 int 10h
 ret
; --------------------------------------
; IN (al,di) OUT () MOD ()
Focus: pusha ;AL is character
 imul si, di, 4 ;DI is index current field [0,6]
 add si, [Tags]
 mov dx, [BaseXY]
 mov cx, 1 ;Replication count
 mov bl, 0Eh ;YellowOnBlack
 call .a
 inc si
 inc si
 call .a
 popa
 ret
.a: add dx, [si] ;dCol and dRow
 call SetCursor ; -> BH (AH)
 mov ah, 09h ;BIOS.WriteCharacterAndAttribute
 int 10h
 ret
; --------------------------------------
; IN () OUT () MOD ()
Form: pusha
 mov si, FormA
 mov bx, [VCols] ;VCols and VPage
 cmp bl, 80
 jnb .a
 mov si, FormB ;Form for the 40 columns screen
.a: mov cx, 1 ;Replication count
 mov bl, 02h ;GreenOnBlack
 jmp .d
.b: cmp al, 32
 jb .c ;Non character
 cmp al, 255
 je .c ;Indentation
 mov ah, 09h ;BIOS.WriteCharacterAndAttribute
 int 10h
.c: mov ah, 0Eh ;BIOS.Teletype
 int 10h
.d: lodsb
 test al, al
 jnz .b
 mov [Tags], si ;Points at field position info
 mov ah, 03h ;BIOS.GetCursor
 int 10h ; -> CX=Shape DL=Col DH=Row
 mov [BaseXY], dx ;Column and row of dialog base
 popa
 ret
; --------------------------------------
 ALIGN 4
Hist1: times 256 db 0
Hist2: times 256 db 0
Hist5: times 256 db 0
ClipB: times 128 db 0
InBufs: dw Hist1
 db ?, ?, 1, 30, 32, 0
 times 32 db 0
 dw Hist2
 db ?, ?, 1, 20, 32, 0
 times 32 db 0
 dw 0
 db 0, 0, 0, 17, 20, 0
 times 32 db 0
 dw 0
 db 0, 0, 0, 11, 5, 0
 times 32 db 0
 dw Hist5
 db ?, ?, 1, 20, 32, 0
 times 32 db 0
FormA: db 13, 10
 db 255, 'ÉÍÍ Rich Edit Form Input ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»', 13, 10
 db 255, 'o ÄÄ Title ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄ Author ÄÄÄÄÄÄÄÄÄÄ o', 13, 10
 db 255, 'o o', 13, 10
 db 255, 'o ÄÄ ISBN ÄÄÄÄÄÄÄÄÄ ÄÄ Pages ÄÄ ÄÄ Publisher ÄÄÄÄÄÄÄ o', 13, 10
 db 255, 'o o', 13, 10
 db 255, 'ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ OK ÍÍÍÍÍ Cancel ÍÍ1⁄4', 13, 10
 db 10, 0
 db +5, -6, +6, +0
 db +37, -6, +7, +0
 db +5, -4, +5, +0
 db +24, -4, +6, +0
 db +37, -4, +10, +0
 db +37, -2, +3, +0
 db +46, -2, +7, +0
FormB: db 13, 10
 db 255, 'ÉÍÍ Rich Edit Form Input ÍÍÍÍÍÍÍÍÍ»', 13, 10
 db 255, 'o ÄÄ Title ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ o', 13, 10
 db 255, 'o o', 13, 10
 db 255, 'o ÄÄ Author ÄÄÄÄÄÄÄÄÄÄ o', 13, 10
 db 255, 'o o', 13, 10
 db 255, 'o ÄÄ ISBN ÄÄÄÄÄÄÄÄÄ ÄÄ Pages ÄÄ o', 13, 10
 db 255, 'o o', 13, 10
 db 255, 'o ÄÄ Publisher ÄÄÄÄÄÄÄ o', 13, 10
 db 255, 'o o', 13, 10
 db 255, 'ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍ OK ÍÍÍÍÍ Cancel ÍÍ1⁄4', 13, 10
 db 10, 0
 db +5, -10, +6, +0
 db +5, -8, +7, +0
 db +5, -6, +5, +0
 db +24, -6, +6, +0
 db +5, -4, +10, +0
 db +16, -2, +3, +0
 db +25, -2, +7, +0
Tags: dw ?
BaseXY: dw ?
VCols: db ?
 VPage: db ?
IsKeyb: db ?
CRLF: db 13, 10, 0
TAB: db 9, 0
; --------------------------------------

Screenshot 1, input in progress working on the 40x25 screen Screenshot 1

Screenshot 2, input in progress showing some selected text Screenshot 2

Screenshot 3, program ran with output redirection Screenshot 3

Screenshot 4, program ran with input redirection Screenshot 4

Screenshot 5, program ran with input redirection and output redirection Screenshot 5

asked Jan 21, 2018 at 15:25
\$\endgroup\$
2
  • \$\begingroup\$ I like the idea of redirection, but don't think I would implement 40x80. Instead of using ":", maybe highlighting the entire field would be easier to see. My personal preference would be ESC clears all fields and goes back to the first. ESC again while Title is empty would terminate app. TAB or ENTER will move to next field and CTL-ENTER posts data, clears all fields and goes back to first one. \$\endgroup\$ Commented Jan 22, 2018 at 13:46
  • \$\begingroup\$ @Shift_Left I agree that my colon-cursor is difficult to see. I also like your suggestion for a 2-stage ESC functionality. \$\endgroup\$ Commented Jan 28, 2018 at 15:19

1 Answer 1

1
\$\begingroup\$

Still using FASM 1.54 ?
If you'd upgrade to say version 1.68 your code could benefit from:

  • putting separators in long runs of digits

    test dl, 0000'0010b
    test al, 0000'0011b
    
  • replacing this un-attractive times 32 db 0 by

    db ?, ?, 1, 30, 32, 0, 32 dup 0
    

A simple solution for the problem of the near invisible focus as mentioned by user Shift_Left is to replace the colon character : by some character that has a much bigger surface (uses more pixels).

 mov ax, 1110h ;ASCII 16 and 17
 call Focus ;Give focus
 ...
 mov ax, " "
 call Focus ;Remove focus
 ...
Focus:
 pusha
 ...
 call .a
 mov al, [esp+15]

Placing the Hist1, Hist2, Hist3, and ClipB buffers at the end of the file and using the rb directive, will allow FASM to not include these in the file. This will lead to faster load times and reduce the disk storage requirements.

TAB db 9, 0 ; The last item in the file
 ALIGN 4
Hist1 rb 256
Hist2 rb 256
Hist3 rb 256
ClipB rb 128

Of course this also means that you need to initialize this memory before using it:

xor ax, ax
mov cx, (256+256+256+128)/2
mov di, Hist1
rep stosw

Those several dialogs take up a lot of space in the file. It would be easy to compress their data a bit by applying some form of run length encoding. The .RLE subroutine will write all except the last character if it encounters a long run of ASCII's 205, 196, or 32.

 cmp al, 255
 je .c ;Indentation
 CALL .RLE
 mov ah, 09h ;BIOS.WriteCharacterAndAttribute
 int 10h
.c:
 ...
.Done: ret
.RLE:
 cmp al, "Í" ;ASCII 205
 je @f
 cmp al, "Ä" ;ASCII 196
 je @f
 cmp al, " " ;ASCII 32
 jne .Done
@@:
 mov dh, [si]
 sub dh, "0"
 cmp dh, 9
 ja .Done
 inc si
 mov dl, [si]
 sub dl, "0"
 cmp dl, 9
 ja @f
 inc si
 xchg ax, dx
 aad
 xchg ax, dx
 mov dh, dl
@@:
 dec dh ;[0,99]
 js .Done
 mov ah, 09h ;BIOS.WriteCharacterAndAttribute
 int 10h
 mov ah, 0Eh ;BIOS.Teletype
 int 10h
 jmp @b

This is what it looks like when applied:

 db 255, 'ÉÍÍ Rich Edit Form Input ÍÍÍÍÍÍÍÍÍ»', 13, 10
 db 255, 'o ÄÄ Title ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ o', 13, 10
 db 255, 'o o', 13, 10
 ...
 db 255, 'ÉÍÍ Rich Edit Form Input Í8»', 13, 10
 db 255, 'o ÄÄ Title Ä20 o', 13, 10
 db 255, 'o 32o', 13, 10
 ...
answered May 31, 2018 at 10:55
\$\endgroup\$

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.