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

⟦9bd582595⟧ TextFile

    Length: 85537 (0x14e21)
    Types: TextFile
    Names: »msuibm.asm«

Derivation

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

TextFile

        NAME    msuibm
; File MSUIBM.ASM
; Keyboard translator, by Joe R. Doupnik, Dec 1986
;  with contributions from David L. Knoell.
; edit history:
; Last edit 21 Nov 1988
; 21 Nov 1988 Version 2.32
; 12 Nov 1988 Add verbs terminalr and terminals, unassigned to keys.
; 1 July 1988 Version 2.31
; 20 May 1988 Add parser confirm requests below dfky13. [jrd]
; 10 March 1988 Add Holdscreen verb, holdscrn, for Terry Kennedy. [jrd]
; 27 Feb 1988 Add capability of stdin being a file rather than device. [jrd]
; 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
enhanced equ    1000h                   ; IBM, enhanced keyboard code

rgt_shift equ   1                       ; IBM shift state bits
lft_shift equ   2
ctl_shift equ   4
alt_shift equ   8
numlock   equ   20h

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, holdscrn, modeline, break,'
        db      ' lbreak,'
        db      cr,lf
        db   '   hangup, null (send one), terminalR, terminalS, DOS, help,'
        db   ' 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     (14*scan)+bs            ; Backspace key [hi=scan, lo=ascii]
        dw      (55*scan)+'*'           ; keypad asterisk
        dw      (74*scan)+'-'           ; keypad minus
        dw      (78*scan)+'+'           ; keypad plus
        dw      (71*scan)+'7'           ; keypad numeric area
        dw      (72*scan)+'8'
        dw      (73*scan)+'9'
        dw      (75*scan)+'4'
        dw      (76*scan)+'5'
        dw      (77*scan)+'6'
        dw      (79*scan)+'1'
        dw      (80*scan)+'2'
        dw      (81*scan)+'3'
        dw      (82*scan)+'0'
        dw      (83*scan)+'.'
        dw      (83*scan)+','           ; German keypad had comma vs dot
        dw      (15*scan)+tab
aliaslen equ    ($-aliaskey) shr 1      ; number of words in aliaskey table

kverbs  db      48                      ; 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   'holdscrn',khold
        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   'terminalR',vtrmac
        mkeyw   'terminalS',vtsmac
        mkeyw   'DOS',cdos
        mkeyw   'help',cquery
        mkeyw   'status',cstatus
        mkeyw   'exit',cquit

                                        ; Initialization data.
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+shift+90   ; VT100 keypad numeric area, SF7
        mkeyw   '\kkp1',scan+shift+86   ; SF3
        mkeyw   '\kkp2',scan+shift+87   ; SF4
        mkeyw   '\kkp3',scan+shift+88   ; SF5
        mkeyw   '\kkp4',scan+67         ; F9
        mkeyw   '\kkp5',scan+68         ; F10
        mkeyw   '\kkp6',scan+shift+84   ; SF1
        mkeyw   '\kkp7',scan+63         ; F5
        mkeyw   '\kkp8',scan+64         ; F6
        mkeyw   '\kkp9',scan+65         ; F7
        mkeyw   '\kkpenter',scan+shift+89 ; SF6
        mkeyw   '\kkpcoma',scan+shift+85  ; SF2
        mkeyw   '\kkpminus',scan+66     ; F8
        mkeyw   '\kkpdot',scan+shift+91 ; SF8
        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
        mkeyw   '\kupone',scan+control+132 ; Ctrl PgUp  one line scrolls
        mkeyw   '\kdnone',scan+control+118 ; Ctrl PgDn
        mkeyw   '\kuparr',scan+enhanced+72 ; Enhanced kbd duplicate keys
        mkeyw   '\kdnarr',scan+enhanced+80
        mkeyw   '\klfarr',scan+enhanced+75
        mkeyw   '\krtarr',scan+enhanced+77
        mkeyw   '\kupscn',scan+enhanced+73 ; PgUp Kermit screen roll back keys
        mkeyw   '\kdnscn',scan+enhanced+81 ; PgDn
        mkeyw   '\khomscn',scan+enhanced+71 ; Home
        mkeyw   '\kendscn',scan+enhanced+79     ; End
        mkeyw   '\kupone',scan+control+enhanced+132 ;Ctrl PgUp one line scroll
        mkeyw   '\kdnone',scan+control+enhanced+118 ; Ctrl PgDn
        mkeyw   '\kmodeline',scan+74    ; Kermit toggle mode line  Keypad -
        mkeyw   '\ktermtype',scan+alt+130 ; Kermit toggle terminal type  Alt -
        mkeyw   '\kreset',scan+alt+131  ; Kermit reset terminal  Alt =
        mkeyw   '\kprtscn',scan+control+114 ; Kermit toggle prn scrn  Ctrl *
        mkeyw   '\kdump',scan+control+117 ; Kermit Dump Screen  Ctrl End
        mkeyw   '*',scan+55             ; keypad asterisk
        mkeyw   '*',scan+enhanced+55    ; Enhanced kbd keypad asterisk
;;;     mkeyw   '-',scan+74             ; keypad minus
        mkeyw   '+',scan+78             ; keypad plus
        mkeyw   '.',scan+shift+83       ; IBM numeric keypad
        mkeyw   '0',scan+shift+82
        mkeyw   '1',scan+shift+79
        mkeyw   '2',scan+shift+80
        mkeyw   '3',scan+shift+81
        mkeyw   '4',scan+shift+75
        mkeyw   '5',scan+shift+76
        mkeyw   '6',scan+shift+77
        mkeyw   '7',scan+shift+71
        mkeyw   '8',scan+shift+72
        mkeyw   '9',scan+shift+73
        mkeyw   tab,scan+15             ; regular Tab key, made special
        mkeyw   cr,scan+enhanced+cr     ; Enhanced kbd grey Enter key
        mkeyw   lf,scan+enhanced+control+lf ; Enhanced grey Control Enter
        mkeyw   '/',scan+enhanced+'/'   ; Enhanced kbd grey foward slash
        mkeyw   '\0',scan+control+3     ; Control at-sign sends null
        mkeyw   '\0',scan+control+shift+3 ; Control Shift at-sign sends null
        mkeyw   '\x7f',scan+83          ; Del key sends DEL
        mkeyw   '\x7f',scan+enhanced+83 ; Enhanced duplicate DEL sends DEL
        mkeyw   '\x7f',scan+14          ; Backspace key sends DEL
        mkeyw   '\kexit',scan+alt+45    ; Exit connect mode  Alt X
        mkeyw   '\kstatus',scan+alt+31  ; Connect mode status  Alt S
        mkeyw   '\kbreak',scan+alt+48   ; Send a Break  Alt B
        mkeyw   '\kbreak',scan+control+0; Control-Break sends a Break too
        mkeyw   '\khelp',scan+alt+35    ; Connect mode drop down menu  Alt H
        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, 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
        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      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
        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
        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
        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
        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
        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
        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: 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
        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