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
1 Answer 1
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
bydb ?, ?, 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
...
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\$