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/.vscode/settings.json b/.vscode/settings.json index 7022645..fdb0dce 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,40 @@ { "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", + "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", + "chrono": "c", + "i8042.h": "c", + "i8259.h": "c", + "keyboard.h": "c" } } \ No newline at end of file diff --git a/README.md b/README.md index a6155f4..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. @@ -19,6 +25,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 +40,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')) 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 b86ee8f..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 @@ -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); } 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/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++; 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); 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..2be999d --- /dev/null +++ b/src/kernel/hal/keyboard.c @@ -0,0 +1,239 @@ +#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) { + i8042_disableScanning(g_PS2PortKeyboard); + + 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); + + i8042_enableScanning(g_PS2PortKeyboard); +} + +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_clearBuffer() { + while (Keyboard_hasKey()) { + Keyboard_popKey(); + } +} + +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(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 new file mode 100644 index 0000000..a728599 --- /dev/null +++ b/src/kernel/hal/keyboard.h @@ -0,0 +1,24 @@ +#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(); +bool Keyboard_hasKey(); +Keyboard_key* Keyboard_popKey(); +void Keyboard_clearBuffer(); diff --git a/src/kernel/main.c b/src/kernel/main.c index ced6167..32a05f9 100644 --- a/src/kernel/main.c +++ b/src/kernel/main.c @@ -1,4 +1,6 @@ +#include #include +#include "ctype.h" #include "stdio.h" #include "memory.h" #include @@ -6,6 +8,15 @@ #include #include +// 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(); @@ -15,6 +26,20 @@ void timer(Registers* regs) printf("."); } +void ReportPS2Ports() { + const PS2Driver* driver = i8042_getDriver(); + if (driver->KeyboardPortNumber == -1) { + printf("...No keyboard detected.\r\n"); + } else { + printf("...Keyboard detected on PS/2 port %d.\r\n", driver->KeyboardPortNumber); + } + if (driver->MousePortNumber == -1) { + printf("...No mouse detected.\r\n"); + } else { + printf("...Mouse detected on PS/2 port %d.\r\n", driver->MousePortNumber); + } +} + void start(BootParams* bootParams) { // call global constructors @@ -32,15 +57,55 @@ 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"); + printf("\n"); + //i686_IRQ_RegisterHandler(0, timer); + printf("Scanning hardware...\n"); + const PS2Driver* driver = i8042_getDriver(); + Keyboard_initialize(); + ReportPS2Ports(); + + printf("\nOK\n"); + printf("> "); + + Keyboard_clearBuffer(); + int lineLength = 0; + while (true) { + 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) { + 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; + } + } + } + } + } + } //crash_me(); end: