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, 'º ÄÄ Title ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÄÄ Author ÄÄÄÄÄÄÄÄÄÄ º', 13, 10
db 255, 'º º', 13, 10
db 255, 'º ÄÄ ISBN ÄÄÄÄÄÄÄÄÄ ÄÄ Pages ÄÄ ÄÄ Publisher ÄÄÄÄÄÄÄ º', 13, 10
db 255, 'º º', 13, 10
db 255, 'ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ OK ÍÍÍÍÍ Cancel Íͼ', 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, 'º ÄÄ Title ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ º', 13, 10
db 255, 'º º', 13, 10
db 255, 'º ÄÄ Author ÄÄÄÄÄÄÄÄÄÄ º', 13, 10
db 255, 'º º', 13, 10
db 255, 'º ÄÄ ISBN ÄÄÄÄÄÄÄÄÄ ÄÄ Pages ÄÄ º', 13, 10
db 255, 'º º', 13, 10
db 255, 'º ÄÄ Publisher ÄÄÄÄÄÄÄ º', 13, 10
db 255, 'º º', 13, 10
db 255, 'ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍ OK ÍÍÍÍÍ Cancel Íͼ', 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 2, input in progress showing some selected text
Screenshot 3, program ran with output redirection
Screenshot 4, program ran with input redirection
Screenshot 5, program ran with input redirection and output redirection
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\$