diff --git a/CMakeLists.txt b/CMakeLists.txt index be3f4fd..ddbd1c2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,8 @@ target_compile_options(avery_objects PRIVATE $<$:-Wconversion> $<$:-Wshadow> $<$:-Werror> + $<$:-ffile-prefix-map=${CMAKE_SOURCE_DIR}/=> + $<$:-fno-omit-frame-pointer> ) set(KERNEL_ELF ${CMAKE_BINARY_DIR}/avery.elf) diff --git a/README.md b/README.md index 0cff893..e7d208f 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ practices while remaining approachable to contributors and learners alike. - [x] Logging system (both visual + serial) - [x] Halt loop - [x] Basics of the kernel + GDT, IDT and interrupt handlers -- [ ] PIC, timer and keyboard support +- [x] PIC, timer and keyboard support - [ ] Stack tracing support ### Memory @@ -30,6 +30,15 @@ practices while remaining approachable to contributors and learners alike. - [ ] Physical memory manager - [ ] Virtual memory manager - [ ] Kernel heap (malloc, free) +- [ ] Move to Limine 6 (APIC or x2APIC) + +### Internal Driver Framework + +- [ ] Device objects +- [ ] Driver registration +- [ ] Driver lifecycle +- [ ] MMIO port I/O helpers +- [ ] Block device abstraction ### Hardware discovery @@ -38,12 +47,12 @@ practices while remaining approachable to contributors and learners alike. ### Storage -- [ ] Driver system -- [ ] Filesystem VFS layer -- [ ] FAT32 support -- [ ] ext2 support -- [ ] Read from filesystem -- [ ] Write to filesystem +- [ ] ATA driver +- [ ] Block device layer +- [ ] VFS +- [ ] FAT32 +- [ ] ext2 +- [ ] Read & Write files ### Processes @@ -55,19 +64,95 @@ practices while remaining approachable to contributors and learners alike. ## Roadmap (for Beta) -### Toolchain +### Beta 1 + +- [ ] Avery Kernel SDK +- [ ] Driver build system +- [ ] Driver templates +- [ ] Driver documentation +- [ ] Loadable modules +- [ ] Driver manifest format +- [ ] Sample PCI driver +- [ ] Sample block driver +- [ ] Sample character driver +- [ ] Stable kernel driver API + +### Beta 2 + +- [ ] ACPI +- [ ] MADT parsing +- [ ] x2APIC +- [ ] APIC timer +- [ ] IPIs + +### Beta 3 + +- [ ] xHCI +- [ ] USB enumeration +- [ ] USB keyboard +- [ ] USB mouse +- [ ] USB mass storage +- [ ] USB hubs + +### Beta 4 + +- [ ] Ethernet driver (E1000 first) +- [ ] Ethernet layer +- [ ] ARP +- [ ] IPv4 +- [ ] ICMP +- [ ] UCP +- [ ] TCP +- [ ] DNS +- [ ] DHCP + +### Beta 5 + +- [ ] USB Bluetooth adapter support +- [ ] HCI layer +- [ ] L2CAP +- [ ] HID over Bluetooth +- [ ] Bluetooth keyboard and mouse + +### Beta 6 + +- [ ] Intel HDA +- [ ] Audio Mixer +- [ ] PCM playback +- [ ] Basic Sound API + +### Beta 7 + +- [ ] Better graphics abstraction +- [ ] Font renderer +- [ ] Mouse cursor +- [ ] Window manager +- [ ] Simple compositor +- [ ] Terminal app +- [ ] File manager -- [ ] Migrate a C compiler -- [ ] Create a libc port -- [ ] Build C examples +### Beta 8 -### Hardware +- [ ] Port binutils +- [ ] Port Clang +- [ ] libc +- [ ] C examples +- [ ] Build programs for Avery -- [ ] USB support -- [ ] Ethernet / networking +### Beta 9 -### Graphics +- [ ] Self-hosting -- [ ] Graphics abstraction -- [ ] Font renderer -- [ ] Simple compositor \ No newline at end of file +### Beta 10 + +- [ ] Graphics API + +### Beta 11 + +- [ ] Users/groups +- [ ] Permissions +- [ ] Process isolation +- [ ] ASLR +- [ ] NX +- [ ] Sandboxing +- [ ] Driver permissions/signing diff --git a/include/core/apic.h b/include/core/apic.h new file mode 100644 index 0000000..ef81ac7 --- /dev/null +++ b/include/core/apic.h @@ -0,0 +1,39 @@ +/* +* apic.h +* As part of the Avery project +* Created by Max Van den Eynde in 2026 +* -------------------------------------- +* Description: Advanced PIC implementation +* Copyright (c) 2026 Max Van den Eynde +*/ + +#ifndef AVERY_APIC_H +#define AVERY_APIC_H +#include "../types.h" + +namespace lapic { + inline u64 rdmsr(u32 msr) { + u32 lo, hi; + asm volatile("rdmsr" : "=a"(lo), "=d"(hi) : "c"(msr)); + return (static_cast(hi) << 32) | lo; + } + + inline void wrmsr(u32 msr, u64 value) { + u32 lo = value & 0xffffffff; + u32 hi = value >> 32; + + asm volatile("wrmsr" : : "c"(msr), "a"(lo), "d"(hi)); + } + + void initBase(); + void write(u32 reg, u32 value); + u32 read(u32 reg); + + constexpr u32 LAPIC_SRV = 0xF0; + void enable(); + + constexpr u32 LAPIC_LVT_LINT0 = 0x350; + void enableLegacyMode(); +} + +#endif //AVERY_APIC_H diff --git a/include/drivers/driver.h b/include/drivers/driver.h new file mode 100644 index 0000000..6d0a8ff --- /dev/null +++ b/include/drivers/driver.h @@ -0,0 +1,28 @@ +/* +* driver.h +* As part of the Avery project +* Created by Max Van den Eynde in 2026 +* -------------------------------------- +* Description: ${FILE_DESCRIPTION} +* Copyright (c) 2026 Max Van den Eynde +*/ + +#ifndef AVERY_DRIVER_H +#define AVERY_DRIVER_H +#include "../types.h" + +using DriverInitFunction = void (*)(); + +enum class DriverType { + Generic +}; + +class Driver { +public: + string name; + DriverType type; + + DriverInitFunction init; +}; + +#endif //AVERY_DRIVER_H diff --git a/include/drivers/keyboard.h b/include/drivers/keyboard.h new file mode 100644 index 0000000..d445719 --- /dev/null +++ b/include/drivers/keyboard.h @@ -0,0 +1,25 @@ +/* +* keyboard.h +* As part of the Avery project +* Created by Max Van den Eynde in 2026 +* -------------------------------------- +* Description: Basic PS/2 driver support +* Copyright (c) 2026 Max Van den Eynde +*/ + +#ifndef AVERY_KEYBOARD_H +#define AVERY_KEYBOARD_H +#include "../core/isr.h" + +namespace keyboard { + extern const unsigned char es[128]; + extern const unsigned char esShift[128]; + extern const unsigned char* activeLayout; + + bool hasChar(); + char getChar(); + void handler(isr::Registers* regs); + void init(); +} + +#endif //AVERY_KEYBOARD_H diff --git a/include/drivers/pit.h b/include/drivers/pit.h new file mode 100644 index 0000000..e76d091 --- /dev/null +++ b/include/drivers/pit.h @@ -0,0 +1,25 @@ +/* +* pit.h +* As part of the Avery project +* Created by Max Van den Eynde in 2026 +* -------------------------------------- +* Description: Programmable Interval Timer setup +* Copyright (c) 2026 Max Van den Eynde +*/ + +#ifndef AVERY_PIT_H +#define AVERY_PIT_H +#include "../core/isr.h" + +extern "C" void time_handler(isr::Registers* regs); + +namespace time { + u64 getUptime(); + void wait(u64 ticksMs); +} + +namespace core { + void initPit(); +} + +#endif //AVERY_PIT_H diff --git a/include/graphics/framebuffer.h b/include/graphics/framebuffer.h index 45f4b0c..1ffd01b 100644 --- a/include/graphics/framebuffer.h +++ b/include/graphics/framebuffer.h @@ -49,9 +49,11 @@ class FramebufferConsole { void writeLn(string str); void newline(); void backspace(); + void backspace(char c); void setColor(Color foreground, Color background); void setCursor(u64 x, u64 y); + void updateCursor(); private: void drawCursor(); @@ -68,6 +70,9 @@ class FramebufferConsole { u64 cursorX = 0; u64 cursorY = 0; + bool cursorVisible = true; + bool cursorDrawn = false; + u64 cursorBlinkTimestamp = 0; u64 rows; }; diff --git a/include/kernel/console.h b/include/kernel/console.h index 946915d..ac0b064 100644 --- a/include/kernel/console.h +++ b/include/kernel/console.h @@ -40,5 +40,10 @@ namespace out { void setColor(Color fg, Color bg); }; +namespace in { + string getLine(string prompt = nullptr); + char getChar(); +} + #endif //AVERY_CONSOLE_H diff --git a/include/kernel/debug.h b/include/kernel/debug.h index abddce2..3997a3d 100644 --- a/include/kernel/debug.h +++ b/include/kernel/debug.h @@ -31,6 +31,26 @@ __func__ \ } \ } while (0) +#define CHECK(expr) \ +do \ + { \ +if (!(expr)) { \ +debug::warn( \ +"Condition not met" #expr "in" __FILE__ \ +); \ +} \ +} while (0) + +#define EXPECT(expr) \ +do \ +{ \ +if (!(expr)) { \ +debug::error( \ +"Condition not met" #expr "in" __FILE__ \ +); \ +} \ +} while (0) + #ifndef NDEBUG #define ASSERT(expr) VERIFY(expr) #else diff --git a/include/kernel/memory.h b/include/kernel/memory.h index d13887a..95720c8 100644 --- a/include/kernel/memory.h +++ b/include/kernel/memory.h @@ -11,6 +11,8 @@ #define AVERY_MEMORY_H #include "../types.h" +struct limine_hhdm_request; + namespace memory { void copy(u8* dest, const u8* src, int count); @@ -20,6 +22,10 @@ namespace memory { dest[i] = val; } } + + void setHHDM(volatile limine_hhdm_request& request); + u64 getHHDMOffset(); } + #endif //AVERY_MEMORY_H diff --git a/kernel/core/gdt/gdt.cpp b/kernel/core/interrupts/gdt/gdt.cpp similarity index 100% rename from kernel/core/gdt/gdt.cpp rename to kernel/core/interrupts/gdt/gdt.cpp diff --git a/kernel/core/gdt/gdt_flush.asm b/kernel/core/interrupts/gdt/gdt_flush.asm similarity index 100% rename from kernel/core/gdt/gdt_flush.asm rename to kernel/core/interrupts/gdt/gdt_flush.asm diff --git a/kernel/core/idt/idt.cpp b/kernel/core/interrupts/idt/idt.cpp similarity index 100% rename from kernel/core/idt/idt.cpp rename to kernel/core/interrupts/idt/idt.cpp diff --git a/kernel/core/idt/idt_load.asm b/kernel/core/interrupts/idt/idt_load.asm similarity index 100% rename from kernel/core/idt/idt_load.asm rename to kernel/core/interrupts/idt/idt_load.asm diff --git a/kernel/core/irqs/irq.cpp b/kernel/core/interrupts/irqs/irq.cpp similarity index 100% rename from kernel/core/irqs/irq.cpp rename to kernel/core/interrupts/irqs/irq.cpp diff --git a/kernel/core/irqs/irq_stub.asm b/kernel/core/interrupts/irqs/irq_stub.asm similarity index 100% rename from kernel/core/irqs/irq_stub.asm rename to kernel/core/interrupts/irqs/irq_stub.asm diff --git a/kernel/core/isrs/isr.cpp b/kernel/core/interrupts/isrs/isr.cpp similarity index 100% rename from kernel/core/isrs/isr.cpp rename to kernel/core/interrupts/isrs/isr.cpp diff --git a/kernel/core/isrs/isr_stubs.asm b/kernel/core/interrupts/isrs/isr_stubs.asm similarity index 100% rename from kernel/core/isrs/isr_stubs.asm rename to kernel/core/interrupts/isrs/isr_stubs.asm diff --git a/kernel/core/interrupts/pic/lapic.cpp b/kernel/core/interrupts/pic/lapic.cpp new file mode 100644 index 0000000..26e1943 --- /dev/null +++ b/kernel/core/interrupts/pic/lapic.cpp @@ -0,0 +1,36 @@ +/* +* lapic.cpp +* As part of the Avery project +* Created by Max Van den Eynde in 2026 +* -------------------------------------- +* Description: Local Advanced Programmable Interrupt Controler interfaces +* Copyright (c) 2026 Max Van den Eynde +*/ + +#include "core/apic.h" +#include "kernel/memory.h" + +static volatile u32* LapicBase = nullptr; + +void lapic::initBase() { + u64 apicBaseMsr = rdmsr(0x1B); + uptr phys = apicBaseMsr & 0xFFFFF000; + u64 hhdm = memory::getHHDMOffset(); + LapicBase = reinterpret_cast(hhdm + phys); +} + +void lapic::write(u32 reg, u32 value) { + *reinterpret_cast(reinterpret_cast(LapicBase) + reg) = value; +} + +u32 lapic::read(u32 reg) { + return *reinterpret_cast(reinterpret_cast(LapicBase) + reg); +} + +void lapic::enable() { + write(LAPIC_SRV, read(LAPIC_SRV) | 0x100 | 0xFF); +} + +void lapic::enableLegacyMode() { + write(LAPIC_LVT_LINT0, 0x700); +} diff --git a/kernel/core/memory/memory.cpp b/kernel/core/memory/memory.cpp index b54e6ff..3883f0b 100644 --- a/kernel/core/memory/memory.cpp +++ b/kernel/core/memory/memory.cpp @@ -7,10 +7,26 @@ * Copyright (c) 2026 Max Van den Eynde */ +#include #include +#include "kernel/debug.h" + +u64 HHDMOffset = 0; + void memory::copy(u8* dest, const u8* src, int count) { for (int i = 0; i < count; i++) { dest[i] = src[i]; } } + +void memory::setHHDM(volatile limine_hhdm_request& request) { + ASSERT(request.response != nullptr); + ASSERT(request.response->offset != 0); + HHDMOffset = request.response->offset; +} + +u64 memory::getHHDMOffset() { + EXPECT(HHDMOffset != 0); + return HHDMOffset; +} diff --git a/kernel/core/systems.cpp b/kernel/core/systems.cpp index dd1249e..62b89cc 100644 --- a/kernel/core/systems.cpp +++ b/kernel/core/systems.cpp @@ -11,9 +11,12 @@ #include "kernel/debug.h" #include +#include "core/apic.h" #include "core/idt.h" #include "core/irq.h" #include "core/isr.h" +#include "drivers/keyboard.h" +#include "drivers/pit.h" void core::initSystems() { initGdt(); @@ -22,6 +25,18 @@ void core::initSystems() { debug::log("Initialized IDT"); initIsrs(); debug::log("All ISRs bound correctly"); + + //lapic::initBase(); + //lapic::enable(); + //lapic::enableLegacyMode(); + debug::log("Using PIC Legacy Mode"); + initIrq(); debug::log("All IRQs bound correctly"); + initPit(); + debug::log("The Timer is initialized correctly"); + keyboard::init(); + debug::log("The Keyboard is initialized correctly"); + + asm volatile("sti"); } diff --git a/kernel/drivers/input/keyboard.cpp b/kernel/drivers/input/keyboard.cpp new file mode 100644 index 0000000..af3f642 --- /dev/null +++ b/kernel/drivers/input/keyboard.cpp @@ -0,0 +1,211 @@ +/* +* keyboard.cpp +* As part of the Avery project +* Created by Max Van den Eynde in 2026 +* -------------------------------------- +* Description: PS/2 and input driver support +* Copyright (c) 2026 Max Van den Eynde +*/ + +#include + +#include "core/irq.h" +#include "io/io.h" +#include "types.h" + +const unsigned char keyboard::es[128] = +{ + 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', + '9', '0', '\'', '?', '\b', + '\t', + + 'q', 'w', 'e', 'r', + 't', 'y', 'u', 'i', 'o', 'p', + '`', '+', '\n', + + 0, + + 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', + '?', '?', + + 0, + + 0, + + '?', 'z', 'x', 'c', 'v', 'b', 'n', + 'm', ',', '.', '-', + + 0, + + '*', + 0, + ' ', + 0, + + 0, 0, 0, 0, 0, 0, 0, 0, + 0, + + 0, + 0, + + 0, + 0, + 0, + '-', + + 0, + 0, + 0, + '+', + + 0, + 0, + 0, + 0, + 0, + + 0, 0, 0, + + 0, + 0, + + 0, + + '<' // scancode 86 (ISO key, next to left shift) +}; + +const unsigned char keyboard::esShift[128] = +{ + 0, 27, '!', '"', '#', '$', '%', '&', '/', '(', + ')', '=', '?', '?', '\b', + '\t', + + 'Q', 'W', 'E', 'R', + 'T', 'Y', 'U', 'I', 'O', 'P', + '^', '*', '\n', + + 0, + + 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', + '?', '?', + + 0, + + 0, + + '?', 'Z', 'X', 'C', 'V', 'B', 'N', + 'M', ';', ':', '_', + + 0, + + '*', + 0, + ' ', + 0, + + 0, 0, 0, 0, 0, 0, 0, 0, + 0, + + 0, + 0, + + 0, + 0, + 0, + '-', + + 0, + 0, + 0, + '+', + + 0, + 0, + 0, + 0, + 0, + + 0, 0, 0, + + 0, + 0, + + '>' // ISO key (scancode 86) +}; + +const unsigned char* keyboard::activeLayout = es; + +namespace { + constexpr usize BufferSize = 256; + + char inputBuffer[BufferSize]; + volatile usize readIndex = 0; + volatile usize writeIndex = 0; + bool shiftPressed = false; + bool extendedScancode = false; + + void enqueue(char c) { + usize nextIndex = (writeIndex + 1) % BufferSize; + + if (nextIndex == readIndex) { + return; + } + + inputBuffer[writeIndex] = c; + writeIndex = nextIndex; + } +} + +void keyboard::handler([[maybe_unused]] isr::Registers* regs) { + u8 scancode = io::inb(0x60); + + if (scancode == 0xE0) { + extendedScancode = true; + return; + } + + if (extendedScancode) { + extendedScancode = false; + return; + } + + if ((scancode & 0x80) != 0) { + u8 released = static_cast(scancode & 0x7F); + + if (released == 0x2A || released == 0x36) { + shiftPressed = false; + } + + return; + } + + if (scancode == 0x2A || scancode == 0x36) { + shiftPressed = true; + return; + } + + const unsigned char* layout = shiftPressed ? esShift : activeLayout; + char c = static_cast(layout[scancode]); + + if (c != 0) { + enqueue(c); + } +} + +bool keyboard::hasChar() { + return readIndex != writeIndex; +} + +char keyboard::getChar() { + while (readIndex == writeIndex) { + asm volatile("pause"); + } + + char c = inputBuffer[readIndex]; + readIndex = (readIndex + 1) % BufferSize; + return c; +} + +void keyboard::init() { + irq::installHandler(1, &handler); +} diff --git a/kernel/drivers/time/pit.cpp b/kernel/drivers/time/pit.cpp new file mode 100644 index 0000000..ec7b3d7 --- /dev/null +++ b/kernel/drivers/time/pit.cpp @@ -0,0 +1,43 @@ +/* +* pit.cpp +* As part of the Avery project +* Created by Max Van den Eynde in 2026 +* -------------------------------------- +* Description: Programmable Interval Timer +* Copyright (c) 2026 Max Van den Eynde +*/ + +#include + +#include "core/irq.h" +#include "io/io.h" + +volatile u64 timerTicks; + +constexpr int TICKS_PER_SECOND = 1000; + +extern "C" void time_handler([[maybe_unused]] isr::Registers* regs) { + timerTicks = timerTicks + 1; +} + +void core::initPit() { + int divisor = 1193180 / TICKS_PER_SECOND; + io::outb(0x43, 0x36); + io::outb(0x40, divisor & 0xFF); + io::outb(0x40, (divisor >> 8) & 0xFF); + + irq::installHandler(0, time_handler); +} + +u64 time::getUptime() { + return timerTicks; +} + +void time::wait(u64 ms) { + u64 end = timerTicks + ms; + + while (timerTicks < end) { + asm volatile("pause"); + } +} + diff --git a/kernel/graphics/framebuffer.cpp b/kernel/graphics/framebuffer.cpp index cded0f4..05d8d82 100644 --- a/kernel/graphics/framebuffer.cpp +++ b/kernel/graphics/framebuffer.cpp @@ -11,6 +11,7 @@ #include #include +#include "drivers/pit.h" #include "graphics/graphicsTypes.h" #include "../../include/types.h" #include "kernel/debug.h" @@ -353,6 +354,9 @@ void FramebufferConsole::clear() { cursorX = 0; cursorY = 0; + cursorDrawn = false; + cursorBlinkTimestamp = time::getUptime(); + drawCursor(); } void FramebufferConsole::drawCell(u64 pixelX, u64 y, char c) { @@ -378,14 +382,18 @@ void FramebufferConsole::newline() { } void FramebufferConsole::putChar(char c) { + if (cursorDrawn) { + eraseCursor(); + } + switch (c) { case '\n': newline(); - return; + break; case '\r': cursorX = 0; - return; + break; case '\t': { @@ -398,12 +406,12 @@ void FramebufferConsole::putChar(char c) { putChar(' '); } - return; + break; } case '\b': backspace(); - return; + break; default: const u64 scaledFontWidth = FONT_WIDTH * fontScale(scale); @@ -420,7 +428,12 @@ void FramebufferConsole::putChar(char c) { newline(); } - return; + break; + } + + if (cursorVisible) { + cursorBlinkTimestamp = time::getUptime(); + drawCursor(); } } @@ -440,8 +453,16 @@ void FramebufferConsole::writeLn(string str) { } void FramebufferConsole::backspace() { + if (cursorDrawn) { + eraseCursor(); + } + if (cursorX == 0) { if (cursorY == 0) { + if (cursorVisible) { + cursorBlinkTimestamp = time::getUptime(); + drawCursor(); + } return; } @@ -460,6 +481,33 @@ void FramebufferConsole::backspace() { } drawCell(cursorX, cursorY, ' '); + + if (cursorVisible) { + cursorBlinkTimestamp = time::getUptime(); + drawCursor(); + } +} + +void FramebufferConsole::backspace(char c) { + if (cursorDrawn) { + eraseCursor(); + } + + const u64 charWidth = glyphAdvance(c) * fontScale(scale); + + if (cursorX >= charWidth) { + cursorX -= charWidth; + } + else { + cursorX = 0; + } + + drawCell(cursorX, cursorY, ' '); + + if (cursorVisible) { + cursorBlinkTimestamp = time::getUptime(); + drawCursor(); + } } void FramebufferConsole::scroll() { @@ -480,29 +528,39 @@ void FramebufferConsole::scroll() { void FramebufferConsole::drawCursor() { const u64 charHeight = FONT_HEIGHT * fontScale(scale); + const u64 cursorWidth = fontScale(scale); const u64 pixelX = cursorX; const u64 pixelY = cursorY * charHeight; framebuffer.paintRectangle( {pixelX, pixelY}, - {pixelX + FONT_WIDTH * fontScale(scale), pixelY + charHeight}, + {pixelX + cursorWidth, pixelY + charHeight}, fg ); + + cursorDrawn = true; } void FramebufferConsole::eraseCursor() { + if (!cursorDrawn) { + return; + } + const u64 charHeight = FONT_HEIGHT * fontScale(scale); + const u64 cursorWidth = fontScale(scale); const u64 pixelX = cursorX; const u64 pixelY = cursorY * charHeight; framebuffer.paintRectangle( {pixelX, pixelY}, - {pixelX + FONT_WIDTH * fontScale(scale), pixelY + charHeight}, + {pixelX + cursorWidth, pixelY + charHeight}, bg ); + + cursorDrawn = false; } void FramebufferConsole::setColor(Color foreground, Color background) { @@ -511,6 +569,10 @@ void FramebufferConsole::setColor(Color foreground, Color background) { } void FramebufferConsole::setCursor(u64 x, u64 y) { + if (cursorDrawn) { + eraseCursor(); + } + cursorX = x * FONT_WIDTH * fontScale(scale); if (cursorX >= framebuffer.width()) { @@ -522,4 +584,31 @@ void FramebufferConsole::setCursor(u64 x, u64 y) { if (cursorY >= rows) { cursorY = rows - 1; } + + if (cursorVisible) { + cursorBlinkTimestamp = time::getUptime(); + drawCursor(); + } +} + +void FramebufferConsole::updateCursor() { + if (!cursorVisible) { + eraseCursor(); + return; + } + + u64 now = time::getUptime(); + + if (now - cursorBlinkTimestamp < 500) { + return; + } + + cursorBlinkTimestamp = now; + + if (cursorDrawn) { + eraseCursor(); + } + else { + drawCursor(); + } } diff --git a/kernel/io/console.cpp b/kernel/io/console.cpp index ae11de4..038d5f0 100644 --- a/kernel/io/console.cpp +++ b/kernel/io/console.cpp @@ -10,6 +10,7 @@ #include "graphics/framebuffer.h" #include "../../include/kernel/console.h" +#include "drivers/keyboard.h" #include "io/serial.h" static FramebufferConsole* consoleAccess = nullptr; @@ -89,3 +90,55 @@ void out::printHex(u64 num) { print("0x"); print(hex); } + +char in::getChar() { + while (!keyboard::hasChar()) { + if (out::outputMode == ConsoleOutputMode::Framebuffer && GlobalFramebufferConsole != nullptr) { + GlobalFramebufferConsole->updateCursor(); + } + + asm volatile("pause"); + } + + return keyboard::getChar(); +} + +string in::getLine(string prompt) { + static char buffer[256]; + usize length = 0; + + if (prompt != nullptr) { + out::print(prompt); + } + + while (true) { + char c = getChar(); + + if (c == '\n') { + out::putChar('\n'); + buffer[length] = '\0'; + return buffer; + } + + if (c == '\b') { + if (length > 0) { + --length; + + if (out::outputMode == ConsoleOutputMode::Framebuffer && GlobalFramebufferConsole != nullptr) { + GlobalFramebufferConsole->backspace(buffer[length]); + } + else { + out::putChar('\b'); + } + } + + continue; + } + + if (length < 255) { + buffer[length] = c; + ++length; + out::putChar(c); + } + } +} diff --git a/kernel/main.cpp b/kernel/main.cpp index e61b56b..565d4b9 100644 --- a/kernel/main.cpp +++ b/kernel/main.cpp @@ -3,12 +3,14 @@ #include "../include/kernel/console.h" #include "core/regs.h" #include "core/systems.h" +#include "drivers/pit.h" #include "graphics/framebuffer.h" #include "io/serial.h" #include "kernel/debug.h" +#include "kernel/memory.h" __attribute__((used, section(".limine_requests"))) -static volatile uint64_t limine_base_revision[] = LIMINE_BASE_REVISION(6); +static volatile uint64_t limine_base_revision[] = LIMINE_BASE_REVISION(4); __attribute__((used, section(".limine_requests"))) static volatile struct limine_framebuffer_request framebuffer_request = { @@ -17,6 +19,13 @@ static volatile struct limine_framebuffer_request framebuffer_request = { .response = nullptr, }; +__attribute__((used, section(".limine_requests"))) +static volatile struct limine_hhdm_request hddm_request = { + .id = LIMINE_HHDM_REQUEST_ID, + .revision = 0, + .response = nullptr +}; + __attribute__((used, section(".limine_requests_start"))) static volatile uint64_t limine_requests_start_marker[] = LIMINE_REQUESTS_START_MARKER; @@ -24,6 +33,8 @@ __attribute__((used, section(".limine_requests_end"))) static volatile uint64_t limine_requests_end_marker[] = LIMINE_REQUESTS_END_MARKER; extern "C" [[noreturn]] void _start() { + memory::setHHDM(hddm_request); + regs::enableSSE(); core::initSystems(); @@ -33,13 +44,17 @@ extern "C" [[noreturn]] void _start() { } } - Framebuffer framebuffer = Framebuffer::createFromLimineRequest(framebuffer_request); out::initFramebufferConsole(framebuffer); out::println("The Avery Kernel"); out::println("Version Alpha 1 (Development Edition)"); out::println("Made by Neutral Software in 2026"); + string result = in::getLine("What's your name? "); + out::print("Hello, "); + out::println(result); + result = in::getLine("Is this Avery? "); + out::println(result); while (true) { asm("hlt");