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, 'º ÄÄ 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 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

\$\endgroup\$
  • \$\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\$ – Shift_Left Jan 22 '18 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\$ – Sep Roland Jan 28 '18 at 15:19
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, 'º ÄÄ Title ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ  º', 13, 10
    db      255, 'º                                 º', 13, 10
    ...
    db      255, 'ÉÍÍ Rich Edit Form Input Í8»', 13, 10
    db      255, 'º ÄÄ Title Ä20  º', 13, 10
    db      255, 'º 32º', 13, 10
    ...
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.