summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArseney300 <Arseney300@gmail.com>2026-04-23 17:02:05 +0700
committerArseney300 <Arseney300@gmail.com>2026-04-23 17:02:05 +0700
commite935c3afba9b3a492ad804726fa0fd97d437d8f5 (patch)
treec8c26fe36ed4db1ffec4dab5f5bf3b489c064a07
parent496246c92dabe6757480147016490ea6ae43bb66 (diff)
WIP: add driver objectsWIP/add_driver_obj
-rw-r--r--kernel/Makefile6
-rw-r--r--kernel/drivers/char/pl011.cpp83
-rw-r--r--kernel/drivers/char/uart_16550.cpp85
-rw-r--r--kernel/drivers/console/framebuffer.cpp328
-rw-r--r--kernel/drivers/driver_registry.cpp55
-rw-r--r--kernel/include/kernel/console_driver.h58
-rw-r--r--kernel/include/kernel/device.h52
-rw-r--r--kernel/include/kernel/driver.h46
-rw-r--r--kernel/include/kernel/driver_list.h34
-rw-r--r--kernel/include/kernel/early_device.h35
-rw-r--r--kernel/include/kernel/early_driver.h40
-rw-r--r--kernel/include/kernel/placement_new.h4
-rw-r--r--kernel/include/kernel/platform_bus.h22
-rw-r--r--kernel/lib/framebuffer.cpp316
14 files changed, 880 insertions, 284 deletions
diff --git a/kernel/Makefile b/kernel/Makefile
index 40f081e..c3634e0 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -28,6 +28,10 @@ LIB_SRC := $(wildcard lib/*.cpp)
# ── Firmware subsystems (both compile on all arches; unused one stays dormant) ──
FIRMWARE_SRC := $(wildcard firmware/acpi/*.cpp) $(wildcard firmware/dt/*.cpp)
+# ── Driver framework (always compiled) ─────────────────────────────────────
+# TODO: fix that
+DRV_FRAMEWORK_SRC := $(wildcard drivers/driver_registry.cpp)
+
# ── Conditionally-compiled sources (from .config) ───────────────────────────
# TODO: change it to Driver Makefiles
DRV_SRC :=
@@ -72,7 +76,7 @@ endif
# ── Collect all sources ─────────────────────────────────────────────────────
-ALL_CPP_SRC := $(ARCH_CPP_SRC) $(CORE_SRC) $(MM_SRC) $(FIRMWARE_SRC) $(DRV_SRC) $(FS_SRC) $(LDR_SRC) $(LIB_SRC)
+ALL_CPP_SRC := $(ARCH_CPP_SRC) $(CORE_SRC) $(MM_SRC) $(FIRMWARE_SRC) $(DRV_FRAMEWORK_SRC) $(DRV_SRC) $(FS_SRC) $(LDR_SRC) $(LIB_SRC)
ALL_ASM_SRC := $(ARCH_ASM_SRC)
ALL_CPP_OBJ := $(patsubst %.cpp,%.o,$(ALL_CPP_SRC))
diff --git a/kernel/drivers/char/pl011.cpp b/kernel/drivers/char/pl011.cpp
new file mode 100644
index 0000000..7615370
--- /dev/null
+++ b/kernel/drivers/char/pl011.cpp
@@ -0,0 +1,83 @@
+// ============================================================================
+// drivers/char/pl011.cpp — PL011 UART console driver + device (aarch64)
+//
+// Stub implementation that compiles but does no real I/O yet.
+// TODO: Program IBRD, FBRD, LCR_H, CR registers for actual UART output.
+// ============================================================================
+
+#include <kernel/console_driver.h>
+#include <kernel/early_driver.h>
+#include <kernel/early_device.h>
+#include <kernel/platform_bus.h>
+
+// ── PL011Device ───────────────────────────────────────────────────────────
+
+class PL011Device : public Device, public EarlyDevice<PL011Device> {
+public:
+ explicit PL011Device(uintptr_t base_addr) : base_(base_addr) {}
+
+ const char* name() const override { return "uart0"; }
+ uintptr_t base() const { return base_; }
+
+private:
+ uintptr_t base_;
+};
+
+// ── PL011Console driver ──────────────────────────────────────────────────
+
+class PL011Console : public ConsoleDriver,
+ public EarlyDriver<PL011Console> {
+public:
+ PL011Console() = default;
+
+ const char* name() const override { return "pl011-uart"; }
+
+ bool init() override {
+ auto* dev = static_cast<PL011Device*>(device());
+ base_ = dev->base();
+
+ // TODO: Configure UART registers at base_:
+ // UARTIBRD (offset 0x24) — integer baud rate divisor
+ // UARTFBRD (offset 0x28) — fractional baud rate divisor
+ // UARTLCR_H (offset 0x2C) — line control (8N1, FIFO enable)
+ // UARTCR (offset 0x30) — control register (TX enable, UART enable)
+ initialized_ = true;
+ return true;
+ }
+
+ void shutdown() override {
+ initialized_ = false;
+ }
+
+ void putchar(char c) override {
+ if (!initialized_) return;
+ // TODO: while (*(volatile uint32_t*)(base_ + 0x18) & (1 << 5)) {}
+ // *(volatile uint32_t*)(base_ + 0x00) = c;
+ (void)c;
+ }
+
+ void clear() override {
+ // UART has no screen to clear
+ }
+
+private:
+ uintptr_t base_ = 0;
+ bool initialized_ = false;
+};
+
+// ── Driver init function ──────────────────────────────────────────────────
+
+namespace console {
+
+void init_pl011(uintptr_t base_addr) {
+ auto* dev = PL011Device::create(base_addr);
+ platform::bus().add_device(dev);
+
+ auto* drv = PL011Console::create();
+ drv->bind(dev);
+ drv->init();
+
+ console::register_driver(drv);
+}
+
+} // namespace console
diff --git a/kernel/drivers/char/uart_16550.cpp b/kernel/drivers/char/uart_16550.cpp
new file mode 100644
index 0000000..a85b5e7
--- /dev/null
+++ b/kernel/drivers/char/uart_16550.cpp
@@ -0,0 +1,85 @@
+// ============================================================================
+// drivers/char/uart_16550.cpp — 16550 UART console driver + device (x86_64)
+//
+// Stub implementation that compiles but does no real I/O yet.
+// TODO: Program divisor latch, 8N1, FIFO, implement actual port I/O.
+// ============================================================================
+
+#include <kernel/console_driver.h>
+#include <kernel/early_driver.h>
+#include <kernel/early_device.h>
+#include <kernel/platform_bus.h>
+
+// ── UART16550Device ───────────────────────────────────────────────────────
+
+class UART16550Device : public Device, public EarlyDevice<UART16550Device> {
+public:
+ explicit UART16550Device(uint16_t port) : port_(port) {}
+
+ const char* name() const override { return "uart0"; }
+ uint16_t port() const { return port_; }
+
+private:
+ uint16_t port_;
+};
+
+// ── UART16550Console driver ───────────────────────────────────────────────
+
+class UART16550Console : public ConsoleDriver,
+ public EarlyDriver<UART16550Console> {
+public:
+ UART16550Console() = default;
+
+ const char* name() const override { return "uart-16550"; }
+
+ bool init() override {
+ auto* dev = static_cast<UART16550Device*>(device());
+ port_ = dev->port();
+
+ // TODO: outb(port_ + 1, 0x00) — disable interrupts
+ // TODO: outb(port_ + 3, 0x80) — enable DLAB
+ // TODO: outb(port_ + 0, 0x03) — set divisor (lo) 38400 baud
+ // TODO: outb(port_ + 1, 0x00) — set divisor (hi)
+ // TODO: outb(port_ + 3, 0x03) — 8 bits, no parity, one stop
+ // TODO: outb(port_ + 2, 0xC7) — enable FIFO, clear, 14-byte threshold
+ // TODO: outb(port_ + 4, 0x0B) — IRQs enabled, RTS/DSR set
+ initialized_ = true;
+ return true;
+ }
+
+ void shutdown() override {
+ initialized_ = false;
+ }
+
+ void putchar(char c) override {
+ if (!initialized_) return;
+ // TODO: while (!(inb(port_ + 5) & 0x20)) {} — wait for THR empty
+ // TODO: outb(port_, c)
+ (void)c;
+ }
+
+ void clear() override {
+ // UART has no screen to clear
+ }
+
+private:
+ uint16_t port_ = 0;
+ bool initialized_ = false;
+};
+
+// ── Driver init function ──────────────────────────────────────────────────
+
+namespace console {
+
+void init_uart_16550(uint16_t port) {
+ auto* dev = UART16550Device::create(port);
+ platform::bus().add_device(dev);
+
+ auto* drv = UART16550Console::create();
+ drv->bind(dev);
+ drv->init();
+
+ console::register_driver(drv);
+}
+
+} // namespace console
diff --git a/kernel/drivers/console/framebuffer.cpp b/kernel/drivers/console/framebuffer.cpp
new file mode 100644
index 0000000..e7f0577
--- /dev/null
+++ b/kernel/drivers/console/framebuffer.cpp
@@ -0,0 +1,328 @@
+// ============================================================================
+// drivers/console/framebuffer.cpp — Framebuffer console driver + device
+//
+// FramebufferDevice describes the GOP framebuffer hardware (base address,
+// resolution, pixel format). FramebufferConsole is the driver that renders
+// text to it using a built-in 8x16 VGA bitmap font.
+//
+// Both are early-allocated (static storage, no heap).
+// ============================================================================
+
+#include <kernel/console_driver.h>
+#include <kernel/early_driver.h>
+#include <kernel/early_device.h>
+#include <kernel/platform_bus.h>
+#include <boot/boot_info.h>
+
+// ── Minimal 8x16 bitmap font (ASCII 32-126) ──────────────────────────────
+
+static constexpr int FONT_FIRST = 32;
+static constexpr int FONT_LAST = 126;
+
+// clang-format off
+static const uint8_t g_font[][16] = {
+ // 32: space
+ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
+ // 33: !
+ {0x00,0x00,0x18,0x3C,0x3C,0x3C,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00},
+ // 34: "
+ {0x00,0x66,0x66,0x66,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
+ // 35: #
+ {0x00,0x00,0x00,0x6C,0x6C,0xFE,0x6C,0x6C,0xFE,0x6C,0x6C,0x00,0x00,0x00,0x00,0x00},
+ // 36: $
+ {0x00,0x10,0x10,0x7C,0xD6,0xD0,0x7C,0x16,0xD6,0x7C,0x10,0x10,0x00,0x00,0x00,0x00},
+ // 37: %
+ {0x00,0x00,0x00,0x00,0xC2,0xC6,0x0C,0x18,0x30,0x60,0xC6,0x86,0x00,0x00,0x00,0x00},
+ // 38: &
+ {0x00,0x00,0x38,0x6C,0x6C,0x38,0x76,0xDC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00},
+ // 39: '
+ {0x00,0x30,0x30,0x30,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
+ // 40: (
+ {0x00,0x00,0x0C,0x18,0x30,0x30,0x30,0x30,0x30,0x30,0x18,0x0C,0x00,0x00,0x00,0x00},
+ // 41: )
+ {0x00,0x00,0x30,0x18,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x18,0x30,0x00,0x00,0x00,0x00},
+ // 42: *
+ {0x00,0x00,0x00,0x00,0x00,0x66,0x3C,0xFF,0x3C,0x66,0x00,0x00,0x00,0x00,0x00,0x00},
+ // 43: +
+ {0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00},
+ // 44: ,
+ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x30,0x00,0x00,0x00},
+ // 45: -
+ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
+ // 46: .
+ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00},
+ // 47: /
+ {0x00,0x00,0x00,0x00,0x02,0x06,0x0C,0x18,0x30,0x60,0xC0,0x80,0x00,0x00,0x00,0x00},
+ // 48-57: 0-9
+ {0x00,0x00,0x7C,0xC6,0xC6,0xCE,0xDE,0xF6,0xE6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x18,0x38,0x78,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x7C,0xC6,0x06,0x0C,0x18,0x30,0x60,0xC0,0xC6,0xFE,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x7C,0xC6,0x06,0x06,0x3C,0x06,0x06,0x06,0xC6,0x7C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x0C,0x1C,0x3C,0x6C,0xCC,0xFE,0x0C,0x0C,0x0C,0x1E,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xFE,0xC0,0xC0,0xC0,0xFC,0x06,0x06,0x06,0xC6,0x7C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x38,0x60,0xC0,0xC0,0xFC,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xFE,0xC6,0x06,0x06,0x0C,0x18,0x30,0x30,0x30,0x30,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x7C,0xC6,0xC6,0xC6,0x7C,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x7C,0xC6,0xC6,0xC6,0x7E,0x06,0x06,0x06,0x0C,0x78,0x00,0x00,0x00,0x00},
+ // 58: :
+ {0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00},
+ // 59: ;
+ {0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x30,0x00,0x00,0x00,0x00},
+ // 60: <
+ {0x00,0x00,0x00,0x06,0x0C,0x18,0x30,0x60,0x30,0x18,0x0C,0x06,0x00,0x00,0x00,0x00},
+ // 61: =
+ {0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
+ // 62: >
+ {0x00,0x00,0x00,0x60,0x30,0x18,0x0C,0x06,0x0C,0x18,0x30,0x60,0x00,0x00,0x00,0x00},
+ // 63: ?
+ {0x00,0x00,0x7C,0xC6,0xC6,0x0C,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00},
+ // 64: @
+ {0x00,0x00,0x7C,0xC6,0xC6,0xDE,0xDE,0xDE,0xDC,0xC0,0xC0,0x7C,0x00,0x00,0x00,0x00},
+ // 65-90: A-Z
+ {0x00,0x00,0x10,0x38,0x6C,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xFC,0x66,0x66,0x66,0x7C,0x66,0x66,0x66,0x66,0xFC,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x3C,0x66,0xC2,0xC0,0xC0,0xC0,0xC0,0xC2,0x66,0x3C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xF8,0x6C,0x66,0x66,0x66,0x66,0x66,0x66,0x6C,0xF8,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xFE,0x66,0x62,0x68,0x78,0x68,0x60,0x62,0x66,0xFE,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xFE,0x66,0x62,0x68,0x78,0x68,0x60,0x60,0x60,0xF0,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x3C,0x66,0xC2,0xC0,0xC0,0xDE,0xC6,0xC6,0x66,0x3A,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x3C,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x1E,0x0C,0x0C,0x0C,0x0C,0x0C,0xCC,0xCC,0xCC,0x78,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xE6,0x66,0x66,0x6C,0x78,0x78,0x6C,0x66,0x66,0xE6,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xF0,0x60,0x60,0x60,0x60,0x60,0x60,0x62,0x66,0xFE,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xC6,0xEE,0xFE,0xFE,0xD6,0xC6,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xC6,0xE6,0xF6,0xFE,0xDE,0xCE,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xFC,0x66,0x66,0x66,0x7C,0x60,0x60,0x60,0x60,0xF0,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xD6,0xDE,0x7C,0x0C,0x0E,0x00,0x00},
+ {0x00,0x00,0xFC,0x66,0x66,0x66,0x7C,0x6C,0x66,0x66,0x66,0xE6,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x7C,0xC6,0xC6,0x60,0x38,0x0C,0x06,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xFF,0xDB,0x99,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x6C,0x38,0x10,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xD6,0xD6,0xD6,0xFE,0xEE,0x6C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xC6,0xC6,0x6C,0x7C,0x38,0x38,0x7C,0x6C,0xC6,0xC6,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xCC,0xCC,0xCC,0xCC,0x78,0x30,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xFE,0xC6,0x86,0x0C,0x18,0x30,0x60,0xC2,0xC6,0xFE,0x00,0x00,0x00,0x00},
+ // 91: [
+ {0x00,0x00,0x3C,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x3C,0x00,0x00,0x00,0x00},
+ // 92: backslash
+ {0x00,0x00,0x00,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x02,0x00,0x00,0x00,0x00,0x00},
+ // 93: ]
+ {0x00,0x00,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00,0x00,0x00,0x00},
+ // 94: ^
+ {0x10,0x38,0x6C,0xC6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
+ // 95: _
+ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00},
+ // 96: `
+ {0x00,0x30,0x18,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
+ // 97-122: a-z
+ {0x00,0x00,0x00,0x00,0x00,0x78,0x0C,0x7C,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0xE0,0x60,0x60,0x78,0x6C,0x66,0x66,0x66,0x66,0x7C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0x7C,0xC6,0xC0,0xC0,0xC0,0xC6,0x7C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x1C,0x0C,0x0C,0x3C,0x6C,0xCC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0x7C,0xC6,0xFE,0xC0,0xC0,0xC6,0x7C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x38,0x6C,0x64,0x60,0xF0,0x60,0x60,0x60,0x60,0xF0,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0x76,0xCC,0xCC,0xCC,0xCC,0xCC,0x7C,0x0C,0xCC,0x78,0x00},
+ {0x00,0x00,0xE0,0x60,0x60,0x6C,0x76,0x66,0x66,0x66,0x66,0xE6,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x18,0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x06,0x06,0x00,0x0E,0x06,0x06,0x06,0x06,0x06,0x06,0x66,0x66,0x3C,0x00},
+ {0x00,0x00,0xE0,0x60,0x60,0x66,0x6C,0x78,0x78,0x6C,0x66,0xE6,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0xEC,0xFE,0xD6,0xD6,0xD6,0xD6,0xC6,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0xDC,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0xDC,0x66,0x66,0x66,0x66,0x66,0x7C,0x60,0x60,0xF0,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0x76,0xCC,0xCC,0xCC,0xCC,0xCC,0x7C,0x0C,0x0C,0x1E,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0xDC,0x76,0x66,0x60,0x60,0x60,0xF0,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0x7C,0xC6,0x60,0x38,0x0C,0xC6,0x7C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x10,0x30,0x30,0xFC,0x30,0x30,0x30,0x30,0x36,0x1C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0x6C,0x38,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0xC6,0xC6,0xD6,0xD6,0xD6,0xFE,0x6C,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0xC6,0x6C,0x38,0x38,0x38,0x6C,0xC6,0x00,0x00,0x00,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7E,0x06,0x0C,0xF8,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0xFE,0xCC,0x18,0x30,0x60,0xC6,0xFE,0x00,0x00,0x00,0x00},
+ // 123: {
+ {0x00,0x00,0x0E,0x18,0x18,0x18,0x70,0x18,0x18,0x18,0x18,0x0E,0x00,0x00,0x00,0x00},
+ // 124: |
+ {0x00,0x00,0x18,0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00},
+ // 125: }
+ {0x00,0x00,0x70,0x18,0x18,0x18,0x0E,0x18,0x18,0x18,0x18,0x70,0x00,0x00,0x00,0x00},
+ // 126: ~
+ {0x00,0x00,0x76,0xDC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
+};
+// clang-format on
+
+// ── FramebufferDevice ─────────────────────────────────────────────────────
+
+class FramebufferDevice : public Device, public EarlyDevice<FramebufferDevice> {
+public:
+ explicit FramebufferDevice(const FramebufferInfo& fb)
+ : base_(fb.base), width_(fb.width), height_(fb.height)
+ , pitch_(fb.pitch), format_(fb.format)
+ {}
+
+ const char* name() const override { return "framebuffer0"; }
+
+ uint64_t base() const { return base_; }
+ uint32_t width() const { return width_; }
+ uint32_t height() const { return height_; }
+ uint32_t pitch() const { return pitch_; }
+ PixelFormat format() const { return format_; }
+
+private:
+ uint64_t base_;
+ uint32_t width_;
+ uint32_t height_;
+ uint32_t pitch_;
+ PixelFormat format_;
+};
+
+// ── FramebufferConsole driver ─────────────────────────────────────────────
+
+class FramebufferConsole : public ConsoleDriver,
+ public EarlyDriver<FramebufferConsole> {
+public:
+ FramebufferConsole() = default;
+
+ const char* name() const override { return "framebuffer-console"; }
+
+ bool init() override {
+ auto* dev = static_cast<FramebufferDevice*>(device());
+ fb_ = reinterpret_cast<uint32_t*>(dev->base());
+ width_ = dev->width();
+ height_ = dev->height();
+ pitch_ = dev->pitch();
+ is_bgr_ = (dev->format() == PixelFormat::BGR);
+ clear();
+ return true;
+ }
+
+ void shutdown() override {}
+
+ void putchar(char c) override {
+ uint32_t max_cols = width_ / CHAR_W;
+ uint32_t max_rows = height_ / CHAR_H;
+
+ if (c == '\n') {
+ col_ = 0;
+ row_++;
+ } else if (c == '\r') {
+ col_ = 0;
+ } else if (c == '\t') {
+ col_ = (col_ + 4) & ~3u;
+ } else {
+ render_char(col_, row_, c);
+ col_++;
+ }
+
+ if (col_ >= max_cols) {
+ col_ = 0;
+ row_++;
+ }
+
+ if (row_ >= max_rows) {
+ scroll();
+ row_ = max_rows - 1;
+ }
+ }
+
+ void clear() override {
+ for (uint32_t y = 0; y < height_; y++) {
+ for (uint32_t x = 0; x < width_; x++) {
+ put_pixel(x, y, BG_COLOR);
+ }
+ }
+ col_ = 0;
+ row_ = 0;
+ }
+
+private:
+ uint32_t* fb_ = nullptr;
+ uint32_t width_ = 0;
+ uint32_t height_ = 0;
+ uint32_t pitch_ = 0;
+ bool is_bgr_ = false;
+ uint32_t col_ = 0;
+ uint32_t row_ = 0;
+
+ static constexpr uint32_t CHAR_W = 8;
+ static constexpr uint32_t CHAR_H = 16;
+ static constexpr uint32_t FG_COLOR = CONFIG_FBCON_FG_COLOR;
+ static constexpr uint32_t BG_COLOR = CONFIG_FBCON_BG_COLOR;
+
+ void put_pixel(uint32_t x, uint32_t y, uint32_t rgb) {
+ if (x >= width_ || y >= height_) return;
+
+ uint32_t color;
+ if (is_bgr_) {
+ color = ((rgb & 0xFF) << 16) | (rgb & 0xFF00) | ((rgb >> 16) & 0xFF);
+ } else {
+ color = rgb;
+ }
+
+ auto* row = reinterpret_cast<uint32_t*>(
+ reinterpret_cast<uint8_t*>(fb_) + y * pitch_);
+ row[x] = color;
+ }
+
+ void scroll() {
+ uint32_t row_bytes = pitch_;
+ uint32_t shift = CHAR_H * row_bytes;
+ uint32_t total = height_ * row_bytes;
+
+ auto* fb = reinterpret_cast<uint8_t*>(fb_);
+
+ for (uint32_t i = 0; i < total - shift; i++) {
+ fb[i] = fb[i + shift];
+ }
+ for (uint32_t i = total - shift; i < total; i++) {
+ fb[i] = 0;
+ }
+ uint32_t start_y = height_ - CHAR_H;
+ for (uint32_t y = start_y; y < height_; y++) {
+ for (uint32_t x = 0; x < width_; x++) {
+ put_pixel(x, y, BG_COLOR);
+ }
+ }
+ }
+
+ void render_char(uint32_t col, uint32_t row, char c) {
+ int idx = static_cast<int>(c) - FONT_FIRST;
+ if (idx < 0 || idx > (FONT_LAST - FONT_FIRST)) idx = 0;
+
+ uint32_t px = col * CHAR_W;
+ uint32_t py = row * CHAR_H;
+
+ for (uint32_t y = 0; y < CHAR_H; y++) {
+ uint8_t bits = g_font[idx][y];
+ for (uint32_t x = 0; x < CHAR_W; x++) {
+ uint32_t color = (bits & (0x80 >> x)) ? FG_COLOR : BG_COLOR;
+ put_pixel(px + x, py + y, color);
+ }
+ }
+ }
+};
+
+// ── Driver init function ──────────────────────────────────────────────────
+
+namespace console {
+
+void init_framebuffer(const FramebufferInfo& fb) {
+ // Create device on the platform bus
+ auto* dev = FramebufferDevice::create(fb);
+ platform::bus().add_device(dev);
+
+ // Create driver and bind to device
+ auto* drv = FramebufferConsole::create();
+ drv->bind(dev);
+ drv->init();
+
+ // Register as active console
+ console::register_driver(drv);
+ console::set_active(drv);
+}
+
+} // namespace console
diff --git a/kernel/drivers/driver_registry.cpp b/kernel/drivers/driver_registry.cpp
new file mode 100644
index 0000000..43b2b5a
--- /dev/null
+++ b/kernel/drivers/driver_registry.cpp
@@ -0,0 +1,55 @@
+// ============================================================================
+// drivers/driver_registry.cpp — Driver/device registry globals
+//
+// Provides storage for per-category driver lists, the active console
+// pointer, and the platform bus singleton. Always compiled.
+// ============================================================================
+
+#include <kernel/console_driver.h>
+#include <kernel/platform_bus.h>
+
+// ── ConsoleDriver default write implementation ────────────────────────────
+
+void ConsoleDriver::write(const char* s, size_t len) {
+ for (size_t i = 0; i < len; ++i) {
+ putchar(s[i]);
+ }
+}
+
+// ── Console registry ──────────────────────────────────────────────────────
+
+static DriverList<ConsoleDriver> g_console_drivers;
+static ConsoleDriver* g_active_console = nullptr;
+
+namespace console {
+
+void register_driver(ConsoleDriver* driver) {
+ g_console_drivers.add(driver);
+}
+
+void set_active(ConsoleDriver* driver) {
+ g_active_console = driver;
+}
+
+ConsoleDriver* active() {
+ return g_active_console;
+}
+
+DriverList<ConsoleDriver>& drivers() {
+ return g_console_drivers;
+}
+
+} // namespace console
+
+// ── Platform bus (lazy singleton) ─────────────────────────────────────────
+
+namespace platform {
+
+Bus& bus() {
+ if (!PlatformBus::instance()) {
+ PlatformBus::create();
+ }
+ return *PlatformBus::instance();
+}
+
+} // namespace platform
diff --git a/kernel/include/kernel/console_driver.h b/kernel/include/kernel/console_driver.h
new file mode 100644
index 0000000..f34c4e0
--- /dev/null
+++ b/kernel/include/kernel/console_driver.h
@@ -0,0 +1,58 @@
+#pragma once
+
+// ============================================================================
+// kernel/console_driver.h — Console driver category + registry
+//
+// ConsoleDriver is the category base for all text output devices
+// (framebuffer, UART, etc.). The console:: namespace provides the global
+// registry and active-console tracking.
+// ============================================================================
+
+#include <stdint.h>
+#include <stddef.h>
+#include <kernel/driver.h>
+#include <kernel/driver_list.h>
+
+struct FramebufferInfo; // forward declaration (defined in boot/boot_info.h)
+
+class ConsoleDriver : public Driver {
+public:
+ // Write a single character (handles newline, cursor, scrolling).
+ virtual void putchar(char c) = 0;
+
+ // Write a buffer of characters. Default: loops over putchar().
+ virtual void write(const char* s, size_t len);
+
+ // Clear the screen / output buffer.
+ virtual void clear() = 0;
+};
+
+namespace console {
+
+// Register a console driver in the global list.
+void register_driver(ConsoleDriver* driver);
+
+// Set the active console (the one kcon:: delegates to).
+void set_active(ConsoleDriver* driver);
+
+// Get the current active console (nullptr before any console is initialized).
+ConsoleDriver* active();
+
+// Get the driver list for enumeration.
+DriverList<ConsoleDriver>& drivers();
+
+// ── Per-driver init functions (guarded by config) ─────────────────────────
+
+#ifdef CONFIG_DRIVER_FBCON
+void init_framebuffer(const FramebufferInfo& fb);
+#endif
+
+#ifdef CONFIG_DRIVER_UART_16550
+void init_uart_16550(uint16_t port = 0x3F8);
+#endif
+
+#ifdef CONFIG_DRIVER_PL011_UART
+void init_pl011(uintptr_t base_addr = 0x09000000);
+#endif
+
+} // namespace console
diff --git a/kernel/include/kernel/device.h b/kernel/include/kernel/device.h
new file mode 100644
index 0000000..222ff9e
--- /dev/null
+++ b/kernel/include/kernel/device.h
@@ -0,0 +1,52 @@
+#pragma once
+
+// ============================================================================
+// kernel/device.h — Device and Bus base classes
+//
+// A Device represents a piece of hardware. Devices form a tree via
+// intrusive parent-child pointers (no heap needed). Each Device can be
+// bound to a Driver that knows how to operate it.
+//
+// A Bus is a Device that can have child devices. It serves as a grouping
+// point for device enumeration and driver matching (future: PCI, DT).
+// ============================================================================
+
+#include <stdint.h>
+
+class Driver;
+
+class Device {
+public:
+ virtual ~Device() = default;
+
+ virtual const char* name() const = 0;
+
+ // ── Hierarchy ─────────────────────────────────────────────────────────
+ Device* parent() const { return parent_; }
+ Device* first_child() const { return first_child_; }
+ Device* next_sibling() const { return next_sibling_; }
+
+ void add_child(Device* child) {
+ child->parent_ = this;
+ child->next_sibling_ = first_child_;
+ first_child_ = child;
+ }
+
+ // ── Driver binding ────────────────────────────────────────────────────
+ Driver* driver() const { return driver_; }
+ void set_driver(Driver* drv) { driver_ = drv; }
+
+protected:
+ Device* parent_ = nullptr;
+ Device* first_child_ = nullptr;
+ Device* next_sibling_ = nullptr;
+ Driver* driver_ = nullptr;
+};
+
+// ── Bus — a Device that groups child devices ──────────────────────────────
+// Future: enumerate() discovers children, match() pairs devices with drivers.
+
+class Bus : public Device {
+public:
+ void add_device(Device* dev) { add_child(dev); }
+};
diff --git a/kernel/include/kernel/driver.h b/kernel/include/kernel/driver.h
new file mode 100644
index 0000000..564590b
--- /dev/null
+++ b/kernel/include/kernel/driver.h
@@ -0,0 +1,46 @@
+#pragma once
+
+// ============================================================================
+// kernel/driver.h - Base driver class
+//
+// Abstract root of the driver hierarchy. Every driver inherits from this.
+// Registration uses an intrusive singly-linked list (next_ pointer).
+// A driver can be bound to a Device via bind().
+// ============================================================================
+
+#include <stdint.h>
+#include <kernel/device.h>
+
+template <typename T>
+class DriverList;
+
+class Driver {
+public:
+ virtual ~Driver() = default;
+
+ // Human-readable name, e.g. "framebuffer-console", "uart-16550"
+ virtual const char* name() const = 0;
+
+ // Initialize driver hardware/state. Returns true on success.
+ // Called after bind() — device() is available for reading hw info.
+ virtual bool init() = 0;
+
+ // Shut down driver (release resources, disable hardware).
+ virtual void shutdown() = 0;
+
+ // ── Device binding ────────────────────────────────────────────────────
+ // Links this driver to a device. Sets bidirectional pointers.
+ void bind(Device* dev) {
+ device_ = dev;
+ dev->set_driver(this);
+ }
+
+ Device* device() const { return device_; }
+
+protected:
+ Driver* next_ = nullptr;
+ Device* device_ = nullptr;
+
+ template <typename T>
+ friend class DriverList;
+};
diff --git a/kernel/include/kernel/driver_list.h b/kernel/include/kernel/driver_list.h
new file mode 100644
index 0000000..1ba9ab2
--- /dev/null
+++ b/kernel/include/kernel/driver_list.h
@@ -0,0 +1,34 @@
+#pragma once
+
+// ============================================================================
+// kernel/driver_list.h — Typed intrusive singly-linked list for drivers
+//
+// Each driver category (console, block, etc.) maintains its own DriverList.
+// Insertion is O(1) prepend. No heap allocation — the list node is the
+// driver's own next_ pointer inherited from Driver.
+// ============================================================================
+
+#include <kernel/driver.h>
+
+template <typename T>
+class DriverList {
+public:
+ // Prepend a driver to the list.
+ void add(T* driver) {
+ driver->next_ = head_;
+ head_ = driver;
+ }
+
+ // Iterate all drivers. fn(T*) returns true to stop early.
+ template <typename Fn>
+ void for_each(Fn fn) const {
+ for (auto* d = head_; d != nullptr; d = static_cast<T*>(d->next_)) {
+ if (fn(d)) return;
+ }
+ }
+
+ T* head() const { return head_; }
+
+private:
+ T* head_ = nullptr;
+};
diff --git a/kernel/include/kernel/early_device.h b/kernel/include/kernel/early_device.h
new file mode 100644
index 0000000..5ddec7b
--- /dev/null
+++ b/kernel/include/kernel/early_device.h
@@ -0,0 +1,35 @@
+#pragma once
+
+// ============================================================================
+// kernel/early_device.h — CRTP mixin for pre-heap device allocation
+//
+// Same pattern as EarlyDriver<T>, but for Device subclasses. Provides
+// static storage for exactly one instance of the derived device type.
+//
+// Usage:
+// class MyDevice : public Device, public EarlyDevice<MyDevice> { };
+// auto* dev = MyDevice::create(args...);
+// MyDevice::instance();
+// ============================================================================
+
+#include <stdint.h>
+#include <stddef.h>
+#include <kernel/placement_new.h>
+
+template <typename Derived>
+class EarlyDevice {
+public:
+ template <typename... Args>
+ static Derived* create(Args&&... args) {
+ alignas(Derived) static char storage[sizeof(Derived)];
+ instance_ = new (storage) Derived(static_cast<Args&&>(args)...);
+ return instance_;
+ }
+
+ static Derived* instance() {
+ return instance_;
+ }
+
+private:
+ static inline Derived* instance_ = nullptr;
+};
diff --git a/kernel/include/kernel/early_driver.h b/kernel/include/kernel/early_driver.h
new file mode 100644
index 0000000..ff1b3b9
--- /dev/null
+++ b/kernel/include/kernel/early_driver.h
@@ -0,0 +1,40 @@
+#pragma once
+
+// ============================================================================
+// kernel/early_driver.h — CRTP mixin for pre-heap driver allocation
+//
+// Early drivers (framebuffer console, serial UART) must work before kmalloc
+// exists. This template provides static storage for exactly one instance of
+// the derived driver, constructed via placement new.
+//
+// Usage:
+// class MyDriver : public SomeCategory, public EarlyDriver<MyDriver> { };
+// auto* drv = MyDriver::create(args...); // placement new into BSS
+// MyDriver::instance(); // get the singleton
+// ============================================================================
+
+#include <stdint.h>
+#include <stddef.h>
+#include <kernel/placement_new.h>
+
+template <typename Derived>
+class EarlyDriver {
+public:
+ // Construct the driver in static storage. Call exactly once.
+ // sizeof(Derived) is evaluated here, not at class definition time,
+ // so Derived is guaranteed to be complete.
+ template <typename... Args>
+ static Derived* create(Args&&... args) {
+ alignas(Derived) static char storage[sizeof(Derived)];
+ instance_ = new (storage) Derived(static_cast<Args&&>(args)...);
+ return instance_;
+ }
+
+ // Returns the instance, or nullptr if create() hasn't been called.
+ static Derived* instance() {
+ return instance_;
+ }
+
+private:
+ static inline Derived* instance_ = nullptr;
+};
diff --git a/kernel/include/kernel/placement_new.h b/kernel/include/kernel/placement_new.h
new file mode 100644
index 0000000..6f7f1f3
--- /dev/null
+++ b/kernel/include/kernel/placement_new.h
@@ -0,0 +1,4 @@
+#pragma once
+
+// Placement new for freestanding C++ (no <new> header available).
+inline void* operator new(size_t, void* ptr) noexcept { return ptr; }
diff --git a/kernel/include/kernel/platform_bus.h b/kernel/include/kernel/platform_bus.h
new file mode 100644
index 0000000..8561ca6
--- /dev/null
+++ b/kernel/include/kernel/platform_bus.h
@@ -0,0 +1,22 @@
+#pragma once
+
+// ============================================================================
+// kernel/platform_bus.h — Platform bus for fixed-address devices
+//
+// The PlatformBus is the root of the device hierarchy. Non-discoverable
+// devices (framebuffer, UART at fixed addresses) are registered here.
+// Future buses (PCI, device-tree) will be added as siblings or children.
+// ============================================================================
+
+#include <kernel/device.h>
+#include <kernel/early_device.h>
+
+class PlatformBus : public Bus, public EarlyDevice<PlatformBus> {
+public:
+ const char* name() const override { return "platform"; }
+};
+
+// Lazy-initialized singleton accessor for the platform bus.
+namespace platform {
+ Bus& bus();
+} // namespace platform
diff --git a/kernel/lib/framebuffer.cpp b/kernel/lib/framebuffer.cpp
index 476abaa..4cd163d 100644
--- a/kernel/lib/framebuffer.cpp
+++ b/kernel/lib/framebuffer.cpp
@@ -1,297 +1,37 @@
// ============================================================================
-// lib/framebuffer.cpp - Early kernel framebuffer console
+// lib/framebuffer.cpp — kcon:: delegation layer
//
-// Renders text directly to the linear framebuffer using a minimal
-// built-in 8x16 bitmap font (PC VGA style, ASCII 32-126).
+// The kcon:: namespace preserves the original console API. All rendering
+// is now handled by whichever ConsoleDriver is active (typically
+// FramebufferConsole via the driver framework). Formatting functions
+// (put_hex, put_dec, kprintf) are kept here since they are pure formatters
+// that call putchar/puts.
// ============================================================================
#include <kernel/kprint.h>
+#include <kernel/console_driver.h>
#include <stdarg.h>
namespace kcon {
-// ── Character cell dimensions ──────────────────────────────────────────────
-
-static constexpr uint32_t CHAR_W = 8; // Font glyph width in pixels
-static constexpr uint32_t CHAR_H = 16; // Font glyph height in pixels
-static constexpr uint32_t TAB_WIDTH = 4; // Tab stop width in character cells
-
-// ── Colors (0x00RRGGBB) ────────────────────────────────────────────────────
-
-static constexpr uint32_t FG_COLOR = CONFIG_FBCON_FG_COLOR; // Light grey
-static constexpr uint32_t BG_COLOR = CONFIG_FBCON_BG_COLOR; // Dark blue
-
-// ── Console state ──────────────────────────────────────────────────────────
-//
-// All mutable state lives in one struct so callers can reason about it as
-// a unit and we avoid scattered bare globals.
-#pragma message("when i add drivers abstract, move it to another console interface and use framebuffer as driver")
-
-struct Console {
-public:
- uint32_t* fb = nullptr; // Framebuffer base (as 32-bit pixels)
- uint32_t width = 0; // Pixels per row
- uint32_t height = 0; // Rows in pixels
- uint32_t pitch = 0; // Bytes per scanline (>= width * 4)
- uint32_t col = 0; // Cursor column in character cells
- uint32_t row = 0; // Cursor row in character cells
- bool is_bgr = false;
-
- // C++23 multi-dimensional subscript: direct reference to pixel (x, y).
- // pitch is in bytes; each pixel is 4 bytes. No bounds checking — callers
- // must validate coordinates before calling (put_pixel does this).
- [[nodiscard]] uint32_t& operator[](uint32_t x, uint32_t y) noexcept {
- auto* row_ptr = reinterpret_cast<uint32_t*>(
- reinterpret_cast<uintptr_t>(fb) + static_cast<uintptr_t>(y) * pitch);
- return row_ptr[x];
- }
-};
-
-static Console g_con{};
-
-// ── Minimal 8x16 bitmap font (ASCII 32–126) ───────────────────────────────
-// Each character is 16 bytes (one byte per row, 8 pixels wide).
-// We store a blank (space) for anything outside the range.
-
-// clang-format off
-static const uint8_t g_font[][16] = {
- // 32: space
- {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- // 33: !
- {0x00,0x00,0x18,0x3C,0x3C,0x3C,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00},
- // 34: "
- {0x00,0x66,0x66,0x66,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- // 35: #
- {0x00,0x00,0x00,0x6C,0x6C,0xFE,0x6C,0x6C,0xFE,0x6C,0x6C,0x00,0x00,0x00,0x00,0x00},
- // 36: $
- {0x00,0x10,0x10,0x7C,0xD6,0xD0,0x7C,0x16,0xD6,0x7C,0x10,0x10,0x00,0x00,0x00,0x00},
- // 37: %
- {0x00,0x00,0x00,0x00,0xC2,0xC6,0x0C,0x18,0x30,0x60,0xC6,0x86,0x00,0x00,0x00,0x00},
- // 38: &
- {0x00,0x00,0x38,0x6C,0x6C,0x38,0x76,0xDC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00},
- // 39: '
- {0x00,0x30,0x30,0x30,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- // 40: (
- {0x00,0x00,0x0C,0x18,0x30,0x30,0x30,0x30,0x30,0x30,0x18,0x0C,0x00,0x00,0x00,0x00},
- // 41: )
- {0x00,0x00,0x30,0x18,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x18,0x30,0x00,0x00,0x00,0x00},
- // 42: *
- {0x00,0x00,0x00,0x00,0x00,0x66,0x3C,0xFF,0x3C,0x66,0x00,0x00,0x00,0x00,0x00,0x00},
- // 43: +
- {0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00},
- // 44: ,
- {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x30,0x00,0x00,0x00},
- // 45: -
- {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- // 46: .
- {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00},
- // 47: /
- {0x00,0x00,0x00,0x00,0x02,0x06,0x0C,0x18,0x30,0x60,0xC0,0x80,0x00,0x00,0x00,0x00},
- // 48-57: 0-9
- {0x00,0x00,0x7C,0xC6,0xC6,0xCE,0xDE,0xF6,0xE6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x18,0x38,0x78,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x7C,0xC6,0x06,0x0C,0x18,0x30,0x60,0xC0,0xC6,0xFE,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x7C,0xC6,0x06,0x06,0x3C,0x06,0x06,0x06,0xC6,0x7C,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x0C,0x1C,0x3C,0x6C,0xCC,0xFE,0x0C,0x0C,0x0C,0x1E,0x00,0x00,0x00,0x00},
- {0x00,0x00,0xFE,0xC0,0xC0,0xC0,0xFC,0x06,0x06,0x06,0xC6,0x7C,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x38,0x60,0xC0,0xC0,0xFC,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
- {0x00,0x00,0xFE,0xC6,0x06,0x06,0x0C,0x18,0x30,0x30,0x30,0x30,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x7C,0xC6,0xC6,0xC6,0x7C,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x7C,0xC6,0xC6,0xC6,0x7E,0x06,0x06,0x06,0x0C,0x78,0x00,0x00,0x00,0x00},
- // 58: :
- {0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00},
- // 59: ;
- {0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x30,0x00,0x00,0x00,0x00},
- // 60: <
- {0x00,0x00,0x00,0x06,0x0C,0x18,0x30,0x60,0x30,0x18,0x0C,0x06,0x00,0x00,0x00,0x00},
- // 61: =
- {0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- // 62: >
- {0x00,0x00,0x00,0x60,0x30,0x18,0x0C,0x06,0x0C,0x18,0x30,0x60,0x00,0x00,0x00,0x00},
- // 63: ?
- {0x00,0x00,0x7C,0xC6,0xC6,0x0C,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00},
- // 64: @
- {0x00,0x00,0x7C,0xC6,0xC6,0xDE,0xDE,0xDE,0xDC,0xC0,0xC0,0x7C,0x00,0x00,0x00,0x00},
- // 65-90: A-Z
- {0x00,0x00,0x10,0x38,0x6C,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00},
- {0x00,0x00,0xFC,0x66,0x66,0x66,0x7C,0x66,0x66,0x66,0x66,0xFC,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x3C,0x66,0xC2,0xC0,0xC0,0xC0,0xC0,0xC2,0x66,0x3C,0x00,0x00,0x00,0x00},
- {0x00,0x00,0xF8,0x6C,0x66,0x66,0x66,0x66,0x66,0x66,0x6C,0xF8,0x00,0x00,0x00,0x00},
- {0x00,0x00,0xFE,0x66,0x62,0x68,0x78,0x68,0x60,0x62,0x66,0xFE,0x00,0x00,0x00,0x00},
- {0x00,0x00,0xFE,0x66,0x62,0x68,0x78,0x68,0x60,0x60,0x60,0xF0,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x3C,0x66,0xC2,0xC0,0xC0,0xDE,0xC6,0xC6,0x66,0x3A,0x00,0x00,0x00,0x00},
- {0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x3C,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x1E,0x0C,0x0C,0x0C,0x0C,0x0C,0xCC,0xCC,0xCC,0x78,0x00,0x00,0x00,0x00},
- {0x00,0x00,0xE6,0x66,0x66,0x6C,0x78,0x78,0x6C,0x66,0x66,0xE6,0x00,0x00,0x00,0x00},
- {0x00,0x00,0xF0,0x60,0x60,0x60,0x60,0x60,0x60,0x62,0x66,0xFE,0x00,0x00,0x00,0x00},
- {0x00,0x00,0xC6,0xEE,0xFE,0xFE,0xD6,0xC6,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00},
- {0x00,0x00,0xC6,0xE6,0xF6,0xFE,0xDE,0xCE,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
- {0x00,0x00,0xFC,0x66,0x66,0x66,0x7C,0x60,0x60,0x60,0x60,0xF0,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xD6,0xDE,0x7C,0x0C,0x0E,0x00,0x00},
- {0x00,0x00,0xFC,0x66,0x66,0x66,0x7C,0x6C,0x66,0x66,0x66,0xE6,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x7C,0xC6,0xC6,0x60,0x38,0x0C,0x06,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
- {0x00,0x00,0xFF,0xDB,0x99,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00},
- {0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
- {0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x6C,0x38,0x10,0x00,0x00,0x00,0x00},
- {0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xD6,0xD6,0xD6,0xFE,0xEE,0x6C,0x00,0x00,0x00,0x00},
- {0x00,0x00,0xC6,0xC6,0x6C,0x7C,0x38,0x38,0x7C,0x6C,0xC6,0xC6,0x00,0x00,0x00,0x00},
- {0x00,0x00,0xCC,0xCC,0xCC,0xCC,0x78,0x30,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00},
- {0x00,0x00,0xFE,0xC6,0x86,0x0C,0x18,0x30,0x60,0xC2,0xC6,0xFE,0x00,0x00,0x00,0x00},
- // 91: [
- {0x00,0x00,0x3C,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x3C,0x00,0x00,0x00,0x00},
- // 92: backslash
- {0x00,0x00,0x00,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x02,0x00,0x00,0x00,0x00,0x00},
- // 93: ]
- {0x00,0x00,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00,0x00,0x00,0x00},
- // 94: ^
- {0x10,0x38,0x6C,0xC6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- // 95: _
- {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00},
- // 96: `
- {0x00,0x30,0x18,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- // 97-122: a-z
- {0x00,0x00,0x00,0x00,0x00,0x78,0x0C,0x7C,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00},
- {0x00,0x00,0xE0,0x60,0x60,0x78,0x6C,0x66,0x66,0x66,0x66,0x7C,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x00,0x00,0x00,0x7C,0xC6,0xC0,0xC0,0xC0,0xC6,0x7C,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x1C,0x0C,0x0C,0x3C,0x6C,0xCC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x00,0x00,0x00,0x7C,0xC6,0xFE,0xC0,0xC0,0xC6,0x7C,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x38,0x6C,0x64,0x60,0xF0,0x60,0x60,0x60,0x60,0xF0,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x00,0x00,0x00,0x76,0xCC,0xCC,0xCC,0xCC,0xCC,0x7C,0x0C,0xCC,0x78,0x00},
- {0x00,0x00,0xE0,0x60,0x60,0x6C,0x76,0x66,0x66,0x66,0x66,0xE6,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x18,0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x06,0x06,0x00,0x0E,0x06,0x06,0x06,0x06,0x06,0x06,0x66,0x66,0x3C,0x00},
- {0x00,0x00,0xE0,0x60,0x60,0x66,0x6C,0x78,0x78,0x6C,0x66,0xE6,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x00,0x00,0x00,0xEC,0xFE,0xD6,0xD6,0xD6,0xD6,0xC6,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x00,0x00,0x00,0xDC,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x00,0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x00,0x00,0x00,0xDC,0x66,0x66,0x66,0x66,0x66,0x7C,0x60,0x60,0xF0,0x00},
- {0x00,0x00,0x00,0x00,0x00,0x76,0xCC,0xCC,0xCC,0xCC,0xCC,0x7C,0x0C,0x0C,0x1E,0x00},
- {0x00,0x00,0x00,0x00,0x00,0xDC,0x76,0x66,0x60,0x60,0x60,0xF0,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x00,0x00,0x00,0x7C,0xC6,0x60,0x38,0x0C,0xC6,0x7C,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x10,0x30,0x30,0xFC,0x30,0x30,0x30,0x30,0x36,0x1C,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x00,0x00,0x00,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x00,0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0x6C,0x38,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x00,0x00,0x00,0xC6,0xC6,0xD6,0xD6,0xD6,0xFE,0x6C,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x00,0x00,0x00,0xC6,0x6C,0x38,0x38,0x38,0x6C,0xC6,0x00,0x00,0x00,0x00},
- {0x00,0x00,0x00,0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7E,0x06,0x0C,0xF8,0x00},
- {0x00,0x00,0x00,0x00,0x00,0xFE,0xCC,0x18,0x30,0x60,0xC6,0xFE,0x00,0x00,0x00,0x00},
- // 123: {
- {0x00,0x00,0x0E,0x18,0x18,0x18,0x70,0x18,0x18,0x18,0x18,0x0E,0x00,0x00,0x00,0x00},
- // 124: |
- {0x00,0x00,0x18,0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00},
- // 125: }
- {0x00,0x00,0x70,0x18,0x18,0x18,0x0E,0x18,0x18,0x18,0x18,0x70,0x00,0x00,0x00,0x00},
- // 126: ~
- {0x00,0x00,0x76,0xDC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
-};
-// clang-format on
-
-static constexpr int FONT_FIRST = 32;
-static constexpr int FONT_LAST = 126;
-
-// ── Pixel helpers ──────────────────────────────────────────────────────────
-
-static void put_pixel(uint32_t x, uint32_t y, uint32_t rgb) noexcept {
- if (x >= g_con.width || y >= g_con.height) return;
-
- // Swap R and B channels if the framebuffer uses BGR layout.
- const uint32_t color = g_con.is_bgr
- ? ((rgb & 0xFFu) << 16) | (rgb & 0xFF00u) | ((rgb >> 16) & 0xFFu)
- : rgb;
-
- g_con[x, y] = color; // C++23 multi-dimensional subscript
-}
-
-// ── Scroll the screen up by one character row ──────────────────────────────
-
-static void scroll() noexcept {
- const uint32_t shift = CHAR_H * g_con.pitch;
- const uint32_t total = g_con.height * g_con.pitch;
- auto* const fb_bytes = reinterpret_cast<uint8_t*>(g_con.fb);
-
- // Shift everything up by CHAR_H rows (raw byte memmove equivalent).
- for (uint32_t i = 0; i < total - shift; ++i)
- fb_bytes[i] = fb_bytes[i + shift];
-
- // Fill the newly exposed bottom rows with the background color.
- for (uint32_t row = g_con.height - CHAR_H; row < g_con.height; ++row)
- for (uint32_t col = 0; col < g_con.width; ++col)
- put_pixel(col, row, BG_COLOR);
-}
-
-// ── Render a glyph at character-cell position (col, row) ──────────────────
-
-static void render_char(uint32_t col, uint32_t row, char c) noexcept {
- // Treat char as unsigned to avoid negative indices for high-ASCII.
- int idx = static_cast<int>(static_cast<unsigned char>(c)) - FONT_FIRST;
- if (idx < 0 || idx > (FONT_LAST - FONT_FIRST)) idx = 0; // fall back to space
-
- const uint32_t px = col * CHAR_W;
- const uint32_t py = row * CHAR_H;
-
- for (uint32_t y = 0; y < CHAR_H; ++y) {
- const uint8_t bits = g_font[idx][y];
- for (uint32_t x = 0; x < CHAR_W; ++x) {
- const uint32_t color = (bits & (0x80u >> x)) ? FG_COLOR : BG_COLOR;
- put_pixel(px + x, py + y, color);
- }
- }
-}
-
-// ── Public API ─────────────────────────────────────────────────────────────
+// ── Lifecycle ─────────────────────────────────────────────────────────────
void init(const FramebufferInfo& fb) {
- g_con.fb = reinterpret_cast<uint32_t*>(fb.base);
- g_con.width = fb.width;
- g_con.height = fb.height;
- g_con.pitch = fb.pitch;
- g_con.is_bgr = (fb.format == PixelFormat::BGR);
- g_con.col = 0;
- g_con.row = 0;
- clear();
+#ifdef CONFIG_DRIVER_FBCON
+ console::init_framebuffer(fb);
+#endif
+ (void)fb;
}
-void clear() {
- for (uint32_t y = 0; y < g_con.height; ++y)
- for (uint32_t x = 0; x < g_con.width; ++x)
- put_pixel(x, y, BG_COLOR);
- g_con.col = 0;
- g_con.row = 0;
-}
+// ── Character / string output (delegate to active console) ────────────────
void putchar(char c) {
- const uint32_t max_cols = g_con.width / CHAR_W;
- const uint32_t max_rows = g_con.height / CHAR_H;
-
- if (c == '\n') {
- g_con.col = 0;
- ++g_con.row;
- } else if (c == '\r') {
- g_con.col = 0;
- } else if (c == '\t') {
- // Advance to the next TAB_WIDTH-aligned column.
- g_con.col = (g_con.col + TAB_WIDTH) & ~(TAB_WIDTH - 1u);
- } else {
- render_char(g_con.col, g_con.row, c);
- ++g_con.col;
- }
-
- if (g_con.col >= max_cols) {
- g_con.col = 0;
- ++g_con.row;
- }
-
- if (g_con.row >= max_rows) {
- scroll();
- g_con.row = max_rows - 1;
+ if (auto* con = console::active()) {
+ con->putchar(c);
}
}
void puts(const char* s) {
- if (!s) return;
while (*s) putchar(*s++);
}
@@ -300,11 +40,19 @@ void println(const char* s) {
putchar('\n');
}
+void clear() {
+ if (auto* con = console::active()) {
+ con->clear();
+ }
+}
+
+// ── Formatting (unchanged — these only call putchar/puts) ─────────────────
+
void put_hex(uint64_t val) {
puts("0x");
bool leading = true;
for (int i = 60; i >= 0; i -= 4) {
- const uint8_t nibble = static_cast<uint8_t>((val >> i) & 0xFu);
+ uint8_t nibble = (val >> i) & 0xF;
if (nibble == 0 && leading && i > 0) continue;
leading = false;
putchar(nibble < 10 ? '0' + nibble : 'A' + nibble - 10);
@@ -315,12 +63,12 @@ void put_hex(uint64_t val) {
void put_dec(uint64_t val) {
if (val == 0) { putchar('0'); return; }
char buf[20];
- int len = 0;
+ int i = 0;
while (val > 0) {
- buf[len++] = static_cast<char>('0' + val % 10);
+ buf[i++] = '0' + (val % 10);
val /= 10;
}
- for (int j = len - 1; j >= 0; --j) putchar(buf[j]);
+ for (int j = i - 1; j >= 0; j--) putchar(buf[j]);
}
void kprintf(const char* fmt, ...) {
@@ -332,7 +80,7 @@ void kprintf(const char* fmt, ...) {
putchar(*fmt++);
continue;
}
- ++fmt; // skip '%'
+ fmt++; // skip '%'
switch (*fmt) {
case 's': {
@@ -347,12 +95,14 @@ void kprintf(const char* fmt, ...) {
break;
}
case 'u': {
- put_dec(va_arg(args, uint64_t));
+ uint64_t v = va_arg(args, uint64_t);
+ put_dec(v);
break;
}
case 'x':
case 'p': {
- put_hex(va_arg(args, uint64_t));
+ uint64_t v = va_arg(args, uint64_t);
+ put_hex(v);
break;
}
case '%':
@@ -365,7 +115,7 @@ void kprintf(const char* fmt, ...) {
putchar(*fmt);
break;
}
- ++fmt;
+ fmt++;
}
done:
va_end(args);