The Keyboard

A keyboard is the most common way for a user to give a computer input, therefore
it is vital that you create a driver of some sort for handling and managing the
keyboard. When you get down to it, getting the basics of the keyboard isn't too
bad. Here we will show the basics: how to get a key when it is pressed, and how
to convert what's called a 'scancode' to standard ASCII characters that we can
understand properly.

A scancode is simply a key number. The keyboard assigns a number to each key on
the keyboard; this is your scancode. The scancodes are numbered generally from
top to bottom and left to right, with some minor exceptions to keep layouts
backwards compatible with older keyboards. You must use a lookup table (an array
of values) and use the scancode as the index into this table. The lookup table
is called a keymap, and will be used to translate scancodes into ASCII values
rather quickly and painlessly. One last note about a scancode before we head into
code is that if bit 7 is set (test with 'scancode & 0x80'), then this is the
keyboard's way of telling us that a key was just released. Create yourself a 'kb.h'
and do all your standard proceedures like adding a line for GCC and adding a file
to LD's command line.

Note that although we leave comments for the function keys and shift/control/alt,
we leave them as 0's in the array: You need to think up some random values such
as ASCII values that you normally wouldn't use so that you can trap them. I'll
leave this up to you, but you should keep a global variable to be used as a key
status variable. This keystatus variable will have 1 bit set for ALT, one for
CONTROL, and one for SHIFT. It's also a good idea to have one for CAPSLOCK,
NUMLOCK, and SCROLLLOCK. This tutorial will explain how to set the keyboard lights,
but we will leave it up to you to actually write the code for it.

The keyboard is attached to the computer through a special microcontroller chip on
your mainboard. This keyboard controller chip has 2 channels: one for the keyboard,
and one for the mouse. Also note that it is through this keyboard controller chip
that you would enable the A20 address line on the processor to allow you to access
memory past the 1MByte mark (GRUB enables this, you don't need to worry about it).
The keyboard controller, being a device accessible by the system, has an address on
the I/O bus that we can use for access and control. The keyboard controller has 2
main registers: a Data register at 0x60, and a Control register at 0x64. Anything
that the keyboard wants to send the computer is stored into the Data register.
The keyboard will raise IRQ1 whenever it has data for us to read. Observe:

/* Handles the keyboard interrupt */
void keyboard_handler(struct regs *r)
{
unsigned char scancode;
/* Read from the keyboard's data buffer */
scancode = inportb(0x60);
/* If the top bit of the byte we read from the keyboard is
* set, that means that a key has just been released */
if (scancode & 0x80)
{
/* You can use this one to see if the user released the
* shift, alt, or control keys... */
}
else
{
/* Here, a key was just pressed. Please note that if you
* hold a key down, you will get repeated key press
* interrupts. */
/* Just to show you how this works, we simply translate
* the keyboard scancode into an ASCII value, and then
* display it to the screen. You can get creative and
* use some flags to see if a shift is pressed and use a
* different layout, or you can add another 128 entries
* to the above layout to correspond to 'shift' being
* held. If shift is held using the larger lookup table,
* you would add 128 to the scancode when you look for it */
putch(kbdus[scancode]);
}
}

This might look intimidating, but it's 80% comments ;) Add to 'kb.c'

As you can see, the keyboard will generate an IRQ1 telling us that it has data
ready for us to grab. The keyboard's data register exists at 0x60. When the IRQ
happens, we call this handler which reads from port 0x60. This data that we read
is the keyboard's scancode. For this example, we check if the key was pressed or
released. If it was just pressed, we translate the scancode to ASCII, and print
that character out with one line. Write a 'keyboard_install' function that calls
'irq_install_handler' to install the custom keyboard handler for
'keyboard_handler' to IRQ1. Be sure to make a call to 'keyboard_install' from
inside 'main'.

In order to set the lights on your keyboard, you must send the keyboard controller
a command. There is a specific proceedure for sending the keyboard a command. You
must first wait for the keyboard controller to let you know when it's not busy. To
do this, you read from the Control register (When you read from it, it's called a
Status register) in a loop, breaking out when the keyboard isn't busy:

if ((inportb(0x64) & 2) == 0) break;

After that loop, you may write the command byte to the Data register. You don't
write to the control register itself except for in special cases. To set the
lights on the keyboard, you first send the command byte 0xED using the described
method, then you send the byte that says which lights are to be on or off. This
byte has the following format: Bit0 is Scroll lock, Bit1 is Num lock, and Bit2
is Caps lock.

Now that you have basic keyboard support, you may wish to expand upon the code.
This section on the keyboard was more to show you how to do the basics rather than
give an extremely detailed overview of all of the keyboard controller's functions.
Note that you use the keyboard controller to enable and handle the PS/2 mouse port.
The auxilliary channel on the keyboard controller manages the PS/2 mouse. Up to
this point we have a kernel that can draw to the screen, handle exceptions, handle
IRQs, handle the timer, and handle the keyboard. Click to find what's next in store
for your kernel development.