NAME msurmx ; Apapted for RMX, by Jack Bryans, 9/1/87, from: ; Keyboard translator, by Joe R. Doupnik, Dec 1986 ; with contributions from David L. Knoell. ; For Generic keyboard reading (via DOS) ; edit history: ; Last edit 13 Oct 1987 $INCLUDE(MSSDEF.H86) public keybd, msuinit ; some definitions maxkeys equ 128 ; maximum number of key definitions maxstng equ 64 ; maximum number of multi-char strings stbuflen equ 1000 ; length of string buffer (bytes) verb equ 8000h ; dirlist flag: use verb action table strng equ 4000h ; dirlist flag: use string action table scan equ 100h ; keycode flag: code is scan not ascii braceop equ 7bh ; opening curly brace bracecl equ 7dh ; closing curly brace datas segment extrn taklev:byte, comand:byte, intake:byte, flags:byte extrn shkadr:word, stkadr:word, trans:byte ; system dependent references extrn ciptr:word, cifill:byte, cibuf1:word, cibuf2:word ;;; 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.$' dfaskky db cr,lf,' Push key to be defined: $' dfaskdf db ' Enter new definition: $' verbbad db cr,lf,' No such verb',cr,lf,'$' strbad db cr,lf,' Not enough space for new string',cr,lf,'$' keyfull db cr,lf,' No more space to define keys',cr,lf,'$' dfkopps db cr,lf,' Opps! That is Kermit',27h,'s Escape Char.' db ' Translation is not permitted.',cr,lf,'$' shkmsg1 db cr,lf,'Push key to be shown (? shows all): $' shkmsg2 db ' decimal is defined as',cr,lf,'$' shkmsg3 db cr,lf,'... more, push any key to continue ...$' kwarnmsg db cr,lf,' Notice: this form of Set Key is obsolete$' ascmsg db ' Ascii char: $' scanmsg db ' Scan Code $' strngmsg db ' String: $' verbmsg db ' Verb: $' noxmsg db ' Self, no translation.$' fremsg db cr,lf,' Free space: $' kyfrdef db ' key and $' stfrdef db ' string definitions, $' stfrspc db ' string characters.',cr,lf,'$' ; translation tables keylist dw maxkeys dup (0) ; 16 bit keycodes, paralled by dirlist dirlist dw maxkeys dup (0) ; director {v+s} + {index | new char} sptable dw maxstng dup (0) ; list of asciiz string offsets stbuf dw stbuflen dup (0) ; buffer for strings strmax dw stbuf ; first free byte in stbuf listptr dw ? ; item number for keylist and dirlist nkeys dw 0 ; number of actively defined keys keycode dw ? ; ascii/scan code for key kbtemp dw ? ; scratch storage for translator brace db ? ; brace detected flag byte oldform db 0 ; old form Set Key, if non-zero verblen dw ? ; length of user's verb (work temp) kwcnt dw ? ; number of keywords (work temp) msutake db ? ; if being run from take file or not twelve dw 12d ;;; 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 nothing at all to undefine a key.' 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 ' Logoff (suspend logging), Logon (resume logging),' db ' Hangup, null (send a)',cr,lf db ' help, status, exit',cr,lf,'$' kverbs db 7 ; number of table entries below %mkeyw (%('help'),cquery) ; independent of ordering and case! %mkeyw (%('status'),cstatus) ; mkeyw 'name',procedure entry point %mkeyw (%('exit'),cquit) %mkeyw (%('null'),snull) %mkeyw (%('Logoff'),klogof) %mkeyw (%('Logon'),klogon) %mkeyw (%('Hangup'),chang) ; Initialization data. kbdinlst equ this byte ; Kermit IBM initialization time keyboard setup dw 0 ; end of table marker datas ends ; Documentation ;Translating a key: ; The translator is called to obtain keyboard input; it sends characters to ; the serial port through standard controlled echo procedures or invokes ; named procedures. It returns carry clear when its operation is completed ; for normal actions and carry set when Connect mode must be exited. When ; Connect mode is exited the just read char should be passed in Kbdflg ; to msster.asm for invoking actions such as Status, send a break, ; quit connect mode; system dependent procedure Term is responsible for this. ; ; Principal procedures are - ; msuinit Initializes keyboard translator in this file when ; Kermit first begins. Installs dfkey and shkey as the ; procedures used for Set Key and Show Key. Sys Indep. ; Called from msx or msy init procs. System Independent. ; keybd Performs the translation, outputs chars to the serial ; port or invokes a Kermit action routine. Sys Indep. ; dfkey Defines a key's translation. Reads command line ; via Kermit's command parser comnd. System Independent. ; shkey Shows translation of a key. Requests user to push ; selected key. System Independent. ; ; kbdinit optional. Initializes the translation tables when ; Kermit starts up. Called by msuinit. System Dependent. ; getkey Performs the keyboard read and returns results in ; a standardized system independent format. Sys Depend. ; 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. ; 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. ; ; 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 ; ; 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. ; ; is one or more spaces and or tabs. ; ; 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 mynamemypass ; ; 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 ; 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 ; in msscmd extrn strlen:near ; in mssfil extrn cnvlin:near, katoi:near, decout:near ; in msster ; system dependent external items extrn dosint:near ; these are system dependent action verbs, in msxrmx extrn beep:near, chang:near, chrout:near, cstatus:near, cquit:near extrn cquery:near, klogon:near, klogof:near, snull:near ; 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 ; 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 ; active translator call getkey ; read keyboard jnc keybd1 ; nc = data available clc ; else just return carry clear ret keybd1: cmp nkeys,0 ; is number of keys defined = 0? jz keybd3 ; z = none defined push di ; search keylist for this keycode push cx ; save some registers push es mov di,offset keylist ; list of defined keycode words mov ax,keycode ; present keycode mov cx,nkeys ; number of words to examine push ds pop es ; make es:di point to datas segment cld repne scasw ; find keycode in list pop es ; restore regs pop cx je keybd1b ; e = found, work with present di pop di ; restore original di test keycode,scan ; is this a scan code? jz keybd3 ; z = no, it's ascii, use al as char call beep ; say key is a dead one clc ret ; and exit with no action keybd1b:sub di,2 ; correct for auto increment sub di,offset keylist ; subtract start of list ==> listptr mov ax,dirlist[di] ; ax = contents of director word pop di ; restore original di ; dispatch on Director code test ax,verb ; verb only? jnz keyvb ; e = yes test ax,strng ; multi-char string only? jnz keyst ; e = yes, else single char & no xlat. ; ; do single CHAR output (char in al) keybd3: cmp al,trans.escchr ; Kermit's escape char? je keybd3a ; e = yes, handle separately call chrout ; transmit the char clc ; return success ret keybd3a:stc ; set carry for jump to Quit ret keyvb: and ax,not(verb+strng) ; VERB (ax=index, remove type bits) mov bx,offset kverbs ; start of verb table cmp al,byte ptr [bx] ; index > number of entries? jae keybdx ; ae = illegal, indices start at 0 inc bx ; bx points to first entry push cx ; save reg mov cx,ax ; save the index in cx inc cx ; counter, indices start at 0 keyvb1: mov al,byte ptr [bx] ; cnt value xor ah,ah add ax,4 ; skip text and '?' and value word add bx,ax ; look at next slot loop keyvb1 ; walk to correct slot sub bx,2 ; backup to value field pop cx ; restore reg mov bx,[bx] ; get value field of this slot cmp bx,0 ; jump address defined? je keybdx ; e = no, skip the action jmp bx ; perform the function keyst: and ax,not(verb+strng) ; STRING (ax=index, remove type bits) shl ax,1 ; convert to word index push si ; save working reg mov si,ax ; word subscript in table mov si,sptable[si] ; memory offset of selected string cmp si,0 ; is there a string pointer present? je keyst3 ; e = no, skip operation cld ; scan forward lodsb ; get string length byte mov cl,al xor ch,ch ; to cx for looping jcxz keybdx ; z = null length keyst2: lodsb ; get new string char into al push si ; save si and cx around call push cx call chrout ; send out the char in al pop cx ; recover regs pop si loop keyst2 ; loop through whole string keyst3: pop si ; restore reg keybdx: clc ; return success ret KEYBD ENDP ; SET KEY - define a key (procedure dfkey) ; SET KEY ; Call from Kermit level. Returns as ret if failure or as rskp if success. ; DFKEY PROC ; define a key as a verb or a string mov keycode,0 ; clear keycode mov oldform,0 ; say no old form Set Key yet mov dx,offset tranbuf ; our work space mov word ptr tranbuf,0 ; insert terminator mov bx,offset dfhelp1 ; first help message mov ah,cmfile ; parse a word call comnd ; get key code or original ascii char nop nop nop mov al,intake ; reading from Take file indirectly or al,taklev ; ditto, directly mov msutake,al ; save here or ah,ah ; any text given? jnz dfkey12 ; nz = yes, so don't consider prompts ; interactive key request cmp intake,0 ; in a Take file? je dfkey10 ; e = no, prompt for keystroke jmp dfkey0 ; else say bad syntax dfkey10:mov ah,prstr mov dx,offset dfaskky ; ask for key to be pressed call dosint 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 call dosint call shkey0 ; show current definition (in SHKEY) jmp dfkey1e ; prompt for and process definition dfkey12: ; Look for word SCAN and ignore it mov dx,word ptr tranbuf ; get first two characters or dx,2020h ; map upper to lower case cmp dx,'cs' ; first two letters of word "scan"? je dfkey ; e = yes, skip the word cmp dx,'lc' ; first two letters of word "clear"? je dfkeyc ; e = yes, reinit keyboard cmp 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 dfkey0: mov dx,offset dfhelp1 ; say bad definition command mov ah,prstr call dosint jmp rskp dfkeyc: ; CLEAR key defs, restore startup defs mov cx,maxkeys ; size of keycode tables push es ; save register push ds pop es ; make es point to datas segment mov ax,0 ; null, value to be stored mov di,offset dirlist ; director table cld rep stosw ; clear it mov cx,maxkeys mov di,offset keylist ; keycode table rep stosw ; clear it mov cx,maxstng mov di,offset sptable ; string pointer table rep stosw ; clear it pop es ; recover register mov strmax,offset stbuf ; clear string buffer mov stbuf,0 ; mov nkeys,0 ; clear number of defined keys call msuinit ; restore startup definitions jmp rskp ; Multi-char key identification dfkey1: mov si,offset tranbuf ; point to key ident text cmp byte ptr [si],'0' ; is first character numeric? jb dfkey1a ; b = no cmp byte ptr [si],'9' ; in numbers? ja dfkey1a ; a = no mov keycode,scan ; setup keycode for scan value mov dx,si ; get length of string in cx call strlen push ds pop es ; make es point to datas segment push si add si,cx ; point at string terminator mov di,si inc di ; place to store string (1 byte later) inc cx ; include null terminator std ; work backward rep movsb ; move string one place later cld pop si mov byte ptr [si],'\' ; make ascii digits into \nnn form mov oldform,0ffh ; set old form flag mov dx,offset kwarnmsg ; tell user this is old form mov ah,prstr call dosint dfkey1a:call katoi ; convert ascii number to binary in ax jc dfkey0 ; c = no number converted or keycode,ax ; store in keycode dfkey1b: ; Get Definition proper test oldform,0ffh ; old form Set Key active? jz dfkey1f ; z = no mov bx,offset tranbuf ; get new definition on main cmd line mov word ptr [bx],0 ; insert terminator mov dx,offset dfhelp2 ; help for definition of key mov ah,cmtxt ; read rest of line into tranbuf call comnd nop ; allow null definitions nop nop or ah,ah ; char count zero? jz dfkey1e ; z = zero, prompt for definition jmp dfkey1g ; process definition dfkey1e:mov ah,prstr mov dx,offset crlf call dosint mov dx,offset dfaskdf ; prompt for definition string call prompt ; Kermit prompt routine mov comand.cmcr,1 ; permit bare carriage returns dfkey1f:mov bx,offset tranbuf ; get new definition mov word ptr [bx],0 ; insert terminator mov dx,offset dfhelp2 ; help for definition of key mov ah,cmtxt ; read rest of line into tranbuf call comnd ret ; exit now on ^C from user nop nop cmp comand.cmcr,0 ; prompting for definition? je dfkey1g ; e = no, trim leading whitespace mov comand.cmcr,0 ; turn off allowance for bare c/r's jmp dfkey2 ; interactive, allow leading whitespace dfkey1g:xchg ah,al ; put byte count in al xor ah,ah ; clear high byte mov kbtemp,ax ; and save count in kbtemp mov ah,cmcfm ; get a confirm call comnd ret ; none so declare parse error nop nop ; round out to three bytes mov cx,kbtemp ; string length jcxz dfkey2 ; z = empty string push si push di mov si,offset tranbuf ; strip leading white space dfkey1c:cld ; work forward lodsb ; read string char into al dec cx ; number of chars left to read cmp al,' ' ; a space? je dfkey1c ; e = yes, keep going cmp al,tab ; tab? je dfkey1c ; e = yes, keep going dec si ; offset inc si in lodsb add cx,2 ; include terminator, offset dec above jcxz dfkey1d ; z = nothing to move mov di,offset tranbuf ; destination is start of buffer push ds pop es cld rep movsb ; copy text part of string dfkey1d:pop di pop si dfkey2: ; Examine translation mov al,trans.escchr ; current escape char (port dependent) cmp al,byte ptr keycode ; is this Kermit's escape char? jne dfkey2a ; ne = no test keycode,scan ; see if scan code jnz dfkey2a ; nz = scan, so not ascii esc char mov dx,offset dfkopps ; Opps! msg mov ah,prstr ; complain and don't redefine call dosint jmp rskp dfkey2a:push di ; get a director code for this key push cx mov di,offset keylist ; list of keycodes mov cx,nkeys ; number currently defined mov ax,keycode ; present keycode jcxz dfkey2b ; cx = 0 means none defined yet cld push ds pop es repne scasw ; is current keycode in the list? jne dfkey2b ; ne = not in list sub di,2 ; correct for auto increment sub di,offset keylist mov listptr,di ; list pointer for existing definition pop cx pop di jmp dfkey3 ; go process definition dfkey2b:pop cx ; key not currently defined so pop di ; make a new director entry for it mov bx,nkeys ; number of keys previously defined cmp bx,maxkeys ; enough space? jae dfkey2c ; ae = no, complain shl bx,1 ; count words mov listptr,bx ; index into word list mov ax,keycode ; get key's code mov keylist[bx],ax ; store it in list of keycodes mov dirlist[bx],0 ; clear the new director entry inc nkeys ; new number of keys jmp dfkey3 ; go process definition dfkey2c:mov dx,offset keyfull ; say key space is full already mov ah,prstr call dosint jmp rskp ; tell parser we are happy ; listptr has element number in keylist or dirlist; keycode has key's code. ; Parse new definition. First look for Kermit verbs as a line beginning ; as \K or \{K. Otherwise, consider the line to be a string. ; In any case, update the Director table for the new definition. dfkey3: mov brace,0 ; assume not using braces mov si,offset tranbuf ; start of definition text cmp byte ptr [si],'\' ; starts with escape char? jne dfkey5 ; ne = no, so we have a string inc si ; skip the backslash cmp byte ptr [si],braceop ; starts with \{? jne dfkey3a ; ne = no inc si ; skip the opening brace mov brace,bracecl ; expect closing brace dfkey3a:cmp byte ptr [si],'K' ; starts with \{K or \K? je dfkey3b ; e = yes cmp byte ptr [si],'k' ; starts as \{k or \k? jne dfkey5 ; ne = no, then it's a string dfkey3b:inc si ; yes, skip the K too ; Kermit action VERBS push si ; save verb name start address dfkey4a: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 dfkey4a ; 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 call dosint jmp rskp ; Here we are left with the definition string; si points to its start, and ; kbtemp holds its length (number of bytes). Null termination. If the string ; begins with an opening brace it terminates on a matching closing brace ; or the end of line, whichever occurs first. Trailing whitespace removed ; before examining braces. ; Null length strings mean define key as Self. ; STRING definitions dfkey5: call remstr ; first, clear old string, if any mov si,offset tranbuf ; si=source, di=dest, convert in-place mov di,si call cnvlin ; convert numbers, cx gets line length mov si,offset tranbuf ; provide address of new string cmp cx,1 ; just zero or one byte to do? jbe dfkey6 ; e = yes, do as a char call insertst ; insert new string, returns reg cx. jc dfkey5h ; c = could not do insert mov si,listptr ; cx has type and string number mov dirlist[si],cx ; update Director table from insertst jmp dfkey7 ; show results and return success dfkey5h:mov dx,offset strbad ; display complaint mov ah,prstr call dosint jmp rskp ; define SINGLE CHAR replacement or CLEAR a key definition. ; cx has char count 1 (normal) or 0 (to undefine the key). dfkey6: jcxz dfkey6c ; z = cx= 0, clear definition mov al,byte ptr [si] ; get first byte from definition xor ah,ah ; set the type bits to Char mov si,listptr mov dirlist[si],ax ; store type and key's new code jmp dfkey7 ; return success dfkey6c:push si ; clear a definition, push di ; listptr points to current def mov si,listptr ; starting address to clear add si,offset dirlist mov di,si ; destination add si,2 ; source is next word mov cx,nkeys ; current number of keys defined add cx,cx ; double for listptr being words sub cx,listptr ; cx = number of words to move shr cx,1 ; convert to actual number of moves jcxz dfkey6d ; z = none, just remove last word push es push ds pop es ; make es:di point to datas segment cld push cx ; save cx rep movsw ; move down higher list items pop cx mov si,listptr ; do keylist too, same way add si,offset keylist mov di,si add si,2 rep movsw pop es dfkey6d:mov si,nkeys ; clear old highest list element shl si,1 ; address words mov dirlist[si],0 ; null the element mov keylist[si],0 ; null the element dec nkeys ; say one less key defined now pop di ; restore saved registers pop si dfkey7: mov ah,msutake ; Finish up. In a Take file? or ah,taklev ; or even directly cmp ah,0 je dfkey7a ; e = no cmp flags.takflg,0 ; echo Take commands? je dfkey7b ; e = no dfkey7a:mov ah,prstr ; display cr/lf mov dx,offset crlf call dosint call shkey0 ; show new definition (in SHKEY) call shkfre ; show free string space dfkey7b:jmp rskp ; return success DFKEY ENDP ; SHOW KEY 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 ; Show key's definition command mov ah,cmcfm ; get a confirm call comnd nop ; ignore any additional text nop nop push bx mov dx,offset shkmsg1 ; ask for original key mov ah,prstr call dosint 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 call dosint 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 call dosint 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 call dosint 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 call dosint mov ah,conout mov dl,'\' ; add backslash before number call dosint 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 call dosint 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 call dosint pop dx add dl,'A'-1 ; ascii bias shkey1a:cmp dl,del ; DEL? jne shkey1b ; ne = no mov dl,'D' ; spell out DEL call dosint mov dl,'E' call dosint mov dl,'L' shkey1b:call dosint mov dl,spc ; add a couple of spaces call dosint call dosint mov dl,'\' ; add backslash before number call dosint 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 " call dosint push di ; get a director code for this key push cx mov di,offset keylist ; list of keycodes mov cx,nkeys ; number currently defined jcxz shkey2a ; z = none mov ax,keycode ; present keycode push ds pop es ; use datas segment for es:di cld repne scasw ; is current keycode in the list? jne shkey2a ; ne = not in list sub di,2 ; correct for auto increment sub di,offset keylist mov listptr,di ; list pointer for existing definition pop cx pop di jmp shkey3 ; go process definition shkey2a:pop cx pop di mov dx,offset noxmsg ; say Self (no translation) mov ah,prstr call dosint 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 call dosint 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 call dosint pop dx add dl,'A'-1 ; add ascii bias shkey4: cmp dl,del ; DEL? jne shkey4a ; ne = no mov dl,'D' ; spell out DEL call dosint mov dl,'E' call dosint mov dl,'L' shkey4a:call dosint mov dl,spc ; add a couple of spaces mov ah,conout call dosint call dosint mov dl,'\' ; add backslash before number call dosint 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' call dosint 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 call dosint mov ah,conout mov dl,spc ; add a couple of spaces call dosint call dosint mov dl,'\' ; show verb name as \Kverb call dosint mov dl,'K' call dosint mov ah,prstr mov dx,bx ; show name part again call dosint ret ; return to main show key loop shkey8: mov ah,prstr ; STRING mov dx,offset strngmsg ; say String: call dosint 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 call dosint pop ax add al,40h ; convert to printable for display shkey8b:mov dl,al call dosint ; display it loop shkey8a ; do another ret ; return to main show key loop shkeyx: pop bx ; restore reg jmp rskp ; return success SHKEY ENDP ;;; keyboard translator local support procedures, system independent ; Tstkeyw checks text word pointed to by si against table of keywords (pointed ; to by kverbs, made by mkeyw macro); returns in bx either action value or 0. ; Returns in kbtemp the number of the keyword and carry clear, or if failure ; returns kbtemp zero and carry set. ; Keyword structure is: db cnt (length of string 'word') ; db 'word' (keyword string) ; db '$' (printing terminator) ; dw value (value returned in bx) ; Make these with macro mkeyw such as mkeyw 'test',15 with the list of ; such keywords headed by a byte giving the number of keywords in the list. tstkeyw proc 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 push bx push dx push si push di push kbtemp ; save this variable too mov dx,cx ; save length of incoming string in dx mov bx,offset sptable ; table of string offsets mov kbtemp,0 ; slot number mov cx,maxstng ; number of entries, find an empty slot insert1:cmp word ptr[bx],0 ; slot empty? je insert2 ; e = yes inc kbtemp ; remember slot number add bx,2 ; look at next slot loop insert1 ; keep looking jmp short insert4 ; get here if no empty slots insert2: ; see if stbuf has sufficient space mov cx,dx ; length of new string to cx mov di,strmax ; offset of first free byte in stbuf add di,cx ; di = address where this string would end cmp di,offset stbuf+stbuflen ; beyond end of buffer? jae insert4 ; ae = yes, not enough room mov di,strmax ; point to first free slot in stbuf mov [bx],di ; fill slot with address offset of buffer push es push ds pop es ; point es:di to datas segment cld mov byte ptr [di],cl ; length of text for new string inc di ; move to next storage slot rep movsb ; copy string text pop es mov strmax,di ; offset of next free byte mov cx,kbtemp ; return new slot number with Director Index and cx,not(strng+verb) ; clear type bits or cx,strng ; say type is multi-char string clc ; say success jmp short insertx ; exit insert4:stc ; say no-can-do insertx:pop kbtemp pop di pop si pop dx pop bx ret insertst endp ; Remove (delete) string. Enter with listptr preset for director entry. ; Acts only on existing multi-char strings; recovers freed space. ; All registers preserved. remstr proc 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 ; 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 call dosint 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 call dosint 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 call dosint 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 call dosint 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 ; read keyword kbdinlst and setup push ds ; initial keyboard assignments. pop es ; set es:di to datas segment mov taklev,1 ; pretend that we are in Take file mov si,offset kbdinlst ; start of list of definitions kbdini1:mov cl,byte ptr [si] ; cnt field (keyword length of macro) xor ch,ch jcxz kbdinix ; z = null cnt field = end of list inc si ; look at text field mov di,offset tranbuf ; where defkey expects text cld rep movsb ; copy cx chars to tranbuf mov byte ptr [di],0 ; insert null terminator inc si ; skip '$' field mov ax,word ptr [si] ; get value field mov keycode,ax ; set key ident value push si call dfkey2 ; put dfkey to work nop nop nop pop si add si,2 ; point to next entry jmp kbdini1 ; keep working kbdinix:mov taklev,0 ; reset Take file level ret kbdinit endp ;;; End of System Independent Procedures ;;; Begin System Dependent Procedures ; Read keyboard. System dependent. ; Return carry set if nothing at keyboard. ; If char present return carry clear with key's code in Keycode. ; If key is ascii put that in the low byte of Keycode and clear bit Scan in ; the high byte; otherwise, put the scan code in the lower byte and set bit ; Scan in the high byte. ; Bit Scan is set if key is not an ascii code. ; Modifies register ax. getkey proc mov si,ciptr ; read 1 from keyboard lodsb cmp al,cifill jne GETKY1 ; ne = got one stc ; no char, ret w/carry set ret GETKY1: cmp si,offset cibuf2 + cibufl jne GETKY2 mov si,offset cibuf1 GETKY2: mov ciptr,si xor ah,ah cmp al,80h ; if > 7fh, jb GETKY3 or ax,scan ; set scan flag GETKY3: mov keycode,ax ; ret w/carry clear = keycode's got one clc ret getkey endp ; Jumping to this location is like retskp. It assumes the instruction ; after the call is a jmp addr. RSKP PROC pop bp add bp,3 jmp bp RSKP ENDP code ends end