The keyboard itself has its own processor chip called the Intel 8042 whose task is to track the changes in the states of the keys on the keyboard and alert the connect computer of these changes. This is achieved by initiating an interrupt request of the main microprocessor of the host computer and providing the necessary information in one of the port addresses of the main microprocessor. In the CPU's core memory, an interrupt service routine (ISR) is written instructing the main microprocessor to read the values off the keyboard port and signaling back to the keyboard that the values have been read. On an IBM PC, the keyboard ports that we are interested in are located at port addresses 6016 and 6116. The ROM routine provided by the PC BIOS chip reads the keyboard scan codes and key board states provided by the 8042 chip and translates them into their appropriate ASCII codes and stuffs them in a buffer. It also provides special actions for combinations of key strokes, like CTRL+ALT+DEL to reboot the system. Certain programs and operating systems circumvent the BIOS keyboard ISR and provide other actions as well, such as pressing ALT to activate menus in the DOS editor or intercepting the CTRL+ALT+DEL key combinations in multitasking systems. Our programs can be written to intercept the keyboard interrupts and provide our own responses to keyboard events, but in order to do so we must write our own keyboard ISR.
The 8042 chip interrupts the microprocessor any time a key is pressed or released and provides the scan code of the key of interest and a flag indicating whether the key is being pressed or released. This information can be found at port address 6016. With this knowledge a keyboard ISR can be easily written to detect key presses and releases. In addition to this, some other house keeping chores must be done by our keyboard ISR if it is to circumvent the BIOS ISR. Rather than inundate you with all the details, let us examine to code to our replacement ISR.
To read a key from the keyboard port, just simply read port address 6016 like so.
in al,60h ; Read the scan code into AL
The most significant bit (D7) of register AL will contain the state of the key. If the key is being released, it will contain a 1, otherwise it will contain a 0. In C, the code to read the keyboard port would look something like this:
int scanCode; scanCode = inp(0x60); /* Read the scan code */
After reading the scan code, our program can acknowledge that the scan code was read to the keyboard controller like so:
in al,61h ; Read keyboard control register mov ah,al ; Preserve control register state or al,80h ; Toggle acknowledge bit high out 61h,al ; Write control register mov al,ah ; Get previous control register state out 61h,al ; Toggle acknowledge bit low
Of course, we could use C code to achieve the same end.
register int i; /* A register is desirable */ i = inp(0x61); /* Get keyboard control register */ outp(0x61, i | 0x80); /* Toggle acknowledge bit high */ outp(0x61, i); /* Toggle acknowledge bit low */
Now that our program has read the scan code, it can first determine if key was pressed or released and act upon it in like fashion.
if (scanCode & 0x80) /* Key was released, do something */ else /* Key was pressed, do something */
The remaining 7 bits of data in the scan code contain the actual scan code itself. A single scan code is assigned to each of the keys on the keyboard including two separate scan codes for each of the shift keys on the keyboard. Keep in mind that our program is within an interrupt service routine so whatever we do, we must do it quickly and get out as soon as possible. With that in mind, our program should mainly set flags indicating the states of the keys themselves. A complete and functional keyboard ISR would look something like this:
#include <dos.h> /* For getvect and setvect functions */ #include <conio.h> /* For outp and inp macros */ /* Keyboard state tables */ static char keyIsPressed[128]; /* Flags indicating current keyboard state */ static char keyWasPressed[128]; /* Flags indicating state since last read */ static void interrupt far KeyboardInterruptHandler() { int scanCode; /* Storage for scan code */ register int i; /* Register is desirable */ enable(); /* Enable CPU interrupts immediately */ /* Substitute 'asm sti;' for enable() if this does not compile */ scanCode = inp(0x60); /* Read the key board scan code */ /* Acknowledge the reading of the scan code to the keyboard controller */ i = inp(0x61); /* Get keyboard control register */ outp(0x61, i | 0x80); /* Toggle acknowledge bit high */ outp(0x61, i); /* Toggle acknowledge bit low */ /* Acknowledge the interrupt to the programmable interrupt controller */ outp(0x20, 0x20); /* Signal non specific end of interrupt */ /* Set the appropriate flags in the state tables */ if (scanCode & 0x80) keyIsPressed[scanCode & 0x7F] = 0; else keyIsPressed[scanCode] = keyWasPressed[scanCode] = 1; }.
Send your questions, comments, or ideas here.