From ec34094b6845b221153ad1d59a88ada708866b01 Mon Sep 17 00:00:00 2001 From: Max Van den Eynde Date: Sun, 31 May 2026 19:09:25 +0200 Subject: [PATCH 1/5] Added basic framebuffer functions --- .gitignore | 3 +- README.md | 2 +- include/core/regs.h | 51 +++++++++++++++++++++ include/graphics/framebuffer.h | 29 ++++++++++++ include/io/io.h | 1 - include/types.h | 21 +++++++++ kernel/core/regs.cpp | 28 ++++++++++++ kernel/graphics/framebuffer.cpp | 80 +++++++++++++++++++++++++++++++++ kernel/main.cpp | 23 +++------- scripts/debug.sh | 18 ++++++++ 10 files changed, 236 insertions(+), 20 deletions(-) create mode 100644 include/core/regs.h create mode 100644 include/graphics/framebuffer.h create mode 100644 kernel/core/regs.cpp create mode 100644 kernel/graphics/framebuffer.cpp create mode 100755 scripts/debug.sh diff --git a/.gitignore b/.gitignore index 8b1aab2..0a52be1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .idea -cmake-build-debug \ No newline at end of file +cmake-build-debug +.qemu.pid \ No newline at end of file diff --git a/README.md b/README.md index 2fda64a..913ddfa 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ practices while remaining approachable to contributors and learners alike. ### Foundation -- [ ] Serial output +- [x] Serial output - [ ] Framebuffer abstraction + character output - [ ] Panic functions + Assertion system - [ ] Logging system (both visual + serial) diff --git a/include/core/regs.h b/include/core/regs.h new file mode 100644 index 0000000..dbf90a5 --- /dev/null +++ b/include/core/regs.h @@ -0,0 +1,51 @@ +/* +* regs.h +* As part of the Avery project +* Created by Max Van den Eynde in 2026 +* -------------------------------------- +* Description: Register functions +* Copyright (c) 2026 Max Van den Eynde +*/ + +#ifndef AVERY_REGS_H +#define AVERY_REGS_H + +#include + +namespace regs { + inline u64 read_cr0() { + u64 val; + asm volatile( + "mov %%cr0, %0" + : "=r"(val) + ); + + return val; + } + + inline void write_cr0(u64 val) { + asm volatile( + "mov %0, %%cr0" : : "r"(val) : "memory" + ); + } + + inline u64 read_cr4() { + u64 val; + asm volatile( + "mov %%cr4, %0" + : "=r"(val)); + + return val; + } + + inline void write_cr4(u64 val) { + asm volatile( + "mov %0, %%cr4" : : "r"(val) : "memory" + ); + } + + void enableSSE(); +} + + +#endif //AVERY_REGS_H diff --git a/include/graphics/framebuffer.h b/include/graphics/framebuffer.h new file mode 100644 index 0000000..eae67ba --- /dev/null +++ b/include/graphics/framebuffer.h @@ -0,0 +1,29 @@ +/* +* framebuffer.h +* As part of the Avery project +* Created by Max Van den Eynde in 2026 +* -------------------------------------- +* Description: Framebuffer features +* Copyright (c) 2026 Max Van den Eynde +*/ + +#ifndef AVERY_FRAMEBUFFER_H +#define AVERY_FRAMEBUFFER_H +#include "../types.h" + +struct limine_framebuffer_request; +struct limine_framebuffer; + +class Framebuffer { +public: + static Framebuffer createFromLimineRequest(volatile limine_framebuffer_request& request); + + void setColor(Tuple position, Color color) const; + void paintRectangle(Tuple start, Tuple end, Color color) const; + +private: + volatile u32* fb_ptr = nullptr; + limine_framebuffer* fb = nullptr; +}; + +#endif //AVERY_FRAMEBUFFER_H diff --git a/include/io/io.h b/include/io/io.h index a9dbbd7..ab0fecc 100644 --- a/include/io/io.h +++ b/include/io/io.h @@ -32,5 +32,4 @@ namespace io { return val; } } - #endif //AVERY_IO_H diff --git a/include/types.h b/include/types.h index ff4585c..7b0d450 100644 --- a/include/types.h +++ b/include/types.h @@ -23,5 +23,26 @@ using i64 = long long; using string = const char*; +template +struct Tuple { + T a; + T b; + + Tuple(T a, T b) : a(a), b(b) { + } + + T first() { return a; } + T second() { return b; } +}; + +template +Tuple(T, T) -> Tuple; + +struct Color { + int r; // from 0-255 + int g; + int b; + int a; +}; #endif //AVERY_TYPES_H diff --git a/kernel/core/regs.cpp b/kernel/core/regs.cpp new file mode 100644 index 0000000..d8a5be1 --- /dev/null +++ b/kernel/core/regs.cpp @@ -0,0 +1,28 @@ +/* +* regs.cpp +* As part of the Avery project +* Created by Max Van den Eynde in 2026 +* -------------------------------------- +* Description: +* Copyright (c) 2026 Max Van den Eynde +*/ + +#include + +void regs::enableSSE() { + u64 cr0 = read_cr0(); + + cr0 &= ~(1ull << 2); + cr0 |= (1ull << 1); + + write_cr0(cr0); + + u64 cr4 = read_cr4(); + + cr4 |= (1ull << 9); + cr4 |= (1ull << 10); + + write_cr4(cr4); + + asm volatile("fninit"); +} diff --git a/kernel/graphics/framebuffer.cpp b/kernel/graphics/framebuffer.cpp new file mode 100644 index 0000000..f33fa70 --- /dev/null +++ b/kernel/graphics/framebuffer.cpp @@ -0,0 +1,80 @@ +/* +* framebuffer.cpp +* As part of the Avery project +* Created by Max Van den Eynde in 2026 +* -------------------------------------- +* Description: +* Copyright (c) 2026 Max Van den Eynde +*/ + +#include +#include + +Framebuffer Framebuffer::createFromLimineRequest( + volatile limine_framebuffer_request& request +) { + Framebuffer framebuffer; + + if (request.response == nullptr || request.response->framebuffer_count < 1) { + while (true) { + asm volatile("hlt"); + } + } + + framebuffer.fb = request.response->framebuffers[0]; + framebuffer.fb_ptr = static_cast(framebuffer.fb->address); + + return framebuffer; +} + +void Framebuffer::setColor(Tuple position, Color color) const { + u64 x = position.first(); + u64 y = position.second(); + + if (x >= fb->width || y >= fb->height) { + return; + } + + if (fb->bpp != 32) { + return; + } + + usize pixelsPerRow = fb->pitch / 4; + usize index = y * pixelsPerRow + x; + + u32 packed = + ((static_cast(color.r) >> (8 - fb->red_mask_size)) << fb->red_mask_shift) | + ((static_cast(color.g) >> (8 - fb->green_mask_size)) << fb->green_mask_shift) | + ((static_cast(color.b) >> (8 - fb->blue_mask_size)) << fb->blue_mask_shift); + + fb_ptr[index] = packed; +} + +void Framebuffer::paintRectangle(Tuple start, Tuple end, Color color) const { + u64 startX = start.first(); + u64 startY = start.second(); + u64 endX = end.first(); + u64 endY = end.second(); + + if (endX <= startX || endY <= startY) { + return; + } + + if (startX >= fb->width || startY >= fb->height) { + return; + } + + if (endX > fb->width) { + endX = fb->width; + } + + if (endY > fb->height) { + endY = fb->height; + } + + for (u64 y = startY; y < endY; y++) { + for (u64 x = startX; x < endX; x++) { + setColor(Tuple{x, y}, color); + } + } +} diff --git a/kernel/main.cpp b/kernel/main.cpp index d09e5ce..bab5f83 100644 --- a/kernel/main.cpp +++ b/kernel/main.cpp @@ -1,5 +1,7 @@ #include +#include "core/regs.h" +#include "graphics/framebuffer.h" #include "io/serial.h" __attribute__((used, section(".limine_requests"))) @@ -19,6 +21,8 @@ __attribute__((used, section(".limine_requests_end"))) static volatile uint64_t limine_requests_end_marker[] = LIMINE_REQUESTS_END_MARKER; extern "C" [[noreturn]] void _start() { + regs::enableSSE(); + io::serialWrite("Hello, World!"); if (LIMINE_BASE_REVISION_SUPPORTED(limine_base_revision) == false) { @@ -27,23 +31,8 @@ extern "C" [[noreturn]] void _start() { } } - if (framebuffer_request.response == nullptr - || framebuffer_request.response->framebuffer_count < 1) { - while (true) { - asm("hlt"); - } - } - - struct limine_framebuffer* framebuffer = framebuffer_request.response->framebuffers[0]; - - volatile uint32_t* fb_ptr = static_cast(framebuffer->address); - for (unsigned int y = 0; y < framebuffer->height; y++) { - for (unsigned int x = 0; x < framebuffer->width; x++) { - uint32_t nX = x * 255 / framebuffer->width; - uint32_t nY = y * 255 / framebuffer->height; - fb_ptr[y * (framebuffer->pitch / 4) + x] = (nY << 8) | nX; - } - } + Framebuffer framebuffer = Framebuffer::createFromLimineRequest(framebuffer_request); + framebuffer.paintRectangle({12, 12}, {120, 120}, Color(255, 255, 255, 255)); while (true) { asm("hlt"); diff --git a/scripts/debug.sh b/scripts/debug.sh new file mode 100755 index 0000000..06042c9 --- /dev/null +++ b/scripts/debug.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +set -e + +QEMU="$HOME/opt/qemu-custom/bin/qemu-system-x86_64" + +"$QEMU" \ + -cdrom cmake-build-debug/avery.iso \ + -boot d \ + -m 256M \ + -debugcon stdio \ + -display cocoa \ + -no-reboot \ + -no-shutdown \ + -S \ + -s & + +sleep 1 \ No newline at end of file From 6d5a52ad7b9f71cda058c1cc269ac68499b4e17e Mon Sep 17 00:00:00 2001 From: Max Van den Eynde Date: Sun, 31 May 2026 19:36:58 +0200 Subject: [PATCH 2/5] Added character support --- include/graphics/framebuffer.h | 2 + include/graphics/graphicsTypes.h | 92 +++++++++++++++++++++++++++ include/graphics/vgaFont.h | 105 +++++++++++++++++++++++++++++++ include/types.h | 6 -- kernel/graphics/framebuffer.cpp | 48 ++++++++++++++ kernel/main.cpp | 2 +- scripts/bdf_to_header.py | 84 +++++++++++++++++++++++++ 7 files changed, 332 insertions(+), 7 deletions(-) create mode 100644 include/graphics/graphicsTypes.h create mode 100644 include/graphics/vgaFont.h create mode 100644 scripts/bdf_to_header.py diff --git a/include/graphics/framebuffer.h b/include/graphics/framebuffer.h index eae67ba..1c0864c 100644 --- a/include/graphics/framebuffer.h +++ b/include/graphics/framebuffer.h @@ -9,6 +9,7 @@ #ifndef AVERY_FRAMEBUFFER_H #define AVERY_FRAMEBUFFER_H +#include "graphicsTypes.h" #include "../types.h" struct limine_framebuffer_request; @@ -20,6 +21,7 @@ class Framebuffer { void setColor(Tuple position, Color color) const; void paintRectangle(Tuple start, Tuple end, Color color) const; + void drawCharacter(Tuple pos, Color fg, Color bg, char c, float scaleBy = 1.0) const; private: volatile u32* fb_ptr = nullptr; diff --git a/include/graphics/graphicsTypes.h b/include/graphics/graphicsTypes.h new file mode 100644 index 0000000..56063fd --- /dev/null +++ b/include/graphics/graphicsTypes.h @@ -0,0 +1,92 @@ +/* +* types.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_GRAPHICSTYPES_H +#define AVERY_GRAPHICSTYPES_H + +struct Color { + int r; // from 0-255 + int g; + int b; + int a; + + constexpr Color() : r(0), g(0), b(0), a(255) { + } + + constexpr Color(int r_, int g_, int b_, int a_ = 255) : r(r_), g(g_), b(b_), a(a_) { + } + + static const Color red; + static const Color green; + static const Color blue; + static const Color yellow; + static const Color magenta; + static const Color cyan; + static const Color white; + static const Color black; + static const Color orange; + static const Color purple; + static const Color brown; + static const Color gray; + static const Color lightGray; + static const Color darkGray; + static const Color pink; + static const Color lime; + static const Color navy; + static const Color teal; + static const Color olive; + static const Color maroon; + static const Color silver; + static const Color gold; + static const Color skyBlue; + static const Color lavender; + static const Color coral; + static const Color beige; + static const Color mint; + static const Color ochre; + static const Color indigo; + static const Color sienna; + static const Color turquoise; + static const Color khaki; +}; + +inline const Color Color::red = Color(255, 0, 0, 255); +inline const Color Color::green = Color(0, 255, 0, 255); +inline const Color Color::blue = Color(0, 0, 255, 255); +inline const Color Color::yellow = Color(255, 255, 0, 255); +inline const Color Color::magenta = Color(255, 0, 255, 255); +inline const Color Color::cyan = Color(0, 255, 255, 255); +inline const Color Color::white = Color(255, 255, 255, 255); +inline const Color Color::black = Color(0, 0, 0, 255); +inline const Color Color::orange = Color(255, 165, 0, 255); +inline const Color Color::purple = Color(128, 0, 128, 255); +inline const Color Color::brown = Color(165, 42, 42, 255); +inline const Color Color::gray = Color(128, 128, 128, 255); +inline const Color Color::lightGray = Color(211, 211, 211, 255); +inline const Color Color::darkGray = Color(64, 64, 64, 255); +inline const Color Color::pink = Color(255, 192, 203, 255); +inline const Color Color::lime = Color(191, 255, 0, 255); +inline const Color Color::navy = Color(0, 0, 128, 255); +inline const Color Color::teal = Color(0, 128, 128, 255); +inline const Color Color::olive = Color(128, 128, 0, 255); +inline const Color Color::maroon = Color(128, 0, 0, 255); +inline const Color Color::silver = Color(192, 192, 192, 255); +inline const Color Color::gold = Color(255, 215, 0, 255); +inline const Color Color::skyBlue = Color(135, 206, 235, 255); +inline const Color Color::lavender = Color(230, 230, 250, 255); +inline const Color Color::coral = Color(255, 127, 80, 255); +inline const Color Color::beige = Color(245, 245, 220, 255); +inline const Color Color::mint = Color(189, 252, 201, 255); +inline const Color Color::ochre = Color(204, 119, 34, 255); +inline const Color Color::indigo = Color(75, 0, 130, 255); +inline const Color Color::sienna = Color(160, 82, 45, 255); +inline const Color Color::turquoise = Color(64, 224, 208, 255); +inline const Color Color::khaki = Color(240, 230, 140, 255); + +#endif //AVERY_GRAPHICSTYPES_H diff --git a/include/graphics/vgaFont.h b/include/graphics/vgaFont.h new file mode 100644 index 0000000..9297e6b --- /dev/null +++ b/include/graphics/vgaFont.h @@ -0,0 +1,105 @@ +#pragma once +#include + +#define FONT_WIDTH 8 +#define FONT_HEIGHT 16 +#define FONT_FIRST 32 +#define FONT_LAST 126 + +static const uint8_t font8x16[95][16] = { + /* 32 ' ' */ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 33 '!' */ { 0x60, 0xF0, 0xF0, 0xF0, 0x60, 0x60, 0x60, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 34 '"' */ { 0xCC, 0xCC, 0xCC, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 35 '#' */ { 0x6C, 0x6C, 0xFE, 0x6C, 0x6C, 0x6C, 0xFE, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 36 '$' */ { 0x18, 0x18, 0x7C, 0xC6, 0xC2, 0xC0, 0x7C, 0x06, 0x06, 0x86, 0xC6, 0x7C, 0x18, 0x18, 0x00, 0x00 }, + /* 37 '%' */ { 0xC2, 0xC6, 0x0C, 0x18, 0x30, 0x60, 0xC6, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 38 '&' */ { 0x38, 0x6C, 0x6C, 0x38, 0x76, 0xDC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 39 '\'' */ { 0x60, 0x60, 0x60, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 40 '(' */ { 0x30, 0x60, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 41 ')' */ { 0xC0, 0x60, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x60, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 42 '*' */ { 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 43 '+' */ { 0x30, 0x30, 0xFC, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 44 ',' */ { 0x60, 0x60, 0x60, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 45 '-' */ { 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 46 '.' */ { 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 47 '/' */ { 0x02, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 48 '0' */ { 0x3C, 0x66, 0xC3, 0xC3, 0xDB, 0xDB, 0xC3, 0xC3, 0x66, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 49 '1' */ { 0x30, 0x70, 0xF0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 50 '2' */ { 0x7C, 0xC6, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0xC6, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 51 '3' */ { 0x7C, 0xC6, 0x06, 0x06, 0x3C, 0x06, 0x06, 0x06, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 52 '4' */ { 0x0C, 0x1C, 0x3C, 0x6C, 0xCC, 0xFE, 0x0C, 0x0C, 0x0C, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 53 '5' */ { 0xFE, 0xC0, 0xC0, 0xC0, 0xFC, 0x06, 0x06, 0x06, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 54 '6' */ { 0x38, 0x60, 0xC0, 0xC0, 0xFC, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 55 '7' */ { 0xFE, 0xC6, 0x06, 0x06, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 56 '8' */ { 0x7C, 0xC6, 0xC6, 0xC6, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 57 '9' */ { 0x7C, 0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x06, 0x06, 0x0C, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 58 ':' */ { 0xC0, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 59 ';' */ { 0x60, 0x60, 0x00, 0x00, 0x00, 0x60, 0x60, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 60 '<' */ { 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 61 '=' */ { 0xFC, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 62 '>' */ { 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 63 '?' */ { 0x7C, 0xC6, 0xC6, 0x0C, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 64 '@' */ { 0x7C, 0xC6, 0xC6, 0xDE, 0xDE, 0xDE, 0xDC, 0xC0, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 65 'A' */ { 0x10, 0x38, 0x6C, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 66 'B' */ { 0xFC, 0x66, 0x66, 0x66, 0x7C, 0x66, 0x66, 0x66, 0x66, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 67 'C' */ { 0x3C, 0x66, 0xC2, 0xC0, 0xC0, 0xC0, 0xC0, 0xC2, 0x66, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 68 'D' */ { 0xF8, 0x6C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6C, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 69 'E' */ { 0xFE, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 70 'F' */ { 0xFE, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 71 'G' */ { 0x3C, 0x66, 0xC2, 0xC0, 0xC0, 0xDE, 0xC6, 0xC6, 0x66, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 72 'H' */ { 0xC6, 0xC6, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 73 'I' */ { 0xF0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 74 'J' */ { 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0xCC, 0xCC, 0xCC, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 75 'K' */ { 0xE6, 0x66, 0x66, 0x6C, 0x78, 0x78, 0x6C, 0x66, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 76 'L' */ { 0xF0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 77 'M' */ { 0xC3, 0xE7, 0xFF, 0xFF, 0xDB, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 78 'N' */ { 0xC6, 0xE6, 0xF6, 0xFE, 0xDE, 0xCE, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 79 'O' */ { 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 80 'P' */ { 0xFC, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 81 'Q' */ { 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xD6, 0xDE, 0x7C, 0x0C, 0x0E, 0x00, 0x00, 0x00, 0x00 }, + /* 82 'R' */ { 0xFC, 0x66, 0x66, 0x66, 0x7C, 0x6C, 0x66, 0x66, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 83 'S' */ { 0x7C, 0xC6, 0xC6, 0x60, 0x38, 0x0C, 0x06, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 84 'T' */ { 0xFF, 0xDB, 0x99, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 85 'U' */ { 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 86 'V' */ { 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 87 'W' */ { 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xDB, 0xDB, 0xFF, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 88 'X' */ { 0xC3, 0xC3, 0x66, 0x3C, 0x18, 0x18, 0x3C, 0x66, 0xC3, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 89 'Y' */ { 0xC3, 0xC3, 0xC3, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 90 'Z' */ { 0xFF, 0xC3, 0x86, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0xC3, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 91 '[' */ { 0xF0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 92 '\\' */ { 0x80, 0xC0, 0xE0, 0x70, 0x38, 0x1C, 0x0E, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 93 ']' */ { 0xF0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 94 '^' */ { 0x10, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 95 '_' */ { 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 96 '`' */ { 0xC0, 0xC0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 97 'a' */ { 0x78, 0x0C, 0x7C, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 98 'b' */ { 0xE0, 0x60, 0x60, 0x78, 0x6C, 0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 99 'c' */ { 0x7C, 0xC6, 0xC0, 0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 100 'd' */ { 0x1C, 0x0C, 0x0C, 0x3C, 0x6C, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 101 'e' */ { 0x7C, 0xC6, 0xFE, 0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 102 'f' */ { 0x38, 0x6C, 0x64, 0x60, 0xF0, 0x60, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 103 'g' */ { 0x76, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0xCC, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 104 'h' */ { 0xE0, 0x60, 0x60, 0x6C, 0x76, 0x66, 0x66, 0x66, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 105 'i' */ { 0x60, 0x60, 0x00, 0xE0, 0x60, 0x60, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 106 'j' */ { 0x0C, 0x0C, 0x00, 0x1C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0xCC, 0xCC, 0x78, 0x00, 0x00, 0x00 }, + /* 107 'k' */ { 0xE0, 0x60, 0x60, 0x66, 0x6C, 0x78, 0x78, 0x6C, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 108 'l' */ { 0xE0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 109 'm' */ { 0xE6, 0xFF, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 110 'n' */ { 0xDC, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 111 'o' */ { 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 112 'p' */ { 0xDC, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 113 'q' */ { 0x76, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0x0C, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 114 'r' */ { 0xDC, 0x76, 0x66, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 115 's' */ { 0x7C, 0xC6, 0x60, 0x38, 0x0C, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 116 't' */ { 0x10, 0x30, 0x30, 0xFC, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 117 'u' */ { 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 118 'v' */ { 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 119 'w' */ { 0xC3, 0xC3, 0xC3, 0xDB, 0xDB, 0xFF, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 120 'x' */ { 0xC3, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 121 'y' */ { 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x0C, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 122 'z' */ { 0xFE, 0xCC, 0x18, 0x30, 0x60, 0xC6, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 123 '{' */ { 0x1C, 0x30, 0x30, 0x30, 0xE0, 0x30, 0x30, 0x30, 0x30, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 124 '|' */ { 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 125 '}' */ { 0xE0, 0x30, 0x30, 0x30, 0x1C, 0x30, 0x30, 0x30, 0x30, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 126 '~' */ { 0x76, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, +}; diff --git a/include/types.h b/include/types.h index 7b0d450..c544c9d 100644 --- a/include/types.h +++ b/include/types.h @@ -38,11 +38,5 @@ struct Tuple { template Tuple(T, T) -> Tuple; -struct Color { - int r; // from 0-255 - int g; - int b; - int a; -}; #endif //AVERY_TYPES_H diff --git a/kernel/graphics/framebuffer.cpp b/kernel/graphics/framebuffer.cpp index f33fa70..3034e40 100644 --- a/kernel/graphics/framebuffer.cpp +++ b/kernel/graphics/framebuffer.cpp @@ -8,8 +8,12 @@ */ #include +#include #include +#include "graphics/graphicsTypes.h" +#include "types.h" + Framebuffer Framebuffer::createFromLimineRequest( volatile limine_framebuffer_request& request ) { @@ -78,3 +82,47 @@ void Framebuffer::paintRectangle(Tuple start, Tuple end, Color color) } } } + +void Framebuffer::drawCharacter( + Tuple pos, + Color fg, + Color bg, + char c, + float scaleBy +) const { + if (scaleBy < 1.0f) { + scaleBy = 1.0f; + } + + const u64 scale = static_cast(scaleBy); + + if (c < FONT_FIRST || c > FONT_LAST) { + c = '?'; + } + + const u8* glyph = font8x16[c - FONT_FIRST]; + + for (u64 row = 0; row < FONT_HEIGHT; row++) { + u8 bits = glyph[row]; + + for (u64 col = 0; col < FONT_WIDTH; col++) { + Color color = bg; + + if (bits & (0x80 >> col)) { + color = fg; + } + + const u64 pixelX = pos.first() + col * scale; + const u64 pixelY = pos.second() + row * scale; + + for (u64 sy = 0; sy < scale; sy++) { + for (u64 sx = 0; sx < scale; sx++) { + setColor({ + pixelX + sx, + pixelY + sy + }, color); + } + } + } + } +} diff --git a/kernel/main.cpp b/kernel/main.cpp index bab5f83..9ac4ce1 100644 --- a/kernel/main.cpp +++ b/kernel/main.cpp @@ -32,7 +32,7 @@ extern "C" [[noreturn]] void _start() { } Framebuffer framebuffer = Framebuffer::createFromLimineRequest(framebuffer_request); - framebuffer.paintRectangle({12, 12}, {120, 120}, Color(255, 255, 255, 255)); + framebuffer.drawCharacter({10, 10}, Color::red, Color::black, 'A', 10.0); while (true) { asm("hlt"); diff --git a/scripts/bdf_to_header.py b/scripts/bdf_to_header.py new file mode 100644 index 0000000..7070d06 --- /dev/null +++ b/scripts/bdf_to_header.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 +import sys + +WIDTH = 8 +HEIGHT = 16 +FIRST = 32 +LAST = 126 + +def parse_bdf(path): + glyphs = {} + current_encoding = None + bitmap = [] + in_bitmap = False + + with open(path, "r", encoding="utf-8", errors="replace") as f: + for line in f: + line = line.strip() + + if line.startswith("ENCODING "): + current_encoding = int(line.split()[1]) + + elif line == "BITMAP": + bitmap = [] + in_bitmap = True + + elif line == "ENDCHAR": + if current_encoding is not None and FIRST <= current_encoding <= LAST: + rows = [int(x, 16) for x in bitmap] + + # Force exactly HEIGHT rows + rows = rows[:HEIGHT] + while len(rows) < HEIGHT: + rows.append(0) + + glyphs[current_encoding] = rows + + current_encoding = None + bitmap = [] + in_bitmap = False + + elif in_bitmap: + bitmap.append(line) + + return glyphs + +def emit_header(glyphs, out_path): + with open(out_path, "w") as out: + out.write("#pragma once\n") + out.write("#include \n\n") + out.write("#define FONT_WIDTH 8\n") + out.write("#define FONT_HEIGHT 16\n") + out.write("#define FONT_FIRST 32\n") + out.write("#define FONT_LAST 126\n\n") + + out.write("static const uint8_t font8x16[95][16] = {\n") + + for code in range(FIRST, LAST + 1): + rows = glyphs.get(code, [0] * HEIGHT) + char = chr(code) + + safe_char = char + if char == "\\": + safe_char = "\\\\" + elif char == "'": + safe_char = "\\'" + elif char == "\n": + safe_char = "\\n" + + out.write(f" /* {code} '{safe_char}' */ {{ ") + out.write(", ".join(f"0x{row:02X}" for row in rows)) + out.write(" },\n") + + out.write("};\n") + +def main(): + if len(sys.argv) != 3: + print("usage: bdf_to_header.py input.bdf output.h") + sys.exit(1) + + glyphs = parse_bdf(sys.argv[1]) + emit_header(glyphs, sys.argv[2]) + +if __name__ == "__main__": + main() From 54e0559c94f4ae15efb21ef14ada1a614214f4bd Mon Sep 17 00:00:00 2001 From: Max Van den Eynde Date: Mon, 1 Jun 2026 07:26:38 +0200 Subject: [PATCH 3/5] First iteration of the console --- include/graphics/framebuffer.h | 46 +++++ kernel/graphics/framebuffer.cpp | 329 ++++++++++++++++++++++++++++++++ kernel/main.cpp | 3 +- 3 files changed, 377 insertions(+), 1 deletion(-) diff --git a/include/graphics/framebuffer.h b/include/graphics/framebuffer.h index 1c0864c..5426c3a 100644 --- a/include/graphics/framebuffer.h +++ b/include/graphics/framebuffer.h @@ -22,10 +22,56 @@ class Framebuffer { void setColor(Tuple position, Color color) const; void paintRectangle(Tuple start, Tuple end, Color color) const; void drawCharacter(Tuple pos, Color fg, Color bg, char c, float scaleBy = 1.0) const; + void copyRect(Tuple src, Tuple dst, Tuple size) const; + + [[nodiscard]] Color getColor(Tuple pos) const; + + [[nodiscard]] u64 width() const; + [[nodiscard]] u64 height() const; private: volatile u32* fb_ptr = nullptr; limine_framebuffer* fb = nullptr; }; +class FramebufferConsole { +public: + FramebufferConsole( + const Framebuffer& framebuffer, + Color foreground, + Color background, + float scale = 1.0 + ); + + void clear(); + void putChar(char c); + void write(string str); + void writeLn(string str); + void newline(); + void backspace(); + + void setColor(Color fg, Color bg); + void setCursor(u64 x, u64 y); + +private: + void drawCursor(); + void eraseCursor(); + void scroll(); + void drawCell(u64 x, u64 y, char c); + + const Framebuffer& framebuffer; + + Color fg; + Color bg; + + float scale; + + u64 cursorX = 0; + u64 cursorY = 0; + + u64 rows; + u64 columns; + bool cursorVisible = true; +}; + #endif //AVERY_FRAMEBUFFER_H diff --git a/kernel/graphics/framebuffer.cpp b/kernel/graphics/framebuffer.cpp index 3034e40..61f1fc8 100644 --- a/kernel/graphics/framebuffer.cpp +++ b/kernel/graphics/framebuffer.cpp @@ -126,3 +126,332 @@ void Framebuffer::drawCharacter( } } } + +u64 Framebuffer::height() const { + return fb->height; +} + +u64 Framebuffer::width() const { + return fb->width; +} + +Color Framebuffer::getColor(Tuple position) const { + u64 x = position.first(); + u64 y = position.second(); + + auto expandTo8Bit = [](u32 val, u8 maskSize) -> u8 + { + if (maskSize == 0) { + return 0; + } + + if (maskSize >= 8) { + return static_cast(val & 0xFF); + } + + const u32 maxValue = (1u << maskSize) - 1u; + + return static_cast((val * 255u) / maxValue); + }; + + if (x >= fb->width || y >= fb->height) { + return Color{0, 0, 0}; + } + + if (fb->bpp != 32) { + return Color{0, 0, 0}; + } + + usize pixelsPerRow = fb->pitch / 4; + usize index = y * pixelsPerRow + x; + + u32 raw = fb_ptr[index]; + + u32 rRaw = (raw >> fb->red_mask_shift) & + ((1u << fb->red_mask_size) - 1u); + + u32 gRaw = (raw >> fb->green_mask_shift) & + ((1u << fb->green_mask_size) - 1u); + + u32 bRaw = (raw >> fb->blue_mask_shift) & + ((1u << fb->blue_mask_size) - 1u); + + return Color{ + expandTo8Bit(rRaw, fb->red_mask_size), + expandTo8Bit(gRaw, fb->green_mask_size), + expandTo8Bit(bRaw, fb->blue_mask_size) + }; +} + +void Framebuffer::copyRect(Tuple src, Tuple dst, Tuple size) const { + if (size.first() == 0 || size.second() == 0) { + return; + } + + if (src.first() >= width() || dst.first() >= width()) { + return; + } + + if (src.second() >= height() || dst.second() >= height()) { + return; + } + + if (src.first() + size.first() > width()) { + size.a = width() - src.first(); + } + + if (dst.first() + size.first() > width()) { + size.a = width() - dst.first(); + } + + if (src.second() + size.second() > height()) { + size.b = height() - src.second(); + } + + if (dst.second() + size.second() > height()) { + size.b = height() - dst.second(); + } + + if (size.first() == 0 || size.second() == 0) { + return; + } + + const bool copyBottomToTop = dst.second() > src.second(); + const bool copyRightToLeft = dst.first() > src.first(); + + if (copyBottomToTop) { + for (u64 y = size.second(); y > 0; y--) { + const u64 row = y - 1; + + if (copyRightToLeft) { + for (u64 x = size.first(); x > 0; x--) { + const u64 col = x - 1; + + Color color = getColor({ + src.first() + col, + src.second() + row + }); + + setColor({ + dst.first() + col, + dst.second() + row + }, color); + } + } + else { + for (u64 col = 0; col < size.first(); col++) { + Color color = getColor({ + src.first() + col, + src.second() + row + }); + + setColor({ + dst.first() + col, + dst.second() + row + }, color); + } + } + } + } + else { + for (u64 row = 0; row < size.second(); row++) { + if (copyRightToLeft) { + for (u64 x = size.first(); x > 0; x--) { + const u64 col = x - 1; + + Color color = getColor({ + src.first() + col, + src.second() + row + }); + + setColor({ + dst.first() + col, + dst.second() + row + }, color); + } + } + else { + for (u64 col = 0; col < size.first(); col++) { + Color color = getColor({ + src.first() + col, + src.second() + row + }); + + setColor({ + dst.first() + col, + dst.second() + row + }, color); + } + } + } + } +} + +FramebufferConsole::FramebufferConsole(const Framebuffer& framebuffer, + Color foreground, + Color background, + float scale) : + framebuffer(framebuffer), fg(foreground), bg(background), scale(scale) { + if (this->scale == 0.0) { + this->scale = 1.0f; + } + + const u64 charWidth = static_cast(FONT_WIDTH * static_cast(scale)); + const u64 charHeight = static_cast(FONT_HEIGHT * static_cast(scale)); + + columns = framebuffer.width() / charWidth; + rows = framebuffer.height() / charHeight; + + clear(); +} + +void FramebufferConsole::clear() { + framebuffer.paintRectangle( + {0, 0}, {framebuffer.width(), framebuffer.height()}, bg); + + cursorX = 0; + cursorY = 0; +} + +void FramebufferConsole::drawCell(u64 x, u64 y, char c) { + const u64 pixelX = static_cast(static_cast(x) * FONT_WIDTH * static_cast(scale)); + const u64 pixelY = static_cast(static_cast(y) * FONT_HEIGHT * static_cast(scale)); + + framebuffer.drawCharacter( + {pixelX, pixelY}, + fg, + bg, + c, + scale + ); +} + +void FramebufferConsole::newline() { + cursorX = 0; + cursorY++; + + if (cursorY >= rows) { + scroll(); + cursorY = rows - 1; + } +} + +void FramebufferConsole::putChar(char c) { + eraseCursor(); + switch (c) { + case '\n': + newline(); + break; + + case '\r': + cursorX = 0; + break; + + case '\t': + { + constexpr u64 tabSize = 4; + u64 nextTab = (cursorX + tabSize) & ~(tabSize - 1); + + while (cursorX < nextTab) { + putChar(' '); + } + + drawCursor(); + + return; + } + + case '\b': + backspace(); + break; + + default: + drawCell(cursorX, cursorY, c); + cursorX++; + + if (cursorX >= columns) { + newline(); + } + + return; + } + + + drawCursor(); +} + +void FramebufferConsole::write(string str) { + while (*str) { + putChar(*str); + str++; + } +} + +void FramebufferConsole::writeLn(string str) { + while (*str) { + putChar(*str); + str++; + } + putChar('\n'); +} + +void FramebufferConsole::backspace() { + if (cursorX == 0) { + if (cursorY == 0) { + return; + } + + cursorY--; + cursorX = columns - 1; + } + else { + cursorX--; + } + + drawCell(cursorX, cursorY, ' '); +} + +void FramebufferConsole::scroll() { + const u64 charHeight = static_cast(FONT_HEIGHT * static_cast(scale)); + + framebuffer.copyRect( + {0, charHeight}, + {0, 0}, + {framebuffer.width(), framebuffer.height() - charHeight} + ); + + framebuffer.paintRectangle( + {0, framebuffer.height() - charHeight}, + {framebuffer.width(), charHeight}, + bg + ); +} + +void FramebufferConsole::drawCursor() { + const u64 charWidth = static_cast(FONT_WIDTH * static_cast(scale)); + const u64 charHeight = static_cast(FONT_HEIGHT * static_cast(scale)); + + const u64 pixelX = cursorX * charWidth; + const u64 pixelY = cursorY * charHeight; + + framebuffer.paintRectangle( + {pixelX, pixelY}, + {charWidth, charHeight}, + fg + ); +} + + +void FramebufferConsole::eraseCursor() { + const u64 charWidth = static_cast(FONT_WIDTH * static_cast(scale)); + const u64 charHeight = static_cast(FONT_HEIGHT * static_cast(scale)); + + const u64 pixelX = cursorX * charWidth; + const u64 pixelY = cursorY * charHeight; + + framebuffer.paintRectangle( + {pixelX, pixelY}, + {charWidth, charHeight}, + bg + ); +} diff --git a/kernel/main.cpp b/kernel/main.cpp index 9ac4ce1..763e5bf 100644 --- a/kernel/main.cpp +++ b/kernel/main.cpp @@ -32,7 +32,8 @@ extern "C" [[noreturn]] void _start() { } Framebuffer framebuffer = Framebuffer::createFromLimineRequest(framebuffer_request); - framebuffer.drawCharacter({10, 10}, Color::red, Color::black, 'A', 10.0); + FramebufferConsole console = FramebufferConsole(framebuffer, Color::white, Color::black, 1.0); + console.writeLn("Hello, World from the Console!"); while (true) { asm("hlt"); From 909e83b9600216421274a3d27305db6bf3f57bed Mon Sep 17 00:00:00 2001 From: Max Van den Eynde Date: Mon, 1 Jun 2026 07:39:39 +0200 Subject: [PATCH 4/5] First successful build of the framebuffer console --- include/graphics/vgaFont.h | 188 +++++++++--------- kernel/graphics/framebuffer.cpp | 16 +- kernel/main.cpp | 7 +- .../__pycache__/bdf_to_header.cpython-312.pyc | Bin 0 -> 4378 bytes scripts/bdf_to_header.py | 29 ++- 5 files changed, 127 insertions(+), 113 deletions(-) create mode 100644 scripts/__pycache__/bdf_to_header.cpython-312.pyc diff --git a/include/graphics/vgaFont.h b/include/graphics/vgaFont.h index 9297e6b..ea1b4db 100644 --- a/include/graphics/vgaFont.h +++ b/include/graphics/vgaFont.h @@ -8,98 +8,98 @@ static const uint8_t font8x16[95][16] = { /* 32 ' ' */ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 33 '!' */ { 0x60, 0xF0, 0xF0, 0xF0, 0x60, 0x60, 0x60, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 34 '"' */ { 0xCC, 0xCC, 0xCC, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 35 '#' */ { 0x6C, 0x6C, 0xFE, 0x6C, 0x6C, 0x6C, 0xFE, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 36 '$' */ { 0x18, 0x18, 0x7C, 0xC6, 0xC2, 0xC0, 0x7C, 0x06, 0x06, 0x86, 0xC6, 0x7C, 0x18, 0x18, 0x00, 0x00 }, - /* 37 '%' */ { 0xC2, 0xC6, 0x0C, 0x18, 0x30, 0x60, 0xC6, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 38 '&' */ { 0x38, 0x6C, 0x6C, 0x38, 0x76, 0xDC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 39 '\'' */ { 0x60, 0x60, 0x60, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 40 '(' */ { 0x30, 0x60, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 41 ')' */ { 0xC0, 0x60, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x60, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 42 '*' */ { 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 43 '+' */ { 0x30, 0x30, 0xFC, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 44 ',' */ { 0x60, 0x60, 0x60, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 45 '-' */ { 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 46 '.' */ { 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 47 '/' */ { 0x02, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 48 '0' */ { 0x3C, 0x66, 0xC3, 0xC3, 0xDB, 0xDB, 0xC3, 0xC3, 0x66, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 49 '1' */ { 0x30, 0x70, 0xF0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 50 '2' */ { 0x7C, 0xC6, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0xC6, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 51 '3' */ { 0x7C, 0xC6, 0x06, 0x06, 0x3C, 0x06, 0x06, 0x06, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 52 '4' */ { 0x0C, 0x1C, 0x3C, 0x6C, 0xCC, 0xFE, 0x0C, 0x0C, 0x0C, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 53 '5' */ { 0xFE, 0xC0, 0xC0, 0xC0, 0xFC, 0x06, 0x06, 0x06, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 54 '6' */ { 0x38, 0x60, 0xC0, 0xC0, 0xFC, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 55 '7' */ { 0xFE, 0xC6, 0x06, 0x06, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 56 '8' */ { 0x7C, 0xC6, 0xC6, 0xC6, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 57 '9' */ { 0x7C, 0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x06, 0x06, 0x0C, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 58 ':' */ { 0xC0, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 59 ';' */ { 0x60, 0x60, 0x00, 0x00, 0x00, 0x60, 0x60, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 60 '<' */ { 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 61 '=' */ { 0xFC, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 62 '>' */ { 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 63 '?' */ { 0x7C, 0xC6, 0xC6, 0x0C, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 64 '@' */ { 0x7C, 0xC6, 0xC6, 0xDE, 0xDE, 0xDE, 0xDC, 0xC0, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 65 'A' */ { 0x10, 0x38, 0x6C, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 66 'B' */ { 0xFC, 0x66, 0x66, 0x66, 0x7C, 0x66, 0x66, 0x66, 0x66, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 67 'C' */ { 0x3C, 0x66, 0xC2, 0xC0, 0xC0, 0xC0, 0xC0, 0xC2, 0x66, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 68 'D' */ { 0xF8, 0x6C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6C, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 69 'E' */ { 0xFE, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 70 'F' */ { 0xFE, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 71 'G' */ { 0x3C, 0x66, 0xC2, 0xC0, 0xC0, 0xDE, 0xC6, 0xC6, 0x66, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 72 'H' */ { 0xC6, 0xC6, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 73 'I' */ { 0xF0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 74 'J' */ { 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0xCC, 0xCC, 0xCC, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 75 'K' */ { 0xE6, 0x66, 0x66, 0x6C, 0x78, 0x78, 0x6C, 0x66, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 76 'L' */ { 0xF0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 77 'M' */ { 0xC3, 0xE7, 0xFF, 0xFF, 0xDB, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 78 'N' */ { 0xC6, 0xE6, 0xF6, 0xFE, 0xDE, 0xCE, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 79 'O' */ { 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 80 'P' */ { 0xFC, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 81 'Q' */ { 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xD6, 0xDE, 0x7C, 0x0C, 0x0E, 0x00, 0x00, 0x00, 0x00 }, - /* 82 'R' */ { 0xFC, 0x66, 0x66, 0x66, 0x7C, 0x6C, 0x66, 0x66, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 83 'S' */ { 0x7C, 0xC6, 0xC6, 0x60, 0x38, 0x0C, 0x06, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 84 'T' */ { 0xFF, 0xDB, 0x99, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 85 'U' */ { 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 86 'V' */ { 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 87 'W' */ { 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xDB, 0xDB, 0xFF, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 88 'X' */ { 0xC3, 0xC3, 0x66, 0x3C, 0x18, 0x18, 0x3C, 0x66, 0xC3, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 89 'Y' */ { 0xC3, 0xC3, 0xC3, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 90 'Z' */ { 0xFF, 0xC3, 0x86, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0xC3, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 91 '[' */ { 0xF0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 92 '\\' */ { 0x80, 0xC0, 0xE0, 0x70, 0x38, 0x1C, 0x0E, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 93 ']' */ { 0xF0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 94 '^' */ { 0x10, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 95 '_' */ { 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 96 '`' */ { 0xC0, 0xC0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 97 'a' */ { 0x78, 0x0C, 0x7C, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 98 'b' */ { 0xE0, 0x60, 0x60, 0x78, 0x6C, 0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 99 'c' */ { 0x7C, 0xC6, 0xC0, 0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 100 'd' */ { 0x1C, 0x0C, 0x0C, 0x3C, 0x6C, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 101 'e' */ { 0x7C, 0xC6, 0xFE, 0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 102 'f' */ { 0x38, 0x6C, 0x64, 0x60, 0xF0, 0x60, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 103 'g' */ { 0x76, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0xCC, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 104 'h' */ { 0xE0, 0x60, 0x60, 0x6C, 0x76, 0x66, 0x66, 0x66, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 105 'i' */ { 0x60, 0x60, 0x00, 0xE0, 0x60, 0x60, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 106 'j' */ { 0x0C, 0x0C, 0x00, 0x1C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0xCC, 0xCC, 0x78, 0x00, 0x00, 0x00 }, - /* 107 'k' */ { 0xE0, 0x60, 0x60, 0x66, 0x6C, 0x78, 0x78, 0x6C, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 108 'l' */ { 0xE0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 109 'm' */ { 0xE6, 0xFF, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 110 'n' */ { 0xDC, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 111 'o' */ { 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 112 'p' */ { 0xDC, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 113 'q' */ { 0x76, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0x0C, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 114 'r' */ { 0xDC, 0x76, 0x66, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 115 's' */ { 0x7C, 0xC6, 0x60, 0x38, 0x0C, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 116 't' */ { 0x10, 0x30, 0x30, 0xFC, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 117 'u' */ { 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 118 'v' */ { 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 119 'w' */ { 0xC3, 0xC3, 0xC3, 0xDB, 0xDB, 0xFF, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 120 'x' */ { 0xC3, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 121 'y' */ { 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x0C, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 122 'z' */ { 0xFE, 0xCC, 0x18, 0x30, 0x60, 0xC6, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 123 '{' */ { 0x1C, 0x30, 0x30, 0x30, 0xE0, 0x30, 0x30, 0x30, 0x30, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 124 '|' */ { 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 125 '}' */ { 0xE0, 0x30, 0x30, 0x30, 0x1C, 0x30, 0x30, 0x30, 0x30, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - /* 126 '~' */ { 0x76, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 33 '!' */ { 0x00, 0x00, 0x60, 0xF0, 0xF0, 0xF0, 0x60, 0x60, 0x60, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00 }, + /* 34 '"' */ { 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 35 '#' */ { 0x00, 0x00, 0x6C, 0x6C, 0xFE, 0x6C, 0x6C, 0x6C, 0xFE, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 36 '$' */ { 0x00, 0x00, 0x18, 0x18, 0x7C, 0xC6, 0xC2, 0xC0, 0x7C, 0x06, 0x06, 0x86, 0xC6, 0x7C, 0x18, 0x18 }, + /* 37 '%' */ { 0x00, 0x00, 0xC2, 0xC6, 0x0C, 0x18, 0x30, 0x60, 0xC6, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 38 '&' */ { 0x00, 0x00, 0x38, 0x6C, 0x6C, 0x38, 0x76, 0xDC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00 }, + /* 39 '\'' */ { 0x00, 0x00, 0x60, 0x60, 0x60, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 40 '(' */ { 0x00, 0x00, 0x30, 0x60, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00 }, + /* 41 ')' */ { 0x00, 0x00, 0xC0, 0x60, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x60, 0xC0, 0x00, 0x00, 0x00, 0x00 }, + /* 42 '*' */ { 0x00, 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 43 '+' */ { 0x00, 0x00, 0x30, 0x30, 0xFC, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 44 ',' */ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x60, 0xC0, 0x00, 0x00 }, + /* 45 '-' */ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 46 '.' */ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00 }, + /* 47 '/' */ { 0x00, 0x00, 0x02, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 48 '0' */ { 0x00, 0x00, 0x3C, 0x66, 0xC3, 0xC3, 0xDB, 0xDB, 0xC3, 0xC3, 0x66, 0x3C, 0x00, 0x00, 0x00, 0x00 }, + /* 49 '1' */ { 0x00, 0x00, 0x30, 0x70, 0xF0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xFC, 0x00, 0x00, 0x00, 0x00 }, + /* 50 '2' */ { 0x00, 0x00, 0x7C, 0xC6, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0xC6, 0xFE, 0x00, 0x00, 0x00, 0x00 }, + /* 51 '3' */ { 0x00, 0x00, 0x7C, 0xC6, 0x06, 0x06, 0x3C, 0x06, 0x06, 0x06, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 }, + /* 52 '4' */ { 0x00, 0x00, 0x0C, 0x1C, 0x3C, 0x6C, 0xCC, 0xFE, 0x0C, 0x0C, 0x0C, 0x1E, 0x00, 0x00, 0x00, 0x00 }, + /* 53 '5' */ { 0x00, 0x00, 0xFE, 0xC0, 0xC0, 0xC0, 0xFC, 0x06, 0x06, 0x06, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 }, + /* 54 '6' */ { 0x00, 0x00, 0x38, 0x60, 0xC0, 0xC0, 0xFC, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 }, + /* 55 '7' */ { 0x00, 0x00, 0xFE, 0xC6, 0x06, 0x06, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00 }, + /* 56 '8' */ { 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 }, + /* 57 '9' */ { 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x06, 0x06, 0x0C, 0x78, 0x00, 0x00, 0x00, 0x00 }, + /* 58 ':' */ { 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 59 ';' */ { 0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00, 0x60, 0x60, 0xC0, 0x00, 0x00, 0x00, 0x00 }, + /* 60 '<' */ { 0x00, 0x00, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 61 '=' */ { 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 62 '>' */ { 0x00, 0x00, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x00, 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, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 65 'A' */ { 0x00, 0x00, 0x10, 0x38, 0x6C, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00 }, + /* 66 'B' */ { 0x00, 0x00, 0xFC, 0x66, 0x66, 0x66, 0x7C, 0x66, 0x66, 0x66, 0x66, 0xFC, 0x00, 0x00, 0x00, 0x00 }, + /* 67 'C' */ { 0x00, 0x00, 0x3C, 0x66, 0xC2, 0xC0, 0xC0, 0xC0, 0xC0, 0xC2, 0x66, 0x3C, 0x00, 0x00, 0x00, 0x00 }, + /* 68 'D' */ { 0x00, 0x00, 0xF8, 0x6C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6C, 0xF8, 0x00, 0x00, 0x00, 0x00 }, + /* 69 'E' */ { 0x00, 0x00, 0xFE, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00 }, + /* 70 'F' */ { 0x00, 0x00, 0xFE, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00 }, + /* 71 'G' */ { 0x00, 0x00, 0x3C, 0x66, 0xC2, 0xC0, 0xC0, 0xDE, 0xC6, 0xC6, 0x66, 0x3A, 0x00, 0x00, 0x00, 0x00 }, + /* 72 'H' */ { 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00 }, + /* 73 'I' */ { 0x00, 0x00, 0xF0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00 }, + /* 74 'J' */ { 0x00, 0x00, 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0xCC, 0xCC, 0xCC, 0x78, 0x00, 0x00, 0x00, 0x00 }, + /* 75 'K' */ { 0x00, 0x00, 0xE6, 0x66, 0x66, 0x6C, 0x78, 0x78, 0x6C, 0x66, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00 }, + /* 76 'L' */ { 0x00, 0x00, 0xF0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00 }, + /* 77 'M' */ { 0x00, 0x00, 0xC3, 0xE7, 0xFF, 0xFF, 0xDB, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x00, 0x00, 0x00, 0x00 }, + /* 78 'N' */ { 0x00, 0x00, 0xC6, 0xE6, 0xF6, 0xFE, 0xDE, 0xCE, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00 }, + /* 79 'O' */ { 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 }, + /* 80 'P' */ { 0x00, 0x00, 0xFC, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00 }, + /* 81 'Q' */ { 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xD6, 0xDE, 0x7C, 0x0C, 0x0E, 0x00, 0x00 }, + /* 82 'R' */ { 0x00, 0x00, 0xFC, 0x66, 0x66, 0x66, 0x7C, 0x6C, 0x66, 0x66, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00 }, + /* 83 'S' */ { 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0x60, 0x38, 0x0C, 0x06, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 }, + /* 84 'T' */ { 0x00, 0x00, 0xFF, 0xDB, 0x99, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00 }, + /* 85 'U' */ { 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 }, + /* 86 'V' */ { 0x00, 0x00, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00 }, + /* 87 'W' */ { 0x00, 0x00, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xDB, 0xDB, 0xFF, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00 }, + /* 88 'X' */ { 0x00, 0x00, 0xC3, 0xC3, 0x66, 0x3C, 0x18, 0x18, 0x3C, 0x66, 0xC3, 0xC3, 0x00, 0x00, 0x00, 0x00 }, + /* 89 'Y' */ { 0x00, 0x00, 0xC3, 0xC3, 0xC3, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00 }, + /* 90 'Z' */ { 0x00, 0x00, 0xFF, 0xC3, 0x86, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0xC3, 0xFF, 0x00, 0x00, 0x00, 0x00 }, + /* 91 '[' */ { 0x00, 0x00, 0xF0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xF0, 0x00, 0x00, 0x00, 0x00 }, + /* 92 '\\' */ { 0x00, 0x00, 0x80, 0xC0, 0xE0, 0x70, 0x38, 0x1C, 0x0E, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 93 ']' */ { 0x00, 0x00, 0xF0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xF0, 0x00, 0x00, 0x00, 0x00 }, + /* 94 '^' */ { 0x00, 0x00, 0x10, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 95 '_' */ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00 }, + /* 96 '`' */ { 0x00, 0x00, 0xC0, 0xC0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 97 'a' */ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00 }, + /* 98 'b' */ { 0x00, 0x00, 0xE0, 0x60, 0x60, 0x78, 0x6C, 0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00, 0x00 }, + /* 99 'c' */ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC0, 0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 }, + /* 100 'd' */ { 0x00, 0x00, 0x1C, 0x0C, 0x0C, 0x3C, 0x6C, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00 }, + /* 101 'e' */ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xFE, 0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 }, + /* 102 'f' */ { 0x00, 0x00, 0x38, 0x6C, 0x64, 0x60, 0xF0, 0x60, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00 }, + /* 103 'g' */ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0xCC, 0x78, 0x00 }, + /* 104 'h' */ { 0x00, 0x00, 0xE0, 0x60, 0x60, 0x6C, 0x76, 0x66, 0x66, 0x66, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00 }, + /* 105 'i' */ { 0x00, 0x00, 0x60, 0x60, 0x00, 0xE0, 0x60, 0x60, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00 }, + /* 106 'j' */ { 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x1C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0xCC, 0xCC, 0x78, 0x00 }, + /* 107 'k' */ { 0x00, 0x00, 0xE0, 0x60, 0x60, 0x66, 0x6C, 0x78, 0x78, 0x6C, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00 }, + /* 108 'l' */ { 0x00, 0x00, 0xE0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00 }, + /* 109 'm' */ { 0x00, 0x00, 0x00, 0x00, 0x00, 0xE6, 0xFF, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0x00, 0x00, 0x00, 0x00 }, + /* 110 'n' */ { 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00 }, + /* 111 'o' */ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 }, + /* 112 'p' */ { 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0xF0, 0x00 }, + /* 113 'q' */ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0x0C, 0x1E, 0x00 }, + /* 114 'r' */ { 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x76, 0x66, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00 }, + /* 115 's' */ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0x60, 0x38, 0x0C, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 }, + /* 116 't' */ { 0x00, 0x00, 0x10, 0x30, 0x30, 0xFC, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1C, 0x00, 0x00, 0x00, 0x00 }, + /* 117 'u' */ { 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00 }, + /* 118 'v' */ { 0x00, 0x00, 0x00, 0x00, 0x00, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00 }, + /* 119 'w' */ { 0x00, 0x00, 0x00, 0x00, 0x00, 0xC3, 0xC3, 0xC3, 0xDB, 0xDB, 0xFF, 0x66, 0x00, 0x00, 0x00, 0x00 }, + /* 120 'x' */ { 0x00, 0x00, 0x00, 0x00, 0x00, 0xC3, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0xC3, 0x00, 0x00, 0x00, 0x00 }, + /* 121 'y' */ { 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x0C, 0xF8, 0x00 }, + /* 122 'z' */ { 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xCC, 0x18, 0x30, 0x60, 0xC6, 0xFE, 0x00, 0x00, 0x00, 0x00 }, + /* 123 '{' */ { 0x00, 0x00, 0x1C, 0x30, 0x30, 0x30, 0xE0, 0x30, 0x30, 0x30, 0x30, 0x1C, 0x00, 0x00, 0x00, 0x00 }, + /* 124 '|' */ { 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00 }, + /* 125 '}' */ { 0x00, 0x00, 0xE0, 0x30, 0x30, 0x30, 0x1C, 0x30, 0x30, 0x30, 0x30, 0xE0, 0x00, 0x00, 0x00, 0x00 }, + /* 126 '~' */ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, }; diff --git a/kernel/graphics/framebuffer.cpp b/kernel/graphics/framebuffer.cpp index 61f1fc8..1885de1 100644 --- a/kernel/graphics/framebuffer.cpp +++ b/kernel/graphics/framebuffer.cpp @@ -337,15 +337,14 @@ void FramebufferConsole::newline() { } void FramebufferConsole::putChar(char c) { - eraseCursor(); switch (c) { case '\n': newline(); - break; + return; case '\r': cursorX = 0; - break; + return; case '\t': { @@ -356,14 +355,12 @@ void FramebufferConsole::putChar(char c) { putChar(' '); } - drawCursor(); - return; } case '\b': backspace(); - break; + return; default: drawCell(cursorX, cursorY, c); @@ -375,9 +372,6 @@ void FramebufferConsole::putChar(char c) { return; } - - - drawCursor(); } void FramebufferConsole::write(string str) { @@ -436,7 +430,7 @@ void FramebufferConsole::drawCursor() { framebuffer.paintRectangle( {pixelX, pixelY}, - {charWidth, charHeight}, + {pixelX + charWidth, pixelY + charHeight}, fg ); } @@ -451,7 +445,7 @@ void FramebufferConsole::eraseCursor() { framebuffer.paintRectangle( {pixelX, pixelY}, - {charWidth, charHeight}, + {pixelX + charWidth, pixelY + charHeight}, bg ); } diff --git a/kernel/main.cpp b/kernel/main.cpp index 763e5bf..d3c9f8e 100644 --- a/kernel/main.cpp +++ b/kernel/main.cpp @@ -32,8 +32,11 @@ extern "C" [[noreturn]] void _start() { } Framebuffer framebuffer = Framebuffer::createFromLimineRequest(framebuffer_request); - FramebufferConsole console = FramebufferConsole(framebuffer, Color::white, Color::black, 1.0); - console.writeLn("Hello, World from the Console!"); + FramebufferConsole console = FramebufferConsole(framebuffer, Color::white, Color::black, 1); + console.writeLn("The Avery Kernel"); + console.writeLn("Developed by Neutral Software in 2026"); + console.writeLn("Version - Alpha 1\n"); + console.writeLn("Framebuffer initialized"); while (true) { asm("hlt"); diff --git a/scripts/__pycache__/bdf_to_header.cpython-312.pyc b/scripts/__pycache__/bdf_to_header.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a1f8e95286b299be4cf414e407a14546d62b57ba GIT binary patch literal 4378 zcmbVPUr-yz8Q;~Nq&xiq3Gp8c2O_qGF*e3=h>gJ*K};Nz#wJZd%xGlYA%sCv?v4QR zT|?S*#_rIua5_aa$wZ#GV_K&(bfyn^%uJiS^aYHPQEzx)CVnU{9b!MY;i=yqoWR5m zGhNMo+V9(Mzuo=z+wZscPp8v{pp2C78@_5s=sUs~#kAFUbPgJ`h(|mgLH&p~^<$pu zH}P~o#hd$S-tqzJH}h7Y7M=lW6&RkqdJrLzF(JWvZ_5lulXw#QY-@z?U1NZ*xd`D1 z`2~;=24WNz8Whw=$AQkG2vRYRR1;JRDji0una8R{&W%xEWjw{xpPTQ|n?{~Ts#UT2 zQ4$aIBcw74GfXV*D#rTse)FzQV0p{EtvSSfveoDZTV#d1ieZblmO+m(PBl%R1^uVl zW(=MG+cq^Fe}-)~XM6uouU2JcIQz}Q+de=F%UgMSIr1Z8KZYfhp1!LvyhA@-vwj9n z)iV88kmc#Jq(Z&_f$T7>DOAWbVY*rlvB1iChE(U@iJCBqMisLk-L!>FVH7eAfQD+@ z;bR-0e{U-aXH3&B8mV^G#=F8;CV``u1b9_-DD?XUis^lqLSKfnSDk7e)O^LHyM?eqYRSaR>+ zXZQgl1z!SRVW_HY9C=X_tYjScw-AOO>7Ah7LgU!y?(NpFm@tpYgNIu*t0=@rf+4|Y z(ikBcit&+XShEPC7!#$0qx;p~zCg#N&aU1*ZjFR{g6-<(BVcJ=wpn-gF8QyUrvEkm|<5>)ja?@xM{e^<1;a(rss7O?X)tDNOYIHm( z$`Uzhj@u}eLSiH?OGmEog8?}f7!rcKAU4J)H`vy&5EUlkVq2mTMo2;cR2o}HVxi!O z)Yh2m-3NxGLLmP}|D8hjy(i`V zz5dQ~9T!E?FOs$R>>3@53sKE1$*>~LCdp)(;}LmCB*6^w_((+7%<@=#M9|FLJr^(a zY4j@{KrQFGdR{u$r?EnGY*Y|~vY=UlaWKaF3dwfmA&iNgH46E!@c**CiaVr*Q}Xr1DK z=FsN@cSgcPvPMtF1_w1tj>R<`(I`;TC|;6vw{cG?qMPiP&ADAX4m?SL($^3#Qz+}q zpX$yQ6i=PcmQ*a#g_})X=qj4(dB8fa^U2QifdyZNZCyw8=DII&wy@|C^09T-{aH`R z?8%vvAGTiiujYGhzIWrj)GspmjVo0(>AJi1x9dM^n1AQ?k>#pmGxk+?(QNfhb-E(s zKD^@HpO)_?ZYMrd7CLUfx9ojk$;BB8#Toa(Y-#1EwmI8Jj-+{|$_vN;#dBZcFKfQ2 z&v?6*tGaY+snYxInrvA`(vq#@W>^@WD!cEl&3e3xo+E$jT0DIrbNuzpYp;KE{PpY$ zr@yUjTd29yoPKA%_KznQYul3Nfn+Q9rJC;)&$TA~t0lFIC5;QG7MowrHn%O69!=p? z>%4p3pRMxE_bt4#bok6tRmU8)T2-B*9@HM1Kfhet@(@u)4RaPyZO+1$JLbYEDqB&R zrZN=`cLu+$XviKsl=7!Nf9P4ag66sn9X+1xUaUB@RM@cUJ+SEFvQ@Q7`_?9EZn59F z%d@WHS?7#1#eeJaet!vrn;%o)_4PL~auiHm_~Bs_^0q(r?GSB9hu}Ov?kwtdQh#sq zbTiC9=ng9|UpXoW^%VmBnlTgVG^q)N+>eK(7e^OI^;%&J8(^L8DaDc+%4qWQo(xA;8Zt-$B& zJb_2cGkot>sE-g|pjd!?(W_JTB-TKP&e1XYsbi^euqcr^iRhw$^UY zocs|`P8u*-+N_7K5)&)b`Kzc!8IK_0Ta{%#K@5l7m7tCnHz(N zTLLmi0HS50>BQSFK0okw(}@A@GGQN&HAX zmoRY$64nD;{SodOFb&T@a(J(ABRP34!E;-|JpPFO#I%Lq4#01dj^C7KIy#|IM~}VP z`zPcxi6r(!@)p>_a2+hf0uUssgwcp>VEB)r`VBIu6v!r2SGO-Tr)$%f<~`}2J9$gh z?OSl|D!>VvQyXjvN8Or>D*$jqDQcOmTu0^R@-&rY>^IpP>|#M}hVkZ@Lm8$a$JA$-Lpi1)!yL{rPpBQv zFpaB*we}2i=0RcE$IsmmKxi3(&=jA3E#3L~{@brCdz!BMS3Jc@ z`NJ0hR!b|d_hj=uN%=QQwz77H%>vGOZdGI}Yc|`3w<@8XWLF*eH+ydMq^xN=^=|s& zJe$6@aB#8sRL0So-Rw&8vyqty9L^VYndMPdVRbUMPpDQF~>fe6O*h|cxzX|~HHGh6 z%T^fYV&2INb7}?Rk-5Pn-P865&f?!mvlBBDX*_dn+3B6?UZL}omXE1VnK>ryp0j^L z*F6GjaQ6ZMGD#o+7x)4ZnDrE@U)AmFyS++%HDpvYI4T4J;%OKoUL=Ho%Ldar8so=C zgf{W#z>=*xuhR*}rJ!*WK$BqRaIe{s+n@!OH*u literal 0 HcmV?d00001 diff --git a/scripts/bdf_to_header.py b/scripts/bdf_to_header.py index 7070d06..61f6939 100644 --- a/scripts/bdf_to_header.py +++ b/scripts/bdf_to_header.py @@ -8,7 +8,9 @@ def parse_bdf(path): glyphs = {} + font_ascent = None current_encoding = None + current_bbx = None bitmap = [] in_bitmap = False @@ -16,25 +18,40 @@ def parse_bdf(path): for line in f: line = line.strip() - if line.startswith("ENCODING "): + if line.startswith("FONT_ASCENT "): + font_ascent = int(line.split()[1]) + + elif line.startswith("ENCODING "): current_encoding = int(line.split()[1]) + elif line.startswith("BBX "): + parts = line.split() + current_bbx = tuple(int(part) for part in parts[1:5]) + elif line == "BITMAP": bitmap = [] in_bitmap = True elif line == "ENDCHAR": if current_encoding is not None and FIRST <= current_encoding <= LAST: - rows = [int(x, 16) for x in bitmap] + bitmap_rows = [int(x, 16) for x in bitmap] + rows = [0] * HEIGHT + + if current_bbx is not None and font_ascent is not None: + _, glyph_height, _, yoff = current_bbx + top = font_ascent - (yoff + glyph_height) + else: + top = 0 - # Force exactly HEIGHT rows - rows = rows[:HEIGHT] - while len(rows) < HEIGHT: - rows.append(0) + for i, row in enumerate(bitmap_rows): + dst = top + i + if 0 <= dst < HEIGHT: + rows[dst] = row glyphs[current_encoding] = rows current_encoding = None + current_bbx = None bitmap = [] in_bitmap = False From 3069c4909e0c2bb1231296c76b75c1501a1a1bfe Mon Sep 17 00:00:00 2001 From: Max Van den Eynde Date: Mon, 1 Jun 2026 19:29:23 +0200 Subject: [PATCH 5/5] Fixed some kerning issues --- include/graphics/framebuffer.h | 3 +- kernel/graphics/framebuffer.cpp | 100 +++++++++++++++++++++++++------- 2 files changed, 80 insertions(+), 23 deletions(-) diff --git a/include/graphics/framebuffer.h b/include/graphics/framebuffer.h index 5426c3a..e9258a9 100644 --- a/include/graphics/framebuffer.h +++ b/include/graphics/framebuffer.h @@ -57,7 +57,7 @@ class FramebufferConsole { void drawCursor(); void eraseCursor(); void scroll(); - void drawCell(u64 x, u64 y, char c); + void drawCell(u64 pixelX, u64 y, char c); const Framebuffer& framebuffer; @@ -70,7 +70,6 @@ class FramebufferConsole { u64 cursorY = 0; u64 rows; - u64 columns; bool cursorVisible = true; }; diff --git a/kernel/graphics/framebuffer.cpp b/kernel/graphics/framebuffer.cpp index 1885de1..353ddde 100644 --- a/kernel/graphics/framebuffer.cpp +++ b/kernel/graphics/framebuffer.cpp @@ -14,6 +14,53 @@ #include "graphics/graphicsTypes.h" #include "types.h" +namespace { + u64 fontScale(float scale) { + if (scale < 1.0f) { + return 1; + } + + return static_cast(scale); + } + + u64 glyphAdvance(char c) { + if (c < FONT_FIRST || c > FONT_LAST) { + c = '?'; + } + + if (c == ' ') { + return 4; + } + + const u8* glyph = font8x16[c - FONT_FIRST]; + i64 maxCol = -1; + + for (u64 row = 0; row < FONT_HEIGHT; row++) { + u8 bits = glyph[row]; + + for (u64 col = 0; col < FONT_WIDTH; col++) { + if (bits & (0x80 >> col)) { + if (static_cast(col) > maxCol) { + maxCol = static_cast(col); + } + } + } + } + + if (maxCol < 0) { + return 4; + } + + u64 advance = static_cast(maxCol) + 2; + + if (advance > FONT_WIDTH) { + return FONT_WIDTH; + } + + return advance; + } +} + Framebuffer Framebuffer::createFromLimineRequest( volatile limine_framebuffer_request& request ) { @@ -292,14 +339,12 @@ FramebufferConsole::FramebufferConsole(const Framebuffer& framebuffer, Color background, float scale) : framebuffer(framebuffer), fg(foreground), bg(background), scale(scale) { - if (this->scale == 0.0) { + if (this->scale < 1.0f) { this->scale = 1.0f; } - const u64 charWidth = static_cast(FONT_WIDTH * static_cast(scale)); - const u64 charHeight = static_cast(FONT_HEIGHT * static_cast(scale)); + const u64 charHeight = FONT_HEIGHT * fontScale(scale); - columns = framebuffer.width() / charWidth; rows = framebuffer.height() / charHeight; clear(); @@ -313,9 +358,8 @@ void FramebufferConsole::clear() { cursorY = 0; } -void FramebufferConsole::drawCell(u64 x, u64 y, char c) { - const u64 pixelX = static_cast(static_cast(x) * FONT_WIDTH * static_cast(scale)); - const u64 pixelY = static_cast(static_cast(y) * FONT_HEIGHT * static_cast(scale)); +void FramebufferConsole::drawCell(u64 pixelX, u64 y, char c) { + const u64 pixelY = y * FONT_HEIGHT * fontScale(scale); framebuffer.drawCharacter( {pixelX, pixelY}, @@ -349,7 +393,9 @@ void FramebufferConsole::putChar(char c) { case '\t': { constexpr u64 tabSize = 4; - u64 nextTab = (cursorX + tabSize) & ~(tabSize - 1); + const u64 spaceWidth = glyphAdvance(' ') * fontScale(scale); + const u64 tabWidth = spaceWidth * tabSize; + u64 nextTab = ((cursorX / tabWidth) + 1) * tabWidth; while (cursorX < nextTab) { putChar(' '); @@ -363,10 +409,17 @@ void FramebufferConsole::putChar(char c) { return; default: + const u64 scaledFontWidth = FONT_WIDTH * fontScale(scale); + const u64 advance = glyphAdvance(c) * fontScale(scale); + + if (cursorX + scaledFontWidth > framebuffer.width()) { + newline(); + } + drawCell(cursorX, cursorY, c); - cursorX++; + cursorX += advance; - if (cursorX >= columns) { + if (cursorX >= framebuffer.width()) { newline(); } @@ -396,17 +449,24 @@ void FramebufferConsole::backspace() { } cursorY--; - cursorX = columns - 1; + cursorX = framebuffer.width() - FONT_WIDTH * fontScale(scale); } else { - cursorX--; + const u64 charWidth = FONT_WIDTH * fontScale(scale); + + if (cursorX > charWidth) { + cursorX -= charWidth; + } + else { + cursorX = 0; + } } drawCell(cursorX, cursorY, ' '); } void FramebufferConsole::scroll() { - const u64 charHeight = static_cast(FONT_HEIGHT * static_cast(scale)); + const u64 charHeight = FONT_HEIGHT * fontScale(scale); framebuffer.copyRect( {0, charHeight}, @@ -422,30 +482,28 @@ void FramebufferConsole::scroll() { } void FramebufferConsole::drawCursor() { - const u64 charWidth = static_cast(FONT_WIDTH * static_cast(scale)); - const u64 charHeight = static_cast(FONT_HEIGHT * static_cast(scale)); + const u64 charHeight = FONT_HEIGHT * fontScale(scale); - const u64 pixelX = cursorX * charWidth; + const u64 pixelX = cursorX; const u64 pixelY = cursorY * charHeight; framebuffer.paintRectangle( {pixelX, pixelY}, - {pixelX + charWidth, pixelY + charHeight}, + {pixelX + FONT_WIDTH * fontScale(scale), pixelY + charHeight}, fg ); } void FramebufferConsole::eraseCursor() { - const u64 charWidth = static_cast(FONT_WIDTH * static_cast(scale)); - const u64 charHeight = static_cast(FONT_HEIGHT * static_cast(scale)); + const u64 charHeight = FONT_HEIGHT * fontScale(scale); - const u64 pixelX = cursorX * charWidth; + const u64 pixelX = cursorX; const u64 pixelY = cursorY * charHeight; framebuffer.paintRectangle( {pixelX, pixelY}, - {pixelX + charWidth, pixelY + charHeight}, + {pixelX + FONT_WIDTH * fontScale(scale), pixelY + charHeight}, bg ); }