Hello, in this section we will add ps/2 keyboard support and then we can interact with kernel in the console.
In this lecture, we are going to write a keyboard driver. If you want to test it on the real machine,
be sure to use ps/2 keyboard.
OK, let's get started.
As you see, the keyboard is connected to the PIC using irq1.
So the keyboard uses interrupt signal 1. We should enable it in the PIC chip
so that the keyboard interrupt
will be sent to the cpu.
Generally, the keyboard should be initialized before we use it. But in this example, we assume that
the initialization of the keyboard is done before the kernel is running.
So we only configure the PIC in the kernel file.
As you see here, here we masked all the interrupts except the timer interrupt in the previous lectures, and now we can
unmask the second one which is the irq1.
The next thing we are going to do is install the keyboard interrupt service routine so
that the handler will be called when the keyboard interrupt is fired.
We have done this several times before.
So in the trap.asm file,
we define vector33, push 0 which means there is no error code and index number 33
,
Then we jump to trap.
Declare it global so that we can reference it in other files.
In the trap.h file, we add the declaration of the vector33
and now we can set the corresponding idt entry 33 in this case in the trap.c file.
Ok now we can receive the keyboard requests in the interrupt handler.
In the function handler,
if the index number is 33, it means that this is the keyboard interrupt and we call keyboard handler
to process this request.
In the end, don’t forget to send end of interrupt
otherwise we will not receive keyboard interrupts after we return.
So let’s focus on the keyboard handler which is defined in the keyboard.c file.
As you see, we defined these arrays. Let’s first look at the keymap. These are the characters we have
in the keyboard.
Note that the data sent from keyboard to the handler is not the data we see here. The data is called
scan code, which means we need to convert the scan code to ascii code for the keyboard character
and print it on the screen.
So we define the keymap arrays to do that.
We have normal key map array
and shift key map array.
The shift map is used when we hold shift key and press other keys. As you see, the characters in this array
are the keys we get when we use shift key or capslock key. The shift code and lock code
include the scan code for shift and capslock.
Here we initialize the array with shift constants to the corresponding elements specified by the hexadecimal numbers
,
and other elements are set to 0.
The caps lock array is initialized in the same way.
The index numbers here are the scan codes of the left shift key,
right shift key and capslock key.
In this course, the console we will implement is simple which doesn’t support the numeric keypad
and function keys except capslock and shift keys.
Ok now let’s talk about how to read a key from keyboard. We do it in function keyboard read.
To read a scan code,
we read from port 60.
The in byte function is defined in the assembly code.
In the trap assembly file,
we define the in byte here. As you see, we save the argument which is port number in rdx register
and then read the data to register al using instruction in, and then we simply return.
Ok back to the c file.
After we return from in byte function, the variable scan code saves the data.
Before we move on, we need to talk a little bit about scan code. The scan code is sent to the handler when we press a key and release a key
.
So we have two scenarios.
key down and key up or make code and break code. The break code is the code of corresponding make code ors with 80
.
We have 3 sets of scan code. In this course, the keyboard driver works with scan code set 1
.
We have one byte code for a key, multiple-byte code used for function keys which will be sent one byte at a time
.
Most of the multiple-byte key code comes with E0 first.
So here we do the test.
So here we do the test. If the scan code starts with e0, we will add the e0 sign to the flag and return 0
meaning that the key is not valid.
If the key is not equal to e0, but the e0 is set in the flag, it means that the last scan code is e0,
so this key is a function key which is not used in the system
and we also clear the e0 flag for testing the following keys.
There is pause key which start with code e1. In this example, we don’t handle this case
and we should be fine since other codes in this key are not defined in the key map.
Alright, let’s move on.
The next if statement is for handling the key up. We will see it later. After we pass the check,
we can convert the key to the ascii code. All the code here
is handling the cases
where we press capslock key or hold the shift key.
We first check the shift status using shift code array.
Suppose the key is shift key, the scan code is used as an index into the array. So the shift constant
is selected in this case.
The constants are defined in the header file.
OK, let's move on.
Now if the key is shift key, the shift flag will be set in the flag variable. The next if statement
will find the shift flag is set
and we will get the key character in the shift key map which we want.
Otherwise we will get the character in the normal key map.
We know that when we hold the shift key and press other keys, the shift key is working as we expect.
But if we press the shift key and release it before we press other keys, the shift key will not work,
which means we need to check to see if the shift key is pressed when we receive other keys.
So this is what the code here actually does. To check the key up, we simply test the scan code with 80.
What it does here is just checking the bit 7.
If the bit 7 is set, it means this is a key up and we will find the key in the shift key array.
If the key we receive at this point is actually shift key, we will clear the shift flag
because after we release the shift key, it will not be working for the following keys.
Otherwise, it is a normal key
and the shift flag remains unchanged.
Ok now we have handled both the key down and key up cases.
That is what we do regarding to the shift key.
As you see, there is the capslock key we need to deal with as well. We know that the capslock key
doesn’t work in the same way as shift key does.
For example, after we press and release the caps lock, it will work until the next time we press and release it
.
So what we do here is we xor the capslock flag.
If the flag has no capslock flag, then doing xor will set the capslock flag in the variable flag
.
If the flag has capslock set, then it will clear the capslock flag.
It’s like toggle the key.
As for other keys, the lock code array simply returns 0 and xors with 0 doesn’t change the value.
With capslock case handled,
then we get to the last part.
If the capslock is on, we will change the capitalized English character to the lower-case one, and vice versa.
The lower-case letter is 32 larger than the corresponding upper case one.
So here we convert between these two sets of characters using 32.
The last thing we do is return the character and we are done.
In the keyboard handler, we just print the character on the screen if the return value is larger than 0
.
Here you can see we define array ch with 2 elements.
The reason we define the array is that the print function doesn’t support printing single character.
So instead we save it as a string with the second element being null character
and now we can print the character.
Before we test the project. There is one special case that is when we press backspace to delete characters.
We handle this case in the print.c file.
As you can see, in the function write screen, we can interpret enter key correctly.
Now we add another if statement to handle the backspace key which is this character.
The first thing we will do is check the current position. If we are at the beginning of the screen,
we do nothing and continue the loop.
Otherwise, we will check the column, if we are at the start of the line, we decrement the row
and change the column to the end of the previous line.
After all this is done, we can decrement the column value and write 0 in the current position
which will clear the character.
Let’s build the project and test it out.
Here we have program exception message because we just use the programs in the last lecture.
Alright,
we press some keys to do the test.
This is what we get when we just press key. If I hold shift key and press other keys,
you can see the key changes accordingly. Let’s test caps lock as well.
ok we get correct characters.
When we hold the shift key, you can see,
we print lowercase English letters when capslock is on.
Now, let's test the backspace key. You can see,
We're just delete the characters.
Alright, that's it for this lecture and see you in the next video.