diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 62e28e49..3647406c 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -51,6 +51,7 @@ if("${LUNA_ARCH}" MATCHES "x86_64") src/arch/x86_64/Timer.cpp src/arch/x86_64/Thread.cpp src/arch/x86_64/PCI.cpp + src/arch/x86_64/Keyboard.cpp src/arch/x86_64/init/GDT.cpp src/arch/x86_64/init/IDT.cpp src/arch/x86_64/init/PIC.cpp diff --git a/kernel/src/arch/Keyboard.h b/kernel/src/arch/Keyboard.h new file mode 100644 index 00000000..de9e9370 --- /dev/null +++ b/kernel/src/arch/Keyboard.h @@ -0,0 +1,7 @@ +#pragma once +#include + +namespace Keyboard +{ + Option decode_scancode(u8 scancode); +} diff --git a/kernel/src/arch/x86_64/CPU.cpp b/kernel/src/arch/x86_64/CPU.cpp index 97c1c060..c74d1b6d 100644 --- a/kernel/src/arch/x86_64/CPU.cpp +++ b/kernel/src/arch/x86_64/CPU.cpp @@ -1,5 +1,6 @@ #include "arch/CPU.h" #include "Log.h" +#include "arch/Keyboard.h" #include "arch/Timer.h" #include "arch/x86_64/CPU.h" #include "arch/x86_64/IO.h" @@ -122,7 +123,8 @@ void io_thread() u8 scancode; while (!scancode_queue.try_pop(scancode)) { kernel_sleep(10); } - kinfoln("Read scancode: %#hhx", scancode); + char key; + if (Keyboard::decode_scancode(scancode).try_set_value(key)) { kdbgln("Read key: %c", key); } } } diff --git a/kernel/src/arch/x86_64/Keyboard.cpp b/kernel/src/arch/x86_64/Keyboard.cpp new file mode 100644 index 00000000..8ce2723c --- /dev/null +++ b/kernel/src/arch/x86_64/Keyboard.cpp @@ -0,0 +1,171 @@ +#include "arch/Keyboard.h" + +// PS/2 keyboard decoding routine. + +static bool is_key_released(u8& scancode) +{ + if (scancode > 0x80) + { + scancode -= 0x80; + return true; + } + return false; +} + +constexpr u8 EXTENDED_KEY_CODE = 0xe0; +constexpr u8 LEFT_SHIFT = 0x2a; +constexpr u8 RIGHT_SHIFT = 0x36; +constexpr u8 CAPS_LOCK = 0x3a; + +constexpr u8 LEFT_CONTROL = 0x1D; +constexpr u8 TAB = 0x0F; +constexpr u8 LEFT_ALT = 0x38; +constexpr u8 F11 = 0x57; +constexpr u8 F12 = 0x58; + +static bool should_ignore_key(u8 scancode) +{ + return (scancode > 0x3A && scancode < 0x47) || scancode == LEFT_CONTROL || scancode == TAB || + scancode == LEFT_ALT || scancode == F11 || scancode == F12; +} + +static bool g_ignore_next { false }; +static bool g_left_shift { false }; +static bool g_right_shift { false }; +static bool g_capslock { false }; + +constexpr char key_table[] = { + '\0', + '\1', // escape + '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', + '\0', // tab + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', + '\0', // left ctrl + 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', + '\0', // left shift + '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', + '\0', // right shift + '*', // keypad * + '\0', // left alt + ' ', + '\0', // caps lock + '\0', // f1 + '\0', // f2 + '\0', // f3 + '\0', // f4 + '\0', // f5 + '\0', // f6 + '\0', // f7 + '\0', // f8 + '\0', // f9 + '\0', // f10 + '\0', // num lock + '\0', // scroll lock + '7', // keypad 7 + '8', // keypad 8 + '9', // keypad 9 + '-', // keypad - + '4', // keypad 4 + '5', // keypad 5 + '6', // keypad 6 + '+', // keypad + + '1', // keypad 1 + '2', // keypad 2 + '3', // keypad 3 + '0', // keypad 0 + '.', // keypad . +}; + +constexpr char shifted_key_table[] = { + '\0', + '\1', // escape + '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b', + '\0', // tab + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n', + '\0', // left ctrl + 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', + '\0', // left shift + '|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', + '\0', // right shift + '*', // keypad * + '\0', // left alt + ' ', + '\0', // caps lock + '\0', // f1 + '\0', // f2 + '\0', // f3 + '\0', // f4 + '\0', // f5 + '\0', // f6 + '\0', // f7 + '\0', // f8 + '\0', // f9 + '\0', // f10 + '\0', // num lock + '\0', // scroll lock + '7', // keypad 7 + '8', // keypad 8 + '9', // keypad 9 + '-', // keypad - + '4', // keypad 4 + '5', // keypad 5 + '6', // keypad 6 + '+', // keypad + + '1', // keypad 1 + '2', // keypad 2 + '3', // keypad 3 + '0', // keypad 0 + '.', // keypad . +}; + +static bool is_shifted() +{ + if (g_capslock) return !(g_left_shift || g_right_shift); + return g_left_shift || g_right_shift; +} + +namespace Keyboard +{ + Option decode_scancode(u8 scancode) + { + if (g_ignore_next) + { + g_ignore_next = false; + return {}; + } + + // FIXME: Support extended scancodes. + if (scancode == EXTENDED_KEY_CODE) + { + g_ignore_next = true; + return {}; + } + + bool released = is_key_released(scancode); + + if (scancode == LEFT_SHIFT) + { + g_left_shift = !released; + return {}; + } + + if (scancode == RIGHT_SHIFT) + { + g_right_shift = !released; + return {}; + } + + if (scancode == CAPS_LOCK) + { + if (!released) g_capslock = !g_capslock; + return {}; + } + + if (should_ignore_key(scancode)) return {}; + + if (released) return {}; + + if (is_shifted()) return shifted_key_table[scancode]; + return key_table[scancode]; + } +}