diff --git a/kernel/include/kernel/Shell.h b/kernel/include/kernel/Shell.h new file mode 100644 index 000000000..76c5ee251 --- /dev/null +++ b/kernel/include/kernel/Shell.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +namespace Kernel +{ + + class Shell + { + public: + Shell(const Shell&) = delete; + + static Shell& Get(); + + void Run(); + + private: + Shell(); + void PrintPrompt(); + void ProcessCommand(BAN::StringView); + void KeyEventCallback(Keyboard::KeyEvent); + + private: + BAN::String m_buffer; + }; + +} \ No newline at end of file diff --git a/kernel/kernel/Shell.cpp b/kernel/kernel/Shell.cpp new file mode 100644 index 000000000..9b4996b84 --- /dev/null +++ b/kernel/kernel/Shell.cpp @@ -0,0 +1,147 @@ +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel +{ + + static Shell* s_instance = nullptr; + + Shell& Shell::Get() + { + if (!s_instance) + s_instance = new Shell(); + return *s_instance; + } + + Shell::Shell() + { + Keyboard::register_key_event_callback([](Keyboard::KeyEvent event) { Shell::Get().KeyEventCallback(event); }); + m_buffer.Reserve(128); + } + + void Shell::PrintPrompt() + { + kprint("\e[32muser\e[m# "); + } + + void Shell::Run() + { + PrintPrompt(); + for (;;) + { + asm volatile("hlt"); + Keyboard::update_keyboard(); + } + } + + void Shell::ProcessCommand(BAN::StringView command) + { + auto arguments = command.Split(' '); + if (arguments.Empty()) + return; + + if (arguments.Front() == "date") + { + if (arguments.Size() != 1) + { + kprintln("'date' does not support command line arguments"); + return; + } + auto time = RTC::GetCurrentTime(); + kprintln("{}", time); + return; + } + + if (arguments.Front() == "echo") + { + if (arguments.Size() > 1) + { + kprint("{}", arguments[1]); + for (size_t i = 2; i < arguments.Size(); i++) + kprint(" {}", arguments[i]); + } + kprintln(); + return; + } + + if (arguments.Front() == "clear") + { + if (arguments.Size() != 1) + { + kprintln("'clear' does not support command line arguments"); + return; + } + TTY::clear(); + TTY::set_cursor_pos(0, 0); + return; + } + + if (arguments.Front() == "reboot") + { + if (arguments.Size() != 1) + { + kprintln("'reboot' does not support command line arguments"); + return; + } + uint8_t good = 0x02; + while (good & 0x02) + good = IO::inb(0x64); + IO::outb(0x64, 0xFE); + asm volatile("cli; hlt"); + return; + } + + kprintln("unrecognized command '{}'", arguments.Front()); + } + + void Shell::KeyEventCallback(Keyboard::KeyEvent event) + { + if (!event.pressed) + return; + + switch (event.key) + { + case Keyboard::Key::Backspace: + { + if (!m_buffer.Empty()) + { + kprint("\b \b", 3); + m_buffer.PopBack(); + } + break; + } + + case Keyboard::Key::Enter: + case Keyboard::Key::NumpadEnter: + { + kprint("\n"); + ProcessCommand(m_buffer); + m_buffer.Clear(); + PrintPrompt(); + break; + } + + case Keyboard::Key::Tab: + event.key = Keyboard::Key::Space; + // fall through + + default: + { + char ascii = Keyboard::key_event_to_ascii(event); + if (ascii) + { + kprint("{}", ascii); + m_buffer.PushBack(ascii); + } + break; + } + } + } + + +} \ No newline at end of file diff --git a/kernel/kernel/kernel.cpp b/kernel/kernel/kernel.cpp index b9eddb814..0b8a5652b 100644 --- a/kernel/kernel/kernel.cpp +++ b/kernel/kernel/kernel.cpp @@ -10,71 +10,14 @@ #include #include #include +#include #include -#include -#include - #define DISABLE_INTERRUPTS() asm volatile("cli") #define ENABLE_INTERRUPTS() asm volatile("sti") multiboot_info_t* s_multiboot_info; -char ascii_buffer[32] {}; -static bool has_command(const char* cmd) -{ - size_t len = strlen(cmd); - return memcmp(ascii_buffer + sizeof(ascii_buffer) - len - 1, cmd, len) == 0; -} -void on_key_press(Keyboard::KeyEvent event) -{ - if (event.pressed) - { - char ascii = Keyboard::key_event_to_ascii(event); - - if (ascii) - { - memmove(ascii_buffer, ascii_buffer + 1, sizeof(ascii_buffer) - 1); - ascii_buffer[sizeof(ascii_buffer) - 1] = ascii; - kprint("{}", ascii); - } - - if (event.key == Keyboard::Key::Escape) - { - kprint("time since boot: {} ms\n", PIT::ms_since_boot()); - return; - } - else if (event.key == Keyboard::Key::Backspace) - { - memmove(ascii_buffer + 2, ascii_buffer, sizeof(ascii_buffer) - 2); - kprint(" \b"); - } - else if (event.key == Keyboard::Key::Enter) - { - if (has_command("clear")) - { - TTY::clear(); - TTY::set_cursor_pos(0, 0); - } - else if (has_command("led_disco")) - { - TTY::clear(); - TTY::set_cursor_pos(0, 0); - kprintln("\e[32mLED DISCO\e[m"); - Keyboard::led_disco(); - } - else if (has_command("reboot")) - { - uint8_t good = 0x02; - while (good & 0x02) - good = IO::inb(0x64); - IO::outb(0x64, 0xFE); - asm volatile("cli; hlt"); - } - } - } -} - extern "C" void kernel_main(multiboot_info_t* mbi, uint32_t magic) { @@ -87,11 +30,7 @@ void kernel_main(multiboot_info_t* mbi, uint32_t magic) Serial::initialize(); TTY::initialize(); - - for (int i = 30; i <= 37; i++) - kprint("\e[{}m#", i); - kprint("\e[m\n"); - + kmalloc_initialize(); PIC::initialize(); @@ -99,19 +38,18 @@ void kernel_main(multiboot_info_t* mbi, uint32_t magic) IDT::initialize(); PIT::initialize(); - if (!Keyboard::initialize(on_key_press)) + if (!Keyboard::initialize()) return; - auto time = RTC::GetCurrentTime(); - kprintln("Today is {}", time); - kprintln("Hello from the kernel!"); ENABLE_INTERRUPTS(); + auto& shell = Kernel::Shell::Get(); + shell.Run(); for (;;) { - Keyboard::update_keyboard(); + asm("hlt"); } } \ No newline at end of file