	NAME	msugri
; File MSUGRI.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.
; Keyboard translator, by Joe R. Doupnik, Dec 1986
;  with contributions from David L. Knoell.
; edit history:
; 2 March 1991 version 3.10
; Last edit
; 2 March 1991
; Dec 7, 1990. Add verb hangup, accessed with code H.
; 23 Jan 1990. Fix up for GRiD. [jan]
; 14 Jan 1990
; 7 Oct 1989 Add support for DEC LK250 keyboards, from Terry Kennedy.
; 2 August 1989 Allow verbs in definition strings [jrd]
; 21 Nov 1988 Version 2.32
; 12 Nov 1988 Add verbs terminalr and terminals, unassigned to keys.
; 1 July 1988 Version 2.31
; 20 May 1988 Add parser confirm requests below dfky13. [jrd]
; 10 March 1988 Add Holdscreen verb, holdscrn, for Terry Kennedy. [jrd]
; 27 Feb 1988 Add capability of stdin being a file rather than device. [jrd]
; 1 Jan 1988 version 2.30

	public	keybd, dfkey, shkey, msuinit, kbcodes

; some definitions


termtogchar equ 339			; CODE- toggles terminal type [jan]
clschar equ	338			; CODE= blanks screen


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

data	segment public 'data'
	extrn taklev:byte, comand:byte, flags:byte
	extrn shkadr:word, stkadr:word, trans:byte, ttyact:byte
						; system dependent references
; [jan] extrn vtemu:byte, holdscr:byte		; emulator data [jrd]
	
;;;	System Independent local storage

tranbuf db	132 dup (?)		; 132 byte translator work buffer
crlf	db	cr,lf,'$'
dfhelp1 db    cr,lf,' Enter key',27h,'s identification as a character',cr,lf
	db	'  or as its numerical equivalent \{b##} of ascii',cr,lf
	db	'  or as its scan code \{b##}'
	db	cr,lf,'  or as SCAN followed by its scan code',cr,lf
	db	'    where b is O for octal, X for hex, or D for decimal'
	db	' (default).',cr,lf,'    Braces {} are optional.'
	db	cr,lf,'    Follow the identification with the new definition.'
	db	cr,lf,' or CLEAR to restore initial key settings'
	db	cr,lf,' or ON (def) for Bios i/o or OFF to use DOS i/o.$' ;IBM
dfaskky db	cr,lf,' Push key to be defined: $'
dfaskdf db	' Enter new definition: ',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	12d
dosflg	db	0
;;;	End System Independent Data Area

;;;	System Dependent Data Area
;	edit dfhelp2 to include nice list of verbs for this system.
dfhelp2 db	cr,lf,' Enter either  \Kverb  for a Kermit action verb',cr,lf
	db	' or a replacement string  (single byte binary numbers are'
	db	' \{b##})',cr,lf,' or push Return to undefine a key, ^C to'
	db	' retain current definition.'
	db	cr,lf,' Braces {} are optional, and strings maybe enclosed in'
	db	' them.',cr,lf,' Strings may not begin with the character'
	db	' combinations of  \k  or  \{k',cr,lf
	db	'    (start with a { brace instead).',cr,lf,lf
	db	' Verbs are as follows. ',cr,lf
	db  'Termtype :toggle graphics and alpha screens(CODE-) ',cr,lf
	db	' Clear-Screen- to clear screen (CODE=)',cr,lf
	db	' prtscn ,break,status,exit,null,dos,logoff,logon ',cr,lf
	db	cr,lf,'$'

	; Aliaskey: keys having aliases - same ascii code but more than one
	; scan code, as on auxillary keypads. Use just scan codes with these.
	; Alternative use: force ascii keys to report out just scan codes.
	; Table format: high byte = scan code, low byte = ascii code. 
	; Contents are machine dependent.
aliaskey dw	(14*scan)+bs		; Backspace key [hi=scan, lo=ascii]
			     ; took out a bunch [jan]
aliaslen equ	($-aliaskey) shr 1	; number of words in aliaskey table


kverbs	db	12			; number of table entries below
	mkeyw	'prtscn',trnprs		; independent of ordering and case!
	mkeyw	'break',sendbr		; mkeyw 'name',procedure entry point
	mkeyw	'help',cquery
	mkeyw	'status',cstatus
	mkeyw	'exit',cquit
	mkeyw	'null',snull
	mkeyw	'DOS',kdos
	mkeyw	'Logoff',klogof
	mkeyw	'Logon',klogon
	mkeyw	'Termtype',termtog
	mkeyw	'Clear-screen',kclrscn
	mkeyw	'Hangup',serhng



					; Initialization data.
kbdinlst equ	this byte     ; Kermit Grid initialization time keyboard setup
	mkeyw	'\127',331	  ; left arrow sends DEL
	mkeyw	'\Ktermtype',termtogchar  ;toggle terminal type
	mkeyw	'\Kclear-screen',clschar ;clear screen 
	mkeyw  '\Kexit',301	 ; code x to exit connect mode
	mkeyw  '\KHangup',291	 ; code H to hangup
	mkeyw  '\27D',331
	mkeyw	'\27C',333	 ; right arrow sends escape c
	mkeyw	'\27B',336	  ; down arrow
	mkeyw	'\27A',328	 ; up arrow
	dw	0		; end of table marker






got250	db	0			;** LK250 present if non-zero
					;** LK250 support end
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.
; 
;   Table Aliaskey is a list of scan code/ascii codes for keys which appear
; more than once on a keyboard. This list is examined to distinguish such
; aliased keys (those on an auxillary keypad) from an ordinary ascii key,
; and the aliased key is then referenced by its scan code rather than by
; the ordinary ascii code. Aliaskey is machine and keyboard dependent.
;
;    Procedure Keybd calls Getkey for the Keycode, checks list of translatable
; keys Keylist, and then either sends an ascii string (one or more characters)
; or invokes a Kermit action verb. List Dirlist indicates what kind of 
; translation to do. Keybd is system independent but may contain system
; dependent special actions such as echoing keyclicks. Keybd calls system
; dependent procedure Postkey just after calling getkey so local actions
; such as keyclicks can be activated only during Connect mode operations.
;
;    Keylist is a packed but unordered list of 16 bit keycodes which need
; translation. The lower order byte holds a key code (ascii char or scan code)
; while the high byte holds a scan code marker bit (0 if ascii code in low
; byte) plus any ancillary keyboard information such as Control/Shift/Alt/Meta
; keys being held down; these are of use in Show Key presentations.
;    Dirlist parallels Keylist to provide the kind of translation, verb or
; string, in the two highest bits with the other bits holding either
; a single new replacement character or the item number in lists of verbs
; or strings. If neither verb nor strng type bits are set in a dirlist
; word then the translation is a single new character held in the lower
; eight bits of that dirlist word.
;
;    The number of key translations is assembly constant Maxkeys (def 128).
;    The maximum number of strings is assembly constant Maxstngs (def 64).
;    The maximum number of verbs is 256 and is set by building table Kverbs.
;
;   For verbs, use the Item number from the Director table Dirlist to select
; a procedure offset from the structured list Kverbs and jump to that offset.
; Most verb procedures return carry clear to stay within Connect mode.
; Verbs requiring exiting Connect mode return carry set and may set byte
; Kbdflg to a char code which will be read by msster.asm for activating a
; transient Kermit action such as send a break (Kbdflg = 'b').
; Kbdflg is stored in msster.asm (as zero initially, meaning ignore it).
; Action verb procedures are normally located in a system dependent file.
;
;   For multi-char strings, use Item number from Director table Dirlist to
; select a pointer to a string. The list of string pointers is Sptable
; (string pointer table) which holds the offset in the data segment of the
; strings stored in buffer Stbuf. In stbuf strings are held as: one byte of
; length of following text and then the text itself (permits embedded nulls).
;  Use Chrout to send each string character, and finally return from Keybd
; with carry clear.
;
;   For single character replacements obtain the new character from the lower
; order byte of Director table Dirlist. If the character is Kermit's present
; escape character return from Keybd carry set to leave connect mode.
; Otherwise, send the character via Chrout and return from Keybd carry clear.

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

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

code	segment public 'code'
		; system independent external items
	extrn	comnd:near, prompt:near, iseof:near	; in msscmd
	extrn	strlen:near				; in mssfil
	extrn	cnvlin:near, katoi:near, decout:near	; in msster
		; system dependent external items
	extrn	beep:near 
		; these are system dependent action verbs, in msxgri
	extrn	chrout:near, cstatus:near, cquit:near, cquery:near
	extrn	trnprs:near,sendbr:near
	extrn	klogon:near, klogof:near
	 extrn	snull:near,kdos:near,termtog:near,kclrscn:near
	 extrn serhng: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	al,byte ptr [bx]	; cnt value
	xor	ah,ah
	add	ax,4			; skip text and '?' and value word
	add	bx,ax			; look at next slot
	loop	keyvb1			; walk to correct slot
	sub	bx,2			; backup to value field
	pop	cx			; restore reg
	mov	bx,[bx]			; get value field of this slot
	cmp	bx,0			; jump address defined?
	je	keybdx			; e = no, skip the action
	jmp	bx			; perform the function

keyst:	and	ax,not(verb+strng)	; STRING (ax=index, remove type bits)
	shl	ax,1			; convert to word index
	push	si			; save working reg
	mov	si,ax			; word subscript in table
	mov	si,sptable[si]		; memory offset of selected string
	xor	cx,cx			; init string length to null
	cmp	si,0			; is there a string pointer present?
	je	keyst1			; e = no, skip operation
	cld				; scan forward
	mov	cl,byte ptr [si]	; get string length byte
	inc	si
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
	cmp	stringcnt,0		; end of the string?
	jne	keyst3			; ne = no
	mov	ttyact,0		; pretend not single char output
keyst3: call	keysv			; scan for embedded verbs
	jc	keyst4			; c = not found, al has string char
	jmp	bx			; perform the verb (bx = address)
keyst4: call	xltkey			; do character set translation
	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
	jc	keysv7			; c = no such verb
	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
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	al,taklev		; reading from Take file
	mov	msutake,al		; save here
	or	ah,ah			; 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	ah,1			; number of characters received
	ja	dfkey1			; a = more than one, decode
	mov	ah,byte ptr tranbuf	; get the single char
	mov	byte ptr keycode,ah	; store as ascii keycode
	jmp	dfkey1b			; go get definition

dfkey13:mov	dosflg,0ffh		; set DOS keyboard read flag
	jmp	short dfkey14a
dfkey14:mov	dosflg,0		; clear DOS keyboard read flag
dfkey14a:mov	ah,cmeol		; get end of line confirmation
	call	comnd
	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
	mov	ax,0			; null, value to be stored
	mov	di,offset dirlist	; director table
	cld
	rep	stosw			; clear it
	mov	cx,maxkeys
	mov	di,offset keylist	; keycode table
	rep	stosw			; clear it
	mov	cx,maxstng
	mov	di,offset sptable	; string pointer table
	rep	stosw			; clear it
	pop	es			; recover register
	mov	strmax,offset stbuf	; clear string buffer, free space ptr
	mov	stbuf,0			; first element of buffer 
	mov	nkeys,0			; clear number of defined keys
	call	msuinit			; restore startup definitions
	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	ah,ah			; char count zero?
	jz	dfkey1e			; z = zero, prompt for definition
	jmp	dfkey1g			; process definition

dfkey1e:mov	ah,prstr
	mov	dx,offset crlf
	int	dos
	mov	dx,offset dfaskdf	; prompt for definition string
	call	prompt			; Kermit prompt routine
	mov	comand.cmcr,1		; permit bare carriage returns
	mov	comand.cmwhite,1	; allow leading whitespace
dfkey1f:mov	bx,offset tranbuf	; get new definition
	mov	word ptr [bx],0		; insert terminator
	mov	dx,offset dfhelp2	; help for definition of key
	mov	ah,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:xchg	ah,al			; put byte count in al
	xor	ah,ah			; clear high byte
	mov	kbtemp,ax		; and save count in kbtemp
	mov	ah,cmeol		; get a confirm
	call	comnd
	jc	dfkey1x			; none so declare parse error
	mov	cx,kbtemp		; string length
	
dfkey2:					; Examine translation
	mov	al,trans.escchr		; current escape char (port dependent)
	cmp	al,byte ptr keycode	; is this Kermit's escape char?
	jne	dfkey2a			; ne = no
	test	keycode,scan		; see if scan code
	jnz	dfkey2a			; nz = scan, so not ascii esc char
	mov	dx,offset 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
	cmp	al,0			; premature end?
	je	dfkey4b			; e = yes, accept without brace
	cmp	al,brace		; closing brace?
	je	dfkey4b			; e = yes
	cmp	al,spc			; white space or control char?
	ja	short dfkey4		; a = no, so not at end yet
dfkey4b:mov	byte ptr[si-1],0	; insert null terminator
	pop	si			; recover start address
	call	tstkeyw			; find keyword, kw # returned in kbtemp
	jc	dfkey4d			; c = no keyword found, complain
	call	remstr			; clear old string, if string
	mov	ax,kbtemp		; save keyword number
	and	ax,not(verb+strng)	; clear verb / string field
	or	ax,verb			; set verb ident
	mov	si,listptr
	mov	dirlist[si],ax		; store info in Director table
	jmp	dfkey7			; show results and return success

dfkey4d:mov	dx,offset verbbad	; say no such verb
	mov	ah,prstr
	int	dos
	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
	cmp	ah,0
	je	dfkey7a			; e = no
	cmp	flags.takflg,0		; echo Take commands?
	je	dfkey7b			; e = no
dfkey7a:mov	ah,prstr		; display cr/lf
	mov	dx,offset crlf
	int	dos
	call	shkey0			; show new definition (in SHKEY)
	call	shkfre			; show free string space
dfkey7b: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: call	getkey			; get any key
	jc	shky1a			; c = nothing at keyboard yet, wait
shky1b: pop	cx			; resume loop
	loop	shky1
	pop	si			; clean stack
	call	shkfre			; show free string space
	jmp	shkeyx			; exit

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

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

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

					; Display defintion
shkey2: mov	dx,offset shkmsg2	; intermediate part of reply
	mov	ah,prstr		; " is defined as "
	int	dos
	push	di			; get a director code for this key
	push	cx	
	mov	di,offset keylist	; list of keycodes
	mov	cx,nkeys		; number currently defined
	jcxz	shkey2a			; z = none
	mov	ax,keycode		; present keycode
	push	ds
	pop	es			; use 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
	mov	cx,0			; current slot number
shkey6b:cmp	cx,dx			; this slot?
	je	shkey6c			; e = yes, print the text part
	ja	shkeyx			; a = beyond, exit bad
	mov	al,byte ptr [bx]	; get cnt (keyword length)
	xor	ah,ah
	add	ax,4			; skip over '$' and two byte value
	add	bx,ax			; bx = start of next keyword slot
	inc	cx			; current keyword number
	jmp	short shkey6b		; try another
shkey6c:inc	bx			; look at text field
	mov	dx,bx			; offset for printing
	mov	ah,prstr
	int	dos
	mov	ah,conout
	mov	dl,spc			; add a couple of spaces
	int	dos
	int	dos
	mov	dl,'\'			; show verb name as \Kverb
	int	dos
	mov	dl,'K'
	int	dos
	mov	ah,prstr
	mov	dx,bx			; show name part again
	int	dos
	ret				; return to main show key loop

shkey8: mov	ah,prstr		; STRING
	mov	dx,offset strngmsg	; say String:
	int	dos
	mov	si,listptr		; get index from director
	mov	bx,dirlist[si]
	and	bx,not(verb+strng)	; remove type bits
	shl	bx,1			; index words
	mov	si,sptable[bx]		; table of string offsets
	mov	cl,byte ptr [si]	; get string length byte
	xor	ch,ch
	inc	si			; point to string text
	mov	ah,conout
shkey8a:cld
	lodsb				; get a byte
	cmp	al,spc			; control code?
	jae	shkey8b			; ae = no
	push	ax
	mov	dl,5eh			; show caret first
	int	dos
	pop	ax
	add	al,40h			; convert to printable for display
shkey8b:mov	dl,al
	int	dos			; display it
	loop	shkey8a			; do another
	ret				; return to main show key loop
	
shkeyx: pop	bx			; restore reg
	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:		db	cnt	(length of string 'word')
;				db	'word'	(keyword string)
;				db	'$'	(printing terminator)
;				dw	value	(value returned in bx)
; Make these with macro mkeyw such as	mkeyw 'test',15	  with the list of
; such keywords headed by a byte giving the number of keywords in the list.
tstkeyw proc	near
	push	ax
	push	cx
	push	si
	mov	verblen,0		; verblen will hold verb length
	push	si			; save user's verb pointer
tstkw1: cld
	lodsb				; get a verb character
	cmp	al,spc			; verbs are all non-spaces and above
	jbe	tstkw2			; be = done (space or control char)
	inc	verblen			; count verb length
	jmp	short tstkw1		; printable char, look for more
tstkw2: pop	si			; pointer to verb
	mov	bx,offset kverbs	; table of Kermit verb keywords
	mov	al,byte ptr [bx]	; number of keywords
	xor	ah,ah
	mov	kwcnt,ax		; save number of keywords here
	inc	bx			; point bx to first slot
	mov	kbtemp,0		; remember which keyword

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

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

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

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

shkfre	proc	near			; show free key & string defs & space
	push	ax			; preserves all registers.
	push	bx
	push	cx
	push	dx
	push	kbtemp
	mov	dx,offset fremsg
	mov	ah,prstr
	int	dos
	mov	ax,maxkeys		; max number of key defs
	sub	ax,nkeys		; number currently used
	call	decout			; show the value
	mov	ah,prstr
	mov	dx,offset kyfrdef	; give key defs msg
	int	dos
	mov	bx,offset sptable	; table of string pointers
	mov	cx,maxstng		; number of pointers
	mov	kbtemp,0		; number free
shkfr1: cmp	word ptr [bx],0		; slot empty?
	jne	shkfr2			; ne = no
	inc	kbtemp			; count free defs
shkfr2: add	bx,2			; look at next slot
	loop	shkfr1			; do all of them
	mov	ax,kbtemp		; number of free defs
	call	decout			; display
	mov	dx,offset stfrdef	; say free string defs
	mov	ah,prstr
	int	dos
	mov	ax,offset stbuf+stbuflen ; 1 + last byte in stbuf
	sub	ax,strmax		; offset of last free byte in stbuf
	call	decout
	mov	dx,offset stfrspc	; give free space part of msg
	mov	ah,prstr
	int	dos
	pop	kbtemp
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	ret
shkfre	endp

; Initialize the keyboard tables at Kermit startup time. Optional procedure.
; Requires kbdinlst to be configured with mkeyw macro in the form
;	mkeyw	'definition',keytype*256+keycode
; keytype is 0 for scan codes and non-zero for ascii.
; Returns normally.
kbdinit proc	near			; read keyword kbdinlst and setup
	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
;[jan]	call	chk250			;** LK250 support begin
;	cmp	got250,1		;** is it installed?
;	jne	kbdini0			;** ne = no
;	call	kbrest			;** else initialize to DEC mode
;	mov	si,offset kb250lst	;** load extensions
;	jmp	short kbdini1		;** LK250 support end
kbdini0:mov	si,offset kbdinlst	; start of list of definitions
kbdini1:mov	cl,byte ptr [si]	; cnt field (keyword length of macro)
	xor	ch,ch
	jcxz	kbdinix			; z = null cnt field = end of list
	inc	si			; look at text field
	mov	di,offset tranbuf	; where defkey expects text
	cld
	rep	movsb			; copy cx chars to tranbuf
	mov	byte ptr [di],0		; insert null terminator
	inc	si			; skip '$' field
	mov	ax,word ptr [si]	; get value field
	mov	keycode,ax		; set key ident value
	push	si
	call	dfkey2			; put dfkey to work
	pop	si
	add	si,2			; point to next entry
	jmp	kbdini1			; keep working
kbdinix:dec	taklev			; reset Take file level
; [jan] 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.
; Bit Scan is set if key is not an ascii code.
; Modifies register ax.
getkey	proc	near
	mov	keycode,0
	call	iseof			; is stdin at eof?
	jnc	getky5			; 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
getky5: mov	ah,dconio		; check console
	mov	dl,0ffh			; input desired
	int	dos
	jnz	getky1			; nz = char available
	stc				; carry set = nothing available
	jmp	short getkyx		; exit on no char available
getky1: cmp	al,0			; scan code being returned?
	jne	getky1a			; ne = no
	mov	ah,dconio		; read second byte (scan code)
	mov	dl,0ffh
	int	dos
	jz	getkyx			; z = nothing there
	mov	ah,al			; scan code goes here
	mov	al,0			; ascii code goes here
getky1a:
	push	di			; check key (ax) for aliases
	push	cx
	push	es
	mov	di,offset aliaskey	; list of aliased keys
	mov	cx,aliaslen		; number of entries
	jcxz	getky2			; z = no entries
	push	ds
	pop	es			; make es:di point to datas segment
	cld
	repne	scasw			; look for a match
	jne	getky2			; ne = not there
	mov	al,0			; force use of scan code (in ah)
getky2: pop	es
	pop	cx
	pop	di
	or	al,al			; scan code being returned?
	jnz	getky3			; nz = no
	xchg	ah,al			; put scan code in ident area
	or	keycode,scan		; set scan flag (vs ascii)
getky3: mov	byte ptr keycode,al	; return key's code (usually ascii)
	clc				; carry clear = got a char
getkyx: ret
getkey	endp



; Return modified char code, depending on SET TERM CHAR-SET and active
; Code Page. Enter and exit with char in AL.
xltkey	proc	near
	ret				; do nothing [jan]


xltkey	endp

; get shift state into al.  We care about only shift, ctl, and alt keys.
; right shift is collapsed into left shift. NumLock offsets Shift on keypad
; white keys.
gsh	proc	near
    mov	 al,0
    ret
gsh	endp


; Do any local processing after reading a key during active translation
; Avoids same actions if a key is being defined or shown.
postkey proc	near
	ret				; do nothing [jan]
postkey endp

;;;;;;;;; below stuff unused for HP 150

code	ends
	end
