DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T m

⟦433e69e05⟧ TextFile

    Length: 70161 (0x11211)
    Types: TextFile
    Names: »msu55x.asm«

Derivation

└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit
    └─⟦71044c191⟧ »EurOpenD3/misc/kermit.ms-2.32.tar.Z« 
        └─⟦31f2d420d⟧ 
            └─⟦this⟧ »msu55x.asm« 

TextFile

    NAME    msu55x
; File MSU55X.ASM
; Kermit system dependent module for Sanyo MBC-55x
; Sanyo mods by Robert W. Babcock and Joseph H. White
; For version which replaces the BIOS keycode translation table,
;  use -dMODIFIED flag to MASM.
; Keyboard translator, by Joe R. Doupnik, Dec 1986
;  with contributions from David L. Knoell.
; edit history:
; Last edit 20 April 1988, reconcile with IBM 2.30 release
; Revised Key definitions and removed Alias Keys June 2 1987 [jhw]
; 1 Jan 1988 version 2.30

    include mssdef.h

    public    keybd, dfkey, shkey, msuinit

; some definitions
kbint    equ    16h            ; IBM, Bios keyboard interrupt
shift    equ    200h            ; IBM, synonym for right or left shift
control equ    400h            ; IBM, synonym for control shift
alt    equ    800h            ; IBM, synonym for alt shift

maxkeys equ    200            ; maximum number of key definitions
maxstng equ    100            ; maximum number of multi-char strings
stbuflen equ    1000            ; length of string buffer (bytes)

verb    equ    8000h            ; dirlist flag: use verb action table
strng    equ    4000h            ; dirlist flag: use string action table
scan    equ    100h            ; keycode flag: code is scan not ascii
braceop equ    7bh            ; opening curly brace
bracecl equ    7dh            ; closing curly brace

datas    segment public 'datas'
    extrn taklev:byte, comand:byte, intake:byte, flags:byte
    extrn shkadr:word, stkadr:word, trans:byte
                        ; system dependent references
    extrn vtemu:byte            ; emulator data [jrd]

;;;    System Independent local storage

tranbuf db    132 dup (?)        ; 132 byte translator work buffer
crlf    db    cr,lf,'$'
dfhelp1 db    cr,lf,' Enter key',27h,'s identification as a character',cr,lf
    db    '  or as its numerical equivalent \{b##} of ascii',cr,lf
    db    '  or as its scan code \{b##}'
    db    cr,lf,'  or as SCAN followed by its scan code',cr,lf
    db    '    where b is O for octal, X for hex, or D for decimal'
    db    ' (default).',cr,lf,'    Braces {} are optional.'
    db    cr,lf,'    Follow the identification with the new definition.'
    db    cr,lf,' or CLEAR to restore initial key settings'
    db    cr,lf,' or ON (def) for Bios i/o or OFF to use DOS i/o.$' ;IBM
dfaskky db    cr,lf,' Push key to be defined: $'
dfaskdf db    ' Enter new definition: $'
verbbad db    cr,lf,' No such verb',cr,lf,'$'
strbad    db    cr,lf,' Not enough space for new string',cr,lf,'$'
keyfull db    cr,lf,' No more space to define keys',cr,lf,'$'
dfkopps db    cr,lf,' Opps! That is Kermit',27h,'s Escape Char.'
    db    ' Translation is not permitted.',cr,lf,'$'
shkmsg1 db    cr,lf,'Push key to be shown (? shows all): $'
shkmsg2 db    ' decimal is defined as',cr,lf,'$'
shkmsg3 db    cr,lf,'... more, push any key to continue ...$'
kwarnmsg db    cr,lf,' Notice: this form of Set Key is obsolete$'

ascmsg    db    ' Ascii char: $'
scanmsg db    ' Scan Code $'
strngmsg db    ' String: $'
verbmsg db    ' Verb: $'
noxmsg    db    ' Self, no translation.$'
fremsg    db    cr,lf,' Free space: $'
kyfrdef db    ' key and $'
stfrdef db    ' string definitions, $'
stfrspc db    ' string characters.',cr,lf,'$'
                    ; translation tables
keylist dw    maxkeys dup (0)        ; 16 bit keycodes, paralled by dirlist
dirlist dw    maxkeys dup (0)        ; director {v+s} + {index | new char}
sptable dw    maxstng dup (0)        ; list of asciiz string offsets
stbuf    dw    stbuflen dup (0)    ; buffer for strings
strmax    dw    stbuf            ; first free byte in stbuf
listptr dw    ?            ; item number for keylist and dirlist
nkeys    dw    0            ; number of actively defined keys
keycode dw    ?            ; ascii/scan code for key
kbtemp    dw    ?            ; scratch storage for translator
brace    db    ?            ; brace detected flag byte
oldform db    0            ; old form Set Key, if non-zero
verblen dw    ?            ; length of user's verb (work temp)
kwcnt    dw    ?            ; number of keywords (work temp)
msutake db    ?            ; if being run from take file or not
twelve    dw    12d
dosflg    db    0
;;;    End System Independent Data Area

;;;    System Dependent Data Area
;    edit dfhelp2 to include nice list of verbs for this system.
dfhelp2 db    cr,lf,' Enter either  \Kverb  for a Kermit action verb',cr,lf
    db    ' or a replacement string  (single byte binary numbers are'
    db    ' \{b##})',cr,lf,' or push Return to undefine a key, ^C to'
    db    ' retain current definition.'
    db    cr,lf,' Braces {} are optional, and strings maybe enclosed in'
    db    ' them.',cr,lf,' Strings may not begin with the character'
    db    ' combinations of  \k  or  \{k',cr,lf
    db    '    (start with a { brace instead).',cr,lf,lf
    db    ' Verbs are as follows. VT102 keys (arrows and keypad):',cr,lf
    db   '   uparr, dnarr, lfarr, rtarr, kpminus, kpcoma, kpdot, kpenter,'
    db    cr,lf
    db   '   Gold (same as PF1), PF1, PF2, PF3, PF4, kp0, ... kp9'
    db    cr,lf,' Kermit screen control and actions:',cr,lf
    db   '   upscn, dnscn, homscn, endscn, upone, dnone, prtscn, dump,'
    db    cr,lf
    db   '   logoff, logon, termtype, reset, modeline, break, lbreak,'
    db    cr,lf
    db   '   hangup, null (send one), DOS, help, status, exit'
    db    cr,lf,'$'

    ; Aliaskey: keys having aliases - same ascii code but more than one
    ; scan code, as on auxillary keypads. Use just scan codes with these.
    ; Alternative use: force ascii keys to report out just scan codes.
    ; Table format: high byte = scan code, low byte = ascii code.
    ; Contents are machine dependent.
aliaskey dw    0e08h            ; backspace key
    dw    4603h            ; break key (distinguish from ^C
        dw      320dh                   ; ^M (distinguish from RETURN)
        dw      240ah                   ; ^J (distinguish from ^RETURN)
        dw      1a1bh                   ; ^[ (distinguish from ESCAPE)
aliaslen equ    ($-aliaskey) shr 1    ; number of words in aliaskey table

kverbs    db    45            ; number of table entries below
    mkeyw    'uparr',uparrw        ; independent of ordering and case!
    mkeyw    'dnarr',dnarrw        ; mkeyw 'name',procedure entry point
    mkeyw    'lfarr',lfarr
    mkeyw    'rtarr',rtarr
    mkeyw    'gold',pf1
    mkeyw    'pf1',pf1
    mkeyw    'pf2',pf2
    mkeyw    'pf3',pf3
    mkeyw    'pf4',pf4
    mkeyw    'kp0',kp0
    mkeyw    'kp1',kp1
    mkeyw    'kp2',kp2
    mkeyw    'kp3',kp3
    mkeyw    'kp4',kp4
    mkeyw    'kp5',kp5
    mkeyw    'kp6',kp6
    mkeyw    'kp7',kp7
    mkeyw    'kp8',kp8
    mkeyw    'kp9',kp9
    mkeyw    'kpminus',kpminus
    mkeyw    'kpcoma',kpcoma
    mkeyw    'kpenter',kpenter
    mkeyw    'kpdot',kpdot
    mkeyw    'termtype',vtans52
    mkeyw    'reset',vtinit
    mkeyw    'dnscn',dnwpg
    mkeyw    'upscn',upwpg
    mkeyw    'endscn',endwnd
    mkeyw    'homscn',homwnd
    mkeyw    'upone',upone
    mkeyw    'dnone',dnone
    mkeyw    'prtscn',trnprs
    mkeyw    'dump',dmpscn
    mkeyw    'modeline',trnmod
    mkeyw    'break',sendbr
    mkeyw    'lbreak',sendbl
    mkeyw    'hangup',chang
    mkeyw    'null',snull
    mkeyw    'logon',klogon
    mkeyw    'logoff',klogof
    mkeyw    'DOS',cdos
    mkeyw    'help',cquery
    mkeyw    'status',cstatus
    mkeyw    'exit',cquit

                    ; Initialization data.
;
;  Revised definitions for Sanyo        [begin jhw]
;
kbdinlst equ    this byte     ; Kermit IBM initialization time keyboard setup
    mkeyw    '\kgold',scan+59    ; F1    mkeyw 'definition',keycode
    mkeyw    '\kpf2',scan+60        ; F2
    mkeyw    '\kpf3',scan+61        ; F3
    mkeyw    '\kpf4',scan+62        ; F4
    mkeyw    '\kkp0',scan+93        ; ^0
    mkeyw    '\kkp1',scan+84        ; ^1
    mkeyw    '\kkp2',scan+85        ; ^2
    mkeyw    '\kkp3',scan+86        ; ^3
    mkeyw    '\kkp4',scan+87        ; ^4
    mkeyw    '\kkp5',scan+88        ; ^5
    mkeyw    '\kkp6',scan+89        ; ^6
    mkeyw    '\kkp7',scan+90        ; ^7
    mkeyw    '\kkp8',scan+91        ; ^8
    mkeyw    '\kkp9',scan+92        ; ^9
    mkeyw    '\kkpenter',scan+63    ; F5
    mkeyw    '\kkpcoma',scan+101    ; ^,
    mkeyw    '\kkpminus',scan+103    ; ^/
    mkeyw    '\kkpdot',scan+102    ; ^.
    mkeyw    '\kuparr',scan+72    ; VT100 cursor keys (arrows)
    mkeyw    '\kdnarr',scan+80
    mkeyw    '\klfarr',scan+75
    mkeyw    '\krtarr',scan+77
    mkeyw    '\kupscn',scan+73    ; PgUp    Kermit screen roll back keys
    mkeyw    '\kdnscn',scan+81    ; PgDn
    mkeyw    '\khomscn',scan+71    ; Home
    mkeyw    '\kendscn',scan+79    ; End
ifdef MODIFIED
    mkeyw    '\kupone',scan+134    ; Ctrl Control Pg-Up  one line scrolls
else
    mkeyw    '\kupone',scan+116    ; Ctrl Rt Arrow        one line scrolls
endif
    mkeyw    '\kdnone',scan+118    ; Ctrl PgDn
    mkeyw    '\kmodeline',scan+64    ; F6
    mkeyw    '\ktermtype',scan+65    ; F7
    mkeyw    '\kreset',scan+66    ; F8
    mkeyw    '\kprtscn',scan+67    ; F9
    mkeyw    '\kdump',scan+117    ; Kermit Dump Screen  Ctrl End
    mkeyw    '\x7f',scan+83        ; Del key sends DEL
    mkeyw    '\x7f',scan+14        ; Backspace key sends DEL
    mkeyw    '\kexit',scan+45    ; Exit connect mode  Cntl-shift-X
    mkeyw    '\kstatus',scan+31    ; Connect mode status  Cntl-shift-S
    mkeyw    '\kbreak',scan+70    ; Send a Break      Break key
    mkeyw    '\khelp',scan+35    ; Con. mode drop down menu  Cntl-shift-H
        mkeyw   '\x0d'  ,scan+50    ; ^m sends CR
        mkeyw   '\x0a'  ,scan+36    ; ^j sends line feed
        mkeyw   '\x1b'  ,scan+26    ; ^[ sends ESCAPE
    dw    0        ; end of table marker

datas    ends

;            Documentation
;Translating a key:
;   The translator is called to obtain keyboard input; it sends characters to
; the serial port through standard controlled echo procedures or invokes
; named procedures. It returns carry clear when its operation is completed
; for normal actions and carry set when Connect mode must be exited. When
; Connect mode is exited the just read char should be passed in Kbdflg
; to msster.asm for invoking actions such as Status, send a break,
; quit connect mode; system dependent procedure Term is responsible for this.
;
;  Principal procedures are -
;    msuinit        Initializes keyboard translator in this file when
;            Kermit first begins. Installs dfkey and shkey as the
;            procedures used for Set Key and Show Key. Sys Indep.
;            Called from msx or msy init procs. System Independent.
;    keybd        Performs the translation, outputs chars to the serial
;            port or invokes a Kermit action routine. Sys Indep.
;    dfkey        Defines a key's translation. Reads command line
;            via Kermit's command parser comnd. System Independent.
;    shkey        Shows translation of a key. Requests user to push
;            selected key. System Independent.
;
;    kbdinit        optional. Initializes the translation tables when
;            Kermit starts up. Called by msuinit. System Dependent.
;    getkey        Performs the keyboard read and returns results in
;            a standardized system independent format. Sys Depend.
;    postkey        called by active translator after obtaining a keycode.
;            Used to provide extra local actions (keyclick) only
;            in Connect mode (not during Set/Show key commands).
;            Called by keybd. System dependent.
; Supporting system independent procedures are -
; shkfre (show string free space), tstkeyw (finds user's keyword in the verb
; table), insertst (insert string in buffer), remstr (delete string in buffer).
;
;   System dependent procedure Getkey reads a keycode (usually via a Bios
; call). On IBM compatible machines this yields <ah=scan code, al=ascii>
; for ordinary keys, or <ah=scan code, al=0> for special keys such as F1,
; or <ah=0, al=###> when Alt### is used.
; For any system, the canonical output form is the key's code in Keycode.
; Place the ascii code (or scan code if none) in byte Keycode and ancillary
; info (shift states plus marker bit for scan codes) in byte Keycode + 1.
;
;   Table Aliaskey is a list of scan code/ascii codes for keys which appear
; more than once on a keyboard. This list is examined to distinguish such
; aliased keys (those on an auxillary keypad) from an ordinary ascii key,
; and the aliased key is then referenced by its scan code rather than by
; the ordinary ascii code. Aliaskey is machine and keyboard dependent.
;
;    Procedure Keybd calls Getkey for the Keycode, checks list of translatable
; keys Keylist, and then either sends an ascii string (one or more characters)
; or invokes a Kermit action verb. List Dirlist indicates what kind of
; translation to do. Keybd is system independent but may contain system
; dependent special actions such as echoing keyclicks. Keybd calls system
; dependent procedure Postkey just after calling getkey so local actions
; such as keyclicks can be activated only during Connect mode operations.
;
;    Keylist is a packed but unordered list of 16 bit keycodes which need
; translation. The lower order byte holds a key code (ascii char or scan code)
; while the high byte holds a scan code marker bit (0 if ascii code in low
; byte) plus any ancillary keyboard information such as Control/Shift/Alt/Meta
; keys being held down; these are of use in Show Key presentations.
;    Dirlist parallels Keylist to provide the kind of translation, verb or
; string, in the two highest bits with the other bits holding either
; a single new replacement character or the item number in lists of verbs
; or strings. If neither verb nor strng type bits are set in a dirlist
; word then the translation is a single new character held in the lower
; eight bits of that dirlist word.
;
;    The number of key translations is assembly constant Maxkeys (def 128).
;    The maximum number of strings is assembly constant Maxstngs (def 64).
;    The maximum number of verbs is 256 and is set by building table Kverbs.
;
;   For verbs, use the Item number from the Director table Dirlist to select
; a procedure offset from the structured list Kverbs and jump to that offset.
; Most verb procedures return carry clear to stay within Connect mode.
; Verbs requiring exiting Connect mode return carry set and may set byte
; Kbdflg to a char code which will be read by msster.asm for activating a
; transient Kermit action such as send a break (Kbdflg = 'b').
; Kbdflg is stored in msster.asm (as zero initially, meaning ignore it).
; Action verb procedures are normally located in a system dependent file.
;
;   For multi-char strings, use Item number from Director table Dirlist to
; select a pointer to a string. The list of string pointers is Sptable
; (string pointer table) which holds the offset in the data segment of the
; strings stored in buffer Stbuf. In stbuf strings are held as: one byte of
; length of following text and then the text itself (permits embedded nulls).
;  Use Chrout to send each string character, and finally return from Keybd
; with carry clear.
;
;   For single character replacements obtain the new character from the lower
; order byte of Director table Dirlist. If the character is Kermit's present
; escape character return from Keybd carry set to leave connect mode.
; Otherwise, send the character via Chrout and return from Keybd carry clear.

; Keylist table format:
;    7 bits   1 bit   8 bits
; +----------+----+------------+ scan bit = 1 if key's code is non-ascii
; | aux info |scan| key's code | aux info = system dependent, used only to
; +----------+----+------------+        help identify key
;
; Dirlist table format          v s    meaning
;   1    1      14 bits          0 0    copy out one byte translation
; +---+---+--------------------+  1 0    copy out multi-char string number Item
; | v | s | item # or new char |  0 1    do action verb number Item
; +---+---+--------------------+  1 1    (not used)
;
; Table kverbs is organized by macro mkeyw as -
;    kverbs    db    number of table entries
;    (each entry is in the form below:)
;        db    number of bytes in verbname
;        db    'verbname'        variable length
;        db    '$'            for printing
;        dw    value            offset of procedure
;
;
;   Dfkey defines a key to be itself (undefines it) or a single replacement
; character or a character string or a Kermit action verb. Dfkey requires
; a command line so that it may be invoked by Take files but can be forced
; to prompt an interactive user to push a key. Syntax is discussed below.
; Note that redefined keys have their old definitions cleared so that
; old string space is reclaimed automatically.
;
;   Shkey displays a key's definition and the user is asked to push the
; selected key. The free space for strings is always shown afterward. See
; below for syntax.
;
;   Kbdinit is an optional routine called when Kermit starts up. It fills in
; the translation tables with desirable default values to save having to
; use long mskermit.ini files. The default values are stored in a structured
; table similar to (but not the same as) Dfkey's command lines; the keycode
; values are preset by hand to 16 bit numbers.

;Defining a key:
; Command is SET KEY <key ident><whitespace><definition>
;
; <key ident> is
;        a single ordinary ascii char or
;        the numerical equivalent of an ascii char or
;        a Scan Code written as a number or
;        keyword SCAN followed by a number.
;        ?    Displays help message.
;    Numbers and Binary codes are of the form
;        \123    a decimal number
;        \o456    an octal number        base letters o, d, x can be
;        \d213    a decimal number    upper or lower case
;        \x0d    a hex number
;        \{b###}     braces around above material following slash.
;
; <whitespace> is one or more spaces and or tabs.
;
; <definition> is
;    missing altogether which "undefines" a key.
;    \Kverb        for a Kermit action verb; upper or lower case K is ok
;    \{Kverb}    ditto. Verb is the name of an action verb.
;    text        a string with allowed embedded whitespace and embedded
;            binary chars as above. This kind of string may not
;            commence with sequences \K or \{K; use braces below.
;    {text}        string confined to material within but excluding
;            the braces. Note, where the number of opening braces
;            exceeds the number of closing braces the end of line
;            terminates the string: {ab{}{{c}d ==> ab{}{{c}d
;            but  {ab}{{c}d ==> ab.
;    ?        Displays help message and lists all action verbs.
;
;    If Set Key is given interactively, as opposed to within a Take
;    file, the system will prompt for inputs if none is on the command
;    line. The response to Push key to be defined cannot be edited.
;
;    Text which reduces to a single replacement character is put into a
;    table separate from the multi-character strings (maxstng of these).
;    A key may be translated into any single 8 bit code.
;
;    Comments can follow a Kermit action verb or a braced string; no
;    semicolon is required since all are stripped out by the Take file
;    reader before the defining text is seen by SET KEY.
;
;    The current Kermit escape character cannot be translated without
;    subtrafuge.
;
;    Examples:
;        Set Key q z
;                makes key q send character z
;        Set Key \7 \27[0m
;                makes key Control G send the four byte
;                string    ESC [ 0 m
;        Set Key q
;                undefines key q so it sends itself (q) again.
;        Set Key \2349 \kexit
;                defines IBM Alt-X to invoke the leave connect
;                mode verb "exit" (Kermit's escape-char ^] C).
;        Set Key \x0c Login \{x0d}myname\{x0d}mypass\x0d
;                defines Control L to send the string
;                Login <cr>myname<cr>mypass<cr>
;
; Alternative Set Key syntax for backward compatibility with previous versions
;    The same forms as above except the key identification number must
;    be decimal and must Not have a leading backslash. Example:
;    Set Key Scan 59 This is the F1 key
;
;    If the definition is omitted it may be placed on the following line;
;    if that line is also empty the key is undefined (defined as Self).
;    A warning message about obsolete syntax will be given followed by
;    the key's modern numerical value and new definition. Only "special"
;    keys (those not producing ascii codes) are compatible with this
;    translator.
;
;Showing a key:
; Command is SHOW KEY <cr>
; System prompts user to press a key and shows the definition plus the
; free space for strings. Query response results in showing all definitions.
;            End Documentation

code    segment public 'code'
        ; system independent external items
    extrn    comnd:near, prompt: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            ; 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
ifdef MODIFIED
    extrn    rbtabl:near, sbtabl:near            ;[jhw]
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
    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
    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    dfkeyc            ; e = yes, reinit keyboard
    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    rskp
dfkey14:mov    dosflg,0        ; clear DOS keyboard read flag
    jmp    rskp

dfkey0: mov    dx,offset dfhelp1    ; say bad definition command
    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
    mov    ah,prstr
    int    dos
dfkey1a:call    katoi            ; convert ascii number to binary in ax
    jc    dfkey0            ; c = no number converted
    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
    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
    call    prompt            ; Kermit prompt routine
    mov    comand.cmcr,1        ; permit bare carriage returns
dfkey1f:mov    bx,offset tranbuf    ; get new definition
    mov    word ptr [bx],0        ; insert terminator
    mov    dx,offset dfhelp2    ; help for definition of key
    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
    jcxz    dfkey2            ; z = empty string
    push    si
    push    di
    mov    si,offset tranbuf    ; strip leading white space
dfkey1c:cld                ; work forward
    lodsb                ; read string char into al
    dec    cx            ; number of chars left to read
    cmp    al,' '            ; a space?
    je    dfkey1c            ; e = yes, keep going
    cmp    al,tab            ; tab?
    je    dfkey1c            ; e = yes, keep going
    dec    si            ; offset inc si in lodsb
    add    cx,2            ; include terminator, offset dec above
    jcxz    dfkey1d            ; z = nothing to move
    mov    di,offset tranbuf    ; destination is start of buffer
    push    ds
    pop    es
    cld
    rep    movsb            ; copy text part of string
dfkey1d:pop    di
    pop    si

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
    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
    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
    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
    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
ifdef MODIFIED
    call    sbtabl            ; redefine the keys in Sanyo BIOS [jhw]
endif
    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
    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
    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:
    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
    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
    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)
    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:'
    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'
    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:
    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
ifdef MODIFIED
shkeyz:    call    rbtabl            ; reset to original bios key defs [jhw]
endif
    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
    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
    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
    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
    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: 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
    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:

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
    xor    ah,ah            ; [jhw]
    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