        NAME    msuz10
; file MSUZ10.ASM
	include mssdef.h
;       Copyright (C) 1982,1991, Trustees of Columbia University in the
;       City of New York.  Permission is granted to any individual or
;       institution to use, copy, or redistribute this software as long as
;       it is not sold for profit and this copyright notice is retained.
; Kermit system dependent module for Heath/Zenith Z100
; Keyboard translator, by Joe R. Doupnik, Dec 1986
;  with contributions from David L. Knoell.
; Adapted for the Z-100 by Bo G. Gedda, May 1990
; Edit history
; 2 March 1991 version 3.10
; Last edit 20 August 1992
; Kverb \kscroll (F11) added
; Bo Gedda
;
;

        public  keybd, dfkey, shkey, msuinit, kbcodes, keycom, dosflg, dosflg1

; some definitions

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     128                     ; 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

; Z-100 Keyboard ports
keydat  equ     0f4h            ; data
keycmd  equ     0f5h            ; command
keystat equ     keycmd          ; status

; Z-100 Keyboard status masks
KPR     equ     2               ; 00000010b, ready if this is NOT set
KDA     equ     1               ; 00000001b, data available if set

; Z-100 Keyboard commands
keyrst  equ     00h             ; reset
keyrpon equ     01h             ; auto repeat on
keyrpof equ     02h             ; auto repeat off
keyclon equ     03h             ; click on
keyclof equ     04h             ; click off
keyclr  equ     05h             ; clear buffer
keycli  equ     06h             ; make key click
keybel  equ     07h             ; make bell sound
keyena  equ     08h             ; enable keyboard
keydis  equ     09h             ; disable keyboard
keyeve  equ     0ah             ; make keyboard event driven (up/down mode)
keyasc  equ     0bh             ; make keyboard scan ascii
keyiena equ     0ch             ; enable interrupt
keyidis equ     0dh             ; disable interrupt

data    segment
        extrn taklev:byte, comand:byte, flags:byte
        extrn shkadr:word, stkadr:word, trans:byte, ttyact:byte
                                                ; system dependent references
	extrn vtmacname:word, vtmaclen:word	; external macro, in msyibm
        extrn vtemu:byte                ; emulator data
        
;;;     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) to use SCAN CODES, OFF to use ASCII.$';Z100
dfaskky db      cr,lf,' Push key to be defined: $'
dfaskdf db      ' Enter new definition: ',0             ; asciiz for prompt
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,'$'
dfkoops db      cr,lf,' Oops! 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, press a 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
stringcnt dw    0                       ; qty of string chars to be processed
stringptr dw    0                       ; address of next string char
twelve  dw      11d             	; sorry, one less than that for Z-100
dosflg  db      0
;;;     End System Independent Data Area

;;;     System Dependent Data Area
dosflg1 db      0               ; scan codes enabled/key extension disabled=1
nrcptr  dw      0               ; pointer to code page to NRC table
latinptr dw     0               ; pointer to code page to Latin1 table
DECmnptr dw     0               ; pointer to code page to DEC-MCS table
;       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. VT52 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,' Z-100/Heath-19 function and special key actions:',cr,lf
	db   '   F0, ... F12, SF0, ... SF11, i_chr, d_chr, i_line, d_line'
        db      cr,lf,' Kermit screen control and actions:',cr,lf
        db   '   upscn, dnscn, homscn, endscn, upone, dnone, prtscn, dump,'
        db      cr,lf
	db   '   scroll, logoff, logon, modeline, break, lbreak,'
        db      cr,lf
;        db   '   logoff, logon, termtype, reset, modeline, break,'
 ;       db      ' lbreak, nethold,'
  ;      db      cr,lf
        db   '   hangup, null (send one), DOS, help,'
        db   ' status, exit'
        db      cr,lf,'$'

        ; Z-100 scan codes, used for setting keycode shift and control flags
        ; column 1 unshift, column 2 shift, column 3 control, column 4
        ; shift-control
z10keyl equ     this byte
        db      30h,29h,30h,29h         ; 0,)
        db      31h,21h,31h,21h         ; 1,!
        db      32h,40h,32h,00h         ; 2,@
        db      33h,23h,33h,23h         ; 3,#
        db      34h,24h,34h,24h         ; 4,$
        db      35h,25h,35h,25h         ; 5,%
        db      36h,5eh,36h,1eh         ; 6,^
        db      37h,26h,37h,26h         ; 7,&
        db      38h,2ah,38h,2ah         ; 8,*
        db      39h,28h,39h,28h         ; 9,(
        db      61h,41h,01h,01h         ; a,A
        db      62h,42h,02h,02h         ; b,B
        db      63h,43h,03h,03h         ; c,C
        db      64h,44h,04h,04h         ; d,D
        db      65h,45h,05h,05h         ; e,E
        db      66h,46h,06h,06h         ; f,F
        db      67h,47h,07h,07h         ; g,G
        db      68h,48h,08h,08h         ; h,H
        db      69h,49h,09h,09h         ; i,I
        db      6ah,4ah,0ah,0ah         ; j,J
        db      6bh,4bh,0bh,0bh         ; k,K
        db      6ch,4ch,0ch,0ch         ; l,L
        db      6dh,4dh,0dh,0dh         ; m,M
        db      6eh,4eh,0eh,0eh         ; n,N
        db      6fh,4fh,0fh,0fh         ; o,O
        db      70h,50h,10h,10h         ; p,P
        db      71h,51h,11h,11h         ; q,Q
        db      72h,52h,12h,12h         ; r,R
        db      73h,53h,13h,13h         ; s,S
        db      74h,54h,14h,14h         ; t,T
        db      75h,55h,15h,15h         ; u,U
        db      76h,56h,16h,16h         ; v,V
        db      77h,57h,17h,17h         ; w,W
        db      78h,58h,18h,18h         ; x,X
        db      79h,59h,19h,19h         ; y,Y
        db      7ah,5ah,1ah,1ah         ; z,Z
        db      08h,08h,08h,08h         ; BACKSPACE
        db      09h,09h,09h,09h         ; TAB
        db      0ah,0ah,0ah,0ah         ; LINE FEED
        db      0dh,0dh,0dh,0dh         ; RETURN
        db      1bh,1bh,1bh,1bh         ; ESC
        db      20h,20h,20h,20h         ; SPACE
        db      27h,22h,27h,22h         ; ',"
        db      2ch,3ch,2ch,3ch         ; ,,<
        db      2dh,5fh,2dh,1fh         ; -,_
        db      2eh,3eh,2eh,3eh         ; .,>
        db      2fh,3fh,2fh,3fh         ; /,?
        db      3bh,3ah,3bh,3ah         ; ;,:
        db      3dh,2bh,3dh,2bh         ; =,+
        db      5bh,7bh,1bh,7bh         ; [,{
        db      5ch,7ch,1ch,7ch         ; \,|
        db      5dh,7dh,1dh,7dh         ; ],}
        db      60h,7eh,60h,7eh         ; `,~
        db      7fh,7fh,7fh,7fh         ; DELETE
        db      8dh,0cdh,8dh,0cdh       ; ENTER
        db      95h,0d5h,95h,0c5h       ; HELP
        db      96h,0d6h,96h,0d6h       ; F0
        db      97h,0d7h,97h,0d7h       ; F1
        db      98h,0d8h,98h,0d8h       ; F2
        db      99h,0d9h,99h,0d9h       ; F3
        db      9ah,0dah,9ah,0dah       ; F4
        db      9bh,0dbh,9bh,0dbh       ; F5
        db      9ch,0dch,9ch,0dch       ; F6
        db      9dh,0ddh,9dh,0ddh       ; F7
        db      9eh,0deh,9eh,0deh       ; F8
        db      9fh,0dfh,9fh,0dfh       ; F9
        db      0a0h,0e0h,0a0h,0e0h     ; F10
        db      0a1h,0e1h,0a1h,0e1h     ; F11
        db      0a2h,0e2h,0a2h,0e2h     ; F12
        db      0a3h,0e3h,0a3h,0e3h     ; I CHR, D CHR
        db      0a4h,0e4h,0a4h,0e4h     ; I LINE, D LINE
        db      0a5h,0e5h,0a5h,0e5h     ; UP ARROW
        db      0a6h,0e6h,0a6h,0e6h     ; DOWN ARROW
        db      0a7h,0e7h,0a7h,0e7h     ; RIGHT ARROW
        db      0a8h,0e8h,0a8h,0e8h     ; LEFT ARROW
        db      0a9h,0e9h,0a9h,0e9h     ; HOME
        db      0aah,0eah,0aah,0eah     ; BREAK
        db      0adh,0edh,0adh,0edh     ; keypad -
        db      0aeh,0eeh,0aeh,0eeh     ; keypad .
        db      0b0h,0f0h,0b0h,0f0h     ; keypad 0
        db      0b1h,0f1h,0b1h,0f1h     ; keypad 1
        db      0b2h,0f2h,0b2h,0f2h     ; keypad 2
        db      0b3h,0f3h,0b3h,0f3h     ; keypad 3
        db      0b4h,0f4h,0b4h,0f4h     ; keypad 4
        db      0b5h,0f5h,0b5h,0f5h     ; keypad 5
        db      0b6h,0f6h,0b6h,0f6h     ; keypad 6
        db      0b7h,0f7h,0b7h,0f7h     ; keypad 7
        db      0b8h,0f8h,0b8h,0f8h     ; keypad 8
        db      0b9h,0f9h,0b9h,0f9h     ; keypad 9
lz10keyl equ    $ - z10keyl

kverbs  db      72                      ; 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   'F0',f0
        mkeyw   'F1',f1
        mkeyw   'F2',f2
        mkeyw   'F3',f3
        mkeyw   'F4',f4
        mkeyw   'F5',f5
        mkeyw   'F6',f6
        mkeyw   'F7',f7
        mkeyw   'F8',f8
        mkeyw   'F9',f9
        mkeyw   'F10',f10
        mkeyw   'F11',f11
        mkeyw   'F12',f12
        mkeyw   'SF0',sf0
        mkeyw   'SF1',sf1
        mkeyw   'SF2',sf2
        mkeyw   'SF3',sf3
        mkeyw   'SF4',sf4
        mkeyw   'SF5',sf5
        mkeyw   'SF6',sf6
        mkeyw   'SF7',sf7
        mkeyw   'SF8',sf8
        mkeyw   'SF9',sf9
        mkeyw   'SF10',sf10
        mkeyw   'SF11',sf11
        mkeyw   'i_chr',i_chr
        mkeyw   'd_chr',d_chr
        mkeyw   'i_line',i_line
        mkeyw   'd_line',d_line
comment @
        mkeyw   'decF6',decf6
        mkeyw   'decF7',decf7
        mkeyw   'decF8',decf8
        mkeyw   'decF9',decf9
        mkeyw   'decF10',decf10
        mkeyw   'decF11',decf11
        mkeyw   'decF12',decf12
        mkeyw   'decF13',decf13
        mkeyw   'decF14',decf14
        mkeyw   'decHelp',dechelp
        mkeyw   'decDo',decdo
        mkeyw   'decF17',decf17
        mkeyw   'decF18',decf18
        mkeyw   'decF19',decf19
        mkeyw   'decF20',decf20
        mkeyw   'decFind',decfind
        mkeyw   'decInsert',decinsert
        mkeyw   'decRemove',decremove
        mkeyw   'decSelect',decselect
        mkeyw   'decPrev',decprev
        mkeyw   'decNext',decnext
        mkeyw   'termtype',vtans52
        mkeyw   'reset',vtinit
end comment @
        mkeyw   'dnscn',dnwpg
        mkeyw   'upscn',upwpg
        mkeyw   'endscn',endwnd
        mkeyw   'upone',upone
        mkeyw   'dnone',dnone
        mkeyw   'prtscn',trnprs
        mkeyw   'dump',dmpscn
        mkeyw   'homscn',homwnd
        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
	mkeyw	'scroll',scroll

                                        ; Kermit Z-100 Initialization keypad
                                        ;  data.
                                        ; PF1 to PF4 are shifted home,
                                        ;  leftarrow, rightarrow and uparrow
                                        ; first the Z-100 numeric keypad
kbdinlst equ    this byte               ; mkeyw 'definition',keycode
        mkeyw   '\khomscn',scan+0a9h    ; home key
        mkeyw   '\klfarr',scan+0a8h     ; leftarrow
        mkeyw   '\krtarr',scan+0a7h     ; rightarrow
        mkeyw   '\kuparr',scan+0a5h     ; uparrow
        mkeyw   '\kkp7',scan+0b7h       ; kp7
        mkeyw   '\kkp8',scan+0b8h       ; kp8
        mkeyw   '\kkp9',scan+0b9h       ; kp9
        mkeyw   '\kdnarr',scan+0a6h     ; downarrow
        mkeyw   '\kkp4',scan+0b4h       ; kp4
        mkeyw   '\kkp5',scan+0b5h       ; kp5
        mkeyw   '\kkp6',scan+0b6h       ; kp6
        mkeyw   '\kkpminus',scan+0adh   ; kp minus
        mkeyw   '\kkp1',scan+0b1h       ; kp1
        mkeyw   '\kkp2',scan+0b2h       ; kp2
        mkeyw   '\kkp3',scan+0b3h       ; kp3
        mkeyw   '\kkpenter',scan+08dh   ; Enter key
        mkeyw   '\kkp0',scan+0b0h       ; kp0
        mkeyw   '\kkpdot',scan+0aeh     ; kp dot
        mkeyw   '\kgold',scan+shift+0e9h; shifted home key      => VT100 gold
        mkeyw   '\kpf2',scan+shift+0e8h ; shifted left arrow    => VT100 PF2
        mkeyw   '\kpf3',scan+shift+0e7h ; shifted right arrow   => VT100 PF3
        mkeyw   '\kpf4',scan+shift+0e5h ; shifted up arrow      => VT100 PF4
        mkeyw   '\kd_chr',scan+shift+0f7h   ; shifted kp7
        mkeyw   '\kuparr',scan+shift+0f8h   ; shifted kp8
        mkeyw   '\ki_chr',scan+shift+0f9h   ; shifted kp9
        mkeyw   '\kkpminus',scan+shift+0e6h ; shifted down arrow => VT100 minus
        mkeyw   '\klfarr',scan+shift+0f4h   ; shifted kp4
        mkeyw   '\khomscn',scan+shift+0f5h  ; shifted kp5
        mkeyw   '\krtarr',scan+shift+0f6h   ; shifted kp6
        mkeyw   '\kkpcoma',scan+shift+0edh  ; shifted kp minus   => VT100 comma
        mkeyw   '\ki_line',scan+shift+0f1h  ; shifted kp1
        mkeyw   '\kdnarr',scan+shift+0f2h   ; shifted kp2
        mkeyw   '\kd_line',scan+shift+0f3h  ; shifted kp3
        mkeyw   '\x0d',scan+shift+0cdh      ; shifted enter
        mkeyw   '0',scan+shift+0f0h         ; shifted kp0
        mkeyw   '.',scan+shift+0eeh         ; shifted kp dot
                                        ; Some function keys
                                        ; VT100 function keys
        mkeyw   '\kf0',scan+096h        ; F0
        mkeyw   '\kf1',scan+097h        ; F1
        mkeyw   '\kf2',scan+098h        ; F2
        mkeyw   '\kf3',scan+099h        ; F3
        mkeyw   '\kf4',scan+09ah        ; F4
        mkeyw   '\kf5',scan+09bh        ; F5
        mkeyw   '\kf6',scan+09ch        ; F6
        mkeyw   '\kf7',scan+09dh        ; F7
        mkeyw   '\kf8',scan+09eh        ; F8
        mkeyw   '\kf9',scan+09fh        ; F9
        mkeyw   '\kf10',scan+0a0h       ; F10
	mkeyw	'\kscroll',scan+0a1h	; F11 Recover last screen
        mkeyw   '\kprtscn',scan+0a2h    ; F12 Kermit toggle prn scrn
        mkeyw   '\ksf0',scan+shift+0d6h ; SF0
        mkeyw   '\ksf1',scan+shift+0d7h ; SF1
        mkeyw   '\ksf2',scan+shift+0d8h ; SF2
        mkeyw   '\ksf3',scan+shift+0d9h ; SF3
        mkeyw   '\ksf4',scan+shift+0dah ; SF4
        mkeyw   '\ksf5',scan+shift+0dbh ; SF5
        mkeyw   '\ksf6',scan+shift+0dch ; SF6
        mkeyw   '\ksf7',scan+shift+0ddh ; SF7
        mkeyw   '\ksf8',scan+shift+0deh ; SF8
        mkeyw   '\ksf9',scan+shift+0dfh ; SF9
        mkeyw   '\ksf10',scan+shift+0e0h; SF10
        mkeyw   '\kdump',scan+shift+0e1h; SF11 Kermit Dump Screen
					; SF12 not available, used by DOS prtscr
                                        ; Some special keys
        mkeyw   '\klbreak',scan+shift+0eah; Shift-Break sends a Long Break
        mkeyw   '\khelp',scan+095h        ; Connect mode drop down menu
        mkeyw   '\kstatus',scan+shift+0d5h; Display Status
;       mkeyw   '\kdnone',scan+0a3h       ; I CHR  scrolls down 1 line
;       mkeyw   '\kupone',scan+shift+0e3h ; D CHR  scrolls up   1 line
;       mkeyw   '\kdnscn',scan+0a4h       ; I LINE scrolls down 1 page
;       mkeyw   '\kupscn',scan+shift+0e4h ; D LINE scrolls up   1 page
        mkeyw   '\ki_chr',scan+0a3h       ; I CHR  sends ESC @ or ESC O
        mkeyw   '\kd_chr',scan+shift+0e3h ; D CHR  sends ESC N
        mkeyw   '\ki_line',scan+0a4h      ; I LINE sends ESC L
        mkeyw   '\kd_line',scan+shift+0e4h; D LINE sends ESC M
        dw      0                       ; end of table marker

kbcodes dw      80h                     ; keyboard read codes, 80h=not inited

data    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.
; 
;    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
                ; system independent external items
        extrn   comnd:near, prompt:near, iseof:near     ; in msscmd
        extrn   strlen:near, prtscr:near		; in mssfil
        extrn   cnvlin:near, katoi:near, decout:near    ; in msster
                ; system dependent external items
        extrn   beep:near, ena_scan:near, dis_scan:near ; in msxz10
        extrn   vclick:near                             ; in msxz10
                ; these are system dependent action verbs, in msxibm & msyibm 
        extrn   f0:near, f1:near, f2:near, f3:near, f4:near, f5:near, f6:near
        extrn   f7:near, f8:near, f9:near, f10:near, f11:near, f12:near
        extrn   sf0:near, sf1:near, sf2:near, sf3:near, sf4:near, sf5:near
        extrn   sf6:near, sf7:near, sf8:near, sf9:near, sf10:near, sf11:near
        extrn   i_chr:near, d_chr:near, i_line:near, d_line:near
        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, dnwpg:near, upwpg:near, scroll:near
comment @
        extrn   kpdot:near, decf6:near, decf7:near, decf8:near, decf9:near
        extrn   decf10:near, decf11:near, decf12:near, decf13:near
        extrn   decf14:near, dechelp:near, decdo:near, decf17:near
        extrn   decf18:near, decf19:near, decf20:near, udkclear:near
        extrn   decfind:near, decinsert:near, decremove:near
        extrn   decselect:near, decprev:near, decnext:near
        extrn   chrout:near, cstatus:near, cquit:near, cquery:near
        extrn   vtans52:near, vtinit:near, dnwpg:near, upwpg:near
end comment @
        extrn   endwnd:near, homwnd:near, upone:near, dnone:near, trnprs:near
        extrn   chang:near, klogon:near, klogof:near
        extrn   trnmod:near, sendbr:near, sendbl:near, dmpscn:near, snull:near
        extrn   cplatin:near, nrc2cp:near
        extrn   cpdecsg:near, cdos:near, cquery:near, cstatus:near
        extrn   chrout:near, cquit:near, udkclear:near, extmacro:near
        assume  cs:code, ds:data, es:data

; 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
        mov     ttyact,1                ; doing single char output
        cmp     stringcnt,0             ; any leftover string chars?
        je      keybd0                  ; e = no
        jmp     keyst2                  ; yes, finish string
keybd0: 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 data 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    xltkey                  ; do character set translation
        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     ax,[bx]			; cnt value
        add     ax,4                    ; skip text and value word
        add     bx,ax                   ; look at next slot
        loop    keyvb1                  ; walk to correct slot
        sub     bx,2                    ; backup to value field
        pop     cx                      ; restore reg
        mov     bx,[bx]                 ; get value field of this slot
        or	bx,bx			; jump address defined?
        jz      keybdx                  ; z = 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
        xor     cx,cx                   ; init string length to null
        or	si,si			; is there a string pointer present?
        jz      keyst1                  ; z = no, skip operation
        cld                             ; scan forward
        mov     cx,[si]			; get string length
        add     si,2
keyst1: mov     stringcnt,cx
        mov     stringptr,si
        pop     si
        jcxz    keybdx                  ; z = null length

keyst2: push    si
        mov     si,stringptr            ; pointer to next string char
        cld
        lodsb                           ; get new string char into al
        pop     si
        dec     stringcnt               ; string chars remaining
        inc     stringptr
	call	keysv			; scan for embedded verbs
	jc	keyst4			; c = not found, al has string char
	jmp	bx			; perform the verb (bx = address)
keyst4:	cmp     stringcnt,0             ; end of the string?
        jne     keyst5                  ; ne = no
        mov     ttyact,0                ; pretend not single char output
keyst5:	jmp     chrout                  ; send out the char in al

keybdx: clc                             ; return success (nothing to do)
        ret
KEYBD   ENDP

; Scan for keyboard verbs embedded in outgoing string. If found update
; string pointer and count to just beyond the verb and return action routine
; address in bx with carry clear. If failure return carry set and no change.

keysv   proc    near
        push    ax
        push    si
        push    di
        cmp     al,'\'                  ; escape?
        jne     keysv7                  ; ne = no
        mov     cx,stringcnt            ; chars remaining
        mov     si,stringptr            ; address of next char to read
        mov     brace,0                 ; assume not using braces
        cmp     byte ptr [si],braceop   ; starts with \{?
        jne     keysv1                  ; ne = no
        inc     si                      ; skip the opening brace
        dec     cx
        mov     brace,bracecl           ; expect closing brace
keysv1: cmp     byte ptr [si],'K'       ; starts with \{K or \K?
        je      keysv2                  ; e = yes
        cmp     byte ptr [si],'k'       ; starts as \{k or \k?
        jne     keysv7                  ; ne = no, then it's a string
keysv2: inc     si                      ; yes, skip the K too
        dec     cx
        mov     di,offset tranbuf       ; copy verb name to this work buffer
        xor     ax,ax
        mov     [di],ax                 ; init the buffer to empty
keysv3: cld
        jcxz    keysv4                  ; z = no more string chars
        lodsb                           ; scan til closing brace or w/s or end
        dec     cx
        cmp     al,brace                ; closing brace?
        je      keysv4                  ; e = yes
        cmp     al,spc                  ; white space or control char?
        jbe     keysv3                  ; be = yes
        mov     [di],ax                 ; copy to tranbuf and terminate
        inc     di
        jmp     short keysv3
keysv4:	push	si			; save input reading position
	mov	si,offset tranbuf	; where verb starts (needs si)
	call	tstkeyw			; find keyword, bx = action routine
	pop	si
	jnc	keysv4a			; nc = found the verb
	call	keysv8			; invoke EXTMACRO worker for unknown
	jc	keysv7			; carry = no verb to operate upon
keysv4a:cmp	brace,0			; need to end on a brace?
	je	keysv6			; e = no
	dec	si			; break position
	inc	cx
	cld
keysv5:	jcxz	keysv6			; z = no more string characters
	lodsb				; read string char
	dec	cx
	cmp	al,brace		; the brace?
	jne	keysv5			; ne = no, repeat until it is found
keysv6:	mov	stringptr,si		; where we finished+1
	mov	stringcnt,cx		; new count of remaining chars
	pop	di
	pop	si			; original si, starting place
	pop	ax			; original ax
	clc
	ret
keysv7:	pop	di			; verb not found
	pop	si
	pop	ax
	stc
	ret
; Worker. Unknown verb name as string {\kverb} or {\k{verb}}. Use EXTMACRO
; procedure (in msyibm typically), point to verb name with vtmacname, length
; of it in byte vtmaclen, address of EXTMACRO to BX. Upper case the verb.
; Enter with tranbuf holding the verb, asciiz, without \K and braces.
; Returns BX set to EXTMACRO proc, vtmacname pointing to verb (uppercased)
; and vtmaclen holding the length of verb.
keysv8:	mov	bx,offset extmacro	; use this external macro pointer
	mov	vtmacname,offset tranbuf; select extmacro procedure address
	mov	dx,offset tranbuf	; point to name for extmacro
	push	cx
	call	strlen			; get its length
	mov	vtmaclen,cx		; length for extmacro
	jcxz	keysv11			; z = none
	push	si			; convert verb name to upper case
	mov	si,dx			; verb, without leading \K stuff
	cld
keysv9:	lodsb				; read a name byte
	cmp	al,'a'			; before lower case?
	jb	keysv10			; e = yes
	cmp	al,'z'			; above lower case?
	ja	keysv10			; a = yes
	and	al,not 20h		; convert to upper case
	mov	[si-1],al		; put it back
keysv10:loop	keysv9			; do all bytes, asciiz
	pop	si
	pop	cx
	clc				; carry clear = ready to execute
	ret
keysv11:stc				; carry set = no verb, do nothing
	pop	cx
	ret
keysv   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
        or      byte ptr kbcodes,80h    ; say kbcodes not-initiated
        mov     dx,offset tranbuf       ; our work space
        mov     word ptr tranbuf,0      ; insert terminator
        mov     bx,offset dfhelp1       ; first help message
        mov     ah,cmword               ; parse a word
        call    comnd                   ; get key code or original ascii char
        mov     cl,taklev               ; reading from Take file
        mov     msutake,cl              ; save here
        or      ax,ax                   ; any text given?
        jnz     dfkey12                 ; nz = yes, so don't consider prompts
                                        ; interactive key request
        cmp     taklev,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	ax,1			; number of characters received
	jbe	dfkey12a		; be = stay here
	jmp	dfkey1			; a = more than one, decode
dfkey12a:mov	ah,byte ptr tranbuf	; get the single char
	mov	byte ptr keycode,ah	; store as ascii keycode
	jmp	dfkey1b			; go get definition
dfkey13:push	dx			; save command letters
	mov	ah,cmeol		; get end of line confirmation
	call	comnd
	pop	dx
	jnc	dfkey14			; nc = success
	ret
dfkey14:mov	al,0ffh			; set DOS keyboard read flag
	cmp	dx,'fo'			; first two letters of "off"
	je	dfkey14a		; e = yes, use DOS keyboard calls
	xor	al,al			; clear DOS keyboard read flag
	cmp	dx,'no'			; first two letters of "on"
	je	dfkey14a		; e = yes, use standard kbd calls
	mov	al,dosflg		; get current flag
dfkey14a:mov	dosflg,al		; store new keyboard flag
	ret


dfkey15:mov     ah,cmeol
        call    comnd                   ; confirm request before proceeding
        jnc     dfkeyc                  ; nc = success
        ret                             ; failure

dfkey0: mov     dx,offset dfhelp1       ; say bad definition command
        mov     ah,prstr
        int     dos
        stc                             ; failure
        ret

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 data segment
        xor	ax,ax                   ; 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
        clc                             ; success
        ret
                                        ; 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 data 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,cmline               ; read rest of line into tranbuf
        call    comnd                   ; allow null definitions
        or      ax,ax                   ; 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,cmline               ; read rest of line into tranbuf
        call    comnd
        jc      dfkey1x                 ; exit now on ^C from user
        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
dfkey1x:ret                             ; failure exit

dfkey1g:push	ax			; save count
        mov     ah,cmeol                ; get a confirm
        call    comnd
        pop     cx			; string length
        jc      dfkey1x                 ; none so declare parse error
        
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 dfkoops       ; Oops! msg
        mov     ah,prstr                ; complain and don't redefine
        int     dos
        stc                             ; failure
        ret

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
        stc                             ; failure
        ret

; 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
        or	al,al			; premature end?
        jz      dfkey4b                 ; z = 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
        stc                             ; failure
        ret

; 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
        stc                             ; failure
        ret

                ; 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 data 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
        or	ah,ah
        jz	dfkey7a                 ; z = 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:clc                             ; return success
        ret
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,cmeol                ; get a confirm
        call    comnd                   ; ignore any additional text
        push    bx
        mov     dx,offset shkmsg1       ; ask for original key
        mov     ah,prstr
        int     dos
        or      byte ptr kbcodes,80h    ; say kbcodes not-initiated
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:	mov	ah,0bh			; check console, check ^C
	int	dos
	cmp	flags.cxzflg,'C'	; a ^C?
	je	shky1b			; e = yes, quit
	or	al,al
	jz	shky1a			; z = nothing
	call	getkey			; get any key
	jc	shky1a			; c = nothing at keyboard yet, wait
shky1b:	pop	cx			; resume loop
	cmp	flags.cxzflg,'C'	; a ^C?
	je	shky1c			; e = yes, quit
	loop	shky1
shky1c:	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 data 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
        xor	cx,cx			; current slot number
shkey6b:cmp     cx,dx                   ; this slot?
        je      shkey6c                 ; e = yes, print the text part
        ja      shkeyx                  ; a = beyond, exit bad
        mov     ax,[bx]        		; get cnt (keyword length)
        add     ax,4                    ; skip count and two byte value
        add     bx,ax                   ; bx = start of next keyword slot
        inc     cx                      ; current keyword number
        jmp     short shkey6b           ; try another
shkey6c:push	cx
	mov	cx,[bx]			; length of definition
	add	bx,2			; look at text field
	mov	di,bx			; offset for printing
	call	prtscr			; print counted string
	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
	call	prtscr			; print counted string, again
	pop	cx
	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     cx,word ptr [si]        ; get string length
        add     si,2			; 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
        clc                             ; return success
        ret
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:         dw      cnt     (length of string 'word')
;                               db      'word'  (keyword string)
;                               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     [bx],cx        		; compare length vs table keyword
        jne     tstkw4                  ; ne = not equal lengths, try another
        push    si                      ; lengths match, how about spelling?
        push    bx
        add     bx,2			; 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     ax,[bx]			; cnt (keyword length from macro)
        add     ax,4                    ; skip over count and two byte value
        add     bx,ax                   ; bx = start of next keyword slot
        jmp     tstkw3                  ; do another comparison

tstkw5:                                 ; get action pointer
        mov     ax,[bx]		        ; cnt (keyword length from macro)
        add     ax,2                    ; skip over count
        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 data segment
        cld
        mov     [di],cx 	; length of text for new string
        add     di,2		; 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     cx,[bx]		        ; get length of subject string
        add     cx,2			; length word 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 ; 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
        or      byte ptr kbcodes,80h    ; say kbcodes not-initiated
        push    ds                      ;  initial keyboard assignments.
        pop     es                      ; set es:di to data segment
        inc     taklev                  ; pretend that we are in Take file
kbdini0:mov     si,offset kbdinlst      ; start of list of definitions
kbdini1:mov     cx,[si]			; cnt field (keyword length of macro)
        jcxz    kbdinix                 ; z = null cnt field = end of list
        add     si,2			; 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
        mov     ax,word ptr [si]        ; get value field
        mov     keycode,ax              ; set key ident value
	push	si
        call    dfkey2                  ; put dfkey to work
        pop     si
        add     si,2                    ; point to next entry
        jmp     kbdini1                 ; keep working
kbdinix:dec     taklev                  ; reset Take file level
        call    udkclear                ; clear User Definable Keys
        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.  Uses dos keyboard reading.
; Modifies register ax.
getkey  proc    near
        mov     keycode,0               ; clear old keycode
        cmp     dosflg,0                ; do ascii keyboard reading?
        jnz     getky7                  ; nz = yes, ascii
        jmp     getky6                  ; do scan code form
                                        ; ;;;;;;;; A S C I I ;;;;;;;;;
getky7: test    dosflg1,1               ; scan codes enabled?
        jz      getky7a                 ; z = no, continue
        call    dis_scan                ; disable them
getky7a:test    byte ptr kbcodes,80h    ; kbcodes initiated?            [dan]
        jz      getky5                  ; z = yes                       [dan]
        push    si
        call    nrc2cp                  ; point SI at this code page to NRC
        mov     nrcptr,si
        pop     si
        push    bx
        call    cplatin                 ; code page to Latin1, BX gets ptr
        mov     latinptr,bx
        call    cpdecsg                 ; code page to DEC-MCS, BX gets ptr
        mov     decmnptr,bx
        pop     bx
        and     byte ptr kbcodes,not 80h ; say kbcodes initiated
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:push    dx
        mov     dl,0ffh                 ; DOS read operation
        mov     ah,dconio               ; from stdin
        int     dos
        pop     dx
        jnz     getky5a                 ; nz = char available
        stc                             ; carry set = nothing available
        ret                             ; exit on no char
getky5a:xor     ah,ah
        cmp     al,16                   ; Control P?
        jne     getky5b                 ; ne = no
        mov     al,0a2h                 ; force F12 scan code, put on printer
        or      keycode,scan            ; ensure scan bit is set
        jmp     short getky5c
getky5b:cmp     al,BS                   ; backspace key?
        jne     getky5c                 ; ne = no
;       or      keycode,scan            ; ensure scan bit is set
getky5c:mov     byte ptr keycode,al     ; save ascii char
        clc                             ; carry clear = got a char
        ret                             ; and exit
                                        ; ;;;;;; S C A N C O D E ;;;;;;
getky6: test    dosflg1,1               ; scan codes enabled?
        jnz     getky6a                 ; nz = yes, continue
        call    ena_scan                ; enable them
getky6a:test    byte ptr kbcodes,80h    ; kbcodes initiated?            [dan]
        jz      getky6b                 ; z = yes                       [dan]
        mov     kbcodes,0001h           ; low byte = status, high = read char
        push    si
        call    nrc2cp                  ; point SI at this code page to NRC
        mov     nrcptr,si
        pop     si
        push    bx
        call    cplatin                 ; code page to Latin1, BX gets ptr
        mov     latinptr,bx
        call    cpdecsg                 ; code page to DEC-MCS, BX gets ptr
        mov     decmnptr,bx
        pop     bx
getky6b:call    iseof                   ; is stdin at eof?
        jnc     getky6c                 ; 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
getky6c:push    dx
        mov     dl,0ffh                 ; DOS read operation
        mov     ah,dconio               ; from stdin
        int     dos
        pop     dx
        jnz     getky6d                 ; nz = char available
        stc                             ; carry set = nothing available
        ret                             ; exit on no char
getky6d:call    fixflg                  ; go set keycode flags in ax
        cmp     al,80h                  ; start of special keys
        jb      getky6g                 ; go make these pure ascii
getky6e:or      ax,scan                 ; set the scan bit
getky6f:mov     keycode,ax
        clc
        ret
getky6g:push    cx
        mov     cx,shift
        or      cx,control
        not     cx
        and     ax,cx                   ; clear shift and control bits
        pop     cx
        jmp     short getky6f
getkey  endp

; Return modified char code, depending on SET TERM CHAR-SET and active
; Code Page. Enter and exit with char in AL.
xltkey  proc    near
	cmp	al,' '			; control character?
	jb	xltkey2			; b = yes, do not translate
        cmp     flags.xltkbd,0          ; keyboard translation is off?
        je      xltkey2                 ; e = yes
        cmp     vtemu.vtchset,0         ; NRC's, using ASCII already?
        je      xltkey2                 ; e = yes, use char in AL
        cmp     vtemu.vtchset,12        ; 1-12 for NCRs?
        ja      xltkey3                 ; a = no
        push    si
        push    di
        push    ax
        mov     si,nrcptr               ; point SI at this code page to NRC
        pop     ax
        mov     di,si                   ; start of table are ASCII chars
        mov     cl,vtemu.vtchset        ; get set number (1-12)
        xor     ch,ch
        sub     di,cx                   ; minus one byte per set
        shl     cx,1                    ; 15 bytes per NRC entry
        shl     cx,1
        shl     cx,1
        shl     cx,1
        add     di,cx                   ; +16, source, point at this entry
        mov     cx,12                   ; do first 12 bytes of them
        push    es
        push    ds
        pop     es
        cld
        repne   scasb                   ; look for this byte code in table
        pop     es
        jne     xltkey1                 ; ne = not found, use char in AL
        inc     cx
        sub     cx,12                   ; compute char index
        neg     cx
        add     si,cx                   ; index into ASCII table
        mov     al,[si]                 ; get matching ASCII char (set 0)
xltkey1:pop     di
        pop     si
xltkey2:ret
                                        ; LATIN1 set
xltkey3:test    al,80h                  ; high bit set?
        jz      xltkey2                 ; z = no, no translation
        cmp     vtemu.vtchset,15        ; set term character-set LATIN1?
        jne     xltkey4                 ; ne = no
        and     al,not 80h              ; remove high bit
        push    bx
        mov     bx,latinptr             ; Code Page to Latin1
        xlatb                           ; translate to Latin1 in AL
        pop     bx
        ret
xltkey4:cmp     vtemu.vtchset,16        ; set term character-set DEC-MCS?
        jne     xltkey2                 ; ne = no
        and     al,not 80h              ; remove high bit
        push    bx
        mov     bx,DECmnptr             ; Code Page to DEC-MCS
        xlatb                           ; translate to DEC-MCS in AL
        pop     bx
        ret
xltkey  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

;keycom sends commands in dl to the Z-100 keyboard processor
keycom  proc    near
        in      al,keystat              ; check status
        test    al,KPR                  ; keyboard ready?
        jnz     keycom                  ; keep trying if not ready
        mov     al,dl                   ; we need command here
        out     keycmd,al               ; do it
        ret
keycom  endp

;fixflg updates ax by seting the proper shift keycode flags in ah
;enter with true Z-100 ascii scan code in al
;returns with ax updated per keycode standards, except bit scan not set  
fixflg  proc    near
        xor     ah,ah                   ; we need it clean
        push    di                      ; search list z10keyl for this keycode
        push    cx                      ; save some registers   
        push    es
        mov     di,offset z10keyl       ; list of defined keycode bytes
        mov     cx,lz10keyl             ; number of bytes to examine
        push    ds
        pop     es                      ; make es:di point to data segment
        cld
        repne   scasb                   ; find keycode in list
        pop     es                      ; restore regs
        je      fixflg1                 ; e = found, work with present di
        pop     cx                      ; restore original cx
        pop     di                      ; restore original di
        ret                             ; char is already in al
fixflg1:neg     cx                      ; subtract cx from
        add     cx,lz10keyl             ; we went this far into the table
        and     cx,3                    ; we need these bits
        cmp     cx,1                    ; nothing?
        jz      fixflg4                 ; done
        cmp     cx,2                    ; shift?
        jnz     fixflg2                 ; no, go check control
        or      ax,shift                ; shift, set 200
        jmp     fixflg4                 ; done
fixflg2:cmp     cx,3                    ; control?
        jnz     fixflg3                 ; no, jump
        or      ax,control              ; yes, control, set 400
        jmp     fixflg4                 ; done
fixflg3:or      ax,shift                ; must be
        or      ax,control              ;  both of these
fixflg4:pop     cx
        pop     di
        ret
fixflg  endp

code    ends
        end
