        NAME    ccuibm
; File CCUIBM.ASM
;CHINESE
ifdef   MSDOS
        include msuibm.dat
else
        include ccuibm.dat
endif

code    segment public 'code'
                ; system independent external items
        extrn   comnd:near, prompt:near, iseof:near     ; in msscmd
        extrn   strlen:near                             ; in mssfil
        extrn   cnvlin:near, katoi:near, decout:near    ; in msster
                ; system dependent external items
        extrn   beep:near, prtchr:near, khold:near      ; in msxibm &msyibm
        extrn   anskbi:near, vclick:near                ; in mszibm
                ; these are system dependent action verbs, in msxibm & msyibm
        extrn   uparrw:near, dnarrw:near, rtarr:near, lfarr:near
        extrn   pf1:near, pf2:near, pf3:near, pf4:near, kp0:near, kp1:near
        extrn   kp2:near, kp3:near, kp4:near, kp5:near, kp6:near, kp7:near
        extrn   kp8:near, kp9:near, kpminus:near, kpcoma:near, kpenter:near
        extrn   kpdot:near
        extrn   chrout:near, cstatus:near, cquit:near, cquery:near
        extrn   vtans52:near, vtinit:near, dnwpg:near, upwpg:near
        extrn   endwnd:near, homwnd:near, upone:near, dnone:near, trnprs:near
        extrn   trnmod:near, sendbr:near, sendbl:near, dmpscn:near, snull:near
        extrn   chang:near, klogon:near, klogof:near, cdos:near
        extrn   vtrmac:near, vtsmac:near
ifdef DEBG
        extrn   debgp:near
endif
        assume  cs:code, ds:datas, es:datas

; Begin system independent Keyboard Translator code

; MSUINIT performs Kermit startup initialization for this file.
; Note, shkadr and stkadr are pointers tested by Set/Show Key calls. If they
; are not initialized here then the older Set/Show Key procedures are called.
MSUINIT PROC    NEAR                    ; call from msx/msy init code
        call    kbdinit                 ; optional: init translator tables
        mov     shkadr,offset shkey     ; declare keyboard translator present
        mov     stkadr,offset dfkey     ; via Show and Set Key proc addresses
        ret
MSUINIT ENDP

; Call Keybd to read a keyboard char (just returns carry clear if none) and
; 1) send the replacement string (or original char if not translated)
;    out the serial port, or
; 2) execute a Kermit action verb.
; Returns carry set if Connect mode is to be exited, else carry clear.
; Modifies registers ax and bx.
KEYBD   PROC    NEAR                    ; active translator
        call    getkey                  ; read keyboard
        jnc     keybd1                  ; nc = data available
        jmp     keybdx                  ; else just return carry clear
keybd1: call    postkey                 ; call system dependent post processor
        cmp     nkeys,0                 ; is number of keys defined = 0?
        jz      keybd3                  ; z = none defined
        push    di                      ; search keylist for this keycode
        push    cx                      ; save some registers
        push    es
        mov     di,offset keylist       ; list of defined keycode words
        mov     ax,keycode              ; present keycode
        mov     cx,nkeys                ; number of words to examine
        push    ds
        pop     es                      ; make es:di point to datas segment
        cld
        repne   scasw                   ; find keycode in list
        pop     es                      ; restore regs
        pop     cx
        je      keybd1b                 ; e = found, work with present di
        pop     di                      ; restore original di
        test    keycode,scan            ; is this a scan code?
        jz      keybd3                  ; z = no, it's ascii, use al as char
        call    beep                    ; say key is a dead one
        clc
        ret                             ; and exit with no action

keybd1b:sub     di,2                    ; correct for auto increment
        sub     di,offset keylist       ; subtract start of list ==> listptr
        mov     ax,dirlist[di]          ; ax = contents of director word
        pop     di                      ; restore original di
                                        ; dispatch on Director code
        test    ax,verb                 ; verb only?
        jnz     keyvb                   ; e = yes
        test    ax,strng                ; multi-char string only?
        jnz     keyst                   ; e = yes, else single char & no xlat.
                                        ;
                                        ; do single CHAR output (char in al)
keybd3: cmp     al,trans.escchr         ; Kermit's escape char?
        je      keybd3a                 ; e = yes, handle separately
        call    chrout                  ; transmit the char
        clc                             ; return success
        ret
keybd3a:stc                             ; set carry for jump to Quit
        ret

keyvb:  and     ax,not(verb+strng)      ; VERB (ax=index, remove type bits)
        mov     bx,offset kverbs        ; start of verb table
        cmp     al,byte ptr [bx]        ; index > number of entries?
        jae     keybdx                  ; ae = illegal, indices start at 0
        inc     bx                      ; bx points to first entry
        push    cx                      ; save reg
        mov     cx,ax                   ; save the index in cx
        inc     cx                      ; counter, indices start at 0
keyvb1: mov     al,byte ptr [bx]        ; cnt value
        xor     ah,ah
        add     ax,4                    ; skip text and '?' and value word
        add     bx,ax                   ; look at next slot
        loop    keyvb1                  ; walk to correct slot
        sub     bx,2                    ; backup to value field
keyvb2: pop     cx                      ; restore reg
        mov     bx,[bx]                 ; get value field of this slot
        cmp     bx,0                    ; jump address defined?
        je      keybdx                  ; e = no, skip the action
        jmp     bx                      ; perform the function

keyst:  and     ax,not(verb+strng)      ; STRING (ax=index, remove type bits)
        shl     ax,1                    ; convert to word index
        push    si                      ; save working reg
        mov     si,ax                   ; word subscript in table
        mov     si,sptable[si]          ; memory offset of selected string
        cmp     si,0                    ; is there a string pointer present?
        je      keyst3                  ; e = no, skip operation
        cld                             ; scan forward
        lodsb                           ; get string length byte
        mov     cl,al
        xor     ch,ch                   ; to cx for looping
        jcxz    keybdx                  ; z = null length
keyst2: lodsb                           ; get new string char into al
        push    si                      ; save si and cx around call
        push    cx
        call    chrout                  ; send out the char in al
        pop     cx                      ; recover regs
        pop     si
        loop    keyst2                  ; loop through whole string
keyst3: pop     si                      ; restore reg

keybdx: clc                             ; return success
        ret
KEYBD   ENDP

; SET KEY - define a key   (procedure dfkey)
; SET KEY <key ident><whitespace><new meaning>
; Call from Kermit level. Returns as ret if failure or as rskp if success.
;
DFKEY   PROC    NEAR                    ; define a key as a verb or a string
        mov     keycode,0               ; clear keycode
        mov     oldform,0               ; say no old form Set Key yet
        mov     dx,offset tranbuf       ; our work space
        mov     word ptr tranbuf,0      ; insert terminator
;        mov     bx,offset dfhelp1       ; first help message
        mcmsgb  dfhelp1,cdfhelp1
        mov     ah,cmfile               ; parse a word
        call    comnd                   ; get key code or original ascii char
         nop
         nop
         nop
        mov     al,intake               ; reading from Take file indirectly
        or      al,taklev               ; ditto, directly
        mov     msutake,al              ; save here
        or      ah,ah                   ; any text given?
        jnz     dfkey12                 ; nz = yes, so don't consider prompts
                                        ; interactive key request
        cmp     intake,0                ; in a Take file?
        je      dfkey10                 ; e = no, prompt for keystroke
        jmp     dfkey0                  ;  else say bad syntax
dfkey10:mov     ah,prstr
;        mov     dx,offset dfaskky       ; ask for key to be pressed
        mcmsg   dfaskky,cdfaskky
        int     dos
dfkey11:call    getkey                  ; read key ident from keyboard
        jc      dfkey11                 ; c = no response, wait for keystroke
        mov     ah,prstr                ; display cr/lf
        mov     dx,offset crlf
        int     dos
        call    shkey0                  ; show current definition (in SHKEY)
        jmp     dfkey1e                 ; prompt for and process definition

dfkey12:                                ; Look for word SCAN and ignore it
        mov     dx,word ptr tranbuf     ; get first two characters
        or      dx,2020h                ; map upper to lower case
        cmp     dx,'cs'                 ; first two letters of word "scan"?
        je      dfkey                   ; e = yes, skip the word
        cmp     dx,'lc'                 ; first two letters of word "clear"?
        je      dfkey15                 ; e = yes, reinit keyboard [2.31]
        cmp     dx,'fo'                 ; first two letters of "off"
        je      dfkey13                 ; e = yes, use DOS keyboard calls
        cmp     dx,'no'                 ; first two letters of "on"
        je      dfkey14                 ; e = yes, use standard kbd calls
        cmp     ah,1                    ; number of characters received
        ja      dfkey1                  ; a = more than one, decode
        mov     ah,byte ptr tranbuf     ; get the single char
        mov     byte ptr keycode,ah     ; store as ascii keycode
        jmp     dfkey1b                 ; go get definition

dfkey13:mov     dosflg,0ffh             ; set DOS keyboard read flag
        jmp     short dfkey14a
dfkey14:mov     dosflg,0                ; clear DOS keyboard read flag
dfkey14a:mov    ah,cmcfm                ; added after version 2.30
        call    comnd
         jmp    r
         nop
        jmp     rskp

dfkey15:mov     ah,cmcfm                ; added after version 2.30
        call    comnd                   ; confirm request before proceeding
         jmp    r
         nop
        jmp     short dfkeyc            ; execute command

dfkey0: 
;       mov     dx,offset dfhelp1       ; say bad definition command
        mcmsg  dfhelp1,cdfhelp1
        mov     ah,prstr
        int     dos
        jmp     rskp

dfkeyc:                                 ; CLEAR key defs, restore startup defs
        mov     cx,maxkeys              ; size of keycode tables
        push    es                      ; save register
        push    ds
        pop     es                      ; make es point to datas segment
        mov     ax,0                    ; null, value to be stored
        mov     di,offset dirlist       ; director table
        cld
        rep     stosw                   ; clear it
        mov     cx,maxkeys
        mov     di,offset keylist       ; keycode table
        rep     stosw                   ; clear it
        mov     cx,maxstng
        mov     di,offset sptable       ; string pointer table
        rep     stosw                   ; clear it
        pop     es                      ; recover register
        mov     strmax,offset stbuf     ; clear string buffer, free space ptr
        mov     stbuf,0                 ; first element of buffer
        mov     nkeys,0                 ; clear number of defined keys
        call    msuinit                 ; restore startup definitions
        jmp     rskp
                                        ; Multi-char key identification
dfkey1: mov     si,offset tranbuf       ; point to key ident text
        cmp     byte ptr [si],'0'       ; is first character numeric?
        jb      dfkey1a                 ; b = no
        cmp     byte ptr [si],'9'       ; in numbers?
        ja      dfkey1a                 ; a = no
        mov     keycode,scan            ; setup keycode for scan value
        mov     dx,si                   ; get length of string in cx
        call    strlen
        push    ds
        pop     es                      ; make es point to datas segment
        push    si
        add     si,cx                   ; point at string terminator
        mov     di,si
        inc     di                      ; place to store string (1 byte later)
        inc     cx                      ; include null terminator
        std                             ; work backward
        rep     movsb                   ; move string one place later
        cld
        pop     si
        mov     byte ptr [si],'\'       ; make ascii digits into \nnn form
        mov     oldform,0ffh            ; set old form flag
;        mov     dx,offset kwarnmsg      ; tell user this is old form
        mcmsg   kwarnmsg,ckwarnmsg
        mov     ah,prstr
        int     dos
dfkey1a:call    katoi                   ; convert ascii number to binary in ax
        jnc     dfkey1h
        jmp      dfkey0                  ; c = no number converted
dfkey1h:
        or      keycode,ax              ; store in keycode

dfkey1b:                                ; Get Definition proper
        test    oldform,0ffh            ; old form Set Key active?
        jz      dfkey1f                 ; z = no
        mov     bx,offset tranbuf       ; get new definition on main cmd line
        mov     word ptr [bx],0         ; insert terminator
;        mov     dx,offset dfhelp2       ; help for definition of key
        mcmsg   dfhelp2,cdfhelp2
        mov     ah,cmtxt                ; read rest of line into tranbuf
        call    comnd
         nop                            ; allow null definitions
         nop
         nop
        or      ah,ah                   ; char count zero?
        jz      dfkey1e                 ; z = zero, prompt for definition
        jmp     dfkey1g                 ; process definition

dfkey1e:mov     ah,prstr
        mov     dx,offset crlf
        int     dos
;        mov     dx,offset dfaskdf       ; prompt for definition string
        mcmsg   dfaskdf,cdfaskdf
        call    prompt                  ; Kermit prompt routine
        mov     comand.cmcr,1           ; permit bare carriage returns
        mov     comand.cmwhite,1        ; allow leading whitespace
dfkey1f:mov     bx,offset tranbuf       ; get new definition
        mov     word ptr [bx],0         ; insert terminator
;        mov     dx,offset dfhelp2       ; help for definition of key
        mcmsg   dfhelp2,cdfhelp2
        mov     ah,cmtxt                ; read rest of line into tranbuf
        call    comnd
         jmp r                          ; exit now on ^C from user
         nop
        cmp     comand.cmcr,0           ; prompting for definition?
        je      dfkey1g                 ; e = no, trim leading whitespace
        mov     comand.cmcr,0           ; turn off allowance for bare c/r's
        jmp     dfkey2                  ; interactive, allow leading whitespace
dfkey1g:xchg    ah,al                   ; put byte count in al
        xor     ah,ah                   ; clear high byte
        mov     kbtemp,ax               ; and save count in kbtemp
        mov     ah,cmcfm                ; get a confirm
        call    comnd
         jmp    r                       ; none so declare parse error
         nop                            ; round out to three bytes
        mov     cx,kbtemp               ; string length

dfkey2:                                 ; Examine translation
        mov     al,trans.escchr         ; current escape char (port dependent)
        cmp     al,byte ptr keycode     ; is this Kermit's escape char?
        jne     dfkey2a                 ; ne = no
        test    keycode,scan            ; see if scan code
        jnz     dfkey2a                 ; nz = scan, so not ascii esc char
;        mov     dx,offset dfkopps       ; Opps! msg
        mcmsg   dfkopps,cdfkopps
        mov     ah,prstr                ; complain and don't redefine
        int     dos
        jmp     rskp

dfkey2a:push    di                      ; get a director code for this key
        push    cx
        mov     di,offset keylist       ; list of keycodes
        mov     cx,nkeys                ; number currently defined
        mov     ax,keycode              ; present keycode
        jcxz    dfkey2b                 ; cx = 0 means none defined yet
        cld
        push    ds
        pop     es
        repne   scasw                   ; is current keycode in the list?
        jne     dfkey2b                 ; ne = not in list
        sub     di,2                    ; correct for auto increment
        sub     di,offset keylist
        mov     listptr,di              ; list pointer for existing definition
        pop     cx
        pop     di
        jmp     dfkey3                  ; go process definition

dfkey2b:pop     cx                      ; key not currently defined so
        pop     di                      ;  make a new director entry for it
        mov     bx,nkeys                ; number of keys previously defined
        cmp     bx,maxkeys              ; enough space?
        jae     dfkey2c                 ; ae = no, complain
        shl     bx,1                    ; count words
        mov     listptr,bx              ; index into word list
        mov     ax,keycode              ; get key's code
        mov     keylist[bx],ax          ; store it in list of keycodes
        mov     dirlist[bx],0           ; clear the new director entry
        inc     nkeys                   ; new number of keys
        jmp     dfkey3                  ; go process definition

dfkey2c:
;       mov     dx,offset keyfull       ; say key space is full already
        mcmsg   keyfull,ckeyfull
        mov     ah,prstr
        int     dos
        jmp     rskp                    ; tell parser we are happy
; listptr has element number in keylist or dirlist; keycode has key's code.

; Parse new definition. First look for Kermit verbs as a line beginning
; as \K or \{K. Otherwise, consider the line to be a string.
; In any case, update the Director table for the new definition.

dfkey3: mov     brace,0                 ; assume not using braces
        mov     si,offset tranbuf       ; start of definition text
        cmp     byte ptr [si],'\'       ; starts with escape char?
        jne     dfkey5                  ; ne = no, so we have a string
        inc     si                      ; skip the backslash
        cmp     byte ptr [si],braceop   ; starts with \{?
        jne     dfkey3a                 ; ne = no
        inc     si                      ; skip the opening brace
        mov     brace,bracecl           ; expect closing brace
dfkey3a:cmp     byte ptr [si],'K'       ; starts with \{K or \K?
        je      dfkey3b                 ; e = yes
        cmp     byte ptr [si],'k'       ; starts as \{k or \k?
        jne     dfkey5                  ; ne = no, then it's a string
dfkey3b:inc     si                      ; yes, skip the K too
                                        ; Kermit action VERBS
        push    si                      ; save verb name start address
dfkey4: cld
        lodsb                           ; scan til closing brace or w/s or end
        cmp     al,0                    ; premature end?
        je      dfkey4b                 ; e = yes, accept without brace
        cmp     al,brace                ; closing brace?
        je      dfkey4b                 ; e = yes
        cmp     al,spc                  ; white space or control char?
        ja      short dfkey4            ; a = no, so not at end yet
dfkey4b:mov     byte ptr[si-1],0        ; insert null terminator
        pop     si                      ; recover start address
        call    tstkeyw                 ; find keyword, kw # returned in kbtemp
        jc      dfkey4d                 ; c = no keyword found, complain
        call    remstr                  ; clear old string, if string
        mov     ax,kbtemp               ; save keyword number
        and     ax,not(verb+strng)      ; clear verb / string field
        or      ax,verb                 ; set verb ident
        mov     si,listptr
        mov     dirlist[si],ax          ; store info in Director table
        jmp     dfkey7                  ; show results and return success

dfkey4d:
;       mov     dx,offset verbbad       ; say no such verb
        mcmsg   verbbad,cverbbad
        mov     ah,prstr
        int     dos
        jmp     rskp

; Here we are left with the definition string; si points to its start, and
; kbtemp holds its length (number of bytes). Null termination. If the string
; begins with an opening brace it terminates on a matching closing brace
; or the end of line, whichever occurs first. Trailing whitespace removed
; before examining braces.
; Null length strings mean define key as Self.
                                        ; STRING definitions
dfkey5: call    remstr                  ; first, clear old string, if any
        mov     si,offset tranbuf       ; si=source, di=dest, convert in-place
        mov     di,si
        call    cnvlin                  ; convert numbers, cx gets line length
        mov     si,offset tranbuf       ; provide address of new string
        cmp     cx,1                    ; just zero or one byte to do?
        jbe     dfkey6                  ; e = yes, do as a char
        call    insertst                ; insert new string, returns reg cx.
        jc      dfkey5h                 ; c = could not do insert
        mov     si,listptr              ; cx has type and string number
        mov     dirlist[si],cx          ; update Director table from insertst
        jmp     dfkey7                  ; show results and return success

dfkey5h:
;       mov     dx,offset strbad        ; display complaint
        mcmsg   strbad,cstrbad
        mov     ah,prstr
        int     dos
dfkeyx: jmp     rskp

                ; define SINGLE CHAR replacement or CLEAR a key definition.
                ; cx has char count 1 (normal) or 0 (to undefine the key).
dfkey6: jcxz    dfkey6c                 ; z = cx= 0, clear definition
        mov     al,byte ptr [si]        ; get first byte from definition
        xor     ah,ah                   ; set the type bits to Char
        mov     si,listptr
        mov     dirlist[si],ax          ; store type and key's new code
        jmp     dfkey7                  ; return success

dfkey6c:push    si                      ; clear a definition,
        push    di                      ; listptr points to current def
        mov     si,listptr              ; starting address to clear
        add     si,offset dirlist
        mov     di,si                   ; destination
        add     si,2                    ; source is next word
        mov     cx,nkeys                ; current number of keys defined
        add     cx,cx                   ; double for listptr being words
        sub     cx,listptr              ; cx = number of words to move
        shr     cx,1                    ; convert to actual number of moves
        jcxz    dfkey6d                 ; z = none, just remove last word
        push    es
        push    ds
        pop     es                      ; make es:di point to datas segment
        cld
        push    cx                      ; save cx
        rep     movsw                   ; move down higher list items
        pop     cx
        mov     si,listptr              ; do keylist too, same way
        add     si,offset keylist
        mov     di,si
        add     si,2
        rep     movsw
        pop     es
dfkey6d:mov     si,nkeys                ; clear old highest list element
        shl     si,1                    ; address words
        mov     dirlist[si],0           ; null the element
        mov     keylist[si],0           ; null the element
        dec     nkeys                   ; say one less key defined now
        pop     di                      ; restore saved registers
        pop     si

dfkey7: mov     ah,msutake              ; Finish up. In a Take file?
        or      ah,taklev               ; or even directly
        cmp     ah,0
        je      dfkey7a                 ; e = no
        cmp     flags.takflg,0          ; echo Take commands?
        je      dfkey7b                 ; e = no
dfkey7a:mov     ah,prstr                ; display cr/lf
        mov     dx,offset crlf
        int     dos
        call    shkey0                  ; show new definition (in SHKEY)
        call    shkfre                  ; show free string space
dfkey7b:jmp     rskp                    ; return success
DFKEY   ENDP

; SHOW KEY <cr> command. Call from Kermit level. Vectored here by SHOW
; command. Replaces obsolete procedure in msx---.
; Prompts for a key and shows that key's (or all if ? entered) keycode,
; definition, and the key definition free space remaining.

SHKEY   PROC    NEAR                    ; Show key's definition command
        mov     ah,cmcfm                ; get a confirm
        call    comnd
         nop                            ; ignore any additional text
         nop
         nop
        push    bx
;        mov     dx,offset shkmsg1       ; ask for original key
        mcmsg   shkmsg1,cshkmsg1
        mov     ah,prstr
        int     dos
shky0:  call    getkey                  ; read keyboard, output to keycode
        jc      shky0                   ; wait for a key (c = nothing there)
        cmp     byte ptr keycode,'?'    ; query for all keys?
        jne     shky0a                  ; ne = no, not a query
        test    keycode,scan            ; is this a scan code, vs ascii query?
        jz      shky0c                  ; z = no Scan, so it is a query

shky0a: mov     ah,prstr                ; show single key. Setup display
        mov     dx,offset crlf
        int     dos
        call    shkey0                  ; show just one key
shky0b: call    shkfre                  ; show free string space
        jmp     shkeyx                  ; exit

shky0c: mov     cx,nkeys                ; Show all keys. nkeys = number defined
        jcxz    shky0b                  ; z = none to show
        mov     si,offset keylist       ; list of definitions
        push    si                      ; save pointer
shky1:  pop     si                      ; recover pointer
        cld
        lodsw                           ; get a keycode
        push    si                      ; save pointer
        push    cx                      ; save counter
        mov     keycode,ax              ; save new keycode
        mov     ah,prstr
        mov     dx,offset crlf
        int     dos
        call    shkey0                  ; show this keycode

        pop     cx                      ; pause between screens, recover cntr
        push    cx                      ; save it again
        dec     cx                      ; number yet to be shown
        jcxz    shky1b                  ; z = have now shown all of them
        mov     ax,nkeys                ; number of defined keys
        sub     ax,cx                   ; minus number yet to be displayed
        xor     dx,dx                   ; clear extended numerator
        div     twelve                  ; two lines per definition display
        or      dx,dx                   ; remainder zero (12 defs shown)?
        jnz     shky1b                  ; nz = no, not yet so keep going
        mov     ah,prstr
;        mov     dx,offset shkmsg3       ; "push any key to continue" msg
        mcmsg   shkmsg3,cshkmsg3
        int     dos
shky1a: call    getkey                  ; get any key
        jc      shky1a                  ; c = nothing at keyboard yet, wait
shky1b: pop     cx                      ; resume loop
        loop    shky1
        pop     si                      ; clean stack
        call    shkfre                  ; show free string space
        jmp     shkeyx                  ; exit

                ; show key worker routine, called from above
                                        ; SHKEY0 called by DFKEY just above
SHKEY0: test    keycode,scan            ; scan code?
        jz      shkey1                  ; z = no, regular ascii

                                        ; SCAN codes
;        mov     dx,offset scanmsg       ; say Scan Code:
        mcmsg   scanmsg,cscanmsg
        mov     ah,prstr
        int     dos
        mov     ah,conout
        mov     dl,'\'                  ; add backslash before number
        int     dos
        mov     ax,keycode              ; get key's code again
        call    decout                  ; display 16 bit decimal keycode
        jmp     shkey2                  ; go get definition

shkey1: 
;       mov     dx,offset ascmsg        ; say ASCII CHAR
        mcmsg   ascmsg,cascmsg
        mov     ah,prstr
        int     dos
        mov     dl,byte ptr keycode     ; get ascii code (al part of input)
        mov     ah,conout
        cmp     dl,spc                  ; control code?
        jae     shkey1a                 ; ae = no
        push    dx                      ; save char
        mov     dl,5eh                  ; show caret first
        int     dos
        pop     dx
        add     dl,'A'-1                ; ascii bias
shkey1a:cmp     dl,del                  ; DEL?
        jne     shkey1b                 ; ne = no
        mov     dl,'D'                  ; spell out DEL
        int     dos
        mov     dl,'E'
        int     dos
        mov     dl,'L'
shkey1b:int     dos
        mov     dl,spc                  ; add a couple of spaces
        int     dos
        int     dos
        mov     dl,'\'                  ; add backslash before number
        int     dos
        mov     ax,keycode              ; show 16 bit keycode in decimal
        call    decout                  ; and go get definiton

                                        ; Display defintion
shkey2: 
;       mov     dx,offset shkmsg2       ; intermediate part of reply
        mcmsg   shkmsg2,cshkmsg2
        mov     ah,prstr                ; " is defined as "
        int     dos
        push    di                      ; get a director code for this key
        push    cx
        mov     di,offset keylist       ; list of keycodes
        mov     cx,nkeys                ; number currently defined
        jcxz    shkey2a                 ; z = none
        mov     ax,keycode              ; present keycode
        push    ds
        pop     es                      ; use datas segment for es:di
        cld
        repne   scasw                   ; is current keycode in the list?
        jne     shkey2a                 ; ne = not in list
        sub     di,2                    ; correct for auto increment
        sub     di,offset keylist
        mov     listptr,di              ; list pointer for existing definition
        pop     cx
        pop     di
        jmp     shkey3                  ; go process definition

shkey2a:pop     cx
        pop     di
;        mov     dx,offset noxmsg        ; say Self (no translation)
        mcmsg   noxmsg,cnoxmsg
        mov     ah,prstr
        int     dos
        ret                             ; return to main show key loop

shkey3:                                 ; translations, get kind of.
        mov     si,listptr
        test    dirlist[si],verb        ; defined as verb?
        jnz     shkey6                  ; nz = yes, go do that one
        test    dirlist[si],strng       ; defined as string?
        jz      shkey3a                 ; z = no
        jmp     shkey8                  ; yes, do string display
shkey3a:
;        mov     dx,offset ascmsg        ; CHAR. say 'Ascii char:'
        mcmsg   ascmsg,cascmsg
        mov     ah,prstr
        int     dos
        mov     ax,dirlist [si]         ; get type and char
        mov     dl,al                   ; put char here for display
        push    ax                      ; save here too
        mov     ah,conout
        cmp     dl,spc                  ; control code?
        jae     shkey4                  ; ae = no
        push    dx
        mov     dl,5eh                  ; show caret
        int     dos
        pop     dx
        add     dl,'A'-1                ; add ascii bias
shkey4: cmp     dl,del                  ; DEL?
        jne     shkey4a                 ; ne = no
        mov     dl,'D'                  ; spell out DEL
        int     dos
        mov     dl,'E'
        int     dos
        mov     dl,'L'
shkey4a:int     dos
        mov     dl,spc                  ; add a couple of spaces
        mov     ah,conout
        int     dos
        int     dos
        mov     dl,'\'                  ; add backslash before number
        int     dos
        pop     ax                      ; recover char
        xor     ah,ah                   ; clear high byte
        call    decout                  ; show decimal value
        ret                             ; return to main show key loop

shkey6: mov     ah,prstr                ; VERB
;        mov     dx,offset verbmsg       ; say 'verb'
        mcmsg   verbmsg,cverbmsg
        int     dos
        mov     si,listptr              ; get verb index from director
        mov     dx,dirlist[si]
        and     dx,not(verb+strng)      ; remove type bits, leaves verb number
        mov     bx,offset kverbs        ; table of verbs & actions
        mov     al,byte ptr [bx]        ; number of keywords
        xor     ah,ah
        dec     ax
        mov     kwcnt,ax                ; save number of last one here
        cmp     dx,ax                   ; asking for more than we have?
        ja      shkeyx                  ; a = yes, exit bad
        inc     bx                      ; point to first slot
        mov     cx,0                    ; current slot number
shkey6b:cmp     cx,dx                   ; this slot?
        je      shkey6c                 ; e = yes, print the text part
        ja      shkeyx                  ; a = beyond, exit bad
        mov     al,byte ptr [bx]        ; get cnt (keyword length)
        xor     ah,ah
        add     ax,4                    ; skip over '$' and two byte value
        add     bx,ax                   ; bx = start of next keyword slot
        inc     cx                      ; current keyword number
        jmp     short shkey6b           ; try another
shkey6c:inc     bx                      ; look at text field
        mov     dx,bx                   ; offset for printing
        mov     ah,prstr
        int     dos
        mov     ah,conout
        mov     dl,spc                  ; add a couple of spaces
        int     dos
        int     dos
        mov     dl,'\'                  ; show verb name as \Kverb
        int     dos
        mov     dl,'K'
        int     dos
        mov     ah,prstr
        mov     dx,bx                   ; show name part again
        int     dos
        ret                             ; return to main show key loop

shkey8: mov     ah,prstr                ; STRING
;        mov     dx,offset strngmsg      ; say String:
        mcmsg   strngmsg,cstrngmsg
        int     dos
        mov     si,listptr              ; get index from director
        mov     bx,dirlist[si]
        and     bx,not(verb+strng)      ; remove type bits
        shl     bx,1                    ; index words
        mov     si,sptable[bx]          ; table of string offsets
        mov     cl,byte ptr [si]        ; get string length byte
        xor     ch,ch
        inc     si                      ; point to string text
        mov     ah,conout
shkey8a:cld
        lodsb                           ; get a byte
        cmp     al,spc                  ; control code?
        jae     shkey8b                 ; ae = no
        push    ax
        mov     dl,5eh                  ; show caret first
        int     dos
        pop     ax
        add     al,40h                  ; convert to printable for display
shkey8b:mov     dl,al
        int     dos                     ; display it
        loop    shkey8a                 ; do another
        ret                             ; return to main show key loop

shkeyx: pop     bx                      ; restore reg
        jmp     rskp                    ; return success
SHKEY   ENDP

;;;     keyboard translator local support procedures, system independent

; Tstkeyw checks text word pointed to by si against table of keywords (pointed
; to by kverbs, made by mkeyw macro); returns in bx either action value or 0.
; Returns in kbtemp the number of the keyword and carry clear, or if failure
; returns kbtemp zero and carry set.
; Keyword structure is:         db      cnt     (length of string 'word')
;                               db      'word'  (keyword string)
;                               db      '$'     (printing terminator)
;                               dw      value   (value returned in bx)
; Make these with macro mkeyw such as   mkeyw 'test',15   with the list of
; such keywords headed by a byte giving the number of keywords in the list.
tstkeyw proc    near
        push    ax
        push    cx
        push    si
        mov     verblen,0               ; verblen will hold verb length
        push    si                      ; save user's verb pointer
tstkw1: cld
        lodsb                           ; get a verb character
        cmp     al,spc                  ; verbs are all non-spaces and above
        jbe     tstkw2                  ; be = done (space or control char)
        inc     verblen                 ; count verb length
        jmp     short tstkw1            ; printable char, look for more
tstkw2: pop     si                      ; pointer to verb
        mov     bx,offset kverbs        ; table of Kermit verb keywords
        mov     al,byte ptr [bx]        ; number of keywords
        xor     ah,ah
        mov     kwcnt,ax                ; save number of keywords here
        inc     bx                      ; point bx to first slot
        mov     kbtemp,0                ; remember which keyword

tstkw3:                                 ; match table keyword and text word
        mov     cx,verblen              ; length of user's verb
        cmp     byte ptr [bx],cl        ; compare length vs table keyword
        jne     tstkw4                  ; ne = not equal lengths, try another
        push    si                      ; lengths match, how about spelling?
        push    bx
        inc     bx                      ; point at start of keyword
tstkw3a:mov     ah,byte ptr [bx]        ; keyword char
        mov     al,byte ptr [si]        ; text char
        cmp     ah,'A'
        jb      tstkw3b                 ; b = control chars
        cmp     ah,'Z'
        ja      tstkw3b                 ; a = not upper case alpha
        add     ah,'a'-'A'              ; convert upper case to lower case
tstkw3b:cmp     al,'A'
        jb      tstkw3c
        cmp     al,'Z'
        ja      tstkw3c
        add     al,'a'-'A'              ; convert upper case to lower case
tstkw3c:cmp     al,ah                   ; test characters
        jne     tstkw3d                 ; ne = no match
        inc     si                      ; move to next char
        inc     bx
        loop    tstkw3a                 ; loop through entire length
tstkw3d:pop     bx
        pop     si
        jcxz    tstkw5                  ; z: cx = 0, exit with match;
                                        ;  else select next keyword
tstkw4: inc     kbtemp                  ; number of keyword to test next
        mov     cx,kbtemp
        cmp     cx,kwcnt                ; all done? Recall kbtemp starts at 0
        jae     tstkwx                  ;ae = exhausted search, unsuccessfully
        mov     al,byte ptr [bx]        ; cnt (keyword length from macro)
        xor     ah,ah
        add     ax,4                    ; skip over '$' and two byte value
        add     bx,ax                   ; bx = start of next keyword slot
        jmp     tstkw3                  ; do another comparison

tstkw5:                                 ; get action pointer
        mov     al,byte ptr [bx]        ; cnt (keyword length from macro)
        xor     ah,ah
        add     ax,2                    ; skip over '$'
        add     bx,ax                   ; now bx points to dispatch value
        mov     bx,[bx]                 ; bx holds dispatch value
        clc                             ; carry clear for success
        jmp     short tstkwxx           ; exit
        ret
tstkwx: xor     bx,bx                   ; exit when no match
        mov     kbtemp,bx               ; make verb number be zero too
        stc                             ; carry set for failure
tstkwxx:pop     si
        pop     cx
        pop     ax
        ret
tstkeyw endp

; Insert asciiz string pointed to by si into string buffer stbuf.
; Reg cx has string length upon entry.
; Success: returns offset of first free byte (strmax) in string buffer stbuf,
; cx = type and Index of new string, and carry clear.
; Failure = carry set.
insertst proc   near
        push    bx
        push    dx
        push    si
        push    di
        push    kbtemp          ; save this variable too
        mov     dx,cx           ; save length of incoming string in dx
        mov     bx,offset sptable ; table of string offsets
        mov     kbtemp,0        ; slot number
        mov     cx,maxstng      ; number of entries, find an empty slot
insert1:cmp     word ptr[bx],0  ; slot empty?
        je      insert2         ; e = yes
        inc     kbtemp          ; remember slot number
        add     bx,2            ; look at next slot
        loop    insert1         ; keep looking
        jmp     short insert4   ; get here if no empty slots
insert2:                        ; see if stbuf has sufficient space
        mov     cx,dx           ; length of new string to cx
        mov     di,strmax       ; offset of first free byte in stbuf
        add     di,cx           ; di = address where this string would end
        cmp     di,offset stbuf+stbuflen ; beyond end of buffer?
        jae     insert4         ; ae = yes, not enough room
        mov     di,strmax       ; point to first free slot in stbuf
        mov     [bx],di         ; fill slot with address offset of buffer
        push    es
        push    ds
        pop     es              ; point es:di to datas segment
        cld
        mov     byte ptr [di],cl ; length of text for new string
        inc     di              ; move to next storage slot
        rep     movsb           ; copy string text
        pop     es
        mov     strmax,di       ; offset of next free byte
        mov     cx,kbtemp       ; return new slot number with Director Index
        and     cx,not(strng+verb) ; clear type bits
        or      cx,strng        ; say type is multi-char string
        clc                     ; say success
        jmp     short insertx   ; exit
insert4:stc                     ; say no-can-do
insertx:pop     kbtemp
        pop     di
        pop     si
        pop     dx
        pop     bx
        ret
insertst endp

; Remove (delete) string. Enter with listptr preset for director entry.
; Acts only on existing multi-char strings; recovers freed space.
; All registers preserved.
remstr  proc    near
        push    si
        mov     si,listptr              ; list pointer
        test    dirlist[si],strng       ; multi-char string?
        pop     si
        jnz     remst1                  ; nz = a multi-char string
        ret                             ; else do nothing
remst1: push    ax
        push    bx
        push    cx
        push    dx
        push    si
        mov     si,listptr
        mov     ax,dirlist[si]          ; Director table entry
        and     ax,not(strng+verb) ; clear type bits, leave string's pointer
        mov     dirlist[si],0           ; clear Director table entry
        shl     ax,1                    ; index words not bytes
        mov     si,offset sptable       ; list of string offsets in stbuf
        add     si,ax                   ; plus index = current slot
        mov     bx,[si]                 ; get offset of string to be deleted
        mov     dx,bx                   ; save in dx for later
        mov     cl,byte ptr [bx]        ; get length byte
        xor     ch,ch                   ; get length of subject string
        inc     cx                      ; length byte too, cx has whole length
        sub     strmax,cx       ; count space to be freed (adj end-of-buf ptr)
        mov     word ptr [si],0 ; clear sptable of subject string address
        push    cx                      ; save length of purged string
        push    di                      ; save di
        push    si
        push    es                      ; save es
        push    ds
        pop     es              ; setup es:di to be ds:offset of string
        mov     di,dx           ; destination = start address of purged string
        mov     si,dx           ; source = start address of purged string
        add     si,cx           ;  plus string length of purged string.
        mov     cx,offset stbuf+stbuflen ; 1 + address of buffer end
        sub     cx,si                   ; 1 + number of bytes to move
        dec     cx                      ; number of bytes to move
        jcxz    remst2                  ; z = none
        cld                             ; direction is forward
        rep     movsb                   ; move down preserved strings
remst2: pop     es                      ; restore regs
        pop     di
        pop     si
        pop     ax              ; recover length of purged string (was in cx)
        mov     bx,offset sptable       ; string pointer table
        mov     cx,maxstng              ; max mumber of entries
remst4: cmp     [bx],dx         ; does this entry occur before purged string?
        jbe     remst5          ; be = before or equal, so leave it alone
        sub     [bx],ax         ; recompute address (remove old string space)
remst5: add     bx,2                    ; look at next list entry
        loop    remst4                  ; do all entries in sptable
        pop     si
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
remstr  endp

shkfre  proc    near                    ; show free key & string defs & space
        push    ax                      ; preserves all registers.
        push    bx
        push    cx
        push    dx
        push    kbtemp
;        mov     dx,offset fremsg
        mcmsg   fremsg,cfremsg
        mov     ah,prstr
        int     dos
        mov     ax,maxkeys              ; max number of key defs
        sub     ax,nkeys                ; number currently used
        call    decout                  ; show the value
        mov     ah,prstr
;        mov     dx,offset kyfrdef       ; give key defs msg
        mcmsg   kyfrdef,ckyfrdef
        int     dos
        mov     bx,offset sptable       ; table of string pointers
        mov     cx,maxstng              ; number of pointers
        mov     kbtemp,0                ; number free
shkfr1: cmp     word ptr [bx],0         ; slot empty?
        jne     shkfr2                  ; ne = no
        inc     kbtemp                  ; count free defs
shkfr2: add     bx,2                    ; look at next slot
        loop    shkfr1                  ; do all of them
        mov     ax,kbtemp               ; number of free defs
        call    decout                  ; display
;        mov     dx,offset stfrdef       ; say free string defs
        mcmsg   stfrdef,cstfrdef
        mov     ah,prstr
        int     dos
        mov     ax,offset stbuf+stbuflen ; 1 + last byte in stbuf
        sub     ax,strmax               ; offset of last free byte in stbuf
        call    decout
;        mov     dx,offset stfrspc       ; give free space part of msg
        mcmsg   stfrspc,cstfrspc
        mov     ah,prstr
        int     dos
        pop     kbtemp
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
shkfre  endp
; Initialize the keyboard tables at Kermit startup time. Optional procedure.
; Requires kbdinlst to be configured with mkeyw macro in the form
;       mkeyw   'definition',keytype*256+keycode
; keytype is 0 for scan codes and non-zero for ascii.
; Returns normally.
kbdinit proc    near                    ; read keyword kbdinlst and setup
        push    ds                      ;  initial keyboard assignments.
        pop     es                      ; set es:di to datas segment
        inc     taklev                  ; pretend that we are in Take file
        mov     si,offset kbdinlst      ; start of list of definitions
kbdini1:mov     cl,byte ptr [si]        ; cnt field (keyword length of macro)
        xor     ch,ch
        jcxz    kbdinix                 ; z = null cnt field = end of list
        inc     si                      ; look at text field
        mov     di,offset tranbuf       ; where defkey expects text
        cld
        rep     movsb                   ; copy cx chars to tranbuf
        mov     byte ptr [di],0         ; insert null terminator
        inc     si                      ; skip '$' field
        mov     ax,word ptr [si]        ; get value field
        mov     keycode,ax              ; set key ident value
        push    si
        call    dfkey2                  ; put dfkey to work
         nop
         nop
         nop
        pop     si
        add     si,2                    ; point to next entry
        jmp     kbdini1                 ; keep working
kbdinix:dec     taklev                  ; reset Take file level
        ret
kbdinit endp
;;;     End of System Independent Procedures

;;;     Begin System Dependent Procedures

; Read keyboard. System dependent.
; Return carry set if nothing at keyboard.
; If char present return carry clear with key's code in Keycode.
; If key is ascii put that in the low byte of Keycode and clear bit Scan in
; the high byte; otherwise, put the scan code in the lower byte and set bit
; Scan in the high byte.
; Bit Scan is set if key is not an ascii code.
; Two methods are used: Bios reading for Set Key ON, and DOS reading for
; Set Key OFF. DOS scan codes are coerced to Bios values as much as possible.
; Modifies register ax.
getkey  proc    near
        mov     keycode,0               ; clear old keycode
        cmp     dosflg,0                ; do DOS keyboard reading?
        jne     getky5                  ; ne = yes, DOS
        jmp     getky6                  ; do full Bios form
                                        ; ;;;;;;;; D O S ;;;;;;;;;;
getky5: 
        call    iseof                   ; is stdin at eof?
        jnc     getky5k                 ; nc = not eof, get more
        mov     al,trans.escchr         ; Kermit's escape char
        mov     byte ptr keycode,al     ; save ascii char
        clc                             ;  to get out gracefully at EOF
        ret                             ; and exit

getky5k:mov     dl,0ffh                 ; DOS read operation
        mov     ah,dconio               ; from stdin
        int     dos
        jnz     getky5a                 ; nz = char available
        stc                             ; carry set = nothing available
        ret                             ; exit on no char
getky5a:cmp     al,0                    ; scan code precursor?
        je      getky5d                 ; e = yes
        cmp     al,16                   ; Control P?
        jne     getky5b                 ; ne = no
        mov     al,114                  ; force Control PrtSc scan code
        jmp     short getky5e           ; process as scan code
getky5b:cmp     al,BS                   ; backspace key?
        jne     getky5c                 ; ne = no
        mov     al,14                   ; force scan code for BS key
        jmp     short getky5e           ; process as scan code
getky5c:mov     byte ptr keycode,al     ; save ascii char
        clc                             ; say have an ascii keycode
        ret                             ; and exit

getky5d:mov     dl,0ffh                 ; read second byte (actual scan code)
        mov     ah,dconio               ; read via DOS
        int     dos
        jnz     getky5e                 ; nz = got a char
        stc                             ; none, declare bad read
        ret
                                        ; Check and modify to Bios scan codes
getky5e:mov     byte ptr keycode,al     ; save char code
        cmp     al,1                    ; Alt escape
        je      getkya                  ; set Alt bit
        cmp     al,16                   ; back tab
        jb      getky5g                 ; these remain unchanged
        cmp     al,50                   ; start of meta keys
        jb      getkya                  ; b = set Alt bit
        cmp     al,84                   ; Shift F1
        jb      getky5g                 ; b = no change
        cmp     al,94                   ; Control F1
        jb      getkys                  ; set Shift bit
        cmp     al,104                  ; Alt F1
        jb      getkyc                  ; set Control bit
        cmp     al,114                  ; Control PrtSc
        jb      getkya                  ; set Alt bit
        cmp     al,120                  ; Alt top rank
        jb      getkyc                  ; set Control bit
        cmp     al,132                  ; Control PgUp
        jb      getkya                  ; set Alt bit
        je      getkyc                  ; set Control bit
        cmp     al,135                  ; Shift F11, for Enhanced keyboard
        jb      getky5g                 ; no change
        cmp     al,137                  ; Control F11
        jb      getkys                  ; set Shift bit
        cmp     al,139                  ; Alt F11
        jb      getky5c                 ; set Control bit
        cmp     al,141                  ; Control Up
        jb      getkya                  ; set Alt bit
        cmp     al,151                  ; Alt Home
        jb      getkyc                  ; set Control bit
        jmp     short getkya            ; set Alt bit
getkyc: or      keycode,control         ; set Control bit
        jmp     short getky5g
getkys: or      keycode,shift           ; set Shift bit
        jmp     short getky5g
getkya: or      keycode,alt             ; set Alt bit
getky5g:or      keycode,scan            ; ensure scan bit is set
        clc                             ; report we have a scan keycode
        ret                             ; and exit

                                        ; ;;;;;;;;;; B I O S ;;;;;;;;;;;;;
getky6:                                 ; full BIOS keyboard reading
        mov     kbtemp,0001h            ; low byte = status, high = read char
        push    cx                      ; save registers
        push    es
        mov     cx,40h                  ; segment 40h
        mov     es,cx
        mov     cl,byte ptr es:[96h]    ; kbd_flag_3, Enhanced keyboard area
        and     cl,10h                  ; select Enhanced kbd presence bit
        mov     ch,cl                   ; copy, for both status and read
        or      kbtemp,cx               ; 0 = regular kbd, 10h = enhanced kbd
        pop     es
        pop     cx
;+++++++++++++++++++++++ Change Check Keyboard Method, Sept.3,1990 [zqf].
        mov     ah,0bh
        int     21h
        cmp     al,0
;        mov     ah,byte ptr kbtemp      ; anything at keyboard?
;        int     kbint                   ; Bios keyboard interrupt
;+++++++++++++++++++++++
        jnz     getky1                  ; nz = char available
        stc                             ; carry set = nothing available
        jmp     short getkyx            ; exit on no char available
getky1: mov     ah,byte ptr kbtemp+1    ; read, no echo, wait til done
        int     kbint                   ; ==> ah = scan code, al = char value
        or      ax,ax                   ; Both bytes null for Control-Break?
        jnz     getky1a                 ; nz = no
        push    ax                      ; clear DOS buffer of Control-C by
        mov     ah,coninq               ;  doing DOS level read operation
        int     dos
        pop     ax                      ; restore reg
getky1a:cmp     ah,0                    ; keycode entered by ALT ###?
        je      getky1c                 ; e = yes, not enhanced
        cmp     ah,0e0h                 ; Enhanced kbd Enter, fwd slash keys?
        jne     getky1b                 ; ne = no
        xchg    ah,al                   ; interchange scan and ascii fields
getky1b:cmp     al,0E0h                 ; enhanced key hidden code?
        jne     getky1c                 ; ne = no
        mov     byte ptr keycode,ah     ; retain scan code, supress 0e0h
        or      keycode,scan+enhanced   ; set scan and enhanced idents
        mov     ah,2                    ; use regular keyboard op code here
        int     kbint                   ; get current shift state
        mov     bl,al                   ; copy for a moment
        and     bl,rgt_shift            ; mask out all but right shift
        shl     bl,1                    ; move right shift to left shift pos
        or      al,bl                   ; collapse shift bits
        and     al,(lft_shift + alt_shift + ctl_shift)
        or      byte ptr keycode+1,al   ; store in type field of keycode
        clc                             ; say have a keystroke
        jmp     getkyx                  ; Enhanced kbd end. Skip other tests

getky1c:push    cx
        mov     cx,aliaslen             ; number of aliased keys
        or      cx,cx
        pop     cx
        jz      getky2                  ; z = none
        push    di                      ; check key (ax) for aliases
        push    cx
        push    es
        push    ds
        pop     es                      ; make es:di refer to datas segment
        mov     di,offset aliaskey      ; list of aliased keys
        mov     cx,aliaslen             ; number of entries
        cld
        repne   scasw                   ; look for a match
        pop     es
        pop     cx
        pop     di
        jne     getky2                  ; ne = not there
        mov     al,0                    ; force use of scan code (in ah)
getky2: or      al,al                   ; scan code being returned?
        jnz     getky3                  ; nz = no
        mov     byte ptr keycode,ah     ; store scan code for gsh
        push    ax
        push    bx
        call    gsh                     ; get modified shift state
        or      byte ptr keycode+1,al   ; store in type field of keycode
        pop     bx
        pop     ax
        xchg    ah,al                   ; put scan code in al
        or      keycode,scan            ; set scan flag (vs ascii)
getky3: mov     byte ptr keycode,al     ; return key's code (usually ascii)
        clc                             ; carry clear = got a char
getkyx: ret
getkey  endp

; get shift state into al.  We care about only shift, ctl, and alt keys.
; right shift is collapsed into left shift. NumLock offsets Shift on keypad
; white keys.
gsh     proc    near
        mov     ah,2
        int     kbint                   ; get current shift state
        mov     bl,al                   ; copy for a moment
        and     bl,rgt_shift            ; mask out all but right shift
        shl     bl,1                    ; move right shift to left shift pos
        or      al,bl                   ; collapse shift bits
        cmp     byte ptr keycode,71     ; below numeric key pad?
        jb      gsh1                    ; b = yes
        cmp     byte ptr keycode,83     ; above numeric key pad?
        ja      gsh1                    ; a = yes
        cmp     byte ptr keycode,74     ; grey - key ?
        je      gsh1                    ; e = yes
        cmp     byte ptr keycode,78     ; grey + key
        je      gsh1                    ; e = yes
        test    al,numlock              ; numlock set?
        jz      gsh1                    ; z = no
        xor     al,lft_shift            ; numlock offsets shift and vice versa
gsh1:   and     al,(lft_shift + alt_shift + ctl_shift)
        ret
gsh     endp


; Do any local processing after reading a key during active translation
; Avoids same actions if a key is being defined or shown.
postkey proc    near
                                        ; Key Click code for VT102 emulator
        cmp     flags.vtflg,0           ; emulating? (0 = no)
        je      postke1                 ; e = extra clicks not available
        test    vtemu.vtflgst,vskeyclick ; flags from SET TERM
        jz      postke1                 ; z = extra clicks not wanted
        call    vclick                  ; click, what else?
postke1:ret
postkey endp

; Jumping to this location is like retskp.  It assumes the instruction
;   after the call is a jmp addr.

RSKP    PROC    NEAR
        pop bp
        add bp,3
        push bp
        ret
RSKP    ENDP

; Jumping here is the same as a ret.

R       PROC    NEAR
        ret
R       ENDP

code    ends
        end
