Vinay's World of Programming

Direct Keyboard Access Tutorial

Before You Begin

Before you start the Direct Keyboard Access Tutorial, click here to download the source code for the programs presented in this tutorial.

Introduction

Have you ever tried writing an action game, or a piano-like program or some other program that requires you to detect keys pressed continuously? Or tried to detect more than one key press simultaneously? You probably settled for using shift keys. Directly accessing the keyboard is a better solution, because then you can use any key in the same manner. While it does require you to bypass the BIOS, there is nothing really complicated about it. Bypassing the BIOS has some interesting side-effects. None of the keystrokes you take for granted work anymore - Caps Lock won't toggle Caps, Shift-PrtSc won't dump the screen to the printer and Ctrl-Alt-Delete won't reboot! You'll either have to handle these keystrokes yourself or hand them over to the BIOS if they're the have their normal effect.

Keyboard Basics

Your pressing a key - any key triggers a lot of events. A electrical switch is closed and electrons rush to the chip on your keyboard to inform it of the key press. The chip then sends a signal to the keyboard controller on your motherboard. The keyboard controller generates an IRQ 1, and urges the CPU to drop whatever its doing and take note of the keystroke. The CPU obliges immediately after processing any pending IRQ 0. When the CPU decides to process the IRQ 1, it causes an INT 9h. This is usually handled by the BIOS. The handler then reads I/O port 60h to retrieve the scancode of the pressed key. Then, it analyzes the keystroke and decides what to do with it. Once the BIOS is done with its task, it lets the CPU do whatever it was doing when it was interrupted. But wait... this is only for the key press... all these events have probably occurred so fast that your finger is still on the keyboard. In time which seems like forever in computer terms, you will release the key. This causes a similar sequence of events, and the BIOS now knows that the key's been released.

Now, the general idea is, that instead of letting the BIOS analyze the keystroke, your program gets to it first. You do this simply by replacing the BIOS INT 9h vector with your own! When it gets called, simply read the keyboard port 60h to get the keystroke!

Handling IRQs

An interrupt handler is like any other function, but for the following points:

The First Step

Ultimately we'll get to a generalized set of functions that will be easy to use in a real program. But, first lets write a simple program that just demonstrates the technique of direct keyboard access. The program is included in KEYB.ZIP in the Files Section section.

/* KEYB1.C
   Demonstrates Direct Keyboard Access. Displays scancodes on the screen.
   by Vinay Pai <vinaypai@crosswinds.net >
   Vinay's World of Programming
   http://www.geocities.com/SiliconValley/1017/
*/

#include <dos.h>
#include <conio.h>
#include <stdio.h>

void interrupt (*OldKBHan) (void); /* Pointer to Old Keyboard Handler */
volatile unsigned char kbyte = 0;
/* Variable where last scancode is stored must be volatile, because it is modified by the IRQ handler, and can change at any time */

void interrupt KBHan (void) /* Our Keyboard Handler */
{
   kbyte = inportb (0x60); /* Read Scancode from port 60 */
   outportb (0x20, 0x20); /* Tell the PIC that we're done handling the IRQ */
}

main()
{
   OldKBHan = getvect (0x09); /* Save the old handler vector */
   setvect (0x09, KBHan); /* Install our own keyboard handler */
   clrscr();
   while (kbyte != 0x81) /* Break when the ESC key is released */
   {
     gotoxy (1, 1);
     printf ("Last scancode received: 0x%02X", kbyte);
   }
   setvect (0x09, OldKBHan); /* Restore old keyboard handler */
   return (0);
}

Now that's delightfully simple isn't it! The program is fully self explanatory. It should provide you with the general framework you will need to use direct keyboard access in your programs. The example program KEYB1.C is useful in finding scancodes of keys. Note that break codes are 0x80 higher than the corresponding make codes.

General Functions

Now that you've learn't how to read the keyboard directly, lets write a set of functions, which you can use to easily use in your programs. I will not present the code here, but in KEYBFN.C in the KEYB.ZIP file in the Files Section.

The first step is to intercept the INT 9 vector, and setup our handler. This is done by the kb_init() function.

This time the handler does a little more. In the last program, the only way for main to detect that a key had been pressed or released, was to poll kbyte and watch for a change in value. While it was fine for KEYB1.C, it'll be terribly inconvenient in a more complex program. So we'll include a _kb_int flag this time. The interrupt handler will set this flag every time it is called. The program can instead poll the _kb_int flag now, and reset it after the keystroke has been processed.

But how do you retrieve a keystroke? Well, we'll offer two ways of doing it. One like the previous program where the scancode is returned in the variable _kb_code. This is useful if your program handler a large number of different keys. However, you'll often be more interested in whether a specific key is pressed at a given time or not. So, we'll maintain a buffer to keep track of which keys have been pressed. The function kb_down() will return TRUE if THE key whose scancode is passed is down (pressed). I don't know for sure just how many keys can be detected simultaneously. There is certainly no limit so far as the software interface is converned. So for good measure we'll make the buffer 128 bytes long... the maximum number of scancodes possible.

Finally, once you're done with the keyboard and its time for your program to end,  you will want to restore things to as they were before your program started. You do this by calling the kb_end() function. It simply restores INT 9 vector to its previous value.

Setting Keyboard Lights

Have you ever wondered just who is responsible for setting the Num Lock, Caps Lock and Scroll Lock lights on the keyboard? Since bypassing the BIOS keyboard handler prevents the Num Lock key from switching the Num Lock lights on or off, you've probably guessed that the BIOS is. However, its very easy to handle the lights yourself. All you have to do is send command 0xED to port 0x60... the same port we've been reading scancodes from. Then send another byte telling it which lights to set and reset. This byte is formatted like this:

But remember that you get the same scancode irrespective of which shift keys are pressed or which lights are on... its up to your program to decide how to interpret the keystroke. For example, pressing 'A' will always return 0x1E when it is pressed, and 0x9E when it is released whether caps lock is on or off. Normally the BIOS checks the state of the shift keys and then returns either 'a' or 'A'.

I've included eight functions to handle keyboard lights:

Actually only kb_setlights is a function. The rest are macros.

You can't read back the keyboard light settings from the controller, so you must keep track of which lights are set. This is done using the _kb_lightstate variable.

Accessing The Functions

Okay, now that you've got all the functions, just how do you access them? You compile KEYBFN.C and link it with your program. Also #include the KEYBFN.H file. These two files, along with KEYB1.C are in the file KEYB.ZIP in the Files Section. If you're using Borland's Turbo C, you can create a project (.PRJ) file to compile a multi-file program. I've included a sample .PRJ file in the zip file.

Back to Top


I Want Your Feedback - Sign My Guestbook (View Guestbook)
If you have any suggestions, contributions or comments, E-mail me at vinaypai@crosswinds.net