Keyboard driver portability

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.
Post Reply
NGiddings
Posts: 8
Joined: Fri Dec 30, 2016 12:23 pm

Keyboard driver portability

Post by NGiddings »

(This is my first post here. Hi.)

I've been working on a bootloader, and just got my keyboard "driver" to (barely) work on QEMU. It just disables the translator on the PS/2 controller and then each time it read a key press it writes the applicable ASCII character to the screen. However, just booted up on an old Athlon 64 desktop, and the driver doesn't look like it can correctly translate key presses. It "works" in the sense that it doesn't crash or do anything crazy, but the characters that show up on the screen have nothing to do with what I'm pressing.

The driver expects scan code set 2; since I've cleared the translate bit, isn't that what I should be getting? If I'm getting codes from another set, what do I need to do to ensure that I get set 2 on most systems?

For reference, here are a few keystrokes and what I see on screen:
Pause : "W)"
LShift: "V"
Right Arrow: "VP "

Since I can't get this problem in an emulator, it would be really cumbersome to try to test. I'm hoping someone here has dealt with it.

Keyboard "driver" w/o irrelevant parts:

Code: Select all

#include "keyboard.h"
#include "keyboardtranslator.h"
#include "keyboardevent.h"
#include "pio.h"
#include "system.h"

bool Keyboard::keyflags[128];

void Keyboard::initialize()
{
	waitForBuffer(BUFFER_INPUT, false);
	outb(0x64, 0x20);
	waitForBuffer(BUFFER_OUTPUT, true);
	uint8_t config = inb(0x60);
	config &= 0xBF;
	
	waitForBuffer(BUFFER_INPUT, false);
	outb(0x64, 0x60);
	outb(0x60, config);
}

void Keyboard::sendEvent(KeyboardEvent event)
{
	keyflags[event.getKeycode()] = event.isPressed();
	if(event.isPressed() && KeyboardTranslator::keycodeToAscii(event.getKeycode()) != '\0')
		System::putChar(KeyboardTranslator::keycodeToAscii(keyflags[0x33] ? event.getKeycode() + 0x60 : event.getKeycode()));
}

void Keyboard::scan()
{
	uint8_t scancode[8] = {0};
	int scancodeLength = 0;
	bool pressed = true;

	uint8_t byte = 0;
	do
	{
		byte = readByte();
		if(byte != 0)
		{
			scancode[scancodeLength] = byte;
			scancodeLength++;
		}
	} while(byte != 0);
	
	if(scancode[0] == 0xF0)
	{
		
		pressed = false;
	}
	else if(scancode[0] == 0xE0)
	{
		if(scancode[1] == 0xF0)
		{
			pressed = false;
		}
	}
	
	if(scancodeLength > 0)
	{
		uint8_t lookup = scancode[scancodeLength - 1] + (scancode[0] == 0xE0 ? 0x80 : 0x00);
		KeyboardEvent event(KeyboardTranslator::lookupToKeycode(lookup), pressed);
		sendEvent(event);
	}
}

uint8_t Keyboard::readByte()
{
	if(waitForBuffer(BUFFER_OUTPUT, true))
	{
		uint8_t b = inb(0x60);
		return b;
	}
	else
		return 0;
}

bool Keyboard::waitForBuffer(uint8_t buffer, bool state)
{
	int counter = 0;
	uint8_t status = 0x00;
	do
	{
		status = inb(0x64);
		io_wait();
		counter++;
		if(counter > 16)
			return false;
	} while((status & buffer) != (state ? buffer : 0));
	return true;
}
And if it's relevant, the translator (with my spreadsheet):
https://docs.google.com/spreadsheets/d/ ... edit#gid=0

Code: Select all

#include "keyboardtranslator.h"
#include "system.h"

uint8_t KeyboardTranslator::keycodeLookup[256];

uint8_t KeyboardTranslator::asciiLookup[256];

void KeyboardTranslator::initialize()
{
	uint8_t keycodes[256] = 
		{
			0xFF, 0x2C, 0xFF, 0x28, 0x26, 0x24, 0x25, 0x2F, 0xFF, 0x2D, 0x2B, 0x29, 0x27, 0x30, 0x3D, 0xFF, // 0
			0xFF, 0x32, 0x33, 0xFF, 0x34, 0x10, 0x1B, 0xFF, 0xFF, 0xFF, 0x19, 0x12, 0x00, 0x16, 0x1C, 0xFF, // 1
			0xFF, 0x02, 0x17, 0x03, 0x04, 0x1E, 0x1D, 0xFF, 0xFF, 0x31, 0x15, 0x05, 0x13, 0x11, 0x1F, 0xFF, // 2
			0xFF, 0x0D, 0x01, 0x07, 0x06, 0x18, 0x20, 0xFF, 0xFF, 0xFF, 0x0C, 0x09, 0x14, 0x21, 0x22, 0xFF, // 3
			0xFF, 0x36, 0x0A, 0x08, 0x0E, 0x1A, 0x23, 0xFF, 0xFF, 0x35, 0x37, 0x0B, 0x39, 0x0F, 0x38, 0xFF, // 4
			0xFF, 0xFF, 0x3E, 0xFF, 0x3A, 0x3C, 0xFF, 0xFF, 0x3F, 0x40, 0x41, 0x3B, 0xFF, 0x42, 0xFF, 0xFF, // 5
			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x43, 0xFF, 0xFF, 0x45, 0xFF, 0x48, 0x4B, 0xFF, 0xFF, 0xFF, // 6
			0x44, 0x4E, 0x46, 0x49, 0x4A, 0x4C, 0x52, 0x53, 0x2E, 0x4F, 0x47, 0x50, 0x51, 0x4D, 0x54, 0xFF, // 7
			0xFF, 0xFF, 0xFF, 0x2A, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 8
			0x55, 0x56, 0xFF, 0xFF, 0x57, 0x58, 0xFF, 0xFF, 0x59, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x5A, // 9
			0x5B, 0x5C, 0xFF, 0x5D, 0xFF, 0xFF, 0xFF, 0x5E, 0x5F, 0xFF, 0xFF, 0x60, 0xFF, 0xFF, 0xFF, 0x61, // A
			0x62, 0xFF, 0x63, 0xFF, 0x64, 0xFF, 0xFF, 0x65, 0x66, 0xFF, 0x67, 0x68, 0xFF, 0xFF, 0xFF, 0x69, // B
			0x6A, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x6B, 0xFF, 0x6C, 0xFF, 0xFF, 0x6D, 0xFF, 0xFF, // C
			0x6E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x6F, 0xFF, 0xFF, 0xFF, 0x70, 0xFF, // D
			0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x71, 0xFF, 0x72, 0x73, 0xFF, 0xFF, 0xFF, // E
			0x74, 0x75, 0x76, 0xFF, 0x77, 0x78, 0xFF, 0xFF, 0xFF, 0xFF, 0x79, 0xFF, 0xFF, 0x7A, 0xFF, 0xFF  // F
		};
	
	uint8_t ascii[256] = 
		{// 0    1    2    3    4    5    6    7    8    9    A    B    C    D    E    F
			'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',		// 0
			'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5',		// 1
			'6', '7', '8', '9', '\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',	// 2
			'\t',' ', '\0','\0','\0','.', ',', '/', '-', ';', '[', ']', '=', '`', '\'','\0',	// 3
			'\0','\n','\\','\0','0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '+', 	// 4
			'-', '*', '\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',	// 5
			'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',		// 6
			'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ')', '!', '@', '#', '$', '%',		// 7
			'^', '&', '*', '(', '\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',	// 8
			'\t',' ', '\0','\0','\0','>', '<', '?', '_', ':', '{', '}', '+', '~', '"', '\0', 	// 9
			'\0','\n','|', '\0','0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '+',		// A
			'-', '*', '\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',	// B
			'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',	// C
			'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0', 	// D
			'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',    // E
			'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0', 	// F
		};
	
	for(int i = 0; i < 256; i++)
	{
		keycodeLookup[i] = keycodes[i];
		asciiLookup[i] = ascii[i];
	}
}

uint8_t KeyboardTranslator::lookupToKeycode(uint8_t lookupID)
{
	return keycodeLookup[lookupID];
}

uint8_t KeyboardTranslator::keycodeToAscii(uint8_t keycode)
{
	return asciiLookup[keycode];
}

uint8_t KeyboardTranslator::asciiToKeycode(uint8_t ascii)
{
	
}
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Keyboard driver portability

Post by Brendan »

Hi,
NGiddings wrote:The driver expects scan code set 2; since I've cleared the translate bit, isn't that what I should be getting? If I'm getting codes from another set, what do I need to do to ensure that I get set 2 on most systems?
Is it a real PS/2 keyboard; or is it a USB keyboard where you can expect that the BIOS will do "PS/2 emulation" wrong?


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.
NGiddings
Posts: 8
Joined: Fri Dec 30, 2016 12:23 pm

Re: Keyboard driver portability

Post by NGiddings »

Brendan wrote:Hi,
Is it a real PS/2 keyboard; or is it a USB keyboard where you can expect that the BIOS will do "PS/2 emulation" wrong?
Shoot, I didn't even think of that!

It is a USB keyboard. I'll hook up a real PS/2 keyboard right now and see what happens.
NGiddings
Posts: 8
Joined: Fri Dec 30, 2016 12:23 pm

Re: Keyboard driver portability

Post by NGiddings »

Nope, still doesn't work. I have the exact same problem as before, but with an older keyboard.

This is very odd to me now. Are there any other steps needed to ensure that I'm getting the right code set? From everything I've read, set 2 is the default for the keyboard. I'll also mention that I've tried before to issue command 0xF0 (get/set scan code set), and QEMU just reboots. I found that that running

Code: Select all

outb(0x64, 0xF0); // Crashes here
outb(0x60, 2);
will cause the emulator to reboot the computer. It seems like a triple fault, since but I haven't properly tested my double fault handler, but I have no idea why it's happening.

Is that in any way relevant?
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Keyboard driver portability

Post by Brendan »

Hi,
NGiddings wrote:I found that that running

Code: Select all

outb(0x64, 0xF0); // Crashes here
outb(0x60, 2);
will cause the emulator to reboot the computer. It seems like a triple fault, since but I haven't properly tested my double fault handler, but I have no idea why it's happening.

Is that in any way relevant?
That one is easy. You're not sending that byte to the keyboard, it's a command for the PS/2 controller chip itself. The specific command 0xF0 tells the PS/2 controller chip to pulse "output lines 0 to 3" for 6 ms, and one of those output lines is connected to the computer's reset. It's not a triple fault...

To send bytes to a device connected to a PS/2 port (e.g. to send a keyboard plugged into the first PS/2 port), see this part of the wiki page.

For the original problem/code; I'm wondering if part of the problem is that you're ignoring the return value of "waitForBuffer()" everywhere, have a timeout that has nothing to do with time (e.g. depends on chipset and CPU speed and not time) and have a low "timeout" count.


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.
Post Reply