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..e9258a9 --- /dev/null +++ b/include/graphics/framebuffer.h @@ -0,0 +1,76 @@ +/* +* 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 "graphicsTypes.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; + 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 pixelX, u64 y, char c); + + const Framebuffer& framebuffer; + + Color fg; + Color bg; + + float scale; + + u64 cursorX = 0; + u64 cursorY = 0; + + u64 rows; + bool cursorVisible = true; +}; + +#endif //AVERY_FRAMEBUFFER_H 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..ea1b4db --- /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 '!' */ { 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/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..c544c9d 100644 --- a/include/types.h +++ b/include/types.h @@ -23,5 +23,20 @@ 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; + #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..353ddde --- /dev/null +++ b/kernel/graphics/framebuffer.cpp @@ -0,0 +1,509 @@ +/* +* 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 +#include + +#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 +) { + 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); + } + } +} + +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); + } + } + } + } +} + +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 < 1.0f) { + this->scale = 1.0f; + } + + const u64 charHeight = FONT_HEIGHT * fontScale(scale); + + 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 pixelX, u64 y, char c) { + const u64 pixelY = y * FONT_HEIGHT * fontScale(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) { + switch (c) { + case '\n': + newline(); + return; + + case '\r': + cursorX = 0; + return; + + case '\t': + { + constexpr u64 tabSize = 4; + const u64 spaceWidth = glyphAdvance(' ') * fontScale(scale); + const u64 tabWidth = spaceWidth * tabSize; + u64 nextTab = ((cursorX / tabWidth) + 1) * tabWidth; + + while (cursorX < nextTab) { + putChar(' '); + } + + return; + } + + case '\b': + backspace(); + 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 += advance; + + if (cursorX >= framebuffer.width()) { + newline(); + } + + return; + } +} + +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 = framebuffer.width() - FONT_WIDTH * fontScale(scale); + } + else { + const u64 charWidth = FONT_WIDTH * fontScale(scale); + + if (cursorX > charWidth) { + cursorX -= charWidth; + } + else { + cursorX = 0; + } + } + + drawCell(cursorX, cursorY, ' '); +} + +void FramebufferConsole::scroll() { + const u64 charHeight = FONT_HEIGHT * fontScale(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 charHeight = FONT_HEIGHT * fontScale(scale); + + const u64 pixelX = cursorX; + const u64 pixelY = cursorY * charHeight; + + framebuffer.paintRectangle( + {pixelX, pixelY}, + {pixelX + FONT_WIDTH * fontScale(scale), pixelY + charHeight}, + fg + ); +} + + +void FramebufferConsole::eraseCursor() { + const u64 charHeight = FONT_HEIGHT * fontScale(scale); + + const u64 pixelX = cursorX; + const u64 pixelY = cursorY * charHeight; + + framebuffer.paintRectangle( + {pixelX, pixelY}, + {pixelX + FONT_WIDTH * fontScale(scale), pixelY + charHeight}, + bg + ); +} diff --git a/kernel/main.cpp b/kernel/main.cpp index d09e5ce..d3c9f8e 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,12 @@ 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); + 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 0000000..a1f8e95 Binary files /dev/null and b/scripts/__pycache__/bdf_to_header.cpython-312.pyc differ diff --git a/scripts/bdf_to_header.py b/scripts/bdf_to_header.py new file mode 100644 index 0000000..61f6939 --- /dev/null +++ b/scripts/bdf_to_header.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 +import sys + +WIDTH = 8 +HEIGHT = 16 +FIRST = 32 +LAST = 126 + +def parse_bdf(path): + glyphs = {} + font_ascent = None + current_encoding = None + current_bbx = 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("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: + 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 + + 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 + + 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() 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