From 03e7812caec83a674c8e14fb1783dbf743469ad4 Mon Sep 17 00:00:00 2001 From: Bananymous Date: Sat, 10 Dec 2022 03:53:44 +0200 Subject: [PATCH] Kernel: Add basic ANSI support :) --- kernel/arch/i386/tty.cpp | 266 ++++++++++++++++++++++++++++++++++--- kernel/arch/i386/vga.h | 12 +- kernel/kernel/Keyboard.cpp | 4 + kernel/kernel/kernel.cpp | 27 ++-- 4 files changed, 274 insertions(+), 35 deletions(-) diff --git a/kernel/arch/i386/tty.cpp b/kernel/arch/i386/tty.cpp index e596cbb3..fe445b8f 100644 --- a/kernel/arch/i386/tty.cpp +++ b/kernel/arch/i386/tty.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include "vga.h" @@ -8,6 +9,16 @@ #include #include +#define BEL 0x07 +#define BS 0x08 +#define HT 0x09 +#define LF 0x0A +#define FF 0x0C +#define CR 0x0D +#define ESC 0x1B + +#define CSI '[' + namespace TTY { @@ -20,6 +31,15 @@ namespace TTY static uint8_t terminal_color; static uint16_t* terminal_buffer; + static char s_ansi_escape_mode = '\0'; + static int s_ansi_escape_index = 0; + static int s_ansi_escape_nums[2] = { -1, -1 }; + + + inline constexpr int max(int a, int b) { return a > b ? a : b; } + inline constexpr int min(int a, int b) { return a < b ? a : b; } + inline constexpr int clamp(int x, int a, int b) { return x < a ? a : x > b ? b : x; } + void putentryat(unsigned char c, uint8_t color, size_t x, size_t y) { const size_t index = y * VGA_WIDTH + x; @@ -58,6 +78,13 @@ namespace TTY if (s_multiboot_info->flags & (1 << 12)) if (s_multiboot_info->framebuffer.type != 2) Kernel::panic("Invalid framebuffer_type in multiboot info"); + + for (int i = 0; i < 16; i++) + { + terminal_color = vga_entry_color((vga_color)i, VGA_COLOR_BLACK); + putchar('#'); + } + putchar('\n'); } void setcolor(uint8_t color) @@ -96,42 +123,243 @@ namespace TTY update_cursor(); } + static void reset_ansi_escape() + { + s_ansi_escape_mode = '\0'; + s_ansi_escape_index = 0; + s_ansi_escape_nums[0] = -1; + s_ansi_escape_nums[1] = -1; + } + + static void handle_ansi_SGR() + { + switch (s_ansi_escape_nums[0]) + { + case -1: case 0: + terminal_color = vga_entry_color(VGA_COLOR_WHITE, VGA_COLOR_BLACK); + break; + + case 30: + terminal_color = vga_set_foreground(VGA_COLOR_BLACK, terminal_color); + break; + case 31: + terminal_color = vga_set_foreground(VGA_COLOR_RED, terminal_color); + break; + case 32: + terminal_color = vga_set_foreground(VGA_COLOR_GREEN, terminal_color); + break; + case 33: + terminal_color = vga_set_foreground(VGA_COLOR_LIGHT_BROWN, terminal_color); + break; + case 34: + terminal_color = vga_set_foreground(VGA_COLOR_BLUE, terminal_color); + break; + case 35: + terminal_color = vga_set_foreground(VGA_COLOR_MAGENTA, terminal_color); + break; + case 36: + terminal_color = vga_set_foreground(VGA_COLOR_CYAN, terminal_color); + break; + case 37: + terminal_color = vga_set_foreground(VGA_COLOR_DARK_GREY, terminal_color); + break; + + case 40: + terminal_color = vga_set_background(VGA_COLOR_BLACK, terminal_color); + break; + case 41: + terminal_color = vga_set_background(VGA_COLOR_RED, terminal_color); + break; + case 42: + terminal_color = vga_set_background(VGA_COLOR_GREEN, terminal_color); + break; + case 43: + terminal_color = vga_set_background(VGA_COLOR_LIGHT_BROWN, terminal_color); + break; + case 44: + terminal_color = vga_set_background(VGA_COLOR_BLUE, terminal_color); + break; + case 45: + terminal_color = vga_set_background(VGA_COLOR_MAGENTA, terminal_color); + break; + case 46: + terminal_color = vga_set_background(VGA_COLOR_CYAN, terminal_color); + break; + case 47: + terminal_color = vga_set_background(VGA_COLOR_DARK_GREY, terminal_color); + break; + } + } + + static void handle_ansi_escape(char c) + { + switch (s_ansi_escape_mode) + { + case '\1': + { + if (c == CSI) + { + s_ansi_escape_mode = CSI; + return; + } + return reset_ansi_escape(); + } + + case CSI: + { + switch (c) + { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + { + int& val = s_ansi_escape_nums[s_ansi_escape_index]; + val = (val == -1) ? (c - '0') : (val * 10 + c - '0'); + return; + } + case ';': + s_ansi_escape_index++; + return; + case 'A': // Cursor Up + if (s_ansi_escape_nums[0] == -1) + s_ansi_escape_nums[0] = 1; + terminal_row = max(terminal_row - s_ansi_escape_nums[0], 0); + return reset_ansi_escape(); + case 'B': // Curson Down + if (s_ansi_escape_nums[0] == -1) + s_ansi_escape_nums[0] = 1; + terminal_row = min(terminal_row + s_ansi_escape_nums[0], VGA_HEIGHT - 1); + return reset_ansi_escape(); + case 'C': // Cursor Forward + if (s_ansi_escape_nums[0] == -1) + s_ansi_escape_nums[0] = 1; + terminal_col = min(terminal_col + s_ansi_escape_nums[0], VGA_WIDTH - 1); + return reset_ansi_escape(); + case 'D': // Cursor Back + if (s_ansi_escape_nums[0] == -1) + s_ansi_escape_nums[0] = 1; + terminal_col = max(terminal_col - s_ansi_escape_nums[0], 0); + return reset_ansi_escape(); + case 'E': // Cursor Next Line + if (s_ansi_escape_nums[0] == -1) + s_ansi_escape_nums[0] = 1; + terminal_row = min(terminal_row + s_ansi_escape_nums[0], VGA_HEIGHT - 1); + terminal_col = 0; + return reset_ansi_escape(); + case 'F': // Cursor Previous Line + if (s_ansi_escape_nums[0] == -1) + s_ansi_escape_nums[0] = 1; + terminal_row = max(terminal_row - s_ansi_escape_nums[0], 0); + terminal_col = 0; + return reset_ansi_escape(); + case 'G': // Cursor Horizontal Absolute + if (s_ansi_escape_nums[0] == -1) + s_ansi_escape_nums[0] = 1; + terminal_col = clamp(s_ansi_escape_nums[0] - 1, 0, VGA_WIDTH - 1); + return reset_ansi_escape(); + case 'H': // Cursor Position + if (s_ansi_escape_nums[0] == -1) + s_ansi_escape_nums[0] = 1; + if (s_ansi_escape_nums[1] == -1) + s_ansi_escape_nums[1] = 1; + terminal_row = clamp(s_ansi_escape_nums[0] - 1, 0, VGA_HEIGHT - 1); + terminal_col = clamp(s_ansi_escape_nums[1] - 1, 0, VGA_WIDTH - 1); + return reset_ansi_escape(); + case 'J': // Erase in Display + dprintln("Unsupported ANSI CSI character J"); + return reset_ansi_escape(); + case 'K': // Erase in Line + switch (s_ansi_escape_nums[0]) + { + case -1: case 0: + for (size_t i = terminal_col; i < VGA_WIDTH; i++) + putentryat(' ', terminal_color, i, terminal_row); + break; + case 1: + for (size_t i = 0; i <= terminal_col; i++) + putentryat(' ', terminal_color, i, terminal_row); + break; + case 2: + for (size_t i = 0; i < VGA_WIDTH; i++) + putentryat(' ', terminal_color, i, terminal_row); + break; + } + return reset_ansi_escape(); + case 'S': // Scroll Up + dprintln("Unsupported ANSI CSI character S"); + return reset_ansi_escape(); + case 'T': // Scroll Down + dprintln("Unsupported ANSI CSI character T"); + return reset_ansi_escape(); + case 'f': // Horizontal Vertical Position + dprintln("Unsupported ANSI CSI character f"); + return reset_ansi_escape(); + case 'm': + handle_ansi_SGR(); + return reset_ansi_escape(); + default: + dprintln("Unsupported ANSI CSI character {}", c); + return reset_ansi_escape(); + } + } + + default: + dprintln("Unsupported ANSI mode"); + return reset_ansi_escape(); + } + } + void putchar(char c) { - if (c == '\t') - c = ' '; + if (s_ansi_escape_mode) + return handle_ansi_escape(c); - if (c == '\n') + // https://en.wikipedia.org/wiki/ANSI_escape_code + switch (c) { - terminal_col = 0; - terminal_row++; - } - else if (c == '\b') - { - if (terminal_col > 0) - terminal_col--; - putentryat(' ', terminal_color, terminal_col, terminal_row); - } - else - { - putentryat(c, terminal_color, terminal_col, terminal_row); - terminal_col++; + case BEL: // TODO + break; + case BS: + if (terminal_col > 0) + terminal_col--; + break; + case HT: + terminal_col++; + while (terminal_col % 8) + terminal_col++; + break; + case LF: + terminal_col = 0; + terminal_row++; + break; + case FF: + terminal_row++; + break; + case CR: + terminal_col = 0; + break; + case ESC: + s_ansi_escape_mode = '\1'; + break; + default: + putentryat(c, terminal_color, terminal_col, terminal_row); + terminal_col++; + break; } - if (terminal_col == VGA_WIDTH) + if (terminal_col >= VGA_WIDTH) { terminal_col = 0; terminal_row++; } - if (terminal_row == VGA_HEIGHT) + while (terminal_row >= VGA_HEIGHT) { for (size_t line = 1; line < VGA_HEIGHT; line++) scroll_line(line); clear_line(VGA_HEIGHT - 1); terminal_col = 0; - terminal_row = VGA_HEIGHT - 1; + terminal_row--; } update_cursor(); diff --git a/kernel/arch/i386/vga.h b/kernel/arch/i386/vga.h index 14c7cd22..366cea2f 100644 --- a/kernel/arch/i386/vga.h +++ b/kernel/arch/i386/vga.h @@ -22,9 +22,19 @@ enum vga_color VGA_COLOR_WHITE = 15, }; +static inline uint8_t vga_set_foreground(enum vga_color fg, uint8_t color) +{ + return (color & 0xf0) | fg; +} + +static inline uint8_t vga_set_background(enum vga_color bg, uint8_t color) +{ + return (bg << 4) | (color & 0x0f); +} + static inline uint8_t vga_entry_color(enum vga_color fg, enum vga_color bg) { - return fg | bg << 4; + return (bg << 4) | fg; } static inline uint16_t vga_entry(unsigned char uc, uint8_t color) diff --git a/kernel/kernel/Keyboard.cpp b/kernel/kernel/Keyboard.cpp index 8b9055ae..ebd0f6b2 100644 --- a/kernel/kernel/Keyboard.cpp +++ b/kernel/kernel/Keyboard.cpp @@ -228,6 +228,10 @@ namespace Keyboard s_key_callback = callback; IDT::register_irq_handler(KEYBOARD_IRQ, irq_handler); PIC::unmask(KEYBOARD_IRQ); + + kb_command(0xED, 0b111); + IO::io_wait(); + while (kb_try_read(tmp)); } char key_to_ascii(Key key, uint8_t modifiers) diff --git a/kernel/kernel/kernel.cpp b/kernel/kernel/kernel.cpp index 83ff7448..40d4603f 100644 --- a/kernel/kernel/kernel.cpp +++ b/kernel/kernel/kernel.cpp @@ -28,10 +28,16 @@ void on_key_press(Keyboard::Key key, uint8_t modifiers, bool pressed) kprint("time since boot: {} ms\n", PIT::ms_since_boot()); return; } - - char ascii = Keyboard::key_to_ascii(key, modifiers); - if (ascii) - kprint("{}", ascii); + else if (key == Keyboard::Key::Backspace) + { + kprint("\b \b"); + } + else + { + char ascii = Keyboard::key_to_ascii(key, modifiers); + if (ascii) + kprint("{}", ascii); + } } } @@ -45,9 +51,6 @@ void kernel_main(multiboot_info_t* mbi, uint32_t magic) if (magic != 0x2BADB002) return; - if (mbi->framebuffer.type != 2) - return; - TTY::initialize(); Serial::initialize(); @@ -60,17 +63,11 @@ void kernel_main(multiboot_info_t* mbi, uint32_t magic) PIT::initialize(); Keyboard::initialize(on_key_press); - kprintln("Hello from the kernel!"); - dprintln("Hello emulator from kernel!"); + kprint("HHHHHHHHHHHHHHHHHHHHHHHHHHHHH\e[20D.\e[3K"); - int** lol = new int*[10]; - for (int i = 0; i < 10; i++) - lol[i] = new int; - - kprint("{.2}\n", -12.123f); - kprint("0x{.H}", 0xcafebabe); + ENABLE_INTERRUPTS(); for (;;) {