diff --git a/CMakeLists.txt b/CMakeLists.txt index d8463d3..4e9f366 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,7 +38,7 @@ add_custom_command( add_custom_target(initrd DEPENDS ${INITRD_FILE}) # Create grub.cfg for ISO - includes module2 for the initrd -file(WRITE ${CMAKE_BINARY_DIR}/isodir/boot/grub/grub.cfg "set timeout=0\nset default=0\nsearch --set=root --file /boot/kernel.bin\nmenuentry \"ClaudeOS\" {\n set gfxpayload=text\n multiboot2 /boot/kernel.bin\n module2 /boot/initrd.cpio\n}") +file(WRITE ${CMAKE_BINARY_DIR}/isodir/boot/grub/grub.cfg "set timeout=0\nset default=0\nsearch --set=root --file /boot/kernel.bin\nmenuentry \"ClaudeOS\" {\n multiboot2 /boot/kernel.bin\n module2 /boot/initrd.cpio\n}") # ISO Generation diff --git a/src/font8x16.h b/src/font8x16.h new file mode 100644 index 0000000..f79b769 --- /dev/null +++ b/src/font8x16.h @@ -0,0 +1,215 @@ +/** + * @file font8x16.h + * @brief Embedded 8x16 VGA bitmap font for graphical framebuffer rendering. + * + * Each character is 16 bytes: one byte per scanline, MSB is leftmost pixel. + * Covers ASCII 32 (space) through 126 (~). Characters outside this range + * render as a filled block. + * + * This is the standard VGA 8x16 font data, in the public domain. + */ + +#ifndef FONT8X16_H +#define FONT8X16_H + +#include + +#define FONT_WIDTH 8 +#define FONT_HEIGHT 16 +#define FONT_FIRST 32 +#define FONT_LAST 126 + +static const uint8_t font8x16_data[][16] = { + /* 32: space */ + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, + /* 33: ! */ + {0x00,0x00,0x18,0x3C,0x3C,0x3C,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00}, + /* 34: " */ + {0x00,0x66,0x66,0x66,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, + /* 35: # */ + {0x00,0x00,0x00,0x6C,0x6C,0xFE,0x6C,0x6C,0x6C,0xFE,0x6C,0x6C,0x00,0x00,0x00,0x00}, + /* 36: $ */ + {0x18,0x18,0x7C,0xC6,0xC2,0xC0,0x7C,0x06,0x06,0x86,0xC6,0x7C,0x18,0x18,0x00,0x00}, + /* 37: % */ + {0x00,0x00,0x00,0x00,0xC2,0xC6,0x0C,0x18,0x30,0x60,0xC6,0x86,0x00,0x00,0x00,0x00}, + /* 38: & */ + {0x00,0x00,0x38,0x6C,0x6C,0x38,0x76,0xDC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00}, + /* 39: ' */ + {0x00,0x30,0x30,0x30,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, + /* 40: ( */ + {0x00,0x00,0x0C,0x18,0x30,0x30,0x30,0x30,0x30,0x30,0x18,0x0C,0x00,0x00,0x00,0x00}, + /* 41: ) */ + {0x00,0x00,0x30,0x18,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x18,0x30,0x00,0x00,0x00,0x00}, + /* 42: * */ + {0x00,0x00,0x00,0x00,0x00,0x66,0x3C,0xFF,0x3C,0x66,0x00,0x00,0x00,0x00,0x00,0x00}, + /* 43: + */ + {0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00}, + /* 44: , */ + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x30,0x00,0x00,0x00}, + /* 45: - */ + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, + /* 46: . */ + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00}, + /* 47: / */ + {0x00,0x00,0x00,0x00,0x02,0x06,0x0C,0x18,0x30,0x60,0xC0,0x80,0x00,0x00,0x00,0x00}, + /* 48: 0 */ + {0x00,0x00,0x3C,0x66,0xC3,0xC3,0xDB,0xDB,0xC3,0xC3,0x66,0x3C,0x00,0x00,0x00,0x00}, + /* 49: 1 */ + {0x00,0x00,0x18,0x38,0x78,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,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,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00}, + /* 59: ; */ + {0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x30,0x00,0x00,0x00,0x00}, + /* 60: < */ + {0x00,0x00,0x00,0x06,0x0C,0x18,0x30,0x60,0x30,0x18,0x0C,0x06,0x00,0x00,0x00,0x00}, + /* 61: = */ + {0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, + /* 62: > */ + {0x00,0x00,0x00,0x60,0x30,0x18,0x0C,0x06,0x0C,0x18,0x30,0x60,0x00,0x00,0x00,0x00}, + /* 63: ? */ + {0x00,0x00,0x7C,0xC6,0xC6,0x0C,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00}, + /* 64: @ */ + {0x00,0x00,0x00,0x7C,0xC6,0xC6,0xDE,0xDE,0xDE,0xDC,0xC0,0x7C,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,0x3C,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,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,0xC6,0xEE,0xFE,0xFE,0xD6,0xC6,0xC6,0xC6,0xC6,0xC6,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,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x6C,0x38,0x10,0x00,0x00,0x00,0x00}, + /* 87: W */ + {0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xD6,0xD6,0xD6,0xFE,0xEE,0x6C,0x00,0x00,0x00,0x00}, + /* 88: X */ + {0x00,0x00,0xC6,0xC6,0x6C,0x7C,0x38,0x38,0x7C,0x6C,0xC6,0xC6,0x00,0x00,0x00,0x00}, + /* 89: Y */ + {0x00,0x00,0xC3,0xC3,0x66,0x3C,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00}, + /* 90: Z */ + {0x00,0x00,0xFE,0xC6,0x86,0x0C,0x18,0x30,0x60,0xC2,0xC6,0xFE,0x00,0x00,0x00,0x00}, + /* 91: [ */ + {0x00,0x00,0x3C,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x3C,0x00,0x00,0x00,0x00}, + /* 92: \ */ + {0x00,0x00,0x00,0x80,0xC0,0xE0,0x70,0x38,0x1C,0x0E,0x06,0x02,0x00,0x00,0x00,0x00}, + /* 93: ] */ + {0x00,0x00,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00,0x00,0x00,0x00}, + /* 94: ^ */ + {0x10,0x38,0x6C,0xC6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, + /* 95: _ */ + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00}, + /* 96: ` */ + {0x30,0x30,0x18,0x00,0x00,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,0x1C,0x36,0x32,0x30,0x78,0x30,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00}, + /* 103: g */ + {0x00,0x00,0x00,0x00,0x00,0x76,0xCC,0xCC,0xCC,0xCC,0x7C,0x0C,0xCC,0x78,0x00,0x00}, + /* 104: h */ + {0x00,0x00,0xE0,0x60,0x60,0x6C,0x76,0x66,0x66,0x66,0x66,0xE6,0x00,0x00,0x00,0x00}, + /* 105: i */ + {0x00,0x00,0x18,0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00}, + /* 106: j */ + {0x00,0x00,0x06,0x06,0x00,0x0E,0x06,0x06,0x06,0x06,0x06,0x06,0x66,0x3C,0x00,0x00}, + /* 107: k */ + {0x00,0x00,0xE0,0x60,0x60,0x66,0x6C,0x78,0x78,0x6C,0x66,0xE6,0x00,0x00,0x00,0x00}, + /* 108: l */ + {0x00,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,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,0x7C,0x60,0x60,0xF0,0x00,0x00}, + /* 113: q */ + {0x00,0x00,0x00,0x00,0x00,0x76,0xCC,0xCC,0xCC,0xCC,0x7C,0x0C,0x0C,0x1E,0x00,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,0xC6,0xC6,0xC6,0xD6,0xD6,0xFE,0x6C,0x00,0x00,0x00,0x00}, + /* 120: x */ + {0x00,0x00,0x00,0x00,0x00,0xC6,0x6C,0x38,0x38,0x38,0x6C,0xC6,0x00,0x00,0x00,0x00}, + /* 121: y */ + {0x00,0x00,0x00,0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0x7E,0x06,0x0C,0xF8,0x00,0x00}, + /* 122: z */ + {0x00,0x00,0x00,0x00,0x00,0xFE,0xCC,0x18,0x30,0x60,0xC6,0xFE,0x00,0x00,0x00,0x00}, + /* 123: { */ + {0x00,0x00,0x0E,0x18,0x18,0x18,0x70,0x18,0x18,0x18,0x18,0x0E,0x00,0x00,0x00,0x00}, + /* 124: | */ + {0x00,0x00,0x18,0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00}, + /* 125: } */ + {0x00,0x00,0x70,0x18,0x18,0x18,0x0E,0x18,0x18,0x18,0x18,0x70,0x00,0x00,0x00,0x00}, + /* 126: ~ */ + {0x00,0x00,0x76,0xDC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, +}; + +#endif /* FONT8X16_H */ diff --git a/src/framebuffer.h b/src/framebuffer.h new file mode 100644 index 0000000..585e824 --- /dev/null +++ b/src/framebuffer.h @@ -0,0 +1,44 @@ +/** + * @file framebuffer.h + * @brief Framebuffer information from the bootloader. + * + * Stores the display mode and framebuffer address provided by GRUB + * via the multiboot2 framebuffer tag. The VGA driver uses this to + * decide between text-mode writes (0xB8000) and pixel rendering. + */ + +#ifndef FRAMEBUFFER_H +#define FRAMEBUFFER_H + +#include + +/** Framebuffer types (matches multiboot2 definitions). */ +#define FB_TYPE_INDEXED 0 +#define FB_TYPE_RGB 1 +#define FB_TYPE_EGA_TEXT 2 + +/** + * Framebuffer information structure. + * Populated during boot from the multiboot2 framebuffer tag. + */ +typedef struct { + uint32_t addr; /**< Physical address of the framebuffer. */ + uint32_t pitch; /**< Bytes per scanline. */ + uint32_t width; /**< Width in pixels (or columns for text). */ + uint32_t height; /**< Height in pixels (or rows for text). */ + uint8_t bpp; /**< Bits per pixel. */ + uint8_t type; /**< FB_TYPE_RGB, FB_TYPE_EGA_TEXT, etc. */ + + /* RGB field positions (only valid when type == FB_TYPE_RGB). */ + uint8_t red_pos; + uint8_t red_size; + uint8_t green_pos; + uint8_t green_size; + uint8_t blue_pos; + uint8_t blue_size; +} framebuffer_info_t; + +/** Global framebuffer info, filled by kernel_main. */ +extern framebuffer_info_t fb_info; + +#endif /* FRAMEBUFFER_H */ diff --git a/src/kernel.c b/src/kernel.c index 94334bf..bab47a4 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -1,6 +1,7 @@ #include #include #include +#include #include "gdt.h" #include "idt.h" #include "pic.h" @@ -17,6 +18,10 @@ #include "vfs.h" #include "initrd_fs.h" #include "keyboard.h" +#include "framebuffer.h" + +/* Global framebuffer info, parsed from multiboot2 tags. */ +framebuffer_info_t fb_info; void offset_print(const char *str) { @@ -60,8 +65,9 @@ void kernel_main(uint32_t magic, uint32_t addr) { init_pmm(addr); offset_print("PMM initialized\n"); - /* Scan Multiboot2 tags for the initrd module */ + /* Scan Multiboot2 tags for the initrd module and framebuffer info */ uint32_t initrd_start = 0, initrd_end = 0; + memset(&fb_info, 0, sizeof(fb_info)); { struct multiboot_tag *tag; for (tag = (struct multiboot_tag *)(addr + 8); @@ -75,7 +81,36 @@ void kernel_main(uint32_t magic, uint32_t addr) { print_hex(initrd_start); offset_print(" to "); print_hex(initrd_end); - break; /* Use first module */ + } + if (tag->type == MULTIBOOT_TAG_TYPE_FRAMEBUFFER) { + struct multiboot_tag_framebuffer *fbt = + (struct multiboot_tag_framebuffer *)tag; + fb_info.addr = (uint32_t)fbt->common.framebuffer_addr; + fb_info.pitch = fbt->common.framebuffer_pitch; + fb_info.width = fbt->common.framebuffer_width; + fb_info.height = fbt->common.framebuffer_height; + fb_info.bpp = fbt->common.framebuffer_bpp; + fb_info.type = fbt->common.framebuffer_type; + + if (fb_info.type == FB_TYPE_RGB) { + fb_info.red_pos = fbt->framebuffer_red_field_position; + fb_info.red_size = fbt->framebuffer_red_mask_size; + fb_info.green_pos = fbt->framebuffer_green_field_position; + fb_info.green_size = fbt->framebuffer_green_mask_size; + fb_info.blue_pos = fbt->framebuffer_blue_field_position; + fb_info.blue_size = fbt->framebuffer_blue_mask_size; + } + + offset_print("Framebuffer: type="); + print_hex(fb_info.type); + offset_print(" addr="); + print_hex(fb_info.addr); + offset_print(" "); + print_hex(fb_info.width); + offset_print(" x "); + print_hex(fb_info.height); + offset_print(" bpp="); + print_hex(fb_info.bpp); } } } @@ -83,6 +118,21 @@ void kernel_main(uint32_t magic, uint32_t addr) { init_paging(); offset_print("Paging initialized\n"); + /* If GRUB provided a graphical framebuffer, identity-map it so + * the VGA driver can write pixels to it after paging is enabled. */ + if (fb_info.addr != 0 && fb_info.type == FB_TYPE_RGB) { + uint32_t fb_size = fb_info.pitch * fb_info.height; + uint32_t fb_base = fb_info.addr & ~0xFFFu; /* page-align down */ + uint32_t fb_end = (fb_info.addr + fb_size + 0xFFF) & ~0xFFFu; + offset_print(" Mapping framebuffer "); + print_hex(fb_base); + offset_print(" to "); + print_hex(fb_end); + for (uint32_t pa = fb_base; pa < fb_end; pa += 4096) { + paging_map_page(pa, pa, PAGE_PRESENT | PAGE_WRITE | PAGE_WRITETHROUGH); + } + } + /* Test paging: allocate a page and write to it */ void *test_page = paging_alloc_page(); if (test_page) { diff --git a/src/vga.c b/src/vga.c index 81c7b8f..fc87e8e 100644 --- a/src/vga.c +++ b/src/vga.c @@ -1,10 +1,14 @@ /** * @file vga.c - * @brief VGA text-mode driver implementation. + * @brief Display driver supporting both VGA text mode and graphical framebuffer. * - * Drives the standard VGA text-mode framebuffer at 0xB8000. The buffer - * is an array of 80×25 16-bit values, where the low byte is the ASCII - * character and the high byte encodes foreground and background color. + * Supports two modes depending on what GRUB provides: + * - EGA text mode: writes character+attribute pairs to the text buffer + * - Graphical (RGB) framebuffer: renders an embedded 8x16 bitmap font + * to the pixel framebuffer provided by GRUB + * + * The mode is detected at init time from the global fb_info structure, + * which kernel_main populates from the multiboot2 framebuffer tag. * * This driver registers itself via the REGISTER_DRIVER macro and is * automatically discovered during boot. @@ -14,122 +18,295 @@ #include "driver.h" #include "port_io.h" #include "pmm.h" +#include "framebuffer.h" +#include "font8x16.h" +#include -/** Base address of the VGA text-mode framebuffer. */ -#define VGA_BUFFER 0xB8000 +/* Debug helpers defined in kernel.c */ +extern void offset_print(const char *str); +extern void print_hex(uint32_t val); -/** Pointer to the VGA framebuffer, treated as an array of uint16_t. */ -static uint16_t *vga_buffer = (uint16_t *)VGA_BUFFER; +/* ================================================================ + * Common state + * ================================================================ */ -/** Current cursor row (0-based). */ -static uint8_t cursor_row = 0; +/** Current text cursor position. */ +static uint32_t cursor_row = 0; +static uint32_t cursor_col = 0; -/** Current cursor column (0-based). */ -static uint8_t cursor_col = 0; +/** Columns and rows of the text grid. */ +static uint32_t text_cols = 80; +static uint32_t text_rows = 25; -/** Current text attribute byte (foreground | background << 4). */ -static uint8_t text_attr = 0; +/** Current color attribute (foreground | background << 4). */ +static uint8_t text_attr = 0x07; + +/** Display mode: 0 = text, 1 = pixel. */ +static int display_mode = 0; + +/* ================================================================ + * Text mode (EGA) internals + * ================================================================ */ + +/** VGA text-mode framebuffer default base. */ +#define VGA_TEXT_BUFFER 0xB8000 +static uint16_t *text_buffer = (uint16_t *)VGA_TEXT_BUFFER; -/** - * Create a VGA entry (character + attribute) for the framebuffer. - * - * @param c ASCII character. - * @param attr Color attribute byte. - * @return 16-bit VGA entry. - */ static inline uint16_t vga_entry(char c, uint8_t attr) { return (uint16_t)c | ((uint16_t)attr << 8); } -/** - * Create a color attribute byte from foreground and background colors. - * - * @param fg Foreground color (0–15). - * @param bg Background color (0–15). - * @return Attribute byte. - */ -static inline uint8_t vga_color(vga_color_t fg, vga_color_t bg) { - return (uint8_t)fg | ((uint8_t)bg << 4); -} - -/** - * Update the hardware cursor position via VGA I/O ports. - */ -static void update_cursor(void) { - uint16_t pos = cursor_row * VGA_WIDTH + cursor_col; - +static void text_update_cursor(void) { + uint16_t pos = (uint16_t)(cursor_row * text_cols + cursor_col); outb(0x3D4, 0x0F); outb(0x3D5, (uint8_t)(pos & 0xFF)); outb(0x3D4, 0x0E); outb(0x3D5, (uint8_t)((pos >> 8) & 0xFF)); } -/** - * Scroll the screen up by one line. - * - * The top line is discarded, all other lines move up, and the bottom - * line is filled with spaces. - */ -static void scroll(void) { - /* Move all lines up by one */ - for (int i = 0; i < (VGA_HEIGHT - 1) * VGA_WIDTH; i++) { - vga_buffer[i] = vga_buffer[i + VGA_WIDTH]; +static void text_scroll(void) { + for (uint32_t i = 0; i < (text_rows - 1) * text_cols; i++) { + text_buffer[i] = text_buffer[i + text_cols]; } - - /* Clear the last line */ uint16_t blank = vga_entry(' ', text_attr); - for (int i = (VGA_HEIGHT - 1) * VGA_WIDTH; i < VGA_HEIGHT * VGA_WIDTH; i++) { - vga_buffer[i] = blank; + for (uint32_t i = (text_rows - 1) * text_cols; i < text_rows * text_cols; i++) { + text_buffer[i] = blank; } - - cursor_row = VGA_HEIGHT - 1; + cursor_row = text_rows - 1; } -void vga_clear(void) { +static void text_clear(void) { uint16_t blank = vga_entry(' ', text_attr); - for (int i = 0; i < VGA_WIDTH * VGA_HEIGHT; i++) { - vga_buffer[i] = blank; + for (uint32_t i = 0; i < text_cols * text_rows; i++) { + text_buffer[i] = blank; } cursor_row = 0; cursor_col = 0; - update_cursor(); + text_update_cursor(); } -void vga_set_color(vga_color_t fg, vga_color_t bg) { - text_attr = vga_color(fg, bg); -} - -void vga_putchar(char c) { +static void text_putchar(char c) { if (c == '\n') { cursor_col = 0; cursor_row++; } else if (c == '\r') { cursor_col = 0; } else if (c == '\t') { - cursor_col = (cursor_col + 8) & ~7; - if (cursor_col >= VGA_WIDTH) { + cursor_col = (cursor_col + 8) & ~7u; + if (cursor_col >= text_cols) { cursor_col = 0; cursor_row++; } } else if (c == '\b') { if (cursor_col > 0) { cursor_col--; - vga_buffer[cursor_row * VGA_WIDTH + cursor_col] = vga_entry(' ', text_attr); + text_buffer[cursor_row * text_cols + cursor_col] = vga_entry(' ', text_attr); } } else { - vga_buffer[cursor_row * VGA_WIDTH + cursor_col] = vga_entry(c, text_attr); + text_buffer[cursor_row * text_cols + cursor_col] = vga_entry(c, text_attr); cursor_col++; - if (cursor_col >= VGA_WIDTH) { + if (cursor_col >= text_cols) { cursor_col = 0; cursor_row++; } } + if (cursor_row >= text_rows) { + text_scroll(); + } + text_update_cursor(); +} - if (cursor_row >= VGA_HEIGHT) { - scroll(); +/* ================================================================ + * Pixel mode (graphical framebuffer) internals + * ================================================================ */ + +/** Pointer to the pixel framebuffer. */ +static uint8_t *pixel_fb = (uint8_t *)0; + +/** Framebuffer parameters. */ +static uint32_t fb_pitch = 0; +static uint32_t fb_width = 0; +static uint32_t fb_height = 0; +static uint32_t fb_bpp = 0; + +/** RGB field info. */ +static uint8_t fb_red_pos = 16, fb_red_size = 8; +static uint8_t fb_green_pos = 8, fb_green_size = 8; +static uint8_t fb_blue_pos = 0, fb_blue_size = 8; + +/** + * Pack an RGB color into the framebuffer's native pixel format. + */ +static inline uint32_t pack_color(uint8_t r, uint8_t g, uint8_t b) { + (void)fb_red_size; (void)fb_green_size; (void)fb_blue_size; + return ((uint32_t)r << fb_red_pos) | + ((uint32_t)g << fb_green_pos) | + ((uint32_t)b << fb_blue_pos); +} + +/** + * Set a single pixel in the framebuffer. + */ +static inline void pixel_set(uint32_t x, uint32_t y, uint32_t color) { + if (x >= fb_width || y >= fb_height) return; + uint32_t offset = y * fb_pitch + x * (fb_bpp / 8); + uint32_t bytes = fb_bpp / 8; + + if (bytes == 4) { + *(volatile uint32_t *)(pixel_fb + offset) = color; + } else if (bytes == 3) { + pixel_fb[offset] = (uint8_t)(color & 0xFF); + pixel_fb[offset + 1] = (uint8_t)((color >> 8) & 0xFF); + pixel_fb[offset + 2] = (uint8_t)((color >> 16) & 0xFF); + } else if (bytes == 2) { + *(volatile uint16_t *)(pixel_fb + offset) = (uint16_t)color; + } +} + +/** + * VGA color index to 24-bit RGB mapping. + */ +static const uint32_t vga_palette[16] = { + 0x000000, /* 0 black */ + 0x0000AA, /* 1 blue */ + 0x00AA00, /* 2 green */ + 0x00AAAA, /* 3 cyan */ + 0xAA0000, /* 4 red */ + 0xAA00AA, /* 5 magenta */ + 0xAA5500, /* 6 brown */ + 0xAAAAAA, /* 7 light grey */ + 0x555555, /* 8 dark grey */ + 0x5555FF, /* 9 light blue */ + 0x55FF55, /* 10 light green */ + 0x55FFFF, /* 11 light cyan */ + 0xFF5555, /* 12 light red */ + 0xFF55FF, /* 13 light magenta */ + 0xFFFF55, /* 14 yellow */ + 0xFFFFFF, /* 15 white */ +}; + +/** + * Get packed foreground/background colors from text_attr. + */ +static void attr_to_colors(uint32_t *fg_out, uint32_t *bg_out) { + uint8_t fg_idx = text_attr & 0x0F; + uint8_t bg_idx = (text_attr >> 4) & 0x0F; + uint32_t fg_rgb = vga_palette[fg_idx]; + uint32_t bg_rgb = vga_palette[bg_idx]; + *fg_out = pack_color((fg_rgb >> 16) & 0xFF, (fg_rgb >> 8) & 0xFF, fg_rgb & 0xFF); + *bg_out = pack_color((bg_rgb >> 16) & 0xFF, (bg_rgb >> 8) & 0xFF, bg_rgb & 0xFF); +} + +/** + * Render a single glyph at character grid position (col, row). + */ +static void pixel_render_char(uint32_t col, uint32_t row, char c) { + uint32_t fg, bg; + attr_to_colors(&fg, &bg); + + const uint8_t *glyph; + if (c >= FONT_FIRST && c <= FONT_LAST) { + glyph = font8x16_data[c - FONT_FIRST]; + } else { + glyph = 0; /* NULL = solid block for unknown chars */ } - update_cursor(); + uint32_t px = col * FONT_WIDTH; + uint32_t py = row * FONT_HEIGHT; + + for (uint32_t y = 0; y < FONT_HEIGHT; y++) { + uint8_t bits = glyph ? glyph[y] : 0xFF; + for (uint32_t x = 0; x < FONT_WIDTH; x++) { + uint32_t color = (bits & (0x80 >> x)) ? fg : bg; + pixel_set(px + x, py + y, color); + } + } +} + +/** + * Scroll the pixel framebuffer up by one text row (FONT_HEIGHT pixels). + */ +static void pixel_scroll(void) { + uint32_t row_bytes = FONT_HEIGHT * fb_pitch; + uint32_t total_text_bytes = text_rows * row_bytes; + + /* Move all rows up by one */ + memcpy(pixel_fb, pixel_fb + row_bytes, total_text_bytes - row_bytes); + + /* Clear the last text row */ + uint32_t dummy, bg; + attr_to_colors(&dummy, &bg); + uint32_t last_row_y = (text_rows - 1) * FONT_HEIGHT; + for (uint32_t y = last_row_y; y < last_row_y + FONT_HEIGHT; y++) { + for (uint32_t x = 0; x < text_cols * FONT_WIDTH; x++) { + pixel_set(x, y, bg); + } + } + cursor_row = text_rows - 1; +} + +static void pixel_clear(void) { + uint32_t dummy, bg; + attr_to_colors(&dummy, &bg); + for (uint32_t y = 0; y < fb_height; y++) { + for (uint32_t x = 0; x < fb_width; x++) { + pixel_set(x, y, bg); + } + } + cursor_row = 0; + cursor_col = 0; +} + +static void pixel_putchar(char c) { + if (c == '\n') { + cursor_col = 0; + cursor_row++; + } else if (c == '\r') { + cursor_col = 0; + } else if (c == '\t') { + cursor_col = (cursor_col + 8) & ~7u; + if (cursor_col >= text_cols) { + cursor_col = 0; + cursor_row++; + } + } else if (c == '\b') { + if (cursor_col > 0) { + cursor_col--; + pixel_render_char(cursor_col, cursor_row, ' '); + } + } else { + pixel_render_char(cursor_col, cursor_row, c); + cursor_col++; + if (cursor_col >= text_cols) { + cursor_col = 0; + cursor_row++; + } + } + if (cursor_row >= text_rows) { + pixel_scroll(); + } +} + +/* ================================================================ + * Public interface + * ================================================================ */ + +void vga_clear(void) { + if (display_mode == 0) + text_clear(); + else + pixel_clear(); +} + +void vga_set_color(vga_color_t fg, vga_color_t bg) { + text_attr = (uint8_t)fg | ((uint8_t)bg << 4); +} + +void vga_putchar(char c) { + if (display_mode == 0) + text_putchar(c); + else + pixel_putchar(c); } void vga_puts(const char *str) { @@ -153,21 +330,19 @@ void vga_put_dec(uint32_t val) { vga_putchar('0'); return; } - char buf[12]; int pos = 0; while (val > 0) { buf[pos++] = '0' + (val % 10); val /= 10; } - /* Print in reverse */ while (pos > 0) { vga_putchar(buf[--pos]); } } void vga_show_mem_stats(void) { - uint32_t mem_kb = pmm_get_memory_size() + 1024; /* total including lower */ + uint32_t mem_kb = pmm_get_memory_size() + 1024; vga_set_color(VGA_LIGHT_CYAN, VGA_BLACK); vga_puts("=== ClaudeOS Memory Statistics ===\n"); @@ -199,18 +374,75 @@ void vga_show_mem_stats(void) { vga_set_color(VGA_LIGHT_GREY, VGA_BLACK); } -/* --- Driver registration --- */ +/* ================================================================ + * Driver registration + * ================================================================ */ -/** - * VGA probe: always succeeds since VGA text mode is always available - * on the target platform (i386). - */ static driver_probe_result_t vga_probe(void) { return DRIVER_PROBE_OK; } int vga_init(void) { - text_attr = vga_color(VGA_LIGHT_GREY, VGA_BLACK); + text_attr = (uint8_t)VGA_LIGHT_GREY | ((uint8_t)VGA_BLACK << 4); + + if (fb_info.type == FB_TYPE_EGA_TEXT || fb_info.addr == 0) { + /* Text mode (or no framebuffer tag — assume legacy text mode) */ + display_mode = 0; + text_cols = 80; + text_rows = 25; + + if (fb_info.addr != 0) { + text_buffer = (uint16_t *)(uint32_t)fb_info.addr; + text_cols = fb_info.width; + text_rows = fb_info.height; + } + + offset_print(" VGA: text mode "); + print_hex(text_cols); + offset_print(" VGA: x "); + print_hex(text_rows); + } else if (fb_info.type == FB_TYPE_RGB) { + /* Graphical framebuffer — render with bitmap font */ + display_mode = 1; + pixel_fb = (uint8_t *)(uint32_t)fb_info.addr; + fb_pitch = fb_info.pitch; + fb_width = fb_info.width; + fb_height = fb_info.height; + fb_bpp = fb_info.bpp; + + fb_red_pos = fb_info.red_pos; + fb_red_size = fb_info.red_size; + fb_green_pos = fb_info.green_pos; + fb_green_size = fb_info.green_size; + fb_blue_pos = fb_info.blue_pos; + fb_blue_size = fb_info.blue_size; + + /* Calculate text grid from pixel dimensions */ + text_cols = fb_width / FONT_WIDTH; + text_rows = fb_height / FONT_HEIGHT; + if (text_cols == 0) text_cols = 1; + if (text_rows == 0) text_rows = 1; + + offset_print(" VGA: pixel mode "); + print_hex(fb_width); + offset_print(" VGA: x "); + print_hex(fb_height); + offset_print(" VGA: bpp="); + print_hex(fb_bpp); + offset_print(" VGA: text grid "); + print_hex(text_cols); + offset_print(" VGA: x "); + print_hex(text_rows); + offset_print(" VGA: addr="); + print_hex((uint32_t)pixel_fb); + } else { + /* Indexed or unknown — fall back to text mode */ + display_mode = 0; + text_cols = 80; + text_rows = 25; + offset_print(" VGA: unknown fb type, assuming text mode\n"); + } + vga_clear(); vga_set_color(VGA_LIGHT_GREEN, VGA_BLACK);