From 3d3f12bd300c8f39edf442858c4bf69c823e8bdb Mon Sep 17 00:00:00 2001 From: Bananymous Date: Sat, 10 Dec 2022 00:33:03 +0200 Subject: [PATCH] Kernel: Add new generic Printer class kprint is now just a #define to Printer::print. This allows us to use same print formatting for serial output :) --- kernel/Makefile | 1 + kernel/arch/i386/tty.cpp | 248 ++++++++++++++++---------------- kernel/include/kernel/Printer.h | 233 ++++++++++++++++++++++++++++++ kernel/include/kernel/Serial.h | 15 ++ kernel/include/kernel/kprint.h | 55 ++----- kernel/include/kernel/tty.h | 16 ++- kernel/kernel/Serial.cpp | 48 +++++++ kernel/kernel/kernel.cpp | 32 ++++- qemu.sh | 6 +- 9 files changed, 480 insertions(+), 174 deletions(-) create mode 100644 kernel/include/kernel/Printer.h create mode 100644 kernel/include/kernel/Serial.h create mode 100644 kernel/kernel/Serial.cpp diff --git a/kernel/Makefile b/kernel/Makefile index f5414abb..ef50c4c3 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -36,6 +36,7 @@ kernel/Keyboard.o \ kernel/kmalloc.o \ kernel/PIC.o \ kernel/PIT.o \ +kernel/Serial.o \ kernel/SSP.o \ OBJS=\ diff --git a/kernel/arch/i386/tty.cpp b/kernel/arch/i386/tty.cpp index 4b3d65ec..e596cbb3 100644 --- a/kernel/arch/i386/tty.cpp +++ b/kernel/arch/i386/tty.cpp @@ -5,143 +5,151 @@ #include "vga.h" -#include #include #include -static size_t VGA_WIDTH; -static size_t VGA_HEIGHT; -static uint16_t* VGA_MEMORY; - -static size_t terminal_row; -static size_t terminal_col; -static uint8_t terminal_color; -static uint16_t* terminal_buffer; - -void terminal_putentryat(unsigned char c, uint8_t color, size_t x, size_t y) +namespace TTY { - const size_t index = y * VGA_WIDTH + x; - terminal_buffer[index] = vga_entry(c, color); -} -void terminal_clear() -{ - for (size_t y = 0; y < VGA_HEIGHT; y++) + static size_t VGA_WIDTH; + static size_t VGA_HEIGHT; + static uint16_t* VGA_MEMORY; + + static size_t terminal_row; + static size_t terminal_col; + static uint8_t terminal_color; + static uint16_t* terminal_buffer; + + void putentryat(unsigned char c, uint8_t color, size_t x, size_t y) + { + const size_t index = y * VGA_WIDTH + x; + terminal_buffer[index] = vga_entry(c, color); + } + + void clear() + { + for (size_t y = 0; y < VGA_HEIGHT; y++) + for (size_t x = 0; x < VGA_WIDTH; x++) + putentryat(' ', terminal_color, x, y); + } + + void initialize() + { + if (s_multiboot_info->flags & (1 << 12)) + { + const framebuffer_info_t& fb = s_multiboot_info->framebuffer; + VGA_WIDTH = fb.width; + VGA_HEIGHT = fb.height; + VGA_MEMORY = (uint16_t*)fb.addr; + } + else + { + VGA_WIDTH = 80; + VGA_HEIGHT = 25; + VGA_MEMORY = (uint16_t*)0xB8000; + } + + terminal_row = 0; + terminal_col = 0; + terminal_color = vga_entry_color(VGA_COLOR_WHITE, VGA_COLOR_BLACK); + terminal_buffer = VGA_MEMORY; + clear(); + + if (s_multiboot_info->flags & (1 << 12)) + if (s_multiboot_info->framebuffer.type != 2) + Kernel::panic("Invalid framebuffer_type in multiboot info"); + } + + void setcolor(uint8_t color) + { + terminal_color = color; + } + + void scroll_line(size_t line) + { for (size_t x = 0; x < VGA_WIDTH; x++) - terminal_putentryat(' ', terminal_color, x, y); -} - -void terminal_initialize() -{ - if (s_multiboot_info->flags & (1 << 12)) - { - const framebuffer_info_t& fb = s_multiboot_info->framebuffer; - VGA_WIDTH = fb.width; - VGA_HEIGHT = fb.height; - VGA_MEMORY = (uint16_t*)fb.addr; - } - else - { - VGA_WIDTH = 80; - VGA_HEIGHT = 25; - VGA_MEMORY = (uint16_t*)0xB8000; + { + const size_t index = line * VGA_WIDTH + x; + terminal_buffer[index - VGA_WIDTH] = terminal_buffer[index]; + } } - terminal_row = 0; - terminal_col = 0; - terminal_color = vga_entry_color(VGA_COLOR_WHITE, VGA_COLOR_BLACK); - terminal_buffer = VGA_MEMORY; - terminal_clear(); - - if (s_multiboot_info->flags & (1 << 12)) - if (s_multiboot_info->framebuffer.type != 2) - Kernel::panic("Invalid framebuffer_type in multiboot info"); -} - -void terminal_setcolor(uint8_t color) -{ - terminal_color = color; -} - -void terminal_scroll_line(size_t line) -{ - for (size_t x = 0; x < VGA_WIDTH; x++) + void clear_line(size_t line) { - const size_t index = line * VGA_WIDTH + x; - terminal_buffer[index - VGA_WIDTH] = terminal_buffer[index]; - } -} - -void terminal_clear_line(size_t line) -{ - for (size_t x = 0; x < VGA_WIDTH; x++) - terminal_putentryat(' ', terminal_color, x, line); -} - -static void terminal_update_cursor() -{ - uint16_t pos = terminal_row * VGA_WIDTH + terminal_col; - IO::outb(0x3D4, 0x0F); - IO::outb(0x3D5, (uint8_t) (pos & 0xFF)); - IO::outb(0x3D4, 0x0E); - IO::outb(0x3D5, (uint8_t) ((pos >> 8) & 0xFF)); -} - -void terminal_set_cursor_pos(int x, int y) -{ - terminal_row = y; - terminal_col = x; - terminal_update_cursor(); -} - -void terminal_putchar(char c) -{ - if (c == '\t') - c = ' '; - - if (c == '\n') - { - terminal_col = 0; - terminal_row++; - } - else if (c == '\b') - { - if (terminal_col > 0) - terminal_col--; - terminal_putentryat(' ', terminal_color, terminal_col, terminal_row); - } - else - { - terminal_putentryat(c, terminal_color, terminal_col, terminal_row); - terminal_col++; + for (size_t x = 0; x < VGA_WIDTH; x++) + putentryat(' ', terminal_color, x, line); } - if (terminal_col == VGA_WIDTH) + static void update_cursor() { - terminal_col = 0; - terminal_row++; + uint16_t pos = terminal_row * VGA_WIDTH + terminal_col; + IO::outb(0x3D4, 0x0F); + IO::outb(0x3D5, (uint8_t) (pos & 0xFF)); + IO::outb(0x3D4, 0x0E); + IO::outb(0x3D5, (uint8_t) ((pos >> 8) & 0xFF)); } - if (terminal_row == VGA_HEIGHT) + void set_cursor_pos(int x, int y) { - for (size_t line = 1; line < VGA_HEIGHT; line++) - terminal_scroll_line(line); - terminal_clear_line(VGA_HEIGHT - 1); - - terminal_col = 0; - terminal_row = VGA_HEIGHT - 1; + terminal_row = y; + terminal_col = x; + update_cursor(); } - terminal_update_cursor(); -} + void putchar(char c) + { + if (c == '\t') + c = ' '; -void terminal_write(const char* data, size_t size) -{ - for (size_t i = 0; i < size; i++) - terminal_putchar(data[i]); -} + if (c == '\n') + { + 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++; + } + + if (terminal_col == VGA_WIDTH) + { + terminal_col = 0; + terminal_row++; + } + + if (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; + } + + update_cursor(); + } + + void write(const char* data, size_t size) + { + for (size_t i = 0; i < size; i++) + putchar(data[i]); + } + + void writestring(const char* data) + { + while (*data) + { + putchar(*data); + data++; + } + } -void terminal_writestring(const char* data) -{ - terminal_write(data, strlen(data)); } \ No newline at end of file diff --git a/kernel/include/kernel/Printer.h b/kernel/include/kernel/Printer.h new file mode 100644 index 00000000..bf249a7f --- /dev/null +++ b/kernel/include/kernel/Printer.h @@ -0,0 +1,233 @@ +#pragma once + +#include +#include + +namespace Printer +{ + + struct ValueFormat; + + template + static void print(const char* format); + + template + static void print(const char* format, Arg arg, Args... args); + + template + static void println(const char* format, Args... args); + + template + static size_t print_argument(const char* format, T arg); + + template + static void print_value(T value, const ValueFormat& format); + + + /* + + IMPLEMENTATION + + */ + + struct ValueFormat + { + int base = 10; + int percision = 3; + bool upper = false; + }; + + template + void print(const char* format) + { + while (*format) + { + PUTC_LIKE(*format); + format++; + } + } + + template + void print(const char* format, Arg arg, Args... args) + { + while (*format && *format != '{') + { + PUTC_LIKE(*format); + format++; + } + + if (*format == '{') + { + size_t arg_len = print_argument(format, arg); + if (arg_len == size_t(-1)) + return print(format); + print(format + arg_len, args...); + } + } + + template + void println(const char* format, Args... args) + { + print(format, args...); + PUTC_LIKE('\n'); + } + + + template + size_t print_argument(const char* format, Arg argument) + { + ValueFormat value_format; + + if (format[0] != '{') + return size_t(-1); + + size_t i = 1; + do + { + if (!format[i] || format[i] == '}') + break; + + switch (format[i]) + { + case 'b': value_format.base = 2; value_format.upper = false; i++; break; + case 'B': value_format.base = 2; value_format.upper = true; i++; break; + case 'o': value_format.base = 8; value_format.upper = false; i++; break; + case 'O': value_format.base = 8; value_format.upper = true; i++; break; + case 'd': value_format.base = 10; value_format.upper = false; i++; break; + case 'D': value_format.base = 10; value_format.upper = true; i++; break; + case 'h': value_format.base = 16; value_format.upper = false; i++; break; + case 'H': value_format.base = 16; value_format.upper = true; i++; break; + default: break; + } + + if (!format[i] || format[i] == '}') + break; + + if (format[i] == '.') + { + i++; + int percision = 0; + while ('0' <= format[i] && format[i] <= '9') + { + percision = (percision * 10) + (format[i] - '0'); + i++; + } + value_format.percision = percision; + } + + } while(false); + + if (format[i] != '}') + return size_t(-1); + + print_value(argument, value_format); + + return i + 1; + } + + static char value_to_base_char(uint8_t value, int base, bool upper) + { + if (base <= 10) + return value + '0'; + if (base <= 36) + { + if (value < 10) + return value + '0'; + return value + (upper ? 'A' : 'a') - 10; + } + return '?'; + } + + template + void print_integer(T value, const ValueFormat& format) + { + if (value == 0) + return PUTC_LIKE('0'); + + bool sign = false; + + // Fits signed 64-bit binary number and null + char buffer[66]; + char* ptr = buffer + sizeof(buffer); + *(--ptr) = '\0'; + + if (value < 0) + { + sign = true; + T digit = (format.base - (value % format.base)) % format.base; + *(--ptr) = value_to_base_char(digit, format.base, format.upper); + value = -(value / format.base); + } + + while (value) + { + *(--ptr) = value_to_base_char(value % format.base, format.base, format.upper); + value /= format.base; + } + + if (sign) + *(--ptr) = '-'; + + print(ptr); + } + + template + void print_floating(T value, const ValueFormat& format) + { + int64_t int_part = (int64_t)value; + T frac_part = value - (T)int_part; + if (frac_part < 0) + frac_part = -frac_part; + + print_integer(int_part, format); + + if (format.percision > 0) + PUTC_LIKE('.'); + + for (int i = 0; i < format.percision; i++) + { + frac_part *= format.base; + if (i == format.percision - 1) + frac_part += 0.5; + + PUTC_LIKE(value_to_base_char((uint8_t)frac_part % format.base, format.base, format.upper)); + } + } + + template + void print_pointer(void* ptr, const ValueFormat& format) + { + uintptr_t value = (uintptr_t)ptr; + print("0x"); + for (int i = sizeof(void*) * 8 - 4; i >= 0; i -= 4) + PUTC_LIKE(value_to_base_char((value >> i) & 0xF, 16, format.upper)); + } + + /* + + TEMPLATE SPECIALIZATIONS + + */ + + template void print_value(short value, const ValueFormat& format) { print_integer(value, format); } + template void print_value(int value, const ValueFormat& format) { print_integer(value, format); } + template void print_value(long value, const ValueFormat& format) { print_integer(value, format); } + template void print_value(long long value, const ValueFormat& format) { print_integer(value, format); } + + template void print_value(unsigned short value, const ValueFormat& format) { print_integer(value, format); } + template void print_value(unsigned int value, const ValueFormat& format) { print_integer(value, format); } + template void print_value(unsigned long value, const ValueFormat& format) { print_integer(value, format); } + template void print_value(unsigned long long value, const ValueFormat& format) { print_integer(value, format); } + + template void print_value(float value, const ValueFormat& format) { print_floating(value, format); } + template void print_value(double value, const ValueFormat& format) { print_floating(value, format); } + template void print_value(long double value, const ValueFormat& format) { print_floating(value, format); } + + template void print_value(char value, const ValueFormat&) { PUTC_LIKE(value); } + template void print_value(signed char value, const ValueFormat& format) { print_integer(value, format); } + template void print_value(unsigned char value, const ValueFormat& format) { print_integer(value, format); } + + template void print_value(T* value, const ValueFormat& format) { print_pointer((void*)value, format); } + template void print_value(const char* value, const ValueFormat&) { print(value);} + +} diff --git a/kernel/include/kernel/Serial.h b/kernel/include/kernel/Serial.h new file mode 100644 index 00000000..ca2232df --- /dev/null +++ b/kernel/include/kernel/Serial.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#define dprint Printer::print +#define dprintln Printer::println + +namespace Serial +{ + + void initialize(); + + void serial_putc(char); + +} \ No newline at end of file diff --git a/kernel/include/kernel/kprint.h b/kernel/include/kernel/kprint.h index 0b7f770e..dc43b0d0 100644 --- a/kernel/include/kernel/kprint.h +++ b/kernel/include/kernel/kprint.h @@ -1,7 +1,13 @@ #pragma once +#include #include +#define kprint Printer::print +#define kprintln Printer::println + +#if 0 + #include #include @@ -67,6 +73,8 @@ static void kprint_floating(T value, int percision) terminal_write(".", 1); + + while (percision > 0) { frac_part *= 10; @@ -79,10 +87,7 @@ static void kprint_floating(T value, int percision) } template -static void kprint_val(T) -{ - terminal_writestring(""); -} +static void kprint_val(T); static void kprint(const char* format) { @@ -95,7 +100,7 @@ static void kprint(const char* format, Arg arg, Args... args) const char* next = strstr(format, "{}"); if (next == NULL) { - terminal_writestring(format); + terminal_write(format, strlen(format)); return; } terminal_write(format, next - format); @@ -124,8 +129,6 @@ template<> void kprint_val(unsigned char value) { kprint_unsigned(value); } template<> void kprint_val(const char* value) { terminal_writestring(value); } template<> void kprint_val(char* value) { terminal_writestring(value); } - - static char bits_to_hex(uint8_t val) { val = val & 0xF; @@ -137,37 +140,9 @@ static char bits_to_hex(uint8_t val) template<> void kprint_val(void* value) { terminal_write("0x", 2); - - if constexpr(sizeof(void*) == sizeof(uint32_t)) - { - uint32_t addr = (uint32_t)value; - terminal_putchar(bits_to_hex(addr >> 28)); - terminal_putchar(bits_to_hex(addr >> 24)); - terminal_putchar(bits_to_hex(addr >> 20)); - terminal_putchar(bits_to_hex(addr >> 16)); - terminal_putchar(bits_to_hex(addr >> 12)); - terminal_putchar(bits_to_hex(addr >> 8)); - terminal_putchar(bits_to_hex(addr >> 4)); - terminal_putchar(bits_to_hex(addr >> 0)); - } - else - { - uint64_t addr = (uint64_t)value; - terminal_putchar(bits_to_hex(addr >> 60)); - terminal_putchar(bits_to_hex(addr >> 56)); - terminal_putchar(bits_to_hex(addr >> 52)); - terminal_putchar(bits_to_hex(addr >> 48)); - terminal_putchar(bits_to_hex(addr >> 44)); - terminal_putchar(bits_to_hex(addr >> 40)); - terminal_putchar(bits_to_hex(addr >> 36)); - terminal_putchar(bits_to_hex(addr >> 32)); - terminal_putchar(bits_to_hex(addr >> 28)); - terminal_putchar(bits_to_hex(addr >> 24)); - terminal_putchar(bits_to_hex(addr >> 20)); - terminal_putchar(bits_to_hex(addr >> 16)); - terminal_putchar(bits_to_hex(addr >> 12)); - terminal_putchar(bits_to_hex(addr >> 8)); - terminal_putchar(bits_to_hex(addr >> 4)); - terminal_putchar(bits_to_hex(addr >> 0)); - } + uint32_t addr = (uint32_t)value; + for (int i = sizeof(void*) * 8 - 4; i >= 0; i -= 4) + terminal_putchar(bits_to_hex(addr >> i)); } + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/tty.h b/kernel/include/kernel/tty.h index 107bed9e..2f735fc1 100644 --- a/kernel/include/kernel/tty.h +++ b/kernel/include/kernel/tty.h @@ -1,10 +1,14 @@ #pragma once #include -#include -void terminal_initialize(); -void terminal_putchar(char c); -void terminal_write(const char* data, size_t size); -void terminal_writestring(const char* data); -void terminal_set_cursor_pos(int x, int y); \ No newline at end of file +namespace TTY +{ + + void initialize(); + void putchar(char c); + void write(const char* data, size_t size); + void writestring(const char* data); + void set_cursor_pos(int x, int y); + +} \ No newline at end of file diff --git a/kernel/kernel/Serial.cpp b/kernel/kernel/Serial.cpp new file mode 100644 index 00000000..533933df --- /dev/null +++ b/kernel/kernel/Serial.cpp @@ -0,0 +1,48 @@ +#include +#include +#include + +#define COM1_PORT 0x3f8 + +namespace Serial +{ + + static bool s_initialized = false; + + void initialize() + { + IO::outb(COM1_PORT + 1, 0x00); // Disable all interrupts + IO::outb(COM1_PORT + 3, 0x80); // Enable DLAB (set baud rate divisor) + IO::outb(COM1_PORT + 0, 0x03); // Set divisor to 3 (lo byte) 38400 baud + IO::outb(COM1_PORT + 1, 0x00); // (hi byte) + IO::outb(COM1_PORT + 3, 0x03); // 8 bits, no parity, one stop bit + IO::outb(COM1_PORT + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold + IO::outb(COM1_PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set + IO::outb(COM1_PORT + 4, 0x1E); // Set in loopback mode, test the serial chip + IO::outb(COM1_PORT + 0, 0xAE); // Test serial chip (send byte 0xAE and check if serial returns same byte) + + // Check if serial is faulty (i.e: not same byte as sent) + if(IO::inb(COM1_PORT + 0) != 0xAE) { + kprint("Could not initialize COM1 serial port\n"); + return; + } + + // If serial is not faulty set it in normal operation mode + // (not-loopback with IRQs enabled and OUT#1 and OUT#2 bits enabled) + IO::outb(COM1_PORT + 4, 0x0F); + s_initialized = true; + } + + int is_transmit_empty() { + return IO::inb(COM1_PORT + 5) & 0x20; + } + + void serial_putc(char c) + { + if (!s_initialized) + return; + while (is_transmit_empty() == 0); + IO::outb(COM1_PORT, c); + } + +} \ No newline at end of file diff --git a/kernel/kernel/kernel.cpp b/kernel/kernel/kernel.cpp index 4a999e91..83ff7448 100644 --- a/kernel/kernel/kernel.cpp +++ b/kernel/kernel/kernel.cpp @@ -1,14 +1,15 @@ #include #include +#include #include #include +#include #include #include #include #include +#include #include -#include -#include #include #include @@ -22,6 +23,12 @@ void on_key_press(Keyboard::Key key, uint8_t modifiers, bool pressed) { if (pressed) { + if (key == Keyboard::Key::Escape) + { + kprint("time since boot: {} ms\n", PIT::ms_since_boot()); + return; + } + char ascii = Keyboard::key_to_ascii(key, modifiers); if (ascii) kprint("{}", ascii); @@ -36,9 +43,13 @@ void kernel_main(multiboot_info_t* mbi, uint32_t magic) s_multiboot_info = mbi; if (magic != 0x2BADB002) - goto halt; + return; - terminal_initialize(); + if (mbi->framebuffer.type != 2) + return; + + TTY::initialize(); + Serial::initialize(); kmalloc_initialize(); @@ -49,11 +60,18 @@ void kernel_main(multiboot_info_t* mbi, uint32_t magic) PIT::initialize(); Keyboard::initialize(on_key_press); - kprint("Hello from the kernel!\n"); - ENABLE_INTERRUPTS(); + kprintln("Hello from the kernel!"); + + dprintln("Hello emulator from kernel!"); + + 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); -halt: for (;;) { asm("hlt"); diff --git a/qemu.sh b/qemu.sh index c86b5c45..fcb3c7f5 100755 --- a/qemu.sh +++ b/qemu.sh @@ -2,4 +2,8 @@ set -e . ./iso.sh -qemu-system-$(./target-triplet-to-arch.sh $HOST) -cdrom banan-os.iso +qemu-system-$(./target-triplet-to-arch.sh $HOST) \ + -m 128 \ + -drive format=raw,media=cdrom,file=banan-os.iso \ + -serial stdio \ + \ No newline at end of file