From 2385ee4e8bdaf969146c596a6a403beda774782c Mon Sep 17 00:00:00 2001 From: Trey Tomes Date: Wed, 30 Nov 2022 10:17:13 -0600 Subject: [PATCH 01/11] Fix the .toolchains path. Add some build notes. --- .gitignore | 1 + README.md | 6 ++++++ build_scripts/config.py | 4 +++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index b19d015..5b7a8e7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ build/ toolchain/ +.toolchains/ *.err bx_enh_dbg.ini .vscode/*.log diff --git a/README.md b/README.md index a6155f4..030a43f 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,11 @@ paru -S gcc make bison flex libgmp-static libmpc mpfr texinfo nasm mtools qemu-s ``` NOTE: to install all the required packages on Arch, you need an [AUR helper](https://wiki.archlinux.org/title/AUR_helpers). +I had to run this to get guestmount to work without sudo: +```bash +sudo dpkg-statoverride --update --add root root 0644 /boot/vmlinuz-`uname -r` +``` + Then you must run `python3 -m pip install -r requirements.txt` After that, run `scons toolchain`, this should download and build the required tools (binutils and GCC). If you encounter errors during this step, you might have to modify `build_scripts/config.mk` and try a different version of **binutils** and **gcc**. Using the same version as the one bundled with your distribution is your best bet. @@ -29,3 +34,4 @@ Finally, you should be able to run `scons`. Use `scons run` to test your OS usin * [Discord channel](https://discord.gg/RgHc5XrCEw) * [Patreon](https://www.patreon.com/nanobyte) + diff --git a/build_scripts/config.py b/build_scripts/config.py index b1c75a0..17ab25e 100644 --- a/build_scripts/config.py +++ b/build_scripts/config.py @@ -1,6 +1,8 @@ +import os + #config = 'release' #arch = 'i686' imageType = 'disk' imageFS = 'fat32' imageSize = '250m' -toolchain='../.toolchains' \ No newline at end of file +toolchain = os.path.abspath(os.path.join(os.getcwd(), '.toolchains')) From be84ca02258af0656cf66fe4ad8e84e5b51723a7 Mon Sep 17 00:00:00 2001 From: Trey Tomes Date: Wed, 30 Nov 2022 10:52:55 -0600 Subject: [PATCH 02/11] Begin writing a keyboard driver. --- .vscode/settings.json | 8 +++++++- src/kernel/arch/i686/irq.c | 2 +- src/kernel/main.c | 28 +++++++++++++++++++++++++++- 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 7022645..1c53aeb 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,12 @@ { "files.associations": { "x86.h": "c", - "stdio.h": "c" + "stdio.h": "c", + "hal.h": "c", + "stdint.h": "c", + "stdint-gcc.h": "c", + "cstdint": "c", + "ctype.h": "c", + "stdbool.h": "c" } } \ No newline at end of file diff --git a/src/kernel/arch/i686/irq.c b/src/kernel/arch/i686/irq.c index b86ee8f..f0b9253 100644 --- a/src/kernel/arch/i686/irq.c +++ b/src/kernel/arch/i686/irq.c @@ -59,7 +59,7 @@ void i686_IRQ_Initialize() i686_EnableInterrupts(); // g_Driver->Unmask(0); - // g_Driver->Unmask(1); + g_Driver->Unmask(1); } void i686_IRQ_RegisterHandler(int irq, IRQHandler handler) diff --git a/src/kernel/main.c b/src/kernel/main.c index ced6167..9dee154 100644 --- a/src/kernel/main.c +++ b/src/kernel/main.c @@ -1,3 +1,4 @@ +#include #include #include "stdio.h" #include "memory.h" @@ -6,6 +7,9 @@ #include #include +// For i686_inb/outb. +#include "arch/i686/io.h" + extern void _init(); void crash_me(); @@ -15,6 +19,27 @@ void timer(Registers* regs) printf("."); } +void keyboard(Registers* regs) { + uint8_t scancode = i686_inb(0x60); + + + uint8_t al = i686_inb(0x61); // keyboard control + uint8_t ah = al; + al = al | 0x80; // disable bit 7 + i686_outb(0x61, al); // send it back + al = ah; + i686_outb(0x61, al); // and send that back + + bool released = (scancode & 0x80) != 0; // Was the key released? + scancode = scancode & 0x7F; // Clip off the key released bit. + + if (released) { + printf("Key released: %d\r\n", scancode); + } else { + printf("Key pressed: %d\r\n", scancode); + } +} + void start(BootParams* bootParams) { // call global constructors @@ -39,7 +64,8 @@ void start(BootParams* bootParams) log_crit("Main", "This is a critical msg!"); printf("Nanobyte OS v0.1\n"); printf("This operating system is under construction.\n"); - //i686_IRQ_RegisterHandler(0, timer); + i686_IRQ_RegisterHandler(0, timer); + i686_IRQ_RegisterHandler(1, keyboard); //crash_me(); From c0d37d1a037429bb0bc5507bc6e833805431a3c2 Mon Sep 17 00:00:00 2001 From: Trey Tomes Date: Wed, 30 Nov 2022 18:31:33 -0600 Subject: [PATCH 03/11] Define some ctype functions. --- src/kernel/ctype.c | 21 +++++++++++++++++++++ src/kernel/ctype.h | 9 +++++++++ 2 files changed, 30 insertions(+) create mode 100644 src/kernel/ctype.c create mode 100644 src/kernel/ctype.h diff --git a/src/kernel/ctype.c b/src/kernel/ctype.c new file mode 100644 index 0000000..3671568 --- /dev/null +++ b/src/kernel/ctype.c @@ -0,0 +1,21 @@ +#include "ctype.h" + +bool islower(char ch) { + return ((ch >= 'a') && (ch <= 'z')); +} + +bool isupper(char ch) { + return ((ch >= 'A') && (ch <= 'Z')); +} + +bool isalpha(char ch) { + return isupper(ch) || islower(ch); +} + +char tolower(char ch) { + return isupper(ch) ? (ch - 'A' + 'a') : ch; +} + +char toupper(char ch) { + return islower(ch) ? (ch - 'a' + 'A') : ch; +} diff --git a/src/kernel/ctype.h b/src/kernel/ctype.h new file mode 100644 index 0000000..352bc33 --- /dev/null +++ b/src/kernel/ctype.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +bool islower(char ch); +bool isupper(char ch); +bool isalpha(char ch); +char tolower(char ch); +char toupper(char ch); From b2f2d20c21f65d5b48aaade934060165bc0eaa60 Mon Sep 17 00:00:00 2001 From: Trey Tomes Date: Wed, 30 Nov 2022 18:31:47 -0600 Subject: [PATCH 04/11] Settings. --- .vscode/settings.json | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 1c53aeb..5be560d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,6 +7,30 @@ "stdint-gcc.h": "c", "cstdint": "c", "ctype.h": "c", - "stdbool.h": "c" + "stdbool.h": "c", + "cstddef": "c", + "list": "c", + "string": "c", + "bit": "c", + "limits": "c", + "memory": "c", + "functional": "c", + "compare": "c", + "new": "c", + "*.in": "c", + "*.tcc": "c", + "optional": "c", + "ratio": "c", + "system_error": "c", + "type_traits": "c", + "variant": "c", + "array": "c", + "regex": "c", + "tuple": "c", + "utility": "c", + "memory.h": "c", + "irq.h": "c", + "bootparams.h": "c", + "io.h": "c" } } \ No newline at end of file From d7b7ad7dbbec583c4bcdfb86345cb46b02c649dc Mon Sep 17 00:00:00 2001 From: Trey Tomes Date: Wed, 30 Nov 2022 18:32:27 -0600 Subject: [PATCH 05/11] Unmask the IRQ when setting the handler. --- src/kernel/arch/i686/irq.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/kernel/arch/i686/irq.c b/src/kernel/arch/i686/irq.c index f0b9253..c7d52a5 100644 --- a/src/kernel/arch/i686/irq.c +++ b/src/kernel/arch/i686/irq.c @@ -57,12 +57,10 @@ void i686_IRQ_Initialize() // enable interrupts i686_EnableInterrupts(); - - // g_Driver->Unmask(0); - g_Driver->Unmask(1); } void i686_IRQ_RegisterHandler(int irq, IRQHandler handler) { g_IRQHandlers[irq] = handler; + g_Driver->Unmask(irq); } From 9d636ae583776a08c6e7328402bd3f8fcb2b5c16 Mon Sep 17 00:00:00 2001 From: Trey Tomes Date: Wed, 30 Nov 2022 18:32:42 -0600 Subject: [PATCH 06/11] Basic PS/2 keyboard driver. --- src/kernel/main.c | 335 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 316 insertions(+), 19 deletions(-) diff --git a/src/kernel/main.c b/src/kernel/main.c index 9dee154..5a36d4b 100644 --- a/src/kernel/main.c +++ b/src/kernel/main.c @@ -1,5 +1,6 @@ #include #include +#include "ctype.h" #include "stdio.h" #include "memory.h" #include @@ -19,27 +20,307 @@ void timer(Registers* regs) printf("."); } -void keyboard(Registers* regs) { - uint8_t scancode = i686_inb(0x60); - - - uint8_t al = i686_inb(0x61); // keyboard control - uint8_t ah = al; - al = al | 0x80; // disable bit 7 - i686_outb(0x61, al); // send it back - al = ah; - i686_outb(0x61, al); // and send that back +#define PS2_DATA_PORT 0x60 +#define PS2_REGISTER 0x64 // Read for status, write for command. + +/* + * The Status Register contains various flags that show the state of the PS/2 controller. The meanings for each bit are: + * + * Bit Meaning + * 0 Output buffer status (0 = empty, 1 = full) + * (must be set before attempting to read data from IO port 0x60) + * + * 1 Input buffer status (0 = empty, 1 = full) + * (must be clear before attempting to write data to IO port 0x60 or IO port 0x64) + * + * 2 System Flag + * Meant to be cleared on reset and set by firmware (via. PS/2 Controller Configuration Byte) if the system passes self tests (POST) + * + * 3 Command/data (0 = data written to input buffer is data for PS/2 device, 1 = data written to input buffer is data for PS/2 controller command) + * 4 Unknown (chipset specific) + * May be "keyboard lock" (more likely unused on modern systems) + * + * 5 Unknown (chipset specific) + * May be "receive time-out" or "second PS/2 port output buffer full" + * + * 6 Time-out error (0 = no error, 1 = time-out error) + * 7 Parity error (0 = no error, 1 = parity error) + */ +enum { + PS2_STATUS_OUTPUT_BUFFER = 0x01, + PS2_STATUS_INPUT_BUFFER = 0x02, + PS2_STATUS_SYSTEM_FLAG = 0x04, + PS2_STATUS_COMMAND = 0x08, + PS2_STATUS_UNKNOWN0 = 0x10, + PS2_STATUS_UNKNOWN1 = 0x20, + PS2_STATUS_ERROR_TIMEOUT = 0x40, + PS2_STATUS_ERROR_PARITY = 0x80 +} PS2_STATUS; + +enum { + PS2_RESPONSE_ACK = 0xFA, + PS2_RESPONSE_RESEND = 0xF3 +} PS2_RESPONSE; + +enum { + PS2_COMMAND_DISABLE_FIRST_PORT = 0xAD, + PS2_COMMAND_ENABLE_FIRST_PORT = 0xAE, + PS2_COMMAND_SET_LED = 0xED, + PS2_COMMAND_SCANCODE_SET = 0xF0, // get/set + PS2_COMMAND_SET_TYPEMATIC_RATE_AND_DELAY = 0xF3 +} PS2_COMMAND; + +enum { + PS2_SCANCODE_SET_1 = 0x43, + PS2_SCANCODE_SET_2 = 0x41, + PS2_SCANCODE_SET_3 = 0x3f +} PS2_SCANCODE_SET; + +bool Keyboard_keyStates[128] = { false }; + +char kbd_US_lcase[128] = { + 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', + '\t', /* <-- Tab */ + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', + 0, /* <-- control key */ + 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, + '*', + 0, /* Alt */ + ' ', /* Space bar */ + 0, /* Caps lock */ + 0, /* 59 - F1 key ... > */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, /* < ... F10 */ + 0, /* 69 - Num lock*/ + 0, /* Scroll Lock */ + 0, /* Home key */ + 0, /* Up Arrow */ + 0, /* Page Up */ + '-', + 0, /* Left Arrow */ + 0, + 0, /* Right Arrow */ + '+', + 0, /* 79 - End key*/ + 0, /* Down Arrow */ + 0, /* Page Down */ + 0, /* Insert Key */ + 0, /* Delete Key */ + 0, 0, 0, + 0, /* F11 Key */ + 0, /* F12 Key */ + 0, /* All other keys are undefined */ +}; + +char kbd_US_ucase[128] = { + 0, 27, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b', + '\t', /* <-- Tab */ + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n', + 0, /* <-- control key */ + 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', 0, '|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', 0, + '*', + 0, /* Alt */ + ' ', /* Space bar */ + 0, /* Caps lock */ + 0, /* 59 - F1 key ... > */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, /* < ... F10 */ + 0, /* 69 - Num lock*/ + 0, /* Scroll Lock */ + 0, /* Home key */ + 0, /* Up Arrow */ + 0, /* Page Up */ + '-', + 0, /* Left Arrow */ + 0, + 0, /* Right Arrow */ + '+', + 0, /* 79 - End key*/ + 0, /* Down Arrow */ + 0, /* Page Down */ + 0, /* Insert Key */ + 0, /* Delete Key */ + 0, 0, 0, + 0, /* F11 Key */ + 0, /* F12 Key */ + 0, /* All other keys are undefined */ +}; + +typedef struct { + uint8_t scancode; + bool pressed; + char ascii; +} Keyboard_key; + +Keyboard_key g_LastKeyEvent; + +void Keyboard_waitForBufferEmpty() { + // Wait until the 8042 is done processing the current command. + uint16_t count = 0xFFFF; + do { + uint8_t status = i686_inb(PS2_REGISTER); + if ((status & PS2_STATUS_INPUT_BUFFER) != 0) { + break; + } + } while (count--); +} + +/* + * Send the given command to the 8042 keyboard controller. + * + * command: The command byte to send. + */ +void Keyboard_setCommand(uint8_t command) { + i686_DisableInterrupts(); + + Keyboard_waitForBufferEmpty(); + + // Send the command. + i686_outb(PS2_REGISTER, command); + + i686_EnableInterrupts(); +} + +void Keyboard_sendCommand(uint8_t command) { + // Disable the keyboard. + Keyboard_setCommand(PS2_COMMAND_DISABLE_FIRST_PORT); + + i686_DisableInterrupts(); + + // Wait until the 8042 is done processing the current command. + Keyboard_waitForBufferEmpty(); + + // Enable the keyboard. + Keyboard_setCommand(PS2_COMMAND_ENABLE_FIRST_PORT); - bool released = (scancode & 0x80) != 0; // Was the key released? - scancode = scancode & 0x7F; // Clip off the key released bit. + // Send the data to port 0x60. + i686_outb(PS2_DATA_PORT, command); + + i686_EnableInterrupts(); +} + +/** + * Set the value of the 3 LEDs. + * + * scrollLock: Turn on the Scroll Lock LED. + * numLock: Turn on the Num Lock LED. + * capsLock: Turn on the Caps Lock LED. + */ +void Keyboard_setLEDs(bool scrollLock, bool numLock, bool capsLock) { + Keyboard_sendCommand(PS2_COMMAND_SET_LED); + + // Bit values 0..2 = scroll, num, caps. + uint8_t value = (scrollLock ? 0x1 : 0x0) + (numLock ? 0x2 : 0x0) + (capsLock ? 0x4 : 0x0); + Keyboard_sendCommand(value); +} + +/** + * Set how fast keys are repeated, and how long before key repeats begin. + * + * repeatRate: 5-bit number (00000b = 30 Hz, ..., 11111b = 2 Hz) + * repeatDelay: delay before keys repeat, 2-bit number (00b = 250 ms, 01b = 500 ms, 10b = 750 ms, 11b = 1000 ms) + */ +void Keyboard_setTypematicRateAndDelay(uint8_t repeatRate, uint8_t repeatDelay) { + uint8_t portValue = (repeatRate & 0x1F) + ((repeatDelay & 0x03) << 5); + Keyboard_sendCommand(PS2_COMMAND_SET_TYPEMATIC_RATE_AND_DELAY); + Keyboard_sendCommand(portValue); +} + +int Keyboard_getScancodeSet() { + do { + Keyboard_sendCommand(PS2_COMMAND_SCANCODE_SET); // Get/set current scancode set. + Keyboard_sendCommand(0); // Get scancode set. + } while (i686_inb(PS2_DATA_PORT) == PS2_RESPONSE_RESEND); + + uint8_t set = i686_inb(PS2_DATA_PORT); + switch (set) { + case PS2_SCANCODE_SET_1: + return 1; + case PS2_SCANCODE_SET_3: + return 3; + case PS2_SCANCODE_SET_2: + default: + return 2; + } +} + +/* + * setNumber: Scancode sets 1-3. + */ +void Keyboard_setScancodeSet(uint8_t setNumber) { + if (Keyboard_getScancodeSet() == setNumber) { + return; + } - if (released) { - printf("Key released: %d\r\n", scancode); + do { + Keyboard_sendCommand(PS2_COMMAND_SCANCODE_SET); // Get/set current scancode set. + Keyboard_sendCommand(setNumber); // Set scancode set. + } while (i686_inb(PS2_DATA_PORT) == PS2_RESPONSE_RESEND); +} + +bool Keyboard_isKeyPressed(uint8_t scancode) { + return Keyboard_keyStates[scancode]; +} + +bool Keyboard_isShiftPressed() { + return Keyboard_isKeyPressed(0x2a) || Keyboard_isKeyPressed(0x36); +} + +/** + * Process the active key event. + */ +Keyboard_key Keyboard_processKey() { + Keyboard_key k; + + k.scancode = i686_inb(PS2_DATA_PORT); + + // The high bit from the keyboard indicates if the key was pressed or released. + k.pressed = (k.scancode & 0x80) == 0; + + // The remaining 7 bits tell us the scancode. + k.scancode = k.scancode & 0x7F; + + if (Keyboard_isShiftPressed()) { + k.ascii = kbd_US_ucase[k.scancode]; } else { - printf("Key pressed: %d\r\n", scancode); + k.ascii = kbd_US_lcase[k.scancode]; + } + + return k; +} + +void Keyboard_irqHandler(Registers* regs) { + g_LastKeyEvent = Keyboard_processKey(); + + Keyboard_keyStates[g_LastKeyEvent.scancode] = g_LastKeyEvent.pressed; + + if (g_LastKeyEvent.scancode != 0) { + //printf("Key %s: %x, %d\r\n", k.pressed ? "pressed" : "released", k.scancode, k.ascii); + if (g_LastKeyEvent.pressed) { + if (g_LastKeyEvent.ascii != 0) { + putc(g_LastKeyEvent.ascii); + } + } } } +/** + * Return the active key event. + * After reading the event, g_LastKeyEvent.scancode will be reset to 0. + */ +/* +Keyboard_key Keyboard_readKey() { + Keyboard_key nextKey; + nextKey.scancode = g_LastKeyEvent.scancode; + nextKey.ascii = g_LastKeyEvent.ascii; + nextKey.pressed = g_LastKeyEvent.pressed; + g_LastKeyEvent.scancode = 0; + g_LastKeyEvent.ascii = 0; + g_LastKeyEvent.pressed = false; + return nextKey; +} +*/ + void start(BootParams* bootParams) { // call global constructors @@ -57,16 +338,32 @@ void start(BootParams* bootParams) bootParams->Memory.Regions[i].Type); } - log_info("Main", "This is an info msg!"); log_warn("Main", "This is a warning msg!"); log_err("Main", "This is an error msg!"); log_crit("Main", "This is a critical msg!"); - printf("Nanobyte OS v0.1\n"); + printf("BASIC-OS v0.1, forked from Nanobyte OS v0.1\n"); printf("This operating system is under construction.\n"); - i686_IRQ_RegisterHandler(0, timer); - i686_IRQ_RegisterHandler(1, keyboard); + //i686_IRQ_RegisterHandler(0, timer); + + Keyboard_sendCommand(PS2_COMMAND_DISABLE_FIRST_PORT); + //Keyboard_etLEDs(true, true, true); // Doesn't work in QEMU? + //Keyboard_setTypematicRateAndDelay(0x1F, 0); + Keyboard_setScancodeSet(2); // Just to be sure. + i686_IRQ_RegisterHandler(1, Keyboard_irqHandler); + + while (true) { + /* + Keyboard_key k = Keyboard_readKey(); + if (k.scancode != 0) { + //printf("Key %s: %x, %d\r\n", k.pressed ? "pressed" : "released", k.scancode, k.ascii); + if (k.ascii != 0) { + putc(k.ascii); + } + } + */ + } //crash_me(); end: From b375e201a8569ddea99193e051494f0430978e6e Mon Sep 17 00:00:00 2001 From: Trey Tomes Date: Wed, 30 Nov 2022 18:34:53 -0600 Subject: [PATCH 07/11] Update README.md --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 030a43f..7726727 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,9 @@ +# basic_os + +My work in progress. First step is getting the keyboard controller fully operational, then I can start working on parsing user input into something interesting. + +--- + # nanobyte_os This repository contains the code from the ["Building an OS"](https://www.youtube.com/watch?v=9t-SPC7Tczc&list=PLFjM7v6KGMpiH2G-kT781ByCNC_0pKpPN) series on the ["Nanobyte"](https://www.youtube.com/channel/UCSPIuWADJIMIf9Erf--XAsA) YouTube channel. From 6a53bba488c72c0cfb689dbec8e602322f6622b0 Mon Sep 17 00:00:00 2001 From: Trey Tomes Date: Thu, 1 Dec 2022 16:19:32 -0600 Subject: [PATCH 08/11] Greatly improved keyboard interface. --- .vscode/settings.json | 6 +- src/kernel/arch/i686/i8042.c | 158 +++++++++++++++++ src/kernel/arch/i686/i8042.h | 99 +++++++++++ src/kernel/arch/i686/irq.c | 2 +- src/kernel/arch/i686/ps2.h | 10 ++ src/kernel/hal/hal.c | 2 + src/kernel/hal/keyboard.c | 228 ++++++++++++++++++++++++ src/kernel/hal/keyboard.h | 21 +++ src/kernel/main.c | 335 +++-------------------------------- 9 files changed, 551 insertions(+), 310 deletions(-) create mode 100644 src/kernel/arch/i686/i8042.c create mode 100644 src/kernel/arch/i686/i8042.h create mode 100644 src/kernel/arch/i686/ps2.h create mode 100644 src/kernel/hal/keyboard.c create mode 100644 src/kernel/hal/keyboard.h diff --git a/.vscode/settings.json b/.vscode/settings.json index 5be560d..fdb0dce 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -31,6 +31,10 @@ "memory.h": "c", "irq.h": "c", "bootparams.h": "c", - "io.h": "c" + "io.h": "c", + "chrono": "c", + "i8042.h": "c", + "i8259.h": "c", + "keyboard.h": "c" } } \ No newline at end of file diff --git a/src/kernel/arch/i686/i8042.c b/src/kernel/arch/i686/i8042.c new file mode 100644 index 0000000..c3bf50b --- /dev/null +++ b/src/kernel/arch/i686/i8042.c @@ -0,0 +1,158 @@ +#include "i8042.h" + +// For i686_inb/outb. +#include "arch/i686/io.h" + +static PS2Driver g_PS2Driver = { + .Name = "8042 PS/2", + .KeyboardPortNumber = -1, + .MousePortNumber = -1 +}; + +/** + * Wait until the 8042 is done processing the current command. + */ +void i8042_waitForBufferEmpty() { + uint16_t count = 0xFFFF; + do { + uint8_t status = i686_inb(PS2_REGISTER); + if ((status & PS2_STATUS_INPUT_BUFFER) != 0) { + break; + } + } while (count--); +} + +/* + * Send the given command to the 8042 PS/2 controller. + * + * command: The command byte to send. + */ +void i8042_setCommand(uint8_t command) { + i686_DisableInterrupts(); + + i8042_waitForBufferEmpty(); + + // Send the command. + i686_outb(PS2_REGISTER, command); + + i686_EnableInterrupts(); +} + +/** + * portNumber: [0..1] to specify the first or second PS/2 port. + */ +void i8042_sendCommand(uint8_t command, int portNumber) { + // Disable the port. + i8042_setCommand((portNumber == 0) ? PS2_COMMAND_DISABLE_FIRST_PORT : PS2_COMMAND_DISABLE_SECOND_PORT); + + i686_DisableInterrupts(); + + // Wait until the 8042 is done processing the current command. + i8042_waitForBufferEmpty(); + + if (portNumber != 0) { + i8042_setCommand(PS2_COMMAND_SELECT_PORT2); // Address the second PS/2 port. + } + + // Enable the keyboard. + i8042_setCommand((portNumber == 0) ? PS2_COMMAND_ENABLE_FIRST_PORT : PS2_COMMAND_ENABLE_SECOND_PORT); + + // Send the data to port 0x60. + i686_outb(PS2_DATA_PORT, command); + + i686_EnableInterrupts(); +} + +void i8042_disableScanning(int portNumber) { + do { + i8042_sendCommand(PS2_COMMAND_DISABLE_SCANNING, portNumber); // Disable Scanning + } while (i686_inb(PS2_DATA_PORT) == PS2_RESPONSE_RESEND); +} + +void i8042_enableScanning(int portNumber) { + do { + i8042_sendCommand(PS2_COMMAND_ENABLE_SCANNING, portNumber); // Enable Scanning + } while (i686_inb(PS2_DATA_PORT) == PS2_RESPONSE_RESEND); +} + +/* + * Inputs: + * portNumber: [0, 1] for the first or second PS/2 ports respectively. + */ +PS2_DEVICE_TYPE i8042_getDeviceType(uint8_t portNumber) { + i8042_disableScanning(portNumber); + i8042_waitForBufferEmpty(); + do { + i8042_sendCommand(PS2_COMMAND_IDENTIFY_HARDWARE, portNumber); // Identify + } while (i686_inb(PS2_DATA_PORT) == PS2_RESPONSE_RESEND); + + PS2_DEVICE_TYPE type = PS2_DEVICE_TYPE_KEYBOARD_AT; + for (int n = 0; n < 16; n++) { + uint8_t response = i686_inb(PS2_DATA_PORT); + switch (response) { + case 0x00: + type = PS2_DEVICE_TYPE_MOUSE_STANDARD; + break; + case 0x03: + type = PS2_DEVICE_TYPE_MOUSE_WHEEL; + break; + case 0x04: + type = PS2_DEVICE_TYPE_MOUSE_5BUTTON; + break; + case 0x41: + case 0xC1: + type = PS2_DEVICE_TYPE_KEYBOARD_MF2_TRANSLATED; + break; + case 0x83: + type = PS2_DEVICE_TYPE_KEYBOARD_MF2; + break; + } + if (type != PS2_DEVICE_TYPE_KEYBOARD_AT) { + break; + } + } + + i8042_enableScanning(portNumber); + return type; +} + +PS2_DEVICE_CATEGORY i8042_getDeviceCategory(uint8_t portNumber) { + switch (i8042_getDeviceType(portNumber)) { + case PS2_DEVICE_TYPE_MOUSE_STANDARD: + case PS2_DEVICE_TYPE_MOUSE_WHEEL: + case PS2_DEVICE_TYPE_MOUSE_5BUTTON: + return PS2_DEVICE_CATEGORY_MOUSE; + default: + case PS2_DEVICE_TYPE_KEYBOARD_AT: + case PS2_DEVICE_TYPE_KEYBOARD_MF2: + case PS2_DEVICE_TYPE_KEYBOARD_MF2_TRANSLATED: + return PS2_DEVICE_CATEGORY_KEYBOARD; + } +} + +void i8042_Initialize() { + g_PS2Driver.KeyboardPortNumber = -1; + g_PS2Driver.MousePortNumber = -1; + + switch (i8042_getDeviceCategory(0)) { + case PS2_DEVICE_CATEGORY_KEYBOARD: + g_PS2Driver.KeyboardPortNumber = 0; + break; + case PS2_DEVICE_CATEGORY_MOUSE: + g_PS2Driver.MousePortNumber = 0; + break; + } + + switch (i8042_getDeviceCategory(1)) { + case PS2_DEVICE_CATEGORY_KEYBOARD: + g_PS2Driver.KeyboardPortNumber = 1; + break; + case PS2_DEVICE_CATEGORY_MOUSE: + g_PS2Driver.MousePortNumber = 1; + break; + } +} + +const PS2Driver* i8042_getDriver() { + return &g_PS2Driver; +} \ No newline at end of file diff --git a/src/kernel/arch/i686/i8042.h b/src/kernel/arch/i686/i8042.h new file mode 100644 index 0000000..e308f69 --- /dev/null +++ b/src/kernel/arch/i686/i8042.h @@ -0,0 +1,99 @@ +#pragma once + +#include +#include "ps2.h" + +#define PS2_DATA_PORT 0x60 +#define PS2_REGISTER 0x64 // Read for status, write for command. + +/* + * Byte/s Device Type + * None Ancient AT keyboard with translation enabled in the PS/Controller (not possible for the second PS/2 port) + * 0x00 Standard PS/2 mouse + * 0x03 Mouse with scroll wheel + * 0x04 5-button mouse + * 0xAB, 0x41 or 0xAB, 0xC1 MF2 keyboard with translation enabled in the PS/Controller (not possible for the second PS/2 port) + * 0xAB, 0x83 MF2 keyboard + */ +typedef enum { + PS2_DEVICE_TYPE_KEYBOARD_AT, + PS2_DEVICE_TYPE_MOUSE_STANDARD, + PS2_DEVICE_TYPE_MOUSE_WHEEL, + PS2_DEVICE_TYPE_MOUSE_5BUTTON, + PS2_DEVICE_TYPE_KEYBOARD_MF2_TRANSLATED, + PS2_DEVICE_TYPE_KEYBOARD_MF2 +} PS2_DEVICE_TYPE; + +typedef enum { + PS2_DEVICE_CATEGORY_MOUSE, + PS2_DEVICE_CATEGORY_KEYBOARD +} PS2_DEVICE_CATEGORY; + +typedef enum { + PS2_COMMAND_DISABLE_FIRST_PORT = 0xAD, + PS2_COMMAND_ENABLE_FIRST_PORT = 0xAE, + PS2_COMMAND_DISABLE_SECOND_PORT = 0xA7, + PS2_COMMAND_ENABLE_SECOND_PORT = 0xA8, + PS2_COMMAND_SELECT_PORT2 = 0xD4, + PS2_COMMAND_SET_LED = 0xED, + PS2_COMMAND_SCANCODE_SET = 0xF0, // get/set + PS2_COMMAND_IDENTIFY_HARDWARE = 0xF2, + PS2_COMMAND_SET_TYPEMATIC_RATE_AND_DELAY = 0xF3, + PS2_COMMAND_ENABLE_SCANNING = 0xF4, + PS2_COMMAND_DISABLE_SCANNING = 0xF5 +} PS2_COMMAND; + +typedef enum { + PS2_SCANCODE_SET_1 = 0x43, + PS2_SCANCODE_SET_2 = 0x41, + PS2_SCANCODE_SET_3 = 0x3f +} PS2_SCANCODE_SET; + +/* + * The Status Register contains various flags that show the state of the PS/2 controller. The meanings for each bit are: + * + * Bit Meaning + * 0 Output buffer status (0 = empty, 1 = full) + * (must be set before attempting to read data from IO port 0x60) + * + * 1 Input buffer status (0 = empty, 1 = full) + * (must be clear before attempting to write data to IO port 0x60 or IO port 0x64) + * + * 2 System Flag + * Meant to be cleared on reset and set by firmware (via. PS/2 Controller Configuration Byte) if the system passes self tests (POST) + * + * 3 Command/data (0 = data written to input buffer is data for PS/2 device, 1 = data written to input buffer is data for PS/2 controller command) + * 4 Unknown (chipset specific) + * May be "keyboard lock" (more likely unused on modern systems) + * + * 5 Unknown (chipset specific) + * May be "receive time-out" or "second PS/2 port output buffer full" + * + * 6 Time-out error (0 = no error, 1 = time-out error) + * 7 Parity error (0 = no error, 1 = parity error) + */ +typedef enum { + PS2_STATUS_OUTPUT_BUFFER = 0x01, + PS2_STATUS_INPUT_BUFFER = 0x02, + PS2_STATUS_SYSTEM_FLAG = 0x04, + PS2_STATUS_COMMAND = 0x08, + PS2_STATUS_UNKNOWN0 = 0x10, + PS2_STATUS_UNKNOWN1 = 0x20, + PS2_STATUS_ERROR_TIMEOUT = 0x40, + PS2_STATUS_ERROR_PARITY = 0x80 +} PS2_STATUS; + +typedef enum { + PS2_RESPONSE_ACK = 0xFA, + PS2_RESPONSE_RESEND = 0xF3 +} PS2_RESPONSE; + +void i8042_waitForBufferEmpty(); +void i8042_setCommand(uint8_t command); +void i8042_sendCommand(uint8_t command, int portNumber); +void i8042_disableScanning(int portNumber); +void i8042_enableScanning(int portNumber); +PS2_DEVICE_TYPE i8042_getDeviceType(uint8_t portNumber); +PS2_DEVICE_CATEGORY i8042_getDeviceCategory(uint8_t portNumber); +void i8042_Initialize(); +const PS2Driver* i8042_getDriver(); diff --git a/src/kernel/arch/i686/irq.c b/src/kernel/arch/i686/irq.c index c7d52a5..7d1ac0b 100644 --- a/src/kernel/arch/i686/irq.c +++ b/src/kernel/arch/i686/irq.c @@ -48,7 +48,7 @@ void i686_IRQ_Initialize() return; } - log_info(MODULE, "Found %s PIC.", g_Driver->Name); + log_info(MODULE, "Found %s.", g_Driver->Name); g_Driver->Initialize(PIC_REMAP_OFFSET, PIC_REMAP_OFFSET + 8, false); // register ISR handlers for each of the 16 irq lines diff --git a/src/kernel/arch/i686/ps2.h b/src/kernel/arch/i686/ps2.h new file mode 100644 index 0000000..ba344e2 --- /dev/null +++ b/src/kernel/arch/i686/ps2.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include + +typedef struct { + const char* Name; + int KeyboardPortNumber; + int MousePortNumber; +} PS2Driver; diff --git a/src/kernel/hal/hal.c b/src/kernel/hal/hal.c index 16ac9bb..86474da 100644 --- a/src/kernel/hal/hal.c +++ b/src/kernel/hal/hal.c @@ -4,6 +4,7 @@ #include #include #include +#include void HAL_Initialize() { @@ -12,4 +13,5 @@ void HAL_Initialize() i686_IDT_Initialize(); i686_ISR_Initialize(); i686_IRQ_Initialize(); + i8042_Initialize(); } diff --git a/src/kernel/hal/keyboard.c b/src/kernel/hal/keyboard.c new file mode 100644 index 0000000..cce4a03 --- /dev/null +++ b/src/kernel/hal/keyboard.c @@ -0,0 +1,228 @@ +#include "keyboard.h" +#include + +// For i686_inb/outb. +#include + +#include + +#include +#include +#include + +bool Keyboard_keyStates[128] = { false }; + +static char kbd_US_lcase[128] = { + 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', + '\t', /* <-- Tab */ + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', + 0, /* <-- control key */ + 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, + '*', + 0, /* Alt */ + ' ', /* Space bar */ + 0, /* Caps lock */ + 0, /* 59 - F1 key ... > */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, /* < ... F10 */ + 0, /* 69 - Num lock*/ + 0, /* Scroll Lock */ + 0, /* Home key */ + 0, /* Up Arrow */ + 0, /* Page Up */ + '-', + 0, /* Left Arrow */ + 0, + 0, /* Right Arrow */ + '+', + 0, /* 79 - End key*/ + 0, /* Down Arrow */ + 0, /* Page Down */ + 0, /* Insert Key */ + 0, /* Delete Key */ + 0, 0, 0, + 0, /* F11 Key */ + 0, /* F12 Key */ + 0, /* All other keys are undefined */ +}; + +static char kbd_US_ucase[128] = { + 0, 27, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b', + '\t', /* <-- Tab */ + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n', + 0, /* <-- control key */ + 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', 0, '|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', 0, + '*', + 0, /* Alt */ + ' ', /* Space bar */ + 0, /* Caps lock */ + 0, /* 59 - F1 key ... > */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, /* < ... F10 */ + 0, /* 69 - Num lock*/ + 0, /* Scroll Lock */ + 0, /* Home key */ + 0, /* Up Arrow */ + 0, /* Page Up */ + '-', + 0, /* Left Arrow */ + 0, + 0, /* Right Arrow */ + '+', + 0, /* 79 - End key*/ + 0, /* Down Arrow */ + 0, /* Page Down */ + 0, /* Insert Key */ + 0, /* Delete Key */ + 0, 0, 0, + 0, /* F11 Key */ + 0, /* F12 Key */ + 0, /* All other keys are undefined */ +}; + +static uint8_t g_PS2PortKeyboard = 0xFF; + +#define SIZE_KEYBUFFER 32 +static Keyboard_key g_KeyBuffer[SIZE_KEYBUFFER]; +static int g_KeyBufferIndex = SIZE_KEYBUFFER; + +/** + * Set the value of the 3 LEDs. + * + * scrollLock: Turn on the Scroll Lock LED. + * numLock: Turn on the Num Lock LED. + * capsLock: Turn on the Caps Lock LED. + */ +void Keyboard_setLEDs(bool scrollLock, bool numLock, bool capsLock) { + i8042_sendCommand(PS2_COMMAND_SET_LED, g_PS2PortKeyboard); + + // Bit values 0..2 = scroll, num, caps. + uint8_t value = (scrollLock ? 0x1 : 0x0) + (numLock ? 0x2 : 0x0) + (capsLock ? 0x4 : 0x0); + i8042_sendCommand(value, g_PS2PortKeyboard); +} + +/** + * Set how fast keys are repeated, and how long before key repeats begin. + * + * repeatRate: 5-bit number (00000b = 30 Hz, ..., 11111b = 2 Hz) + * repeatDelay: delay before keys repeat, 2-bit number (00b = 250 ms, 01b = 500 ms, 10b = 750 ms, 11b = 1000 ms) + */ +void Keyboard_setTypematicRateAndDelay(uint8_t repeatRate, uint8_t repeatDelay) { + uint8_t portValue = (repeatRate & 0x1F) + ((repeatDelay & 0x03) << 5); + i8042_sendCommand(PS2_COMMAND_SET_TYPEMATIC_RATE_AND_DELAY, g_PS2PortKeyboard); + i8042_sendCommand(portValue, g_PS2PortKeyboard); +} + +int Keyboard_getScancodeSet() { + do { + i8042_sendCommand(PS2_COMMAND_SCANCODE_SET, g_PS2PortKeyboard); // Get/set current scancode set. + i8042_sendCommand(0, g_PS2PortKeyboard); // Get scancode set. + } while (i686_inb(PS2_DATA_PORT) == PS2_RESPONSE_RESEND); + + uint8_t set = i686_inb(PS2_DATA_PORT); + switch (set) { + case PS2_SCANCODE_SET_1: + return 1; + case PS2_SCANCODE_SET_3: + return 3; + case PS2_SCANCODE_SET_2: + default: + return 2; + } +} + +/* + * setNumber: Scancode sets 1-3. + */ +void Keyboard_setScancodeSet(uint8_t setNumber) { + if (Keyboard_getScancodeSet() == setNumber) { + return; + } + + do { + i8042_sendCommand(PS2_COMMAND_SCANCODE_SET, g_PS2PortKeyboard); // Get/set current scancode set. + i8042_sendCommand(setNumber, g_PS2PortKeyboard); // Set scancode set. + } while (i686_inb(PS2_DATA_PORT) == PS2_RESPONSE_RESEND); +} + +bool Keyboard_isKeyPressed(uint8_t scancode) { + return Keyboard_keyStates[scancode]; +} + +bool Keyboard_isShiftPressed() { + return Keyboard_isKeyPressed(0x2a) || Keyboard_isKeyPressed(0x36); +} + +/** + * Process the active key event. + */ +Keyboard_key Keyboard_processKey() { + Keyboard_key k; + + k.scancode = i686_inb(PS2_DATA_PORT); + + // The high bit from the keyboard indicates if the key was pressed or released. + k.pressed = (k.scancode & 0x80) == 0; + + // The remaining 7 bits tell us the scancode. + k.scancode = k.scancode & 0x7F; + + if (Keyboard_isShiftPressed()) { + k.ascii = kbd_US_ucase[k.scancode]; + } else { + k.ascii = kbd_US_lcase[k.scancode]; + } + + return k; +} + +void Keyboard_setKey(int index, Keyboard_key* key) { + g_KeyBuffer[g_KeyBufferIndex].ascii = key->ascii; + g_KeyBuffer[g_KeyBufferIndex].pressed = key->pressed; + g_KeyBuffer[g_KeyBufferIndex].scancode = key->scancode; +} + +void Keyboard_pushKey(Keyboard_key* key) { + if (g_KeyBufferIndex > 0) { + //printf("[Keyboard_pushKey] key %s: %x, %d\r\n", key->pressed ? "pressed" : "released", key->scancode, key->ascii); + Keyboard_setKey(g_KeyBufferIndex--, key); + } +} + +Keyboard_key* Keyboard_popKey() { + if (g_KeyBufferIndex < SIZE_KEYBUFFER) { + return &g_KeyBuffer[g_KeyBufferIndex++]; + } else { + return NULL; + } +} + +bool Keyboard_hasKey() { + return g_KeyBufferIndex != SIZE_KEYBUFFER; +} + +bool Keyboard_isBufferFull() { + return g_KeyBufferIndex == 0; +} + +void Keyboard_irqHandler(Registers* regs) { + if (Keyboard_isBufferFull()) { + return; + } + + Keyboard_key key = Keyboard_processKey(); + + Keyboard_keyStates[key.scancode] = key.pressed; + + if (key.scancode != 0) { + Keyboard_pushKey(&key); + } +} + +void Keyboard_initialize() { + g_PS2PortKeyboard = i8042_getDriver()->KeyboardPortNumber; + //Keyboard_etLEDs(true, true, true); // Doesn't work in QEMU? + //Keyboard_setTypematicRateAndDelay(0x1F, 0); + Keyboard_setScancodeSet(2); // Just to be sure. + i686_IRQ_RegisterHandler(1, Keyboard_irqHandler); +} diff --git a/src/kernel/hal/keyboard.h b/src/kernel/hal/keyboard.h new file mode 100644 index 0000000..71198f9 --- /dev/null +++ b/src/kernel/hal/keyboard.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +typedef struct { + uint8_t scancode; + bool pressed; + char ascii; +} Keyboard_key; + +extern bool Keyboard_keyStates[128]; + +void Keyboard_initialize(); +void Keyboard_setLEDs(bool scrollLock, bool numLock, bool capsLock); +void Keyboard_setTypematicRateAndDelay(uint8_t repeatRate, uint8_t repeatDelay); +int Keyboard_getScancodeSet(); +void Keyboard_setScancodeSet(uint8_t setNumber); +bool Keyboard_isKeyPressed(uint8_t scancode); +bool Keyboard_isShiftPressed(); +Keyboard_key Keyboard_processKey(); diff --git a/src/kernel/main.c b/src/kernel/main.c index 5a36d4b..406d6c0 100644 --- a/src/kernel/main.c +++ b/src/kernel/main.c @@ -11,6 +11,12 @@ // For i686_inb/outb. #include "arch/i686/io.h" +#include "arch/i686/i8259.h" + +#include "arch/i686/i8042.h" + +#include + extern void _init(); void crash_me(); @@ -20,307 +26,20 @@ void timer(Registers* regs) printf("."); } -#define PS2_DATA_PORT 0x60 -#define PS2_REGISTER 0x64 // Read for status, write for command. - -/* - * The Status Register contains various flags that show the state of the PS/2 controller. The meanings for each bit are: - * - * Bit Meaning - * 0 Output buffer status (0 = empty, 1 = full) - * (must be set before attempting to read data from IO port 0x60) - * - * 1 Input buffer status (0 = empty, 1 = full) - * (must be clear before attempting to write data to IO port 0x60 or IO port 0x64) - * - * 2 System Flag - * Meant to be cleared on reset and set by firmware (via. PS/2 Controller Configuration Byte) if the system passes self tests (POST) - * - * 3 Command/data (0 = data written to input buffer is data for PS/2 device, 1 = data written to input buffer is data for PS/2 controller command) - * 4 Unknown (chipset specific) - * May be "keyboard lock" (more likely unused on modern systems) - * - * 5 Unknown (chipset specific) - * May be "receive time-out" or "second PS/2 port output buffer full" - * - * 6 Time-out error (0 = no error, 1 = time-out error) - * 7 Parity error (0 = no error, 1 = parity error) - */ -enum { - PS2_STATUS_OUTPUT_BUFFER = 0x01, - PS2_STATUS_INPUT_BUFFER = 0x02, - PS2_STATUS_SYSTEM_FLAG = 0x04, - PS2_STATUS_COMMAND = 0x08, - PS2_STATUS_UNKNOWN0 = 0x10, - PS2_STATUS_UNKNOWN1 = 0x20, - PS2_STATUS_ERROR_TIMEOUT = 0x40, - PS2_STATUS_ERROR_PARITY = 0x80 -} PS2_STATUS; - -enum { - PS2_RESPONSE_ACK = 0xFA, - PS2_RESPONSE_RESEND = 0xF3 -} PS2_RESPONSE; - -enum { - PS2_COMMAND_DISABLE_FIRST_PORT = 0xAD, - PS2_COMMAND_ENABLE_FIRST_PORT = 0xAE, - PS2_COMMAND_SET_LED = 0xED, - PS2_COMMAND_SCANCODE_SET = 0xF0, // get/set - PS2_COMMAND_SET_TYPEMATIC_RATE_AND_DELAY = 0xF3 -} PS2_COMMAND; - -enum { - PS2_SCANCODE_SET_1 = 0x43, - PS2_SCANCODE_SET_2 = 0x41, - PS2_SCANCODE_SET_3 = 0x3f -} PS2_SCANCODE_SET; - -bool Keyboard_keyStates[128] = { false }; - -char kbd_US_lcase[128] = { - 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', - '\t', /* <-- Tab */ - 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', - 0, /* <-- control key */ - 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, - '*', - 0, /* Alt */ - ' ', /* Space bar */ - 0, /* Caps lock */ - 0, /* 59 - F1 key ... > */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, /* < ... F10 */ - 0, /* 69 - Num lock*/ - 0, /* Scroll Lock */ - 0, /* Home key */ - 0, /* Up Arrow */ - 0, /* Page Up */ - '-', - 0, /* Left Arrow */ - 0, - 0, /* Right Arrow */ - '+', - 0, /* 79 - End key*/ - 0, /* Down Arrow */ - 0, /* Page Down */ - 0, /* Insert Key */ - 0, /* Delete Key */ - 0, 0, 0, - 0, /* F11 Key */ - 0, /* F12 Key */ - 0, /* All other keys are undefined */ -}; - -char kbd_US_ucase[128] = { - 0, 27, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b', - '\t', /* <-- Tab */ - 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n', - 0, /* <-- control key */ - 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', 0, '|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', 0, - '*', - 0, /* Alt */ - ' ', /* Space bar */ - 0, /* Caps lock */ - 0, /* 59 - F1 key ... > */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, /* < ... F10 */ - 0, /* 69 - Num lock*/ - 0, /* Scroll Lock */ - 0, /* Home key */ - 0, /* Up Arrow */ - 0, /* Page Up */ - '-', - 0, /* Left Arrow */ - 0, - 0, /* Right Arrow */ - '+', - 0, /* 79 - End key*/ - 0, /* Down Arrow */ - 0, /* Page Down */ - 0, /* Insert Key */ - 0, /* Delete Key */ - 0, 0, 0, - 0, /* F11 Key */ - 0, /* F12 Key */ - 0, /* All other keys are undefined */ -}; - -typedef struct { - uint8_t scancode; - bool pressed; - char ascii; -} Keyboard_key; - -Keyboard_key g_LastKeyEvent; - -void Keyboard_waitForBufferEmpty() { - // Wait until the 8042 is done processing the current command. - uint16_t count = 0xFFFF; - do { - uint8_t status = i686_inb(PS2_REGISTER); - if ((status & PS2_STATUS_INPUT_BUFFER) != 0) { - break; - } - } while (count--); -} - -/* - * Send the given command to the 8042 keyboard controller. - * - * command: The command byte to send. - */ -void Keyboard_setCommand(uint8_t command) { - i686_DisableInterrupts(); - - Keyboard_waitForBufferEmpty(); - - // Send the command. - i686_outb(PS2_REGISTER, command); - - i686_EnableInterrupts(); -} - -void Keyboard_sendCommand(uint8_t command) { - // Disable the keyboard. - Keyboard_setCommand(PS2_COMMAND_DISABLE_FIRST_PORT); - - i686_DisableInterrupts(); - - // Wait until the 8042 is done processing the current command. - Keyboard_waitForBufferEmpty(); - - // Enable the keyboard. - Keyboard_setCommand(PS2_COMMAND_ENABLE_FIRST_PORT); - - // Send the data to port 0x60. - i686_outb(PS2_DATA_PORT, command); - - i686_EnableInterrupts(); -} - -/** - * Set the value of the 3 LEDs. - * - * scrollLock: Turn on the Scroll Lock LED. - * numLock: Turn on the Num Lock LED. - * capsLock: Turn on the Caps Lock LED. - */ -void Keyboard_setLEDs(bool scrollLock, bool numLock, bool capsLock) { - Keyboard_sendCommand(PS2_COMMAND_SET_LED); - - // Bit values 0..2 = scroll, num, caps. - uint8_t value = (scrollLock ? 0x1 : 0x0) + (numLock ? 0x2 : 0x0) + (capsLock ? 0x4 : 0x0); - Keyboard_sendCommand(value); -} - -/** - * Set how fast keys are repeated, and how long before key repeats begin. - * - * repeatRate: 5-bit number (00000b = 30 Hz, ..., 11111b = 2 Hz) - * repeatDelay: delay before keys repeat, 2-bit number (00b = 250 ms, 01b = 500 ms, 10b = 750 ms, 11b = 1000 ms) - */ -void Keyboard_setTypematicRateAndDelay(uint8_t repeatRate, uint8_t repeatDelay) { - uint8_t portValue = (repeatRate & 0x1F) + ((repeatDelay & 0x03) << 5); - Keyboard_sendCommand(PS2_COMMAND_SET_TYPEMATIC_RATE_AND_DELAY); - Keyboard_sendCommand(portValue); -} - -int Keyboard_getScancodeSet() { - do { - Keyboard_sendCommand(PS2_COMMAND_SCANCODE_SET); // Get/set current scancode set. - Keyboard_sendCommand(0); // Get scancode set. - } while (i686_inb(PS2_DATA_PORT) == PS2_RESPONSE_RESEND); - - uint8_t set = i686_inb(PS2_DATA_PORT); - switch (set) { - case PS2_SCANCODE_SET_1: - return 1; - case PS2_SCANCODE_SET_3: - return 3; - case PS2_SCANCODE_SET_2: - default: - return 2; - } -} - -/* - * setNumber: Scancode sets 1-3. - */ -void Keyboard_setScancodeSet(uint8_t setNumber) { - if (Keyboard_getScancodeSet() == setNumber) { - return; - } - - do { - Keyboard_sendCommand(PS2_COMMAND_SCANCODE_SET); // Get/set current scancode set. - Keyboard_sendCommand(setNumber); // Set scancode set. - } while (i686_inb(PS2_DATA_PORT) == PS2_RESPONSE_RESEND); -} - -bool Keyboard_isKeyPressed(uint8_t scancode) { - return Keyboard_keyStates[scancode]; -} - -bool Keyboard_isShiftPressed() { - return Keyboard_isKeyPressed(0x2a) || Keyboard_isKeyPressed(0x36); -} - -/** - * Process the active key event. - */ -Keyboard_key Keyboard_processKey() { - Keyboard_key k; - - k.scancode = i686_inb(PS2_DATA_PORT); - - // The high bit from the keyboard indicates if the key was pressed or released. - k.pressed = (k.scancode & 0x80) == 0; - - // The remaining 7 bits tell us the scancode. - k.scancode = k.scancode & 0x7F; - - if (Keyboard_isShiftPressed()) { - k.ascii = kbd_US_ucase[k.scancode]; +void ReportPS2Ports() { + const PS2Driver* driver = i8042_getDriver(); + if (driver->KeyboardPortNumber == -1) { + printf("No keyboard detected.\r\n"); } else { - k.ascii = kbd_US_lcase[k.scancode]; + printf("Keyboard detected on PS/2 port %d.\r\n", driver->KeyboardPortNumber); } - - return k; -} - -void Keyboard_irqHandler(Registers* regs) { - g_LastKeyEvent = Keyboard_processKey(); - - Keyboard_keyStates[g_LastKeyEvent.scancode] = g_LastKeyEvent.pressed; - - if (g_LastKeyEvent.scancode != 0) { - //printf("Key %s: %x, %d\r\n", k.pressed ? "pressed" : "released", k.scancode, k.ascii); - if (g_LastKeyEvent.pressed) { - if (g_LastKeyEvent.ascii != 0) { - putc(g_LastKeyEvent.ascii); - } - } + if (driver->MousePortNumber == -1) { + printf("No mouse detected.\r\n"); + } else { + printf("Mouse detected on PS/2 port %d.\r\n", driver->MousePortNumber); } } -/** - * Return the active key event. - * After reading the event, g_LastKeyEvent.scancode will be reset to 0. - */ -/* -Keyboard_key Keyboard_readKey() { - Keyboard_key nextKey; - nextKey.scancode = g_LastKeyEvent.scancode; - nextKey.ascii = g_LastKeyEvent.ascii; - nextKey.pressed = g_LastKeyEvent.pressed; - g_LastKeyEvent.scancode = 0; - g_LastKeyEvent.ascii = 0; - g_LastKeyEvent.pressed = false; - return nextKey; -} -*/ - void start(BootParams* bootParams) { // call global constructors @@ -347,22 +66,22 @@ void start(BootParams* bootParams) //i686_IRQ_RegisterHandler(0, timer); - Keyboard_sendCommand(PS2_COMMAND_DISABLE_FIRST_PORT); - //Keyboard_etLEDs(true, true, true); // Doesn't work in QEMU? - //Keyboard_setTypematicRateAndDelay(0x1F, 0); - Keyboard_setScancodeSet(2); // Just to be sure. - i686_IRQ_RegisterHandler(1, Keyboard_irqHandler); + const PS2Driver* driver = i8042_getDriver(); + Keyboard_initialize(); + ReportPS2Ports(); while (true) { - /* - Keyboard_key k = Keyboard_readKey(); - if (k.scancode != 0) { - //printf("Key %s: %x, %d\r\n", k.pressed ? "pressed" : "released", k.scancode, k.ascii); - if (k.ascii != 0) { - putc(k.ascii); + while (Keyboard_hasKey()) { + Keyboard_key* key = Keyboard_popKey(); + //printf("[main] key %s: %x, %d\r\n", key->pressed ? "pressed" : "released", key->scancode, key->ascii); + if (key != NULL) { + if (key->pressed) { + if (key->ascii != 0) { + putc(key->ascii); + } + } } } - */ } //crash_me(); From fe0ffb76a60cd5270ff5bbaf1871785e066c30bb Mon Sep 17 00:00:00 2001 From: Trey Tomes Date: Fri, 2 Dec 2022 10:42:23 -0600 Subject: [PATCH 09/11] Use scancode set 1. --- src/kernel/hal/keyboard.h | 2 ++ src/kernel/main.c | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/kernel/hal/keyboard.h b/src/kernel/hal/keyboard.h index 71198f9..dd17dc6 100644 --- a/src/kernel/hal/keyboard.h +++ b/src/kernel/hal/keyboard.h @@ -19,3 +19,5 @@ void Keyboard_setScancodeSet(uint8_t setNumber); bool Keyboard_isKeyPressed(uint8_t scancode); bool Keyboard_isShiftPressed(); Keyboard_key Keyboard_processKey(); +bool Keyboard_hasKey(); +Keyboard_key* Keyboard_popKey(); diff --git a/src/kernel/main.c b/src/kernel/main.c index 406d6c0..c1e9832 100644 --- a/src/kernel/main.c +++ b/src/kernel/main.c @@ -32,6 +32,8 @@ void ReportPS2Ports() { printf("No keyboard detected.\r\n"); } else { printf("Keyboard detected on PS/2 port %d.\r\n", driver->KeyboardPortNumber); + + printf("Keyboard is using scancode set %d.\r\n", Keyboard_getScancodeSet()); } if (driver->MousePortNumber == -1) { printf("No mouse detected.\r\n"); @@ -68,6 +70,7 @@ void start(BootParams* bootParams) const PS2Driver* driver = i8042_getDriver(); Keyboard_initialize(); + Keyboard_setScancodeSet(1); ReportPS2Ports(); while (true) { From f14a5b01624c3043c23a0c6fbc1a9425e85c8d7b Mon Sep 17 00:00:00 2001 From: Trey Tomes Date: Fri, 2 Dec 2022 11:17:28 -0600 Subject: [PATCH 10/11] Handle the backspace character. --- src/kernel/arch/i686/vga_text.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/kernel/arch/i686/vga_text.c b/src/kernel/arch/i686/vga_text.c index 861346e..cb739d1 100644 --- a/src/kernel/arch/i686/vga_text.c +++ b/src/kernel/arch/i686/vga_text.c @@ -92,6 +92,18 @@ void VGA_putc(char c) g_ScreenX = 0; break; + case '\b': + g_ScreenX--; + if (g_ScreenX < 0) { + g_ScreenX = SCREEN_WIDTH - 1; + g_ScreenY--; + if (g_ScreenY < 0) { + g_ScreenY = 0; + } + } + VGA_putchr(g_ScreenX, g_ScreenY, '\0'); + break; + default: VGA_putchr(g_ScreenX, g_ScreenY, c); g_ScreenX++; From 61140c6a45ad4638d4a15621f480c489ef2a3992 Mon Sep 17 00:00:00 2001 From: Trey Tomes Date: Fri, 2 Dec 2022 11:17:44 -0600 Subject: [PATCH 11/11] Cleaning up the output. --- src/kernel/hal/keyboard.c | 15 +++++++++++++-- src/kernel/hal/keyboard.h | 1 + src/kernel/main.c | 36 ++++++++++++++++++++++++++++-------- 3 files changed, 42 insertions(+), 10 deletions(-) diff --git a/src/kernel/hal/keyboard.c b/src/kernel/hal/keyboard.c index cce4a03..2be999d 100644 --- a/src/kernel/hal/keyboard.c +++ b/src/kernel/hal/keyboard.c @@ -135,6 +135,8 @@ int Keyboard_getScancodeSet() { * setNumber: Scancode sets 1-3. */ void Keyboard_setScancodeSet(uint8_t setNumber) { + i8042_disableScanning(g_PS2PortKeyboard); + if (Keyboard_getScancodeSet() == setNumber) { return; } @@ -143,6 +145,8 @@ void Keyboard_setScancodeSet(uint8_t setNumber) { i8042_sendCommand(PS2_COMMAND_SCANCODE_SET, g_PS2PortKeyboard); // Get/set current scancode set. i8042_sendCommand(setNumber, g_PS2PortKeyboard); // Set scancode set. } while (i686_inb(PS2_DATA_PORT) == PS2_RESPONSE_RESEND); + + i8042_enableScanning(g_PS2PortKeyboard); } bool Keyboard_isKeyPressed(uint8_t scancode) { @@ -198,13 +202,19 @@ Keyboard_key* Keyboard_popKey() { } bool Keyboard_hasKey() { - return g_KeyBufferIndex != SIZE_KEYBUFFER; + return g_KeyBufferIndex < SIZE_KEYBUFFER; } bool Keyboard_isBufferFull() { return g_KeyBufferIndex == 0; } +void Keyboard_clearBuffer() { + while (Keyboard_hasKey()) { + Keyboard_popKey(); + } +} + void Keyboard_irqHandler(Registers* regs) { if (Keyboard_isBufferFull()) { return; @@ -223,6 +233,7 @@ void Keyboard_initialize() { g_PS2PortKeyboard = i8042_getDriver()->KeyboardPortNumber; //Keyboard_etLEDs(true, true, true); // Doesn't work in QEMU? //Keyboard_setTypematicRateAndDelay(0x1F, 0); - Keyboard_setScancodeSet(2); // Just to be sure. + Keyboard_setScancodeSet(1); // Just to be sure. i686_IRQ_RegisterHandler(1, Keyboard_irqHandler); + Keyboard_clearBuffer(); } diff --git a/src/kernel/hal/keyboard.h b/src/kernel/hal/keyboard.h index dd17dc6..a728599 100644 --- a/src/kernel/hal/keyboard.h +++ b/src/kernel/hal/keyboard.h @@ -21,3 +21,4 @@ bool Keyboard_isShiftPressed(); Keyboard_key Keyboard_processKey(); bool Keyboard_hasKey(); Keyboard_key* Keyboard_popKey(); +void Keyboard_clearBuffer(); diff --git a/src/kernel/main.c b/src/kernel/main.c index c1e9832..32a05f9 100644 --- a/src/kernel/main.c +++ b/src/kernel/main.c @@ -29,16 +29,14 @@ void timer(Registers* regs) void ReportPS2Ports() { const PS2Driver* driver = i8042_getDriver(); if (driver->KeyboardPortNumber == -1) { - printf("No keyboard detected.\r\n"); + printf("...No keyboard detected.\r\n"); } else { - printf("Keyboard detected on PS/2 port %d.\r\n", driver->KeyboardPortNumber); - - printf("Keyboard is using scancode set %d.\r\n", Keyboard_getScancodeSet()); + printf("...Keyboard detected on PS/2 port %d.\r\n", driver->KeyboardPortNumber); } if (driver->MousePortNumber == -1) { - printf("No mouse detected.\r\n"); + printf("...No mouse detected.\r\n"); } else { - printf("Mouse detected on PS/2 port %d.\r\n", driver->MousePortNumber); + printf("...Mouse detected on PS/2 port %d.\r\n", driver->MousePortNumber); } } @@ -63,16 +61,23 @@ void start(BootParams* bootParams) log_warn("Main", "This is a warning msg!"); log_err("Main", "This is an error msg!"); log_crit("Main", "This is a critical msg!"); + printf("BASIC-OS v0.1, forked from Nanobyte OS v0.1\n"); printf("This operating system is under construction.\n"); + printf("\n"); //i686_IRQ_RegisterHandler(0, timer); + printf("Scanning hardware...\n"); const PS2Driver* driver = i8042_getDriver(); Keyboard_initialize(); - Keyboard_setScancodeSet(1); ReportPS2Ports(); + printf("\nOK\n"); + printf("> "); + + Keyboard_clearBuffer(); + int lineLength = 0; while (true) { while (Keyboard_hasKey()) { Keyboard_key* key = Keyboard_popKey(); @@ -80,7 +85,22 @@ void start(BootParams* bootParams) if (key != NULL) { if (key->pressed) { if (key->ascii != 0) { - putc(key->ascii); + if (key->ascii == '\b') { + if (lineLength > 0) { + putc(key->ascii); + lineLength--; + } else { + // TODO: Else beep? + } + } else { + putc(key->ascii); + lineLength++; + } + if (key->scancode == 0x1C) { // + // TODO: Process the line. + printf("> "); // Re-print the prompt. + lineLength = 0; + } } } }