Kernel: Add basic Shell to test functionality

This commit is contained in:
Bananymous 2022-12-13 21:34:50 +02:00
parent f8224e55b1
commit 711ba19a82
3 changed files with 181 additions and 68 deletions

View File

@ -0,0 +1,28 @@
#pragma once
#include <BAN/String.h>
#include <kernel/Keyboard.h>
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;
};
}

147
kernel/kernel/Shell.cpp Normal file
View File

@ -0,0 +1,147 @@
#include <BAN/StringView.h>
#include <BAN/Vector.h>
#include <kernel/IO.h>
#include <kernel/Keyboard.h>
#include <kernel/RTC.h>
#include <kernel/Shell.h>
#include <kernel/tty.h>
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;
}
}
}
}

View File

@ -10,71 +10,14 @@
#include <kernel/PIT.h>
#include <kernel/RTC.h>
#include <kernel/Serial.h>
#include <kernel/Shell.h>
#include <kernel/tty.h>
#include <string.h>
#include <stdlib.h>
#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)
{
@ -88,10 +31,6 @@ 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");
}
}