        push    bp              ; save the base pointer
        mov     bp,sp           ; point it to the stack
        push    bp              ; save new value on stack
        jmp     start

;--------------------------------------------------------------------------
;  Key code equates...
;--------------------------------------------------------------------------
CR      equ     13              ; Carraige return
Dkey    equ     339             ; Delete key code
BkSpace equ     8               ; Back-space
Escape  equ     27              ; Escape code
UpArrw  equ     328             ; Up Arrow
DnArrw  equ     336             ; Down Arrow
LftArrw equ     331             ; Left Arrow
RtArrw  equ     333             ; Right Arrow
HmKey   equ     327             ; Home key
EKey    equ     335             ; End key
CBkKey  equ     127             ; Ctrl-Backspace
PgUp    equ     329             ; Page Up
PgDn    equ     337             ; Page Down
CtrlCR  equ     10              ; Ctrl-Enter

;--------------------------------------------------------------------------
;  The following are offsets in the stack for the input parameters:
;--------------------------------------------------------------------------
Fptr    equ     -4
Opts    equ     4               ; Options[0] = length of input options
UpCnv   equ     02h             ; Bit #1     = Convert input to Uppercase!
AutoX   equ     20h             ; Bit #6     = Auto exit from field
InitVal equ     80h             ; Bit #7     = Use initial value of Ibuf
ISize   equ      5              ; Position of Size variable on stack
col     equ      7              ; Position of Column value on stack
row     equ      9              ; Position of Row value on stack
Atrib   equ     11              ; Position of Attribute to use for field
Ibuf    equ     13              ; Position of pointer to input buffer
Legal   equ     17              ; Positon of legal string on stack
KeyVal  equ     21              ; Position of pointer to return variable

;--------------------------------------------------------------------------
;  Getkey function:
;    Return: AX = 0 if no key pressed
;            AX = 1..255 if standard key code
;            AX = 256..  if function key or cursor control key...
;--------------------------------------------------------------------------
GetKey:
        mov     bx,0            ;temporary return value
        mov     ah,1            ; check kbd buffer
        int     16h             ; kbd IO interrupt
        jz      GetKey          ; keep waiting until key is found
        mov     ah,0            ; else get kbd char
        int     16h             ; kbd io interupt
        mov     bl,al           ; put into return register
        or      al,al           ; check for extended code
        jnz     GY1             ; skip if not extended code
        mov     bh,1            ; set MSByte for extended code
        mov     bl,ah           ; move scan code into bl
GY1:
        mov     ax,bx           ; set up for return from function
        ret                     ; exit module

;--------------------------------------------------------------------------
;  Put out field with attribute specified by user
;--------------------------------------------------------------------------
RevFld:
        mov     ax,0920h        ; get blank...& write char code
        mov     bh,0            ; set for page 0
        mov     bl,Atrib[bp]    ; get video attribute
        mov     cx,ISize[bp]    ; get length of field
        int     10h             ; go do it!
        ret

;--------------------------------------------------------------------------
;  Write a character to the screen...
;--------------------------------------------------------------------------
WChar:
        mov     ah,10           ; write character only at current position
        mov     bh,0            ; page 0 of video board!
        mov     cx,1            ; only write one character...
        int     10h             ; video I/O interrupt (write character)
        inc     Fptr[bp]
        ret

;--------------------------------------------------------------------------
;  Goto X,Y position  (dh = row, dl = column)
;--------------------------------------------------------------------------
GotoXY:
        dec     dh              ; Adjust row & column for BIOS
        dec     dl              ; (0,0) = upper left corner!
        mov     ah,2            ; set cursor function code
        mov     bh,0            ; set for current page!
        int     10h             ; video I/O interrupt
        ret

;--------------------------------------------------------------------------
; Position cursor to Col+Fptr-1,Row
;--------------------------------------------------------------------------
CurPos:
        mov     dl,Fptr[bp]     ; get Field pointer value
        dec     dl              ; subtract 1
        add     dl,Col[bp]      ; dl = Fptr+Col-1
        mov     dh,Row[bp]      ; get Row
        call    GotoXY          ; position cursor
        ret                     ; exit module

;--------------------------------------------------------------------------
;  UpCase function: convert a character to its upper-case equivalent -
;                   if it is a lowercase letter!
;--------------------------------------------------------------------------
UpCase:
        cmp     al,'a'          ; Check against 'a'
        jb      UE1             ; nope, its below that
        cmp     al,'z'          ; check against 'z'
        ja      UE1             ; nope, its above that!
        xor     al,20h          ; make it UPPERCASE!
UE1:
        ret                     ; exit module

;--------------------------------------------------------------------------
; Check for legal characters in input!
;--------------------------------------------------------------------------
LegalCk:
        les     di,Legal[bp]    ; get address of legal string
        mov     cl,es:[di]      ; get length of legal string
        xor     ch,ch           ; zero out msb
        or      cl,cl           ; check for zero length
        je      LK1             ; exit if no length
LK2:
        inc     di              ; increment address pointer
        cmp     al,es:[di]      ; is character in legal string?
        loopne  LK2             ; repeat until found or end of table
LK1:
        ret                     ; exit from module

;--------------------------------------------------------------------------
; Home key function
;--------------------------------------------------------------------------
HomeKey:
        mov     Fptr[bp],1      ; set Fptr back to start of field
        ret

;--------------------------------------------------------------------------
; End Key function
;--------------------------------------------------------------------------
EndKey:
        les     di,Ibuf[bp]     ; get address of Ibuf
        mov     al,es:[di]      ; get length of Ibuf...
        inc     al              ; Plus one!
        mov     Fptr[bp],al
        ret

;--------------------------------------------------------------------------
;  Right Arrow key
;--------------------------------------------------------------------------
RightKey:
        les     di,Ibuf[bp]     ; get address of Ibuf
        mov     al,es:[di]      ; get length of Ibuf
        inc     al              ; plus one!
        cmp     Fptr[bp],al     ; compare with Fptr?
        jnb     RY1             ; exit module if ok...
        inc     Fptr[bp]        ; increment Fptr!
RY1:
        ret

;--------------------------------------------------------------------------
;  Left Arrow Key
;--------------------------------------------------------------------------
LeftKey:
        cmp     Fptr[bp],1      ; Fptr > 1?
        jna     LY1             ; move left if ok...
        dec     Fptr[bp]        ; Fptr := Fptr - 1;
LY1:
        ret

;--------------------------------------------------------------------------
;  Control-Backspace Key
;--------------------------------------------------------------------------
CBackKey:
        call    Init            ; Erase entire field & start over!
        ret

;--------------------------------------------------------------------------
;  String Delete: delete a character from the current position of a string
;--------------------------------------------------------------------------
StrDel:
        les     di,Ibuf[bp]     ; get input buffer address
        mov     cx,Fptr[bp]     ; get field pointer
        cmp     cl,es:[di]      ; compare Ibuf[0] w/ Fptr
        ja      DY1             ; Fptr > Ibuf[0]
        je      DY2             ; Ibuf[0] = Fptr
        mov     bx,cx           ; Fptr < Ibuf[0]
        mov     cl,es:[di]      ; get length of Ibuf
        sub     cl,bl           ; cx = # of char's to move
DYLP1:
        mov     al,es:[di+bx+1] ; get char @ next position
        mov     es:[di+bx],al   ; place char @ current position
        inc     bx              ; get ready for next char
        loop    DYLP1           ; repeat until cx = 0
DY2:
        dec     es:[di] ; decrement Ibuf length by one
DY1:
        ret                     ; exit from module!

;--------------------------------------------------------------------------
;  Screen delete: remove a character from the field shown on the screen!
;--------------------------------------------------------------------------
ScrDel:
        mov     ax,row[bp]      ; get starting row of field
        dec     ax              ; set up for calc
        mov     di,col[bp]      ; get starting col of field
        dec     di              ; set up for calc
        add     di,Fptr[bp]     ; add in Fptr offset!
        dec     di              ; Col + Fptr - 1 !!!
        les     bx,Ibuf[bp]     ; get address of input string
        mov     cl,es:[bx]      ; get length of input string
        xor     ch,ch
        mov     bx,0040h        ; Calc actual address in video memory...
        mov     es,bx           ; set up segment for following:
        mul     es:word ptr[4ah]  ; Calc Row * current width
        add     di,ax           ; add in # of col's
        shl     di,1            ; multiply by two for Attrib/Value pair
        mov     si,di           ; get as source address
        add     si,2            ; next video loc
        mov     dx,es:[63h]     ; test video board
        add     dx,6            ; offset of register in video adapter
        mov     ax,0b800h       ; Color ?
        mov     bx,es:[10h]     ; test for Mono vs Color
        and     bx,30h
        cmp     bx,30h
        jne     setcard         ; Board is color... else
        mov     ax,0B000h       ; Mono !
setcard:
        mov     es,ax           ; set up segment for transfer
        push    ds
        mov     ds,ax
        sub     cx,Fptr[bp]     ; Calc actual # of char's to move!
        jb      SRExit1         ; if < 0 then skip ...
        je      SRExit2 ; if = 0 then just put out space & exit!
test_low:
        in      al,dx           ; wait for Horizontal retrace to end
        test    al,1
        jnz     test_low
        cli
test_hi:
        in      al,dx           ; wait for next one to start
        test    al,1
        jz      test_hi
	cld			; set direction to forward
movelp:
        movsb                   ; found it! now move data!
        inc     di
        inc     si
        loop    movelp
SREXit2:
        mov     byte ptr ds:[di],' ' ; blank out last position!
        sti
SRExit1:
        pop     ds
        ret

;--------------------------------------------------------------------------
;  Delete Key
;--------------------------------------------------------------------------
DelKey:
        call    ScrDel          ; delete char from screen field
        call    StrDel          ; delete char from string
        ret

;--------------------------------------------------------------------------
; Backspace Key
;--------------------------------------------------------------------------
BackKey:
        cmp     Fptr[bp],1      ; Fptr > 1?
        jna     BY1             ; move lef if ok
        dec     Fptr[bp]        ; Fptr := Fptr - 1;
        call    DelKey          ; and then delete char!!!
BY1:
        ret

;--------------------------------------------------------------------------
;  This routine accesses all of the key subroutines!
;--------------------------------------------------------------------------
Gosub:
        jmp     cx              ; jump to address found from table!

;--------------------------------------------------------------------------
;  Test content of AX against entry in JUMP Table!
;--------------------------------------------------------------------------
TabTest2:
        pop     bx              ; get address of Table!
        mov     cx,0            ; preset ending value
T2Lp1:
        cmp     cs:[bx],0       ; at end of table?
        je      T2Exit          ; yes, get out of subroutine!
        cmp     ax,cs:[bx]      ; is char in table entry?
        jne     T2Skip1
        mov     cx,bx           ; get address of match position!
        add     cx,2            ; increment to jump address
T2Skip1
        add     bx,5            ; get to next address!
        jmp     T2Lp1
T2Exit:
        inc     bx
        inc     bx
        or      ch,ch
        push    bx
        ret                     ; exit from module!


;--------------------------------------------------------------------------
; Initialize system
;--------------------------------------------------------------------------
Init:
        mov     Fptr[bp],1               ; set up Fptr for following...
        call    CurPos                   ; position cursor to start of field
        call    RevFld                   ; put out video area
        test    byte ptr Opts[bp],InitVal ; use initial value of Ibuf?
        jz      IT1                      ; no, skip setup rtn
        xor     byte ptr Opts[bp],InitVal         ; reset flag!!
        les     di,Ibuf[bp]              ; get address of Input buffer
        cmp     byte ptr es:[di],0       ; initial value for Ibuf?
        je      IT1                      ; no, skip anything else...
        mov     cl,es:[di]               ; get length of Ibuf
        xor     ch,ch                    ; zero out CH
IT3:
        inc     di                       ; increment string pointer
        mov     al,es:[di]               ; get byte from Ibuf
        push    cx                       ; save count!
        call    WChar                    ; write character to screen
        call    CurPos                   ; re-position cursor!
        pop     cx                       ; restore counter
        loop    IT3                      ; repeat until message is printed
        mov     Fptr[bp],1               ; reset field pointer to field start
        les     di,Ibuf[bp]              ; get address of Input buffer
        jmp     IT2                      ; exit from module
IT1:
        mov     Fptr[bp],1               ; reset field pointer to field start
        les     di,Ibuf[bp]              ; get address of Input buffer
        mov     byte ptr es:[di],0       ; set Input length to zero
IT2:
        ret                              ; exit module

;--------------------------------------------------------------------------
; Main module
;--------------------------------------------------------------------------
Main:
        call    CurPos                   ; position cursor
        call    GetKey                   ; get char from kbd
        cmp     ax,' '                   ; is it >= a space?
        jb      MN1                      ; not a valid printable character!
        cmp     ax,127                   ; is it <= chr(127)?
        jnb     MN1                      ; not a valid printable character!
        test    byte ptr Opts[bp],UpCnv  ; convert to uppercase option active?
        jz      MN0                      ; nope, continue...
        call    UpCase                   ; else convert it!
MN0:
        call    LegalCk                  ; check for legal character
        jnz     Main                     ; ignore if not found in legal string
        mov     bl,Fptr[bp]              ; get current field pointer
        cmp     bl,ISize[bp]             ; at end of field?
        ja      Main                     ; no, skip this test!
        les     di,Ibuf[bp]              ; get address of input buffer
        mov     bl,Fptr[bp]              ; get input pointer value
        xor     bh,bh                    ; zero out BH
        mov     es:[di+bx],al            ; save char into input buffer
        cmp     bl,es:[di]               ; increase return size of buffer?
        jna     MN2                      ; no,
        mov     es:[di],bl               ; yes, store ptr @ buffer length pos
MN2:
        call    WChar                    ; write character to screen
        mov     bl,Fptr[bp]              ; get current field pointer
        cmp     bl,ISize[bp]             ; at end of field?
        jna     Main                     ; don't worry about it!
        test    byte ptr Opts[bp],AutoX  ; auto-exit on?
        jz      Main                     ; nope - try again
        mov     ax,CR                    ; pretend to be a CR
        jmp     Skip1                    ; go to exit from module
MN1:
        call    TabTest2                 ; check for address of subroutine...
        dw      EKey                     ; scan code for [END] key
        jmp     EndKey                   ; jump to routine
        dw      LftArrw                  ; scan code for [LEFT ARROW] key
        jmp     LeftKey
        dw      RtArrw                   ; scan code for [RIGHT ARROW] key
        jmp     RightKey
        dw      CBkKey                   ; scan code for [^BACKSPACE]
        jmp     CBackKey
        dw      BkSpace                  ; scan code for [BACKSPACE]
        jmp     BackKey
        dw      HmKey                    ; scan code for [HOME] key
        jmp     HomeKey
        dw      DKey                     ; scan code for [DEL] key
        jmp     DelKey
        dw      0                        ; mark end of table
        jz      Skip1
        call    Gosub                    ; go do subroutine
        jmp     Main
Skip1:
        ret                              ; exit from module!

;--------------------------------------------------------------------------
; Conclusion to program
;--------------------------------------------------------------------------
Conc:
        les     di,KeyVal[bp]            ; get address of return variable
        mov     es:[di],ax               ; store keycode of last keystroke
        ret                              ; exit module

;--------------------------------------------------------------------------
; Start of Program !!!!
;--------------------------------------------------------------------------
start:
        push    ax


CmdLoop:
        call    Init                     ; set up for program
        call    Main                     ; do main loop
        call    Conc                     ; conclusion of program

        pop     ax                       ; get rid of Fptr area!
        mov     sp,bp                    ; reset stack pointer
        pop     bp                       ; reset base pointer
        ret     21                       ; exit & clear stack of variables
