Overcoming the Keyboard (PC Magazine Vol 2 No 1 June 1983 User-to-User) Here's some handy information for the BASIC programmer. DOS keeps track of the NumLock, CapsLock, and ScrollLock key states by setting and resetting bits in low memory. A BASIC program may alter these bits to force entry of uppercase characters from the keyboard, or to automatically toggle the numeric keypad for either cursor control or numeric key entry. The relevant address is at 0040:0017H, as described on page A-2 of the Technical Reference manual. In the program below, lines 20 through 160 provide ways to test the state of CapsLock, NumLock and ScrollLock, along with ways to force and exit CapsLock and NumLock. The state of the Alt, Ctrl, and the left or right shift keys may also be tested (lines 170 through 260). Another byte at 0040:0018H (see Technical Reference manual, page A-3) may be tested to see if the Ins, CapsLock, NumLock or ScrollLock keys are currently being depressed. Lines 270 to 340 perform these tests. A game program could move a paddle without using the INKEY$ command by applying this method to test the states of the left and right shifts or any of the other keys mentioned above. The beauty is that no keys are queued up into a buffer, so a program can sense exactly when a key is pressed or released. The here-and-now information that the program gleans from these memory locations is critical to real-time operations like games and graphics control. Setting these bits yourself frees your program's users from getting dumb instructions like "Make sure you have pressed the NumLock Key." 10 DEF SEG=&H40 20 'Test CapsLock state 30 IF PEEK(&H17) AND 64 THEN CLOCK=TRUE 40 'Force CapsLock state 50 POKE &H17,PEEK(&H17) OR 64 60 'Exit CapsLock state 70 POKE &H17, PEEK(&H17) AND 191 80 'Test NumLock state 90 IF PEEK(&H17) AND 32 THEN NLOCK=TRUE 100 'Force NumLock state 110 POKE &H17,PEEK(&H17) OR 32 120 'Exit NumLock state 130 POKE &H17,PEEK(&H17) AND 223 140 ' 150 'Test ScrollLock state 160 IF PEEK(&H17) AND 16 THEN SLOCK=TRUE 170 'Test if Alt Key is depressed 180 IF PEEK(&H17) AND 8 THEN ALT=TRUE 190 'Test if Ctrl key is depressed 200 IF PEEK(&H17) AND 4 THEN CTRL=TRUE 210 'Test if either shift key is depressed 220 IF PEEK(&H17) AND 3 THEN SHFT=TRUE 230 'Test if left shift key is depressed 240 IF PEEK(&H17) AND 2 THEN LS=TRUE 250 'Test if right shift key is depressed 260 IF PEEK(&H17) AND 1 THEN RS=TRUE 270 'Test if Ins key is depressed 280 IF PEEK(&H18) AND 128 THEN INS=TRUE 290 'Test if CapsLock is depressed 300 IF PEEK(&H18) AND 64 THEN CL=TRUE 310 'Test if NumLock is depressed 320 IF PEEK(&H18) AND 32 THEN NL=TRUE 330 'Test if ScrollLock is depressed 340 IF PEEK(&H18) AND 16 THEN SL=TRUE ----------------------------------------------------------------- Keyboard Codes (PC Magazine Vol 4 No 1 Jan 8, 1985 by P. Abel) Each key on the PC keyboard has a designated physical position, number from 1 (Esc) through 83 (Del). When a key is pressed it generates a scan code, which is the same as its position number, though it is expressed in hex notation. Thus, pressing the Esc key produces 01H; Del generates scan code 53H. (A second set of scan codes -- made by adding 80H to the first -- is generated when the key is released, but the PC does not seem to use these latter codes.) Any program can determine the source of any keystroke by using the scan codes. When you press the A key, the computer actually gets two pieces of information: the scan code for the letter A (1EH) and, of course, the ASCII character code for A (41H). The table below shows the scan codes for some common extended functions. A simple request for input of one character from the keyboard involves setting the AH register to zero and requesting DOS interrupt 16H: SUB AH,AH ;or MOV AH,00 INT 16H ;request keyboard input The operation responds in one of two ways, depending on whether you pressed a character code or an extended function code. Suppose that a key you pressed represents a displayable character, such as a letter (A through Z), a number (0 through 9) or a symbol such as !, @, #, or $. The INT operation delivers the ASCII character code to the AL register and the scan code to the AH register. Thus, pressing the letter A puts character code 41H in the AL register and also places 1EH in the AH register. For most purposes, the character code in the AL register is the only information your computer needs. Suppose, however, that the key you press represents an extended function code, such as one of the ten function keys or a cursor arrow key. For these noncharacter requests, the operation sets the AL register to zero and inserts the scan code in the AH register. Consequently, when an INT 16H instruction is used to request a keyboard entry, the program can then test the AL register. If it is nonzero, the request is for an extended function code. The following instructions test if an INT 16H operation has returned a zero value to the AL register: SUB AH,AH INT 16H ;request input CMP AL,00 ;extended function code? JZ exit1 ;yes -- exit to routine You probably have noticed that certain characters, such as +, -, and *, appear twice on the keyboard. To the PC, each of these similarly labeled keys is quite different. The plus symbol puts the character code 2BH in the AL register and one of two scan codes in the AH register. If the plus key pressed was in the top row above the equals sign, the scan code is 0DH. If the plus key pressed was at the far right, the scan code is 4EH. In the following routine, if the character is a plus sign (2BH), the logic tests for the scan code: CMP AL,2BH ;plus sign? JNE exit2 ;no -- exit CMP AH,4EH ;which scan code? JE exit3 Following is a simple example that sets the cursor to row 0, column 0, if you press the Home key (scan code 47H). Interrupt 10H, for setting the cursor, requires 02 in the AH register, zero in BH, the row in DH, and the column DL: SUB AH,AH ;clear AH INT 16H ;request input CMP AL,00 ;extended function? JNE exit4 ;no -- exit CMP AH,47H ;scan code is Home? JNE exit5 ;no -- exit MOV DX,00 ;set row/col to zero MOV AH,02 ;set AH to 02 SUB BH,BH ;set BH to zero INT 10H ;set cursor A program can respond to a scan code in many different ways. The example above interprets the Home key to mean that the cursor is to be set at row 0, column 0. But a program can respond with any other action or can even ignore the keystroke entirely. A program could make further tests for scan codes. The function keys deliver scan codes 3BH through 44H, respectively. Following is a test for function key F1: CMP AH,3BH ;function key F1? JE exit6 ;yes, exit Here again, a program could perform any designated action for a user request. Suppose that a program stores the current row/column setting for the cursor in fields named ROW and COL, respectively. You press the down arrow to request moving the cursor down one row. The following program segment moves the cursor, provided that ROW is not already at 24, to the bottom of the screen: SUB AH,AH INT 16H ;request input CMP AL,00 ;extended function? JNE exit1 ;no -- exit CMP AH,50H ;down cursor? JNE exit2 ;no -- exit CMP ROW,24 ;bottom of screen? JAE exit3 ;yes -- exit INC ROW ;increment ROW by 1 MOV AH,02 SUB BH,BH MOV DH,ROW MOV DL,COL INT 10H ;set cursor to 00,00 Since it is the program that interprets the scan codes, you could intentionally or erroneously cause the down cursor key to perform any other action, even to move up the screen! For further reference, the complete technical details for all scan codes as well as the use of the Shift, Ctrl and Alt keys are available in the IBM PC Technical Reference manual, in the Keyboard Encoding and Usage section. The character codes are listed in Appendix C. To see how this works, you can type in the short assembly language program below, using the A (Assemble) command of the DEBUG mini-Assembler. When you use the A command in DEBUG, the code will automatically begin at 100H. After the last line, just press Enter a second time to exit from the assembler. The source program is very short and consists of just three instructions: SUB AH,AH INT 16H JMP 100H The INT 16H instruction requests input from the keyboard. After you enter a character, the JMP instruction returns you to the beginning creating an endless loop. Normally, you would scrupulously avoid this practice; however, DEBUG provides a simple way out of the program. To enter the program directly in machine language, insert your DOS disk in drive A: and boot up in the usual way. When the DOS A> prompt appears, type DEBUG and hit Enter. When the DEBUG hyphen prompt appears, use DEBUG's E command to enter the three instructions in hex machine language. Type these instructions exactly as they appear, including the blanks. Hit the Enter key at the end of each line. E CS:100 28 E4 E CS:102 CD 16 E CS:104 EB FA The CS stands for Code Segment, the area for executable code. The values 100, 102 and 104 are the relative locations within the CS where each of the three two-byte instructions begin. The normal starting location of the first instruction in the Code Segment is 100H. The values 28 E4, CD 16 and EB FA are the actual machine code for each of the three assembly language instructions. Before attempting to execute this program, make sure that you have keyed each entry exactly as shown (you can repeat any line that contains an error). On completion, type R and Enter to display the starting instruction. The operation displays the contents of the registers in hex and the first instruction: 3B33:0100 28 E4 SUB AH,AH The first four hex digits, 3B33 (which is actually 3B330), indicate the address of the beginning of the Code Segment, but your version will probably differ from this value. The rest of the line should be identical, however. You should now press T and then Enter to trace execution of the next instruction. The screen should display: 3B33:0102 CD 16 INT 16 The computer is now about to execute the interrupt instruction. If you wanted, you could trace the operation through the entire BIOS routine, but for this exercise, it's as well to execute right through the INT operation and stop at the JMP instruction that follows at 104. To do this, type G 104 and Enter. The program should stop with the cursor blinking, waiting for the keyboard input that INT 16H has requested. Type a lowercase a. The program should accept this character and immediately display the registers and the next instruction. Your interest is only in the contents of the AX register, which should be 1E61. 1EH in the AH register represents the scan code for keyboard position 30 (the letter a), and 61H in register AL represents the ASCII code for a. The instruction that displays should be: 08FD:0104 EBFA JMP 0100 Press T and Enter to execute the JMP instruction, and enter G 104 again to execute the interrupt. This time, type an uppercase A. The AX register will now contain 1E41. You can continued indefinitely by repeatedly using T and G 104. Try entering a variety of keyboard entries, such as Esc, Ins, Del, F1, Home, cursor keys, both plus signs, both minus signs and numbers. A few keys, such as Shift by itself, do not cause the keyboard to produce a code. When you've had enough scan codes for one session, you can exit from DEBUG by typing Q (for Quit) instead of T or G 104. Selected keys and their hex scan codes. Extended Function Hex Scan Code Alt/A - Alt/Z 1E - 2C F1 - F10 3B - 44 Home 47 Up arrow 48 PgUp 49 Left arrow 4B Right arrow 4D End 4F Down arrow 50 PgDn 51 Ins 52 Del 53 ----------------------------------------------------------------- AT Keyboard Tricks (PC Magazine Vol 4 No 19 Sept 17, 1985 User-to-User) The AT keyboard is programmable. For instance, it's simple to change the keyboard's LED shift-lock indicators. First issue an OUT &H60,&HED (the SET/RESET LEDs command), and follow this immediately with an OUT &H60,nn(where nn is a binary value indicating which LEDs to turn on). Bit 0 is for the ScrollLock indicator, bit 1 is for NumLock, and bit 2 is for CapsLock. The LEDCYCLE.BAS program cycles the AT's LEDs through a binary counting sequence. However, turning an LED on or off does not change the ScrollLock, NumLock or CapsLock keyboard shift states. These actual shift states are controlled by bits 4, 5 and 6 of the BIOS KB_FLAG at address 0040:0017. To illustrate this, the TOGGLE.BAS program turns on the NumLock LED and sets the NumLock state. But the most significant new feature of the AT keyboard is that its typematic action is programmable. You can specify the amount of time to wait before sending the first repeat (the delay) and the time between repeats (the rate). First, output a command (&HF3), then output a delay-and-rate value. Bits 5 and 6 control the delay, ranging from 250 ms up to 1 second. The lowest 5 bits (bits 0 through 4) identify the repeat rate, ranging from about 30 repeats per second to 2 repeats to second. The following two commands set the keyboard to perform with a 1/4-second delay and 20 repeats per second: OUT &H60,&HF3:OUT &H60,4 Be sure to enter both commands on the same line. After receiving the &HF3 command, the keyboard locks up until it receives the rate and delay byte. You can use the KEYRATE.BAS program to set the keyboard typematic parameters. It includes the logic to display the exact delay and rate you select, calculated with formulats from the AT Technical Reference Manual. The default power-on settings are a 1/2-second delay and a repeat rate of 10 per second. A better choice of settings is a 1/4-second delay and 15 to 20 repeats per second. It is possible to set the keyboard to output up to 30 repeats per second. LEDCYCLE.BAS: 110 DEF SEG=&HFFFF:IF PEEK(14)=252 THEN 130 120 PRINT "Try this on an AT only!":END 130 FOR J=0 TO 7 140 OUT &h60,&hed:OUT &h60,J 150 FOR DELAY=1 TO 1000:NEXT 160 IF INKEY$ > "" THEN STOP 170 NEXT 180 GOTO 130 TOGGLE.BAS: 110 DEF SEG=&HFFFF:IF PEEK(14)=252 THEN 130 120 PRINT "Try this on an AT only!":END 130 NUM.LED=4:NUM.FLG=&H10 140 CPS.LED=2:CPS.FLG=&H20 150 SCR.LED=1:SCR.FLG=&H40 160 DEF SEG=&H40 170 POKE &H17,PEEK(&H17) OR NUM.FLG 180 OUT &H60,&HED:OUT &H60,NUM.LED KEYRATE.BAS: 110 DEF SEG=&HFFFF:IF PEEK(14)=252 THEN 130 120 PRINT "Try this on an AT only!":END 130 PRINT "=low values are fastest=" 140 INPUT "initial delay (0-3): ",ID 150 INPUT "repeat rate (0-31): ",RR 160 OUT &H60,&HF3:OUT &H60,(ID*32) OR RR 170 PRINT "initial delay is"; 180 PRINT (ID+1)*.25;"seconds" 190 A=(RR AND 7):B=(RR AND 24)\8 200 P=(8+A)*(2^B)*.00417 210 PRINT "repeat rate is"; 220 PRINT 1/P;"per second" ----------------------------------------------------------------- Free Graphics Keyboard (PC Magazine Vol 4 No 19 Sept 17, 1985 User-to-User) DOS 3.x includes five programs on the main DOS disk that you can adapt to create a totally customized keyboard. The KEYBxx.COM programs let you turn your IBM/American keyboard into one more suitable for use in the UK, Germany, France, Italy or Spain by switching some of the letters and punctuation marks around and substituting things like pound signs for dollar signs. To create a custom keyboard, copy one of these five programs onto a work disk, and give a name like KEYCUST.COM. The go into DEBUG and make the changes. Search through the file until you see the familiar "asdf ..." and "ASDF ..." characters at the right side of your screen, and then replace the existing letters with the ones you want. For instance, to customize the KEYBUK.COM so that hitting the leftmost nine keys --- "qwe" on one line, then "asd" on the second line, and then "zxc" on the third line -- produces a box, type: COPY A:KEYBUK.COM B:KEYCUST.COM and then DEBUG KEYCUST.COM When you see the DEBUG prompt, type: e 592 da c2 bf e 5a0 c3 c5 b4 e 5ae c0 c1 d9 w q Then load the new keyboard program by typing KEYCUST. You can toggle back and forth between your normal keyboard and the customized one. Hit Ctrl-Alt-F1 for you normal keyboard or Ctrl-Alt-F2 for your new one. By following these procedures, you can create all sorts of alternate keyboard layouts -- one with graphics symbols, one with math symbols, etc., and load them whenever you need them. ----------------------------------------------------------------- DOS Keyboard Macros (PC Magazine Vol 4 No 26 Dec 24, 1985 User-to-User) Would you like to log into drive C:, change the active subdirectory and then execute a program like 1-2-3, all with one keystroke? The NEWKEYBD.BAS program creates a file called NEWDOS.KBD that sets this up for you automatically. In addition, you can read the directory of your logged drive by hitting Alt-D and load a print spool buffer by hitting Alt-B. And you can do much more, all without having to purchase any memory-draining commercial keyboard macro programs such as SuperKey. Lines 110 to 180 read the DATA at the end of the program and write it to the NEWDOS.KBD file. To create your own macros, just change the DATA statements following line 180. The structure of the DATA fields at the end of NEWKEYBD.BAS is as follows: 1. The first field specifies the total number of commands you want to assign to a single key. 2. The second field is the number of the key you want to redefine (see the table below). 3. The third field is the exact text of the command that you would have ordinarily had to type manually. Each must be inside quotation marks, and each must be separated by a comma. 4. After you're all done, the very last line of DATA must be just a zero (0). Once you've entered your customized DATA statements, run NEWKEYBD.BAS to create your copy of NEWDOS.KBD. Then, to execute your redefinitions, get into DOS and type: COPY NEWDOS.KBD CON: You can put this command in an AUTOEXEC.BAT file to load the keyboard redefinitions automatically. For this to work properly, you have to have booted your system with a CONFIG.SYS on your disk that includes the line DEVICE=ANSI.SYS. And, of course, ANSI.SYS has to be on your disk as well. Editor's Note: The table that follows are the extended ASCII codes for every shifted key on the keyboard. (Actually, they are all preceded by a 0, but NEWKEYBD.BAS takes care of this automatically in line 140.) You could also reassign the normal, nonshifted keys by removing the 0 and the semicolon in line 140 and creating the appropriate DATA statement. For instance, if you never use the grave accent (the backward apostrophe key), you could change the NEWKEYBD.BAS program to make this key give you a directory listing by removing the 0; from line 140 and creating the DATA line: 190 DATA 1,96, "DIR" since 96 is the decimal ASCII code for the grave accent. If you try this, remember not to mix shifted keys (which need the 0;) with nonshifted keys (which don't need the 0;). And whether you are redefining just one key or several, DATA 0 must be the last line in the program. Remember also that this won't work unless you type the line COPY NEWDOS.KBD CON: while in DOS. You can keep several different files of reassigned keys under different names, but you have to COPY each to CON: when you want to use it. Finally, if you create your own NEWDOS.KBD file, remember that you had to have already loaded ANSI.SYS by including DEVICE=ANSI.SYS in your CONFIG.SYS file in the root directory of your boot disk, which means you may have to adjust your CONFIG.SYS file and reboot for your keys to be redefined. 100 'NEWKEYBD.BAS 110 OPEN "O", #1, "NEWDOS.KBD" 120 READ HOWMANY:IF HOWMANY=0 THEN CLOSE:END 130 READ KEYNUMBR:KEYNO$=MID$(STR$(KEYNUMBR),2) 140 PRINT #1,CHR$(27)"[0;"KEYNO$";"CHR$(34); 150 FOR I=1 TO HOWMANY:READ TEXT$ 160 PRINT #1,TEXT$;CHR$(13); 170 NEXT 180 PRINT #1,CHR$(34)"p":GOTO 120 190 DATA 3,60,"C:","CD\LOTUS","123" 200 DATA 1,32,"DIR" 210 DATA 1,48,"PRINTSPL -LPT1 -BUF32" 220 DATA 0 Table of Extended ASCII Codes F1 59 Alt-F1 104 Shift-F1 84 Ctrl-F1 94 Alt-1 120 F2 60 Alt-F2 105 Shift-F2 85 Ctrl-F2 95 Alt-2 121 F3 61 Alt-F3 106 Shift-F3 86 Ctrl-F3 96 Alt-3 122 F4 62 Alt-F4 107 Shift-F4 87 Ctrl-F4 97 Alt-4 123 F5 63 Alt-F5 108 Shift-F5 88 Ctrl-F5 98 Alt-5 124 F6 64 Alt-F6 109 Shift-F6 89 Ctrl-F6 99 Alt-6 125 F7 65 Alt-F7 110 Shift-F7 90 Ctrl-F7 100 Alt-7 126 F8 66 Alt-F8 111 Shift-F8 91 Ctrl-F8 101 Alt-8 127 F9 67 Alt-F9 112 Shift-F9 92 Ctrl-F9 102 Alt-9 128 F10 68 Alt-F10 113 Shift-F10 93 Ctrl-F10 103 Alt-10 129 Alt-A 30 Alt-H 35 Alt-O 24 Alt-V 47 Ctrl-PrtSc 114 Alt-B 48 Alt-I 23 Alt-P 25 Alt-W 17 Ctrl-Left 115 Alt-C 46 Alt-J 36 Alt-Q 16 Alt-X 45 Ctrl-Right 116 Alt-D 32 Alt-K 37 Alt-R 19 Alt-Y 21 Ctrl-End 117 Alt-E 18 Alt-L 38 Alt-S 31 Alt-Z 44 Ctrl-PgDn 118 Alt-F 33 Alt-M 50 Alt-T 20 Alt-- 130 Ctrl-Home 119 Alt-G 34 Alt-N 49 Alt-U 22 Alt-+ 131 Ctrl-PgUp 132 Home 71 Up 72 PgUp 73 Left 75 Right 77 End 79 Down 80 PgDn 81 Ins 82 Del 83 Shift-Tab 15