NAME msurb ; File MSURB1.ASM for the DEC Rainbow 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 3 Feb 1991 ; 21-SEP-90 [rhw-1] Fixed bug when using SET KEY, prompt string DFASKDF had ; '$' terminator but needed NULL termination, ie. bug was ; it displayed lots of junk after the prompt string. ; Robert H. Weiner (robert%progplus.uucp@uunet.uu.net) ; 11 Nov 1989 ; 1 July 1988 Version 2.31 ; 1 Jan 1987 version 2.30 public keybd, dfkey, shkey, msuinit ; some definitions ; Rainbow level 1 console definitions fnkey equ 100H ; function key flag shfkey equ 200H ; shift key ctlkey equ 400H ; control key cplk equ 800H ; caps lock key firmwr equ 18H ; Bios interrupt maxkeys equ 200 ; maximum number of key definitions maxstng equ 100 ; maximum number of multi-char strings stbuflen equ 1000 ; length of string buffer (bytes) verb equ 8000h ; dirlist flag: use verb action table strng equ 4000h ; dirlist flag: use string action table scan equ 100h ; keycode flag: code is scan not ascii braceop equ 7bh ; opening curly brace bracecl equ 7dh ; closing curly brace data segment public 'data' extrn taklev:byte, comand:byte, flags:byte extrn shkadr:word, stkadr:word, trans:byte, kbdflg:byte, ttyact:byte ; system dependent references ;;; 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 cr,lf,' Enter new definition: ',0 ; [rhw-1] made '$'->0 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, 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 ? ; 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 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 ' uparr, dnarr, lfarr, rtarr, kpminus, kpcoma, kpdot, kpenter,' db cr,lf db ' gold (same as pf1), pf1, pf2, pf3, pf4, kp0, ... kp9,' db cr,lf db ' upscn, dnscn, homscn, endscn, upone, dnone' db cr,lf db ' toggle_prn, dump, prtscr, logon, logof, break, lbreak' db cr,lf db ' hangup, null, DOS, help, status, exit' db cr,lf,'$' ; Aliaskey: keys having aliases - same ascii code but more than one ; scan code, as on auxillary keypads. Use just scan codes with these. ; Alternative use: force ascii keys to report out just scan codes. ; Table format: high byte = scan code, low byte = ascii code. ; Contents are machine dependent. aliaskey dw (55*scan)+'*' ; keypad asterisk [hi=scan, lo=ascii] aliaslen equ ($-aliaskey) shr 1 ; number of words in aliaskey table kverbs db 42 ; number of table entries below mkeyw 'uparr',uparrw ; max of 100 entries. mkeyw 'dnarr',dnarrw ; independent of ordering and case! mkeyw 'lfarr',lfarr ; mkeyw 'name',procedure entry point mkeyw 'rtarr',rtarr mkeyw 'gold',pf1 mkeyw 'pf1',pf1 mkeyw 'pf2',pf2 mkeyw 'pf3',pf3 mkeyw 'pf4',pf4 mkeyw 'kp0',kp0 mkeyw 'kp1',kp1 mkeyw 'kp2',kp2 mkeyw 'kp3',kp3 mkeyw 'kp4',kp4 mkeyw 'kp5',kp5 mkeyw 'kp6',kp6 mkeyw 'kp7',kp7 mkeyw 'kp8',kp8 mkeyw 'kp9',kp9 mkeyw 'kpminus',kpminus mkeyw 'kpcoma',kpcoma mkeyw 'kpenter',kpenter mkeyw 'kpdot',kpdot mkeyw 'dnscn',prvscr mkeyw 'upscn',nxtscr mkeyw 'endscn',nxtbot mkeyw 'homscn',nxttop mkeyw 'upone',nxtlin mkeyw 'dnone',prvlin mkeyw 'toggle_prn',trnprs mkeyw 'prtscr',prtscn mkeyw 'dump',dumpscr ;; mkeyw 'modeline',trnmod mkeyw 'break',sendbr mkeyw 'lbreak',sendbl mkeyw 'logon',klogon mkeyw 'logoff',klogof mkeyw 'hangup',chang mkeyw 'null',snull mkeyw 'DOS',cdos mkeyw 'help',cquery mkeyw 'status',cstatus mkeyw 'exit',cquit ; Initialization data. kbdinlst equ this byte ; Kermit initialization time keyboard setup mkeyw '\kgold',scan+89 ; mkeyw 'definition',keytype*256+keycode mkeyw '\kpf1',scan+89 mkeyw '\kpf2',scan+92 mkeyw '\kpf3',scan+95 mkeyw '\kpf4',scan+98 mkeyw '\kkp0',scan+47 mkeyw '\kkp1',scan+50 mkeyw '\kkp2',scan+53 mkeyw '\kkp3',scan+56 mkeyw '\kkp4',scan+59 mkeyw '\kkp5',scan+62 mkeyw '\kkp6',scan+65 mkeyw '\kkp7',scan+68 mkeyw '\kkp8',scan+71 mkeyw '\kkp9',scan+74 mkeyw '\kkpenter',scan+86 mkeyw '\kkpcoma',scan+80 mkeyw '\kkpminus',scan+77 mkeyw '\kkpdot',scan+83 mkeyw '\kuparr',scan+39 ; VT100 cursor control keys mkeyw '\kdnarr',scan+41 mkeyw '\klfarr',scan+45 mkeyw '\krtarr',scan+43 mkeyw '\kupscn',scan+37 ; Nxtkey Kermit screen roll back keys mkeyw '\kdnscn',scan+35 ; Prvkey mkeyw '\khomscn',scan+shfkey+35 ; Home Shift Prvkey mkeyw '\kendscn',scan+shfkey+37 ; End Shift Nxtkey mkeyw '\kupone',scan+ctlkey+37 ; one line scrolls Ctrl Nxtkey mkeyw '\kdnone',scan+ctlkey+35 ; Ctrl Prvkey mkeyw '\kdos',scan+13 ; DOS is Main Screen key mkeyw '\kprtscr',scan+3 ; Print screen, Print Screen key mkeyw '\ktoggle_prn',scan+ctlkey+3 ; Kermit toggle prn scn,^Prt Scn mkeyw '\kdump',scan+1 ; Kermit Dump Screen DO key mkeyw '\kbreak',scan+101 ; Send a Break, Break key mkeyw '\klbreak',scan+shfkey+101 ; Send Long Break, Shift Break key mkeyw '\khelp',scan+0 ; Help on HELP key mkeyw '\kexit',scan+15 ; Exit Kermit with EXIT key dw 0 ; end of table marker 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 ; for ordinary keys, or for special keys such as F1, ; or 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 ; asciiz strings stored tip-to-tail in buffer Stbuf. 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 public 'code' ; system independent external items extrn comnd:near, prompt:near ; in msscmd extrn isfile:near, strlen:near, strcpy:near, prtasz:near ; in mssfil extrn cnvlin:near, katoi:near, decout:near ; in mssset ; system dependent external items extrn beep:near, prtchr:near ; in msxrb ; these are system dependent action verbs, in msxrb extrn uparrw:near, dnarrw:near, rtarr:near, lfarr:near extrn pf1:near, pf2:near, pf3:near, pf4:near, kp0:near, kp1:near extrn kp2:near, kp3:near, kp4:near, kp5:near, kp6:near, kp7:near extrn kp8:near, kp9:near, kpminus:near, kpcoma:near, kpenter:near extrn kpdot:near extrn chrout:near, cstatus:near, cquit:near, cquery:near extrn prvlin:near, nxtlin:near, nxtbot:near extrn nxttop:near, prvscr:near, nxtscr:near, trnprs:near extrn sendbr:near, sendbl:near, snull:near, dumpscr:near extrn chang:near, klogon:near, klogof:near, cdos:near extrn prtscn:near ;;; trnmod:near assume cs:code, ds:data, es:nothing ; 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 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: jmp chrout ; send out the char in al keybdx: clc ; return success 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 ; Call from Kermit level. Returns as ret if failure or as rskp if success. ; DFKEY PROC NEAR ; define a key as a verb or a string mov keycode,0 ; clear keycode mov oldform,0 ; say no old form Set Key yet mov dx,offset tranbuf ; our work space mov word ptr tranbuf,0 ; insert terminator mov bx,offset dfhelp1 ; first help message mov ah,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 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 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 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 pop si add si,2 ; point to next entry jmp kbdini1 ; keep working kbdinix:dec taklev ; reset Take file level ret kbdinit endp ;;; End of System Independent Procedures ;;; Begin System Dependent Procedures ; Read keyboard. System dependent. ; Return carry set if nothing at keyboard. ; If char present return carry clear with key's code in Keycode. ; If key is ascii put that in the low byte of Keycode and clear bit Scan in ; the high byte; otherwise, put the scan code in the lower byte and set bit ; Scan in the high byte. ; Bit Scan is set if key is not an ascii code. ; Modifies register ax. ; Read keyboard. System dependent. ; Return carry set if nothing at keyboard. ; If char present return carry clear, key's code in Keycode, and key's ; type in Keytype (0 for scan codes, non-zero for ascii) in Keycode. ; Modifies register ax. getkey proc near mov kbdflg,0 ; char passed to msster.asm, clear it push bx ; firmwr corrupts all but cs,ds,ss,sp push cx push dx push si push di getky1: mov di,6 ; get level 1 (Bios) character push es int firmwr pop es cmp cl,0ffh ; level 1 character available? je getky2 ; e = yes cmp cl,1 ; is level 2 sequence is in progress? jne getkyz ; ne = no, nothing available mov di,2 ; get level 2 character push es int firmwr pop es cmp cl,0ffh ; did we really get one? jne getkyz ; ne = no, something strange happened jmp getky1 ; else skip and keep trying getky2: and ax,not cplk ; ignore caps lock key test ax,fnkey ; function-type key? jz getky2a ; z = no, assume ascii key and ax,not fnkey ; strip function key bit or ax,scan ; set scan bit jmp short getky3 getky2a:cmp al,',' ; main comma key? jne getky2b ; ne = no test ax,shfkey ; shifted? jz getky2b ; z = no, leave as a comma or ax,scan ; declare to be a function key jmp getky3 getky2b:cmp al,'.' ; main period key? jne getky2c ; ne = no test ax,shfkey ; shifted? jz getky2c ; z = no, leave as a period or ax,scan ; declare to be a function key jmp getky3 getky2c:and ax,not(shfkey+ctlkey) ; ignore shift and control key on alphas getky3: mov keycode,ax ; return key's code (usually ascii) pop di pop si pop dx pop cx pop bx clc ; carry clear = got a char ret getkyz: pop di pop si pop dx pop cx pop bx stc ; carry set = no char avaiable ret getkey 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 postkey endp code ends end