NAME msup98 ; File MSUP98.ASM ; Keyboard translator for NEC PC-9801 by Hirofumi Fujii (KEK) ; ; 17-Apr-1991 Many modifications have been done by Joe Doupnik. ; 13-Aug-1990 the input for external procedure PROMPT has been changed ; It accepts ASCIIZ string. The 'dsafkdf' string has been ; chagned to ASCIIZ for this purpose. ; ; 10-Dec-1988 ; To use CTRL+SPACE, ROLL-UP, ROLL-DOWN etc., I introduced new scan codes. ; These scan codes are private, NOT the NEC codes. Therefore you must be ; careful for new models. ; ; Normal +SHIFT +CTRL ; SPACE 34h A4h B4h ; ROLL-UP 36h A6h B6h ; ROLL-DOWN 37h A7h B7h ; INS 38h A8h B8h ; DEL 39h A9h B9h ; up arrow 3Ah AAh BAh ; left arrow 3Bh ABh BBh ; right arrow 3Ch ACh BCh ; down arrow 3Dh ADh BDh ; HOME CLR 3Eh AEh --- ; HELP 3Fh AFh BFh ; ; Getkey has three modes, BIOS mode, CON mode and DOS mode. In the BIOS mode, ; keys are read by using NEC keyboard bios (int 18h). The scan code of ; the key can be read as well as JIS-Roman code. This method is quite safe ; for terminal emulation unless Japanese front end processor is used. ; In the CON mode, keys are read by using MS-DOS function call (int 21h). ; In this mode, the scan code of the key cannot be read. Function keys, ; ROLL-UP, ROLL-DOWN, INS, DEL, arrow keys, HOME-CLR and HELP keys are ; programable, so that they can be programmed to return its scan code. ; However, numeric keys, minus, star, plus, equal, comma, period keys ; in the keypad are not programmed. They cannot be distinguished from ; the nomal keys. Therefore, a dangerous method is employed in this ; program to read the keypad keys. If you feel something wrong at keypad ; keys, ESC, TAB or BS key, try to use in BIOS mode. ; ; Last edit 23 Oct 1988 ; 23 Oct 1988 Special treatment for CTRL-@ (NUL). ; 30 Aug 1988 Keypad keys can be read in CON mode. ; 16 Jul 1988 all the Kanji features went to MSXP98.ASM ; Keyboard translator, by Joe R. Doupnik, Dec 1986 ; with contributions from David L. Knoell. ; For Generic keyboard reading (via DOS) ; edit history: ; Last edit 1 Jan 1988 ; 1 Jan 1988 version 2.30 include mssdef.h include msxp98.h public keybd, dfkey, shkey, msuinit public ans_keystr public kbdlini, kbdlend KEYCLICK_BIT equ 4 ; NEC PC-9801 key related bios functions read_key equ 0 sense_key equ 1 sense_shift equ 2 ; sense_shift returns shift key state in AL ; b5 b4 b3 b2 b1 b0 ; -+----+----+----+----+----+-----+ ; |****|CTRL|GRPH|KANA|CAPS|SHIFT| ; -+----+----+----+----+----+-----+ ; NEC PC-9801 STOP and COPY key interrupt numbers INTNUM_COPY equ 5 INTNUM_STOP equ 6 ; some definitions maxkeys equ 256 ; maximum number of key definitions maxstng equ 128 ; maximum number of multi-char strings stbuflen equ 1024 ; 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 ; system dependent references extrn vt100_flags:byte ; in MSXP98 extrn keyin_dos:byte ; in MSXP98 extrn vtmacname:word, vtmaclen:byte ; external macro ;;; 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: ',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,' 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$' 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 0 ; item number for keylist and dirlist nkeys dw 0 ; number of actively defined keys keycode dw 0 ; ascii/scan code for key kbtemp dw 0 ; scratch storage for translator brace db 0 ; brace detected flag byte oldform db 0 ; old form Set Key, if non-zero verblen dw 0 ; length of user's verb (work temp) kwcnt dw 0 ; number of keywords (work temp) msutake db 0 ; if being run from take file or not twelve dw 12d stringcnt dw 0 ; qty of string chars to be processed stringptr dw 0 ; address of next string char ;;; End System Independent Data Area ;;; System Dependent Data Area ; old interrupt vectors for COPY and STOP key old_copy_ofs dw ? old_copy_sgm dw ? old_stop_ofs dw ? old_stop_sgm dw ? intrkey dw 0 ; intkey_tab dw 06000h ; STOP dw 06100h ; COPY dw 08000h ; Shift+STOP dw 08100h ; Shift+COPY dw 09000h ; Ctrl+STOP dw 09100h ; Ctrl+COPY ; ; edit dfhelp2 to include nice list of verbs for this system. dfhelp2 db cr,lf,' Enter either \Kverb for a Kermit action verb',cr,lf db ' or a replacement string (single byte binary numbers are' db ' \{b##})',cr,lf,' or push Return to undefine a key, ^C to' db ' retain current definition.' db cr,lf,' Braces {} are optional, and strings maybe enclosed in' db ' them.',cr,lf,' Strings may not begin with the character' db ' combinations of \k or \{k',cr,lf db ' (start with a { brace instead).',cr,lf,lf db ' Verbs are as follows. VT320 keys (arrows and keypad):',cr,lf db ' uparr, dnarr, lfarr, rtarr, kpminus, kpcoma, kpdot, kpenter,' db cr,lf db ' Gold (same as PF1), PF1, PF2, PF3, PF4, kp0, ... kp9' db cr,lf,' decFind, decInsert, decRemove, decSelect, decPrev,' db ' decNext' db cr,lf,' User Definable Keys decF6, ...decF14, decHelp,' db ' decDO, decF17, ...decF20' db cr,lf,' Kermit screen control and actions:',cr,lf db ' upscn, dnscn, homscn, endscn, upone, dnone, prtscn, dump,' db cr,lf db ' logoff, logon, termtype, reset, holdscrn, modeline, break,' db ' lbreak, nethold,' db cr,lf db ' hangup, null (send one), terminalR, terminalS, DOS, help,' db ' status, exit' db cr,lf,'$' ; Aliaskey: keys having aliases - same ascii code but more than one ; scan code, as on auxillary keypads. Use just scan codes with these. ; Alternative use: force ascii keys to report out just scan codes. ; Table format: high byte = scan code, low byte = ascii code. ; Contents are machine dependent. aliaskey dw 001Bh ; ESC dw 0E08h ; BS dw 0F09h ; TAB dw 402Dh dw 412Fh dw 4237h dw 4338h dw 4439h dw 452Ah dw 4634h dw 4735h dw 4836h dw 492Bh dw 4A31h dw 4B32h dw 4C33h dw 4D3Dh dw 4E30h dw 4F2Ch dw 502Eh dw 0A420h ; SHIFT+space dw 0B420h ; CTRL+space aliaslen equ ($-aliaskey) shr 1 ; number of words in aliaskey table kverbs db 66 ; number of table entries below mkeyw 'uparr',uparrw ; independent of ordering and case! mkeyw 'dnarr',dnarrw ; mkeyw 'name',procedure entry point mkeyw 'lfarr',lfarrw mkeyw 'rtarr',rtarrw 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',kpmins mkeyw 'kpcoma',kpcoma mkeyw 'kpenter',kpentr mkeyw 'kpdot',kpdot mkeyw 'decF6',decf6 mkeyw 'decF7',decf7 mkeyw 'decF8',decf8 mkeyw 'decF9',decf9 mkeyw 'decF10',decf10 mkeyw 'decF11',decf11 mkeyw 'decF12',decf12 mkeyw 'decF13',decf13 mkeyw 'decF14',decf14 mkeyw 'decHelp',dechelp mkeyw 'decDo',decdo mkeyw 'decF17',decf17 mkeyw 'decF18',decf18 mkeyw 'decF19',decf19 mkeyw 'decF20',decf20 mkeyw 'decFind',decfind mkeyw 'decInsert',decinsert mkeyw 'decRemove',decremove mkeyw 'decSelect',decselect mkeyw 'decPrev',decprev mkeyw 'decNext',decnext mkeyw 'termtype',vtchg mkeyw 'reset',vtreset mkeyw 'prtscn',prtscn mkeyw 'trnprs',trnprs mkeyw 'modeline',trnmod mkeyw 'break',sendbr mkeyw 'hangup',chang mkeyw 'null',snull mkeyw 'help',cquery mkeyw 'status',cstatus mkeyw 'exit',cquit mkeyw 'terminalR',vtrmac mkeyw 'terminalS',vtsmac mkeyw 'DOS',kdos mkeyw 'logoff',klogof mkeyw 'logon',klogon mkeyw 'upone',upone mkeyw 'dnone',dnone mkeyw 'upscn',upscn mkeyw 'dnscn',dnscn mkeyw 'gupone',gupone mkeyw 'gdnone',gdnone ; Initialization data. kbdinlst equ this byte ; Kermit PC98 initialization time keyboard setup mkeyw '\x1b',256 ; ESC key sends ESC mkeyw '\x7f',270 ; BS key sends delete mkeyw '\9',271 ; TAB key sends TAB mkeyw '\8',313 ; DEL key sends backspace mkeyw '\kuparr',314 ; Up arrow key mkeyw '\klfarr',315 ; Left arrow key mkeyw '\krtarr',316 ; Right arrow key mkeyw '\kdnarr',317 ; Down arrow key send [B mkeyw '\kpf1',318 ; HOME/CLR mkeyw '\kpf2',319 ; HELP mkeyw '\kpf3',320 ; keypad minus mkeyw '\kpf4',321 ; keypad slash mkeyw '\kkp7',322 ; keypad 7 mkeyw '\kkp8',323 ; keypad 8 mkeyw '\kkp9',324 ; keypad 9 mkeyw '\kkpminus',325 ; keypad * mkeyw '\kkp4',326 ; keypad 4 mkeyw '\kkp5',327 ; keypad 5 mkeyw '\kkp6',328 ; keypad 6 mkeyw '\kkpcoma',329 ; keypad + mkeyw '\kkp1',330 ; keypad 1 mkeyw '\kkp2',331 ; keypad 2 mkeyw '\kkp3',332 ; keypad 3 mkeyw '\kkpenter',333 ; keypad = mkeyw '\kkp0',334 ; keypad 0 mkeyw '\kkp0',335 ; keypad , mkeyw '\kkpdot',336 ; keypad . mkeyw '\kdnone',310 ; ROLL UP mkeyw '\kupone',311 ; ROLL DOWN mkeyw '\kexit',352 ; STOP mkeyw '\kprtscn',353 ; COPY mkeyw '\kbreak',384 ; shift+STOP mkeyw '\ktrnprs',385 ; shift+COPY mkeyw '\x20',420 ; shift+SPACE mkeyw '\knull',436 ; ctrl+SPACE mkeyw '\kdnscn',438 ; ctrl+ROLL UP mkeyw '\kupscn',439 ; ctrl+ROLL DOWN mkeyw '\x1b$B',KI_CODE ; KI virtual key mkeyw '\x1b(J',KO_CODE ; KO virtual key dw 0 ; end of table marker keypadnum dw 452Ah ; * dw 492Bh ; + dw 4F2Ch ; , dw 402Dh ; - dw 502Eh ; . dw 412Fh ; / dw 4E30h ; 0 dw 4A31h ; 1 dw 4B32h ; 2 dw 4C33h ; 3 dw 4634h ; 4 dw 4735h ; 5 dw 4836h ; 6 dw 4237h ; 7 dw 4338h ; 8 dw 4439h ; 9 dw 273Ah dw 263Bh dw 302Ch dw 4D3Dh ; = keypadgrp dw 2008h ; * dw 0209h ; + dw 8009h ; , dw 0108h ; - dw 010Ah ; . dw 0208h ; / dw 4009h ; 0 dw 0409h ; 1 dw 0809h ; 2 dw 1009h ; 3 dw 4008h ; 4 dw 8008h ; 5 dw 0109h ; 6 dw 0408h ; 7 dw 0808h ; 8 dw 1008h ; 9 dw 000Ah dw 000Ah dw 000Ah dw 2009h ; = 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 ; 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 public 'code' ; 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 ; these are system dependent action verbs, in msxgen extrn beep:near, trnprs:near, sendbr:near extrn chrout:near, cstatus:near, cquit:near, cquery:near extrn klogon:near, klogof:near, kdos:near, snull:near, chang:near extrn trnmod:near extrn pf1:near, pf2:near, pf3:near, pf4:near extrn kp0:near, kp1:near, kp2:near, kp3:near, kp4:near extrn kp5:near, kp6:near, kp7:near, kp8:near, kp9:near extrn kpmins:near, kpcoma:near, kpentr:near, kpdot:near extrn uparrw:near, dnarrw:near, lfarrw:near, rtarrw:near extrn decf6:near, decf7:near, decf8:near, decf9:near, decf10:near extrn decf11:near, decf12:near, decf13:near, decf14:near extrn dechelp:near, decdo:near extrn decf17:near, decf18:near, decf19:near, decf20:near extrn decfind:near, decinsert:near, decremove:near, decselect:near extrn decprev:near, decnext:near extrn keyinchg:near extrn vtchg:near, vtreset:near extrn upone:near, dnone:near, upscn:near, dnscn:near extrn gupone:near, gdnone:near extrn prtscn:near extrn vtrmac:near, vtsmac:near, extmacro:near extrn s2jis:near, is_kanji1:near assume cs:code, ds:data, es:data ; Begin system independent Keyboard Translator code ; MSUINIT performs Kermit startup initialization for this file. ; Note, shkadr and stkadr are pointers tested by Set/Show Key calls. If they ; are not initialized here then the older Set/Show Key procedures are called. MSUINIT PROC NEAR ; call from msx/msy init code call kbdinit ; optional: init translator tables mov shkadr,offset shkey ; declare keyboard translator present mov stkadr,offset dfkey ; via Show and Set Key proc addresses ret MSUINIT ENDP ; Call Keybd to read a keyboard char (just returns carry clear if none) and ; 1) send the replacement string (or original char if not translated) ; out the serial port, or ; 2) execute a Kermit action verb. ; Returns carry set if Connect mode is to be exited, else carry clear. ; Modifies registers ax and bx. KEYBD PROC NEAR ; active translator cmp stringcnt,0 ; any leftover string chars? je keybd0 ; e = no jmp keyst2 ; yes, finish string keybd0: call getkey ; read keyboard jnc keybd1 ; nc = data available jmp keybdx ; else just return carry clear keybd1: call postkey ; call system dependent post processor cmp nkeys,0 ; is number of keys defined = 0? jz keybd3 ; z = none defined push di ; search keylist for this keycode push cx ; save some registers push es mov di,offset keylist ; list of defined keycode words mov ax,keycode ; present keycode mov cx,nkeys ; number of words to examine push ds pop es ; make es:di point to data segment cld repne scasw ; find keycode in list pop es ; restore regs pop cx je keybd1b ; e = found, work with present di pop di ; restore original di test keycode,scan ; is this a scan code? jz keybd3 ; z = no, it's ascii, use al as char call beep ; say key is a dead one clc ret ; and exit with no action keybd1b:sub di,2 ; correct for auto increment sub di,offset keylist ; subtract start of list ==> listptr mov ax,dirlist[di] ; ax = contents of director word pop di ; restore original di ; dispatch on Director code test ax,verb ; verb only? jnz keyvb ; e = yes test ax,strng ; multi-char string only? jnz keyst ; e = yes, else single char & no xlat. ; ; do single CHAR output (char in al) keybd3: cmp al,trans.escchr ; Kermit's escape char? je keybd3a ; e = yes, handle separately ;;; call xltkey ; do character set translation call chrout ; transmit the char clc ; return success ret keybd3a:stc ; set carry for jump to Quit ret keyvb: and ax,not(verb+strng) ; VERB (ax=index, remove type bits) mov bx,offset kverbs ; start of verb table cmp al,byte ptr [bx] ; index > number of entries? jae keybdx ; ae = illegal, indices start at 0 inc bx ; bx points to first entry push cx ; save reg mov cx,ax ; save the index in cx inc cx ; counter, indices start at 0 keyvb1: mov al,byte ptr [bx] ; cnt value xor ah,ah add ax,4 ; skip text and '?' and value word add bx,ax ; look at next slot loop keyvb1 ; walk to correct slot sub bx,2 ; backup to value field pop cx ; restore reg mov bx,[bx] ; get value field of this slot cmp bx,0 ; jump address defined? je keybdx ; e = no, skip the action jmp bx ; perform the function keyst: and ax,not(verb+strng) ; STRING (ax=index, remove type bits) shl ax,1 ; convert to word index push si ; save working reg mov si,ax ; word subscript in table mov si,sptable[si] ; memory offset of selected string xor cx,cx ; init string length to null cmp si,0 ; is there a string pointer present? je keyst1 ; e = no, skip operation cld ; scan forward mov cl,byte ptr [si] ; get string length byte inc si keyst1: mov stringcnt,cx mov stringptr,si pop si jcxz keybdx ; z = null length keyst2: push si mov si,stringptr ; pointer to next string char cld lodsb ; get new string char into al pop si dec stringcnt ; string chars remaining inc stringptr call keysv ; scan for embedded verbs jc keyst4 ; c = not found, al has string char jmp bx ; perform the verb (bx = address) keyst4: ;;; call xltkey ; do character set translation cmp stringcnt,0 ; last character? je keyst5 ; e = yes, stop grouping for nets keyst5: jmp chrout ; send out the char in al keybdx: clc ; return success (nothing to do) ret KEYBD ENDP ; Scan for keyboard verbs embedded in outgoing string. If found update ; string pointer and count to just beyond the verb and return action routine ; address in bx with carry clear. If failure return carry set and no change. ; Can invoke external procedure EXTMACRO if the verb is not known here. keysv proc near push ax push si push di cmp al,'\' ; escape? jne keysv7 ; ne = no mov cx,stringcnt ; chars remaining mov si,stringptr ; address of next char to read mov brace,0 ; assume not using braces cmp byte ptr [si],braceop ; starts with \{? jne keysv1 ; ne = no inc si ; skip the opening brace dec cx mov brace,bracecl ; expect closing brace keysv1: cmp byte ptr [si],'K' ; starts with \{K or \K? je keysv2 ; e = yes cmp byte ptr [si],'k' ; starts as \{k or \k? jne keysv7 ; ne = no, then it's a string keysv2: inc si ; yes, skip the K too dec cx mov di,offset tranbuf ; copy verb name to this work buffer xor ax,ax mov [di],ax ; init the buffer to empty keysv3: cld jcxz keysv4 ; z = no more string chars lodsb ; scan til closing brace or w/s or end dec cx cmp al,brace ; closing brace? je keysv4 ; e = yes cmp al,spc ; white space or control char? jbe keysv3 ; be = yes mov [di],ax ; copy to tranbuf and terminate inc di jmp short keysv3 keysv4: push si ; save input reading position mov si,offset tranbuf ; where verb starts (needs si) call tstkeyw ; find keyword, bx = action routine pop si jnc keysv4a ; nc = found the verb call keysv8 ; invoke EXTMACRO worker for unknown jc keysv7 ; carry = no verb to operate upon keysv4a:cmp brace,0 ; need to end on a brace? je keysv6 ; e = no dec si ; break position inc cx cld keysv5: jcxz keysv6 ; z = no more string characters lodsb ; read string char dec cx cmp al,brace ; the brace? jne keysv5 ; ne = no, repeat until it is found keysv6: mov stringptr,si ; where we finished+1 mov stringcnt,cx ; new count of remaining chars pop di pop si ; original si, starting place pop ax ; original ax clc ret keysv7: pop di ; verb not found pop si pop ax stc ret ; Worker. Unknown verb name as string {\kverb} or {\k{verb}}. Use EXTMACRO ; procedure (in msyibm typically), point to verb name with vtmacname, length ; of it in byte vtmaclen, address of EXTMACRO to BX. Upper case the verb. ; Enter with tranbuf holding the verb, asciiz, without \K and braces. ; Returns BX set to EXTMACRO proc, vtmacname pointing to verb (uppercased) ; and vtmaclen holding the length of verb. keysv8: mov bx,offset extmacro ; use this external macro pointer mov vtmacname,offset tranbuf; select extmacro procedure address mov dx,offset tranbuf ; point to name for extmacro push cx call strlen ; get its length mov vtmaclen,cl ; length for extmacro jcxz keysv11 ; z = none push si ; convert verb name to upper case mov si,dx ; verb, without leading \K stuff cld keysv9: lodsb ; read a name byte cmp al,'a' ; before lower case? jb keysv10 ; e = yes cmp al,'z' ; above lower case? ja keysv10 ; a = yes and al,not 20h ; convert to upper case mov [si-1],al ; put it back keysv10:loop keysv9 ; do all bytes, asciiz pop si pop cx clc ; carry clear = ready to execute ret keysv11:stc ; carry set = no verb, do nothing pop cx ret keysv endp ; SET KEY - define a key (procedure dfkey) ; SET KEY ; 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 dfkey13: cmp dx,'ik' ; KI:Kanji-in virtual key? [HF01] jne dfkey14 ; ne = no. [HF01] mov keycode,KI_code ; store keycode [HF01] jmp dfkey1b ; go get definition [HF01] dfkey14: cmp dx,'ok' ; KO:Kanji-out virtual key? [HF01] jne dfkey14a ; ne = no. [HF01] mov keycode,KO_code ; store keycode [HF01] jmp dfkey1b ; go get definition [HF01] dfkey14a: 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 dfkey15:mov ah,cmeol call comnd ; confirm request before proceeding jnc dfkeyc ; nc = success ret ; failure dfkey0: mov dx,offset dfhelp1 ; say bad definition command mov ah,prstr int dos stc ; failure ret dfkeyc: ; CLEAR key defs, restore startup defs mov cx,maxkeys ; size of keycode tables push es ; save register push ds pop es ; make es point to data segment xor ax,ax ; null, value to be stored mov di,offset dirlist ; director table cld rep stosw ; clear it mov cx,maxkeys mov di,offset keylist ; keycode table rep stosw ; clear it mov cx,maxstng mov di,offset sptable ; string pointer table rep stosw ; clear it pop es ; recover register mov strmax,offset stbuf ; clear string buffer, free space ptr mov stbuf,0 ; first element of buffer mov nkeys,0 ; clear number of defined keys call msuinit ; restore startup definitions clc ; success ret ; Multi-char key identification dfkey1: mov si,offset tranbuf ; point to key ident text cmp byte ptr [si],'0' ; is first character numeric? jb dfkey1a ; b = no cmp byte ptr [si],'9' ; in numbers? ja dfkey1a ; a = no mov keycode,scan ; setup keycode for scan value mov dx,si ; get length of string in cx call strlen push ds pop es ; make es point to data segment push si add si,cx ; point at string terminator mov di,si inc di ; place to store string (1 byte later) inc cx ; include null terminator std ; work backward rep movsb ; move string one place later cld pop si mov byte ptr [si],'\' ; make ascii digits into \nnn form mov oldform,0ffh ; set old form flag mov dx,offset kwarnmsg ; tell user this is old form mov ah,prstr int dos dfkey1a:call katoi ; convert ascii number to binary in ax jc dfkey0 ; c = no number converted or keycode,ax ; store in keycode dfkey1b: ; Get Definition proper test oldform,0ffh ; old form Set Key active? jz dfkey1f ; z = no mov bx,offset tranbuf ; get new definition on main cmd line mov word ptr [bx],0 ; insert terminator mov dx,offset dfhelp2 ; help for definition of key mov ah,cmline ; read rest of line into tranbuf call comnd ; allow null definitions or 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 data 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 ans_keystr proc near ; returns key definition strings. ; inputs: ; ax scan code for the key ; outputs: ; cx number of strings (may be zero) ; bx address of the string buffer ; xor cx,cx ; clear counter cmp nkeys,0 jg ans_keystr1 jmp ans_keystrex ans_keystr1: push di ; search keylist for this keycode push cx ; save some registers push es mov di,offset keylist ; list of defined keycode words 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 ans_keystr2 ; e = found, work with present di pop di ; restore original di jmp ans_keystrex ans_keystr2: sub di,2 ; correct for auto increment sub di,offset keylist ; subtract start of list ==> listptr mov bx,offset dirlist ; get the director address add bx,di pop di ; restore original di ; dispatch on Director code mov ax,[bx] test ax,verb ; verb only? jnz ans_keystrex ; nz = yes test ax,strng ; multi-char string only? jnz ans_keystr3 ; nz = yes, else single char & no xlat. mov cx,1 ; do single CHAR output (char in al) inc bx jmp ans_keystrex ; ans_keystr3: 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 bx,sptable[si] ; memory offset of selected string pop si cmp bx,0 ; is there a string pointer present? je ans_keystrex ; e = no, skip operation mov cl,[bx] ; get string length byte xor ch,ch ; to cx inc bx ; adjust pointer ; ans_keystrex: ret ans_keystr endp ; Read keyboard. System dependent. ; Return carry set if nothing at keyboard. ; If char present return carry clear with key's code in Keycode. ; If key is ascii put that in the low byte of Keycode and clear bit Scan in ; the high byte; otherwise, put the scan code in the lower byte and set bit ; Scan in the high byte. ; Bit Scan is set if key is not an ascii code. ; Modifies register ax. getkey proc near cmp intrkey,0 ; Interrupt key pressed ? jne getkey01 ; ne = Yes. jmp getkey00 ; go to normal process getkey01: push bx mov ax,intrkey dec ax ; index starts from 1 shl ax,1 ; item size in the table is 2 bytes mov bx,offset intkey_tab ; set table add bx,ax ; add index mov ax,[bx] ; get keycode mov intrkey,0 ; clear index pop bx jmp getkey1 getkey00: mov keycode,0 cmp keyin_dos,0 ; Keyinput using BIOS ? je getkey0 ; e = yes, BIOS key input cmp keyin_dos,1 ; Keyinput using CON ? jne getkey02 ; ne = no jmp getkey_con getkey02: jmp getkey_dos ; ; Keyinput using BIOS ; getkey0: mov ah,sense_key int bios cmp bh,0 jne getky1 ; ne = char available stc ; carry set = nothing available jmp getkyx ; exit on no char available getky1: mov ah,read_key int bios cmp al,'^' ; check SHIFT+'^' jne getky1_2 mov ah,sense_shift int bios test al,1 jz getky1_1 mov ax,0C60h ; Back quote jmp getkey1 getky1_1: mov ax,0C5Eh ; '^' jmp getkey1 getky1_2: cmp ah,34h jb getkey1 cmp ah,3Fh ja getkey1 push dx mov dx,ax ; save code mov ah,sense_shift ; sense shift key int bios test al,10h ; CTRL pressed ? jz getky1_3 ; z = no add dx,08000h jmp getky1_4 getky1_3: test al,1 ; SHIFT pressed ? jz getky1_4 ; z = no add dx,07000h getky1_4: xchg ax,dx pop dx getkey1: push di ; check key (ax) for aliases push cx push es mov di,offset aliaskey ; list of aliased keys mov cx,aliaslen ; number of entries jcxz getky2 ; z = no entries push ds pop es ; make es:di point to data segment cld repne scasw ; look for a match jne getky2 ; ne = not there mov al,0 ; force use of scan code (in ah) getky2: pop es pop cx pop di or al,al ; scan code being returned? jnz getky3 ; nz = no cmp ax,1A00h ; CTRL-@ key ? je getky3 xchg ah,al ; put scan code in ident area or keycode,scan ; set scan flag (vs ascii) getky3: mov byte ptr keycode,al ; return key's code (usually ascii) clc ; carry clear = got a char getkyx: ret ; ; Keyinput using MS-DOS console I/O + sense shift key ; getkey_con: push dx mov ah,6 ; direct console I/O mov dl,0FFh ; read int dos jnz getkey_con6 jmp getkey_con0 getkey_con6: cmp al,0FFh ; special key ? jne getkey_con61 ; ne = no jmp getkey_conS getkey_con61: mov dx,ax mov ah,sense_shift ; sense shift key int bios xchg ax,dx ; now ax=code, dx=shift_key cmp al,3Dh ; is it '=' key ? jne getkey_con4 jmp getkey_conK ; goto keypad-key check getkey_con4: cmp al,2Ah jb getkey_con3 cmp al,39h ja getkey_con3 jmp getkey_conK ; goto keypad-key check getkey_con3: ; Normal key mov ah,0 cmp al,'^' jne getkey_con5 test dl,1 ; SHIFT pressed ? jz getkey_con2 ; z = no mov al,60h ; backquote jmp getkey_con2 getkey_con5: cmp al,20h ja getkey_con2 cmp al,8 ; BS ? jne getkey_con51 ; ne = No. mov ah,22h ; assume CTRL + H test dl,10h ; test CTRL key jnz getkey_con2 ; nz = Pressed. mov ah,0Eh ; BS key jmp getkey_con2 getkey_con51: cmp al,9 ; TAB ? jne getkey_con52 ; ne = No. mov ah,17h ; assume CTRL + I test dl,10h ; test CTRL key jnz getkey_con2 ; nz = Pressed. mov ah,0Fh ; TAB key jmp getkey_con2 getkey_con52: cmp al,1Bh ; ESC ? jne getkey_con53 ; ne = No. mov ah,1Bh ; assume CTRL + [ test dl,10h ; test CTRL key jnz getkey_con2 ; nz = Pressed. mov ah,0 ; ESC key jmp getkey_con2 getkey_con53: cmp al,0 ; NUL ? jne getkey_con54 ; ne = No. mov ah,1Ah ; CTRL + @ jmp getkey_con2 getkey_con54: cmp al,20h ; SPACE ? jne getkey_con55 ; ne = No. mov ah,34h ; set scan code test dl,10h ; test CTRL key jz getkey_con2 ; z = Not Pressed. mov ax,0B420h ; CTRL-SPACE getkey_con55: getkey_con2: pop dx jmp getkey1 getkey_con0: pop dx stc ret ; getkey_conS: mov ah,6 mov dl,0FFh int dos jnz getkey_conS1 jmp getkey_con0 getkey_conS1: mov ah,al xor al,al cmp ah,34h jb getkey_conS4 cmp ah,3Fh ja getkey_conS4 push dx mov dx,ax ; save code mov ah,sense_shift ; sense shift key int bios test al,10h ; CTRL pressed ? jz getkey_conS2 ; z = no add dx,08000h jmp getkey_conS3 getkey_conS2: test al,1 ; SHIFT pressed ? jz getkey_conS3 ; z = no add dx,07000h getkey_conS3: xchg ax,dx pop dx getkey_conS4: jmp getkey_con2 ; getkey_conK: mov bx,offset keypadgrp xor ah,ah mov dx,ax sub ax,2Ah shl ax,1 add bx,ax mov ax,[bx] mov bx,ax xor bl,bl mov ah,04h int BIOS test ax,bx jnz getkey_conK1 mov ax,dx jmp getkey_con2 getkey_conK1: mov ax,dx mov bx,offset keypadnum sub ax,2Ah shl ax,1 add bx,ax mov ax,[bx] jmp getkey_con2 getkey_dos: push dx mov ah,6 ; direct console I/O mov dl,0FFh ; read int dos jnz getkey_dos6 jmp getkey_dos0 getkey_dos6: cmp al,0FFh ; special key ? je getkey_dos1 ; e = yes jmp getkey_dos2 getkey_dos1: mov ah,6 mov dl,0FFh int dos jz getkey_dos0 mov ah,al xor al,al getkey_dos2: pop dx jmp getkey1 getkey_dos0: pop dx stc ret getkey endp postkey proc near ; do sys dep action after reading test vt100_flags,KEYCLICK_BIT je postkey_ex push ax push cx ; mov al,06h out 37h,al ; bell on mov cx,1000h postkey1: loop postkey1 ; mov al,07h out 37h,al ; bell off ; pop cx pop ax postkey_ex: ret ; key during active translation postkey endp ; Interrupt routine for STOP and COPY keys keyint_entry proc far stop_int: push ax push ds ; mov ax,data mov ds,ax cmp intrkey,0 jne keyint_entry3 mov intrkey,1 jmp keyint_entry1 ; copy_int: push ax push ds ; mov ax,data mov ds,ax cmp intrkey,0 jne keyint_entry3 mov intrkey,2 ; keyint_entry1: ; mov ah,sense_shift int bios test al,10h ; CTRL? jz keyint_entry2 add intrkey,4 jmp keyint_entry3 keyint_entry2: test al,1 ; SHIFT? jz keyint_entry3 add intrkey,2 keyint_entry3: ; pop ds pop ax iret keyint_entry endp ; Keyboard local initialization routine kbdlini proc near pushf cli ; disable interrupt ; push ax push bx push dx push es ; mov intrkey,0 ; mov ah,35h ; read interrupt vector mov al,INTNUM_COPY ; for COPY key interrupt int DOS ; system call mov old_copy_ofs,bx ; save vector mov old_copy_sgm,es ; mov ah,35h ; read interrupt vector mov al,INTNUM_STOP ; for STOP key interrupt int DOS ; system call mov old_stop_ofs,bx ; save vector mov old_stop_sgm,es ; mov dx,offset copy_int mov ax,cs push ds mov ds,ax mov ah,25h mov al,INTNUM_COPY int DOS pop ds ; mov dx,offset stop_int mov ax,cs push ds mov ds,ax mov ah,25h mov al,INTNUM_STOP int DOS pop ds ; pop es pop dx pop bx pop ax ; popf ret kbdlini endp ; keyboard local end routine kbdlend proc near pushf cli ; disable interrupt ; push ax push dx ; mov dx,old_stop_ofs mov ax,old_stop_sgm push ds mov ds,ax mov ah,25h mov al,INTNUM_STOP int DOS pop ds ; mov dx,old_copy_ofs mov ax,old_copy_sgm push ds mov ds,ax mov ah,25h mov al,INTNUM_COPY int DOS pop ds ; pop dx pop ax popf ret kbdlend endp code ends end