(Solved) Keyboard decoder logic

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
User avatar
Octacone
Member
Member
Posts: 1138
Joined: Fri Aug 07, 2015 6:13 am

Re: Keyboard decoder logic

Post by Octacone »

Schol-R-LEA wrote:
octacone wrote:
FusT wrote:You may be going way too fast for the PS/2 controller
"Too fast" (working on this for past 2 weeks). :P
I think that FusT meant that your handler is going to fast, not the code development, and is saying that you need to insert a wait of some kind into the code.
:oops: Oh, code is going too fast! That is probably why it was getting stuck. By looking at some old PS/2 code I was able to read all the correct port values. Also I added some buffer waiting and now I can write and read just fine. My self-test keeps failing I assume that is because of what Sortie said.

Edit:
I keep getting 0x71 as a response when sending 0xAA command. That should not happen at all.
OS: Basic OS
About: 32 Bit Monolithic Kernel Written in C++ and Assembly, Custom FAT 32 Bootloader
User avatar
Octacone
Member
Member
Posts: 1138
Joined: Fri Aug 07, 2015 6:13 am

Re: Keyboard decoder logic

Post by Octacone »

Hooray!
PS2andKeyboard.png
PS2andKeyboard.png (10.32 KiB) Viewed 4007 times
:wink: :-D 8)

After an entire night of bug haunting and writing my PS/2 controller, it is finally over. I can proudly say that I have one really solid PS/2 initialization sequence.
It was a rough ride, almost dropped it all cause of bug. Later I also found out that laptops will not allow me to use the second key set. My time was worth it. It is great when you code something and it pays off. Also I forgot to mention that I added like device types abstraction.

State machine hold in there, I am coming. It shouldn't take me more than a week to get it all sorted out.
OS: Basic OS
About: 32 Bit Monolithic Kernel Written in C++ and Assembly, Custom FAT 32 Bootloader
User avatar
Octacone
Member
Member
Posts: 1138
Joined: Fri Aug 07, 2015 6:13 am

Keyboard State Machine Logic

Post by Octacone »

I have a question about that state machine you guys were talking about. How am I supposed to read for e.g 10 bytes when I can only get one per interrupt? One key press = one interrupt, how do I read multiple bytes when I only get one? It all has to happen inside one interrupt because imagine pressing right alt for three times just to get one event... From my point of view it is impossible to achieve. Also other guys that were suggesting multiple tables for each situation but what about when there are two situations? For e.g caps lock is on, number pad is on and I am holding shift?
OS: Basic OS
About: 32 Bit Monolithic Kernel Written in C++ and Assembly, Custom FAT 32 Bootloader
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Keyboard State Machine Logic

Post by Brendan »

Hi,
octacone wrote:I have a question about that state machine you guys were talking about. How am I supposed to read for e.g 10 bytes when I can only get one per interrupt? One key press = one interrupt, how do I read multiple bytes when I only get one? It all has to happen inside one interrupt because imagine pressing right alt for three times just to get one event... From my point of view it is impossible to achieve.
I answered that question the last time you asked it. Mostly, each time an IRQ occurs you receive one byte, and you use the current state and the received byte to determine which state to transition to next and if a whole scancode has been received.
octacone wrote:Also other guys that were suggesting multiple tables for each situation but what about when there are two situations? For e.g caps lock is on, number pad is on and I am holding shift?
Imagine a "current mode" variable where bit 0 is used as a "with/without capslock" flag, bit 1 is used as a "with/without number lock" flag, bit 2 is used as a "with/without shift key" flag, etc.

Now imagine you convert (variable sized) scan-codes into your own (fixed sized 8-bit integer) key-code; and then did:

Code: Select all

    table_entry = &lookup_table[current_mode << 8 | key_code];
This means that you can have a single lookup table for everything.

After that, you could also do:

Code: Select all

    current_mode = (current_mode & table_entry->mode_mask) ^ table_entry->mode_reversed_flags;
This means that the lookup table can contain information to control/maintain the "current_mode" variable. Note that the "AND then XOR" thing allows the lookup table to clear bit/s, set bit/s, invert bit/s and leave bit/s unchanged; all without any branches or anything.

In that case (lookup table controls/maintains the "current_mode" variable) the keyboard driver's code doesn't really need to know/care what the lookup table uses each bit in the "current_mode" variable for. One lookup table (maybe for "International/English QWERTY") might use bit 0 for a "with/without AltGr" flag, and a different lookup table (maybe for "US Dvorak") might use bit 0 for "with/without capslock" flag.

However, you could design it is so that the lowest n bits of "current_mode" reflect the keyboard's LEDs; and have a "LED_mask" (that can be different for different keyboard layouts - e.g. loaded from a "keyboard layout file"). This allows you to do something like:

Code: Select all

    old_mode = current_mode;
    current_mode = (current_mode & table_entry->mode_mask) ^ table_entry->mode_reversed_flags;
    if( ((old_mode ^ current_mode) & LED_mask) != 0) {
        // LEDs have changed
        set_LEDs(current_mode & LED_mask);
    }
That way, the keyboard driver doesn't need to know/care about how many LEDs there are or what they're used for (only the "keyboard layout file" would know/care).

With some trickery; most of the keyboard driver (everything except loading/parsing the "keyboard layout file") could probably be done with less than 30 lines of code, including full support for all possible keyboard layouts.

Please note that I'm not necessarily suggesting you should write a keyboard driver exactly like this. Mostly I'm using "keyboard code" as a way to trick you into learning a few programming techniques that could be beneficial for more than just keyboard . ;)


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
User avatar
Octacone
Member
Member
Posts: 1138
Joined: Fri Aug 07, 2015 6:13 am

Re: Keyboard State Machine Logic

Post by Octacone »

Brendan wrote:Hi,
octacone wrote:I have a question about that state machine you guys were talking about. How am I supposed to read for e.g 10 bytes when I can only get one per interrupt? One key press = one interrupt, how do I read multiple bytes when I only get one? It all has to happen inside one interrupt because imagine pressing right alt for three times just to get one event... From my point of view it is impossible to achieve.
I answered that question the last time you asked it. Mostly, each time an IRQ occurs you receive one byte, and you use the current state and the received byte to determine which state to transition to next and if a whole scancode has been received.
octacone wrote:Also other guys that were suggesting multiple tables for each situation but what about when there are two situations? For e.g caps lock is on, number pad is on and I am holding shift?
Imagine a "current mode" variable where bit 0 is used as a "with/without capslock" flag, bit 1 is used as a "with/without number lock" flag, bit 2 is used as a "with/without shift key" flag, etc.


Now imagine you convert (variable sized) scan-codes into your own (fixed sized 8-bit integer) key-code; and then did:

Code: Select all

    table_entry = &lookup_table[current_mode << 8 | key_code];
This means that you can have a single lookup table for everything.

After that, you could also do:

Code: Select all

    current_mode = (current_mode & table_entry->mode_mask) ^ table_entry->mode_reversed_flags;
This means that the lookup table can contain information to control/maintain the "current_mode" variable. Note that the "AND then XOR" thing allows the lookup table to clear bit/s, set bit/s, invert bit/s and leave bit/s unchanged; all without any branches or anything.

In that case (lookup table controls/maintains the "current_mode" variable) the keyboard driver's code doesn't really need to know/care what the lookup table uses each bit in the "current_mode" variable for. One lookup table (maybe for "International/English QWERTY") might use bit 0 for a "with/without AltGr" flag, and a different lookup table (maybe for "US Dvorak") might use bit 0 for "with/without capslock" flag.

However, you could design it is so that the lowest n bits of "current_mode" reflect the keyboard's LEDs; and have a "LED_mask" (that can be different for different keyboard layouts - e.g. loaded from a "keyboard layout file"). This allows you to do something like:

Code: Select all

    old_mode = current_mode;
    current_mode = (current_mode & table_entry->mode_mask) ^ table_entry->mode_reversed_flags;
    if( ((old_mode ^ current_mode) & LED_mask) != 0) {
        // LEDs have changed
        set_LEDs(current_mode & LED_mask);
    }
That way, the keyboard driver doesn't need to know/care about how many LEDs there are or what they're used for (only the "keyboard layout file" would know/care).

With some trickery; most of the keyboard driver (everything except loading/parsing the "keyboard layout file") could probably be done with less than 30 lines of code, including full support for all possible keyboard layouts.

Please note that I'm not necessarily suggesting you should write a keyboard driver exactly like this. Mostly I'm using "keyboard code" as a way to trick you into learning a few programming techniques that could be beneficial for more than just keyboard . ;)


Cheers,

Brendan
Wow! This was very inspiring. I actually came up with something that could actually work very well if coded properly.
idea.png
This way I wouldn't have to have tens of lookup tables. I can see that your are trying to get me deeper these into coding techniques. :D
But still, I don't get one thing... Doesn't matter how many times I read your state machine reply, I still don't get it. I don't get the part where I should see what key it was. Like okay I received 0xE0 but that is it, how do I know if it was alt gr or pause?
confused.png
Also I never get pure 0xE0 it is more like other bytes for that key except 0xE0. :? :? :? :?
OS: Basic OS
About: 32 Bit Monolithic Kernel Written in C++ and Assembly, Custom FAT 32 Bootloader
Octocontrabass
Member
Member
Posts: 5587
Joined: Mon Mar 25, 2013 7:01 pm

Re: Keyboard decoder logic

Post by Octocontrabass »

octacone wrote:Like okay I received 0xE0 but that is it, how do I know if it was alt gr or pause?
The answer is: you don't! You have to wait until the next time you receive a byte to figure out what it is.

The state machine is how you remember that you received 0xE0 last time, so if you receive 0x38 this time, it means AltGr instead of Alt.
User avatar
Octacone
Member
Member
Posts: 1138
Joined: Fri Aug 07, 2015 6:13 am

Re: Keyboard decoder logic

Post by Octacone »

Octocontrabass wrote:
octacone wrote:Like okay I received 0xE0 but that is it, how do I know if it was alt gr or pause?
The answer is: you don't! You have to wait until the next time you receive a byte to figure out what it is.

The state machine is how you remember that you received 0xE0 last time, so if you receive 0x38 this time, it means AltGr instead of Alt.
But that is the problem. In order to store that I would have to press that key X times to get it only once.
Also can you explain how is this possible. When I press right alt:
how.png
how.png (477 Bytes) Viewed 3904 times
That is what I get when I print raw scan code. But I can't figure out how to harvest all those scan codes from only one read.
Like this is what I do:

Code: Select all

uint8_t scancode = Inportb(0x60);
	TTY_Put_Character('\n', 0x0F);
	TTY_Put_Hex(scancode, 0x0F);
See that "scancode" variable? How is that possible? How can it store all the scan codes but not allow me to read more than one?
OS: Basic OS
About: 32 Bit Monolithic Kernel Written in C++ and Assembly, Custom FAT 32 Bootloader
Octocontrabass
Member
Member
Posts: 5587
Joined: Mon Mar 25, 2013 7:01 pm

Re: Keyboard decoder logic

Post by Octocontrabass »

octacone wrote:But that is the problem. In order to store that I would have to press that key X times to get it only once.
Why?
User avatar
Octacone
Member
Member
Posts: 1138
Joined: Fri Aug 07, 2015 6:13 am

Re: Keyboard decoder logic

Post by Octacone »

Octocontrabass wrote:
octacone wrote:But that is the problem. In order to store that I would have to press that key X times to get it only once.
Why?
what.png
what.png (56.25 KiB) Viewed 3894 times
OS: Basic OS
About: 32 Bit Monolithic Kernel Written in C++ and Assembly, Custom FAT 32 Bootloader
User avatar
BrightLight
Member
Member
Posts: 901
Joined: Sat Dec 27, 2014 9:11 am
Location: Maadi, Cairo, Egypt
Contact:

Re: Keyboard decoder logic

Post by BrightLight »

I think you are missing the concept of the PS/2 escape sequence. Some keys (like Right Alt, arrow keys, ...) use the same scancodes as other keys, except that they have an escape sequence before the make code and before the break code. For example, 0x47 is the numpad 7 key, and 0xE0 0x47 is the Home key. Now, software that is aware of the escape sequence can parse it to determine whether it was the numpad key or the Home key that was pressed. Older software, which is not aware of the escape sequence, wouldn't break due to the changes; and instead will always assume the Home key has been pressed.
Assuming a user presses and releases the home key, the scancodes sent to the PS/2 driver would look like:

Code: Select all

0xE0 0x47 0xE0 0xC7
Now let's assume a user presses and releases the numpad 7 key:

Code: Select all

0x47 0xC7
Get the picture? Some scancodes can be "prefixed" with sequences that somewhat change their meaning. There is another escape sequence that uses the value 0xE1, but I haven't implemented this yet, but you can search for it. :)

EDIT FOR CLARIFICATION: Notice that you can only read ONE byte on each IRQ. Based on the example above assuming the user pressed and released the Home key, you will receive a total of four IRQs for the four scancode bytes sent to your driver.
You know your OS is advanced when you stop using the Intel programming guide as a reference.
User avatar
Octacone
Member
Member
Posts: 1138
Joined: Fri Aug 07, 2015 6:13 am

Re: Keyboard decoder logic

Post by Octacone »

omarrx024 wrote:I think you are missing the concept of the PS/2 escape sequence. Some keys (like Right Alt, arrow keys, ...) use the same scancodes as other keys, except that they have an escape sequence before the make code and before the break code. For example, 0x47 is the numpad 7 key, and 0xE0 0x47 is the Home key. Now, software that is aware of the escape sequence can parse it to determine whether it was the numpad key or the Home key that was pressed. Older software, which is not aware of the escape sequence, wouldn't break due to the changes; and instead will always assume the Home key has been pressed.
Assuming a user presses and releases the home key, the scancodes sent to the PS/2 driver would look like:

Code: Select all

0xE0 0x47 0xE0 0xC7
Now let's assume a user presses and releases the numpad 7 key:

Code: Select all

0x47 0xC7
Get the picture? Some scancodes can be "prefixed" with sequences that somewhat change their meaning. There is another escape sequence that uses the value 0xE1, but I haven't implemented this yet, but you can search for it. :)

EDIT FOR CLARIFICATION: Notice that you can only read ONE byte on each IRQ. Based on the example above assuming the user pressed and released the Home key, you will receive a total of four IRQs for the four scancode bytes sent to your driver.
Thanks!!!!!!!!!!!! =D> [-o< :lol:
Attachments
1j11bh.jpg
1j11bh.jpg (20.27 KiB) Viewed 3866 times
OS: Basic OS
About: 32 Bit Monolithic Kernel Written in C++ and Assembly, Custom FAT 32 Bootloader
User avatar
matt11235
Member
Member
Posts: 286
Joined: Tue Aug 02, 2016 1:52 pm
Location: East Riding of Yorkshire, UK

Re: Keyboard decoder logic

Post by matt11235 »

octacone wrote:
Octocontrabass wrote:
octacone wrote:But that is the problem. In order to store that I would have to press that key X times to get it only once.
Why?
what.png
Why is Travolta in your keyboard driver?
com.sun.java.swing.plaf.nimbus.InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneMaximizeButtonWindowNotFocusedState
Compiler Development Forum
alexfru
Member
Member
Posts: 1112
Joined: Tue Mar 04, 2014 5:27 am

Re: Keyboard decoder logic

Post by alexfru »

matt11235 wrote:Why is Travolta in your keyboard driver?
It could've been Homunculus Loxodontus, waiting for interrupts to arrive.
User avatar
Octacone
Member
Member
Posts: 1138
Joined: Fri Aug 07, 2015 6:13 am

Re: Keyboard decoder logic

Post by Octacone »

matt11235 wrote:
what.png
Why is Travolta in your keyboard driver?
Cause I feel the same as him. :P
alexfru wrote:
matt11235 wrote:Why is Travolta in your keyboard driver?
It could've been Homunculus Loxodontus, waiting for interrupts to arrive.
That thing looks amazing, it is so good I can't even pronounce it's name. :D
OS: Basic OS
About: 32 Bit Monolithic Kernel Written in C++ and Assembly, Custom FAT 32 Bootloader
User avatar
Octacone
Member
Member
Posts: 1138
Joined: Fri Aug 07, 2015 6:13 am

Re: Keyboard decoder logic

Post by Octacone »

Finally after some time I am getting there. Really really proud because of what I've done. I have a bug doe, how am I supposed to handle an equal state?
For example: 0xE0 = backspace, also 0xE0 == state 2 trigger. Then I need "case 0x1D:" but it is inside the same state...
BugsEtc.png

Code: Select all

case 0:
		{
			switch(scancode)
			{
                                ....
				case 0xE0:
				{
					current_state = 1; // && is defined backspace trigger <--------
					break;
				}
				default:
				{
					return;
				}
			}
			break;
		}
case 1:
		{
			switch(scancode)
			{
                                ...
				case 0x1D:
				{
					current_state = 2; // && also pause pressed <--------
					break;
				}
				default:
				{
					return;
				}
			}	
			break;
		}
OS: Basic OS
About: 32 Bit Monolithic Kernel Written in C++ and Assembly, Custom FAT 32 Bootloader
Post Reply