diff --git a/boredos.iso b/boredos.iso index 4ac2914..dfaa215 100644 Binary files a/boredos.iso and b/boredos.iso differ diff --git a/build/explorer.o b/build/explorer.o index bdd6006..a1ec968 100644 Binary files a/build/explorer.o and b/build/explorer.o differ diff --git a/build/graphics.o b/build/graphics.o index 9368b77..b5f4d26 100644 Binary files a/build/graphics.o and b/build/graphics.o differ diff --git a/build/main.o b/build/main.o index 257cc76..5f3a988 100644 Binary files a/build/main.o and b/build/main.o differ diff --git a/src/kernel/graphics.c b/src/kernel/graphics.c index e14124e..83446f7 100644 --- a/src/kernel/graphics.c +++ b/src/kernel/graphics.c @@ -12,6 +12,8 @@ static uint32_t g_bg_color = 0xFF696969; extern void serial_write(const char *str); +static int g_color_mode = 0; + #define PATTERN_SIZE 128 static uint32_t g_bg_pattern[PATTERN_SIZE * PATTERN_SIZE]; static bool g_use_pattern = false; @@ -54,6 +56,30 @@ void graphics_init_fonts(void) { } } +void graphics_update_resolution(int width, int height, int bpp, void* fb_addr, int color_mode) { + if (!g_fb) return; + + uint64_t rflags; + asm volatile("pushfq; pop %0; cli" : "=r"(rflags)); + + g_fb->width = width; + g_fb->height = height; + g_fb->bpp = bpp; + g_fb->pitch = width * (bpp / 8); + g_fb->address = fb_addr; + g_color_mode = color_mode; + + // Clear back buffer + for (int i = 0; i < MAX_FB_WIDTH * MAX_FB_HEIGHT; i++) { + g_back_buffer[i] = 0; + } + + // Clear dirty rect + g_dirty.active = false; + + asm volatile("push %0; popfq" : : "r"(rflags)); +} + void graphics_set_font(const char *path) { ttf_font_t *new_font = font_manager_load(path, 15.0f); if (new_font) { @@ -576,9 +602,67 @@ void graphics_flip_buffer(void) { for (int i = 0; i < h; i++) { int curr_y = y + i; uint32_t *src_row = &g_back_buffer[curr_y * g_fb->width + x]; - uint32_t *dst_row = (uint32_t *)((uint8_t *)g_fb->address + curr_y * g_fb->pitch) + x; - for (int j = 0; j < w; j++) { - dst_row[j] = src_row[j]; + + if (g_fb->bpp == 32) { + uint32_t *dst_row = (uint32_t *)((uint8_t *)g_fb->address + curr_y * g_fb->pitch) + x; + for (int j = 0; j < w; j++) { + dst_row[j] = src_row[j]; + } + } else if (g_fb->bpp == 16) { + uint16_t *dst_row = (uint16_t *)((uint8_t *)g_fb->address + curr_y * g_fb->pitch) + x; + for (int j = 0; j < w; j++) { + uint32_t c = src_row[j]; + uint16_t r = ((c >> 16) & 0xFF) >> 3; + uint16_t g = ((c >> 8) & 0xFF) >> 2; + uint16_t b = (c & 0xFF) >> 3; + dst_row[j] = (r << 11) | (g << 5) | b; + } + } else if (g_fb->bpp == 8) { + uint8_t *dst_row = (uint8_t *)((uint8_t *)g_fb->address + curr_y * g_fb->pitch) + x; + if (g_color_mode == 1) { // Grayscale + for (int j = 0; j < w; j++) { + uint32_t c = src_row[j]; + uint8_t r = (c >> 16) & 0xFF; + uint8_t g = (c >> 8) & 0xFF; + uint8_t b = c & 0xFF; + dst_row[j] = (uint8_t)((r * 77 + g * 150 + b * 29) >> 8); + } + } else if (g_color_mode == 2) { // Monochrome + static const uint8_t bayer2[2][2] = { + { 0, 128 }, + {192, 64 } + }; + for (int j = 0; j < w; j++) { + uint32_t c = src_row[j]; + uint8_t r = (c >> 16) & 0xFF; + uint8_t g = (c >> 8) & 0xFF; + uint8_t b = c & 0xFF; + + int gray = (r * 77 + g * 150 + b * 29) >> 8; + + // Boost contrast by 2x to separate the dark UI colors: + // Background (~30) -> 60 + // Panel (~40) -> 80 + // With thresholds {0, 64, 128, 192}: + // BG > 0 (1/4 white), Panel > 64 (2/4 white - checkerboard) + // Text (~170) -> 255 (solid white) + gray = gray * 2; + if (gray > 255) gray = 255; + + int sx = x + j; + uint8_t threshold = bayer2[curr_y & 1][sx & 1]; + + dst_row[j] = (gray > threshold) ? 255 : 0; + } + } else { // 256 Colors (Standard) + for (int j = 0; j < w; j++) { + uint32_t c = src_row[j]; + uint8_t r = ((c >> 16) & 0xFF) >> 5; + uint8_t g = ((c >> 8) & 0xFF) >> 5; + uint8_t b = (c & 0xFF) >> 6; + dst_row[j] = (r << 5) | (g << 2) | b; + } + } } } } diff --git a/src/kernel/graphics.h b/src/kernel/graphics.h index 32fd37d..5953a11 100644 --- a/src/kernel/graphics.h +++ b/src/kernel/graphics.h @@ -39,6 +39,7 @@ void draw_boredos_logo(int x, int y, int scale); // Get screen dimensions int get_screen_width(void); int get_screen_height(void); +void graphics_update_resolution(int width, int height, int bpp, void* fb_addr, int color_mode); // Dirty rectangle management void graphics_mark_dirty(int x, int y, int w, int h); diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index f35d710..f66ebe6 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -1156,10 +1156,34 @@ static uint64_t syscall_handler_inner(registers_t *regs) { extern void mem_memcpy(void *dest, const void *src, size_t len); mem_memcpy(user_buf, model, 49); return 0; + } else if (cmd == 47) { // SYSTEM_CMD_SET_RESOLUTION + uint16_t req_w = (uint16_t)arg2; + uint16_t req_h = (uint16_t)arg3; + uint16_t req_bpp = (uint16_t)arg4; + int req_color_mode = (int)arg5; + + extern bool vga_set_mode(uint16_t width, uint16_t height, uint16_t bpp, void **out_framebuffer); + extern void graphics_update_resolution(int width, int height, int bpp, void* fb_addr, int color_mode); + extern void wm_refresh(void); + extern void vga_set_palette_grayscale(void); + extern void vga_set_palette_standard(void); + + void *new_fb = NULL; + if (vga_set_mode(req_w, req_h, req_bpp, &new_fb)) { + if (req_color_mode == 1 || req_color_mode == 2) { + vga_set_palette_grayscale(); + } else if (req_bpp <= 8) { + vga_set_palette_standard(); + } + graphics_update_resolution(req_w, req_h, req_bpp, new_fb, req_color_mode); + wm_refresh(); + return 0; + } + return -1; } return -1; } - + return 0; } diff --git a/src/kernel/syscall.h b/src/kernel/syscall.h index df24444..4f7f83b 100644 --- a/src/kernel/syscall.h +++ b/src/kernel/syscall.h @@ -45,6 +45,7 @@ typedef struct registers_t registers_t; #define SYSTEM_CMD_PROCESS_LIST 44 #define SYSTEM_CMD_GET_CPU_MODEL 45 #define SYSTEM_CMD_SLEEP 46 +#define SYSTEM_CMD_SET_RESOLUTION 47 void syscall_init(void); uint64_t syscall_handler_c(registers_t *regs); diff --git a/src/kernel/userland/libc/libui.c b/src/kernel/userland/libc/libui.c index 580b32a..cd1dd3b 100644 --- a/src/kernel/userland/libc/libui.c +++ b/src/kernel/userland/libc/libui.c @@ -9,6 +9,7 @@ extern uint64_t syscall5(uint64_t sys_num, uint64_t arg1, uint64_t arg2, uint64_ // sys_gui uses syscall #3 #define SYS_GUI 3 +#define GUI_CMD_GET_SCREEN_SIZE 17 ui_window_t ui_window_create(const char *title, int x, int y, int w, int h) { uint64_t params[4] = { (uint64_t)x, (uint64_t)y, (uint64_t)w, (uint64_t)h }; @@ -58,6 +59,10 @@ uint32_t ui_get_font_height(void) { return (uint32_t)syscall3(SYS_GUI, GUI_CMD_GET_FONT_HEIGHT, 0, 0); } +void ui_get_screen_size(uint64_t *out_w, uint64_t *out_h) { + syscall3(SYS_GUI, GUI_CMD_GET_SCREEN_SIZE, (uint64_t)out_w, (uint64_t)out_h); +} + void ui_draw_string_scaled(ui_window_t win, int x, int y, const char *str, uint32_t color, float scale) { uint64_t coords = ((uint64_t)x & 0xFFFFFFFF) | ((uint64_t)y << 32); // Pack color into lower 32, scale (as uint32_t representation) into upper 32 diff --git a/src/kernel/userland/libc/libui.h b/src/kernel/userland/libc/libui.h index 86e79cd..dea0d2d 100644 --- a/src/kernel/userland/libc/libui.h +++ b/src/kernel/userland/libc/libui.h @@ -58,6 +58,7 @@ void ui_mark_dirty(ui_window_t win, int x, int y, int w, int h); void ui_draw_image(ui_window_t win, int x, int y, int w, int h, uint32_t *image_data); uint32_t ui_get_string_width(const char *str); uint32_t ui_get_font_height(void); +void ui_get_screen_size(uint64_t *out_w, uint64_t *out_h); void ui_draw_string_bitmap(ui_window_t win, int x, int y, const char *str, uint32_t color); void ui_draw_string_scaled(ui_window_t win, int x, int y, const char *str, uint32_t color, float scale); diff --git a/src/kernel/userland/settings.c b/src/kernel/userland/settings.c index b894cff..301b07c 100644 --- a/src/kernel/userland/settings.c +++ b/src/kernel/userland/settings.c @@ -29,14 +29,47 @@ #define VIEW_DESKTOP 3 #define VIEW_MOUSE 4 #define VIEW_FONTS 5 +#define VIEW_DISPLAY 6 + +static int disp_sel_res = 2; +static int disp_sel_color = 0; static int current_view = VIEW_MAIN; static char rgb_r[4] = ""; static char rgb_g[4] = ""; static char rgb_b[4] = ""; +static char custom_res_w[6] = ""; +static char custom_res_h[6] = ""; static int focused_field = -1; static int input_cursor = 0; +static int dyn_res_w[3]; +static int dyn_res_h[3]; +static char dyn_res_str[3][32]; + +static void init_dynamic_resolutions(void) { + uint64_t phys_w = 1920, phys_h = 1080; + ui_get_screen_size(&phys_w, &phys_h); + + dyn_res_w[2] = (int)phys_w; dyn_res_h[2] = (int)phys_h; + dyn_res_w[1] = (int)((phys_w * 3) / 4); dyn_res_h[1] = (int)((phys_h * 3) / 4); + dyn_res_w[0] = (int)(phys_w / 2); dyn_res_h[0] = (int)(phys_h / 2); + + for (int i = 0; i < 2; i++) { + dyn_res_w[i] &= ~1; + dyn_res_h[i] &= ~1; + } + + for (int i = 0; i < 3; i++) { + char bw[16], bh[16]; + itoa(dyn_res_w[i], bw); + itoa(dyn_res_h[i], bh); + strcpy(dyn_res_str[i], bw); + strcat(dyn_res_str[i], "x"); + strcat(dyn_res_str[i], bh); + } +} + static char net_status[64] = ""; // Pattern buffers (128x128) @@ -248,6 +281,17 @@ static void control_panel_paint_main(ui_window_t win) { ui_draw_string(win, offset_x + 14, offset_y + item_y + 10, "Aa", 0xFF6A9EF5); ui_draw_string(win, offset_x + 60, offset_y + item_y + 15, "Fonts", COLOR_DARK_TEXT); ui_draw_string(win, offset_x + 60, offset_y + item_y + 35, "Choose system font", COLOR_DKGRAY); + + // Display + item_y += item_h + item_spacing; + ui_draw_rounded_rect_filled(win, offset_x, offset_y + item_y, win_w - 16, item_h, 8, COLOR_DARK_PANEL); + // Monitor icon + ui_draw_rect(win, offset_x + 14, offset_y + item_y + 12, 32, 22, 0xFF4A90E2); + ui_draw_rect(win, offset_x + 16, offset_y + item_y + 14, 28, 18, 0xFF87CEEB); + ui_draw_rect(win, offset_x + 26, offset_y + item_y + 34, 8, 4, 0xFFB0B0B0); + ui_draw_rect(win, offset_x + 22, offset_y + item_y + 38, 16, 2, 0xFFB0B0B0); + ui_draw_string(win, offset_x + 60, offset_y + item_y + 15, "Display", COLOR_DARK_TEXT); + ui_draw_string(win, offset_x + 60, offset_y + item_y + 35, "Screen resolution & color", COLOR_DKGRAY); } static void control_panel_paint_wallpaper(ui_window_t win) { @@ -507,6 +551,73 @@ static void control_panel_paint_fonts(ui_window_t win) { } } +static void control_panel_paint_display(ui_window_t win) { + int offset_x = 8; + int offset_y = 6; + int right_x = offset_x + 160; + + ui_draw_rounded_rect_filled(win, offset_x, offset_y + 5, 80, 25, 6, COLOR_DARK_PANEL); + ui_draw_string(win, offset_x + 10, offset_y + 13, "< Back", COLOR_DARK_TEXT); + ui_draw_string(win, offset_x, offset_y + 40, "Resolution:", COLOR_DARK_TEXT); + + int btn_y = offset_y + 60; + ui_draw_rounded_rect_filled(win, offset_x, btn_y, 140, 30, 6, (disp_sel_res == 0) ? 0xFF3D5A80 : COLOR_DARK_PANEL); + ui_draw_string(win, offset_x + 30, btn_y + 10, "640x480", COLOR_DARK_TEXT); + + btn_y += 35; + ui_draw_rounded_rect_filled(win, offset_x, btn_y, 140, 30, 6, (disp_sel_res == 1) ? 0xFF3D5A80 : COLOR_DARK_PANEL); + ui_draw_string(win, offset_x + 30, btn_y + 10, "800x600", COLOR_DARK_TEXT); + + btn_y += 35; + ui_draw_rounded_rect_filled(win, offset_x, btn_y, 140, 30, 6, (disp_sel_res == 2) ? 0xFF3D5A80 : COLOR_DARK_PANEL); + ui_draw_string(win, offset_x + 30, btn_y + 10, dyn_res_str[0], COLOR_DARK_TEXT); + + btn_y += 35; + ui_draw_rounded_rect_filled(win, offset_x, btn_y, 140, 30, 6, (disp_sel_res == 3) ? 0xFF3D5A80 : COLOR_DARK_PANEL); + ui_draw_string(win, offset_x + 30, btn_y + 10, dyn_res_str[1], COLOR_DARK_TEXT); + + btn_y += 35; + ui_draw_rounded_rect_filled(win, offset_x, btn_y, 140, 30, 6, (disp_sel_res == 4) ? 0xFF3D5A80 : COLOR_DARK_PANEL); + ui_draw_string(win, offset_x + 30, btn_y + 10, dyn_res_str[2], COLOR_DARK_TEXT); + + btn_y += 35; + ui_draw_rounded_rect_filled(win, offset_x, btn_y, 140, 30, 6, (disp_sel_res == 5) ? 0xFF3D5A80 : COLOR_DARK_PANEL); + ui_draw_string(win, offset_x + 30, btn_y + 10, "Custom:", COLOR_DARK_TEXT); + + btn_y += 35; + ui_draw_rounded_rect_filled(win, offset_x, btn_y, 60, 25, 4, (focused_field == 3) ? 0xFF4A90E2 : COLOR_DARK_PANEL); + ui_draw_string(win, offset_x + 5, btn_y + 7, custom_res_w[0] ? custom_res_w : "W", (custom_res_w[0] || focused_field == 3) ? 0xFFFFFFFF : 0xFF888888); + ui_draw_string(win, offset_x + 65, btn_y + 7, "x", COLOR_DARK_TEXT); + ui_draw_rounded_rect_filled(win, offset_x + 80, btn_y, 60, 25, 4, (focused_field == 4) ? 0xFF4A90E2 : COLOR_DARK_PANEL); + ui_draw_string(win, offset_x + 85, btn_y + 7, custom_res_h[0] ? custom_res_h : "H", (custom_res_h[0] || focused_field == 4) ? 0xFFFFFFFF : 0xFF888888); + + btn_y = offset_y + 60; + ui_draw_string(win, right_x, offset_y + 40, "Color Depth:", COLOR_DARK_TEXT); + + ui_draw_rounded_rect_filled(win, right_x, btn_y, 140, 30, 6, (disp_sel_color == 0) ? 0xFF3D5A80 : COLOR_DARK_PANEL); + ui_draw_string(win, right_x + 40, btn_y + 10, "32-bit", COLOR_DARK_TEXT); + + btn_y += 35; + ui_draw_rounded_rect_filled(win, right_x, btn_y, 140, 30, 6, (disp_sel_color == 1) ? 0xFF3D5A80 : COLOR_DARK_PANEL); + ui_draw_string(win, right_x + 40, btn_y + 10, "16-bit", COLOR_DARK_TEXT); + + btn_y += 35; + ui_draw_rounded_rect_filled(win, right_x, btn_y, 140, 30, 6, (disp_sel_color == 2) ? 0xFF3D5A80 : COLOR_DARK_PANEL); + ui_draw_string(win, right_x + 25, btn_y + 10, "256 Colors", COLOR_DARK_TEXT); + + btn_y += 35; + ui_draw_rounded_rect_filled(win, right_x, btn_y, 140, 30, 6, (disp_sel_color == 3) ? 0xFF3D5A80 : COLOR_DARK_PANEL); + ui_draw_string(win, right_x + 30, btn_y + 10, "Grayscale", COLOR_DARK_TEXT); + + btn_y += 35; + ui_draw_rounded_rect_filled(win, right_x, btn_y, 140, 30, 6, (disp_sel_color == 4) ? 0xFF3D5A80 : COLOR_DARK_PANEL); + ui_draw_string(win, right_x + 25, btn_y + 10, "Monochrome", COLOR_DARK_TEXT); + + btn_y = offset_y + 320; + ui_draw_rounded_rect_filled(win, offset_x, btn_y, 300, 35, 6, 0xFF4A90E2); + ui_draw_string(win, offset_x + 125, btn_y + 12, "Apply", 0xFFFFFFFF); +} + static void control_panel_paint(ui_window_t win) { // Fill background ui_draw_rect(win, 0, 0, 350, 500, COLOR_DARK_BG); @@ -523,6 +634,8 @@ static void control_panel_paint(ui_window_t win) { control_panel_paint_mouse(win); } else if (current_view == VIEW_FONTS) { control_panel_paint_fonts(win); + } else if (current_view == VIEW_DISPLAY) { + control_panel_paint_display(win); } } @@ -544,6 +657,7 @@ static void fetch_kernel_state(void) { desktop_max_cols = sys_system(7, 4, 0, 0, 0); mouse_speed = sys_system(8 /*GET_MOUSE_SPEED*/, 0, 0, 0, 0); + init_dynamic_resolutions(); load_wallpapers(); } @@ -579,6 +693,10 @@ static void control_panel_handle_click(int x, int y) { current_view = VIEW_FONTS; if (font_count == 0) load_fonts(); } + item_y += item_h + item_spacing; + if (x >= offset_x && x < win_w - 8 && y >= item_y && y < item_y + item_h) { + current_view = VIEW_DISPLAY; + } } else if (current_view == VIEW_WALLPAPER) { int offset_x = 8; int offset_y = 6; @@ -762,6 +880,73 @@ static void control_panel_handle_click(int x, int y) { } item_y += 40; } + } else if (current_view == VIEW_DISPLAY) { + int offset_x = 8; + int offset_y = 6; + int right_x = offset_x + 160; + + if (x >= offset_x && x < offset_x + 80 && y >= offset_y + 5 && y < offset_y + 30) { + current_view = VIEW_MAIN; + return; + } + + int btn_y = offset_y + 60; + if (x >= offset_x && x < offset_x + 140 && y >= btn_y && y < btn_y + 30) disp_sel_res = 0; + if (x >= right_x && x < right_x + 140 && y >= btn_y && y < btn_y + 30) disp_sel_color = 0; + + btn_y += 35; + if (x >= offset_x && x < offset_x + 140 && y >= btn_y && y < btn_y + 30) disp_sel_res = 1; + if (x >= right_x && x < right_x + 140 && y >= btn_y && y < btn_y + 30) disp_sel_color = 1; + + btn_y += 35; + if (x >= offset_x && x < offset_x + 140 && y >= btn_y && y < btn_y + 30) disp_sel_res = 2; + if (x >= right_x && x < right_x + 140 && y >= btn_y && y < btn_y + 30) disp_sel_color = 2; + + btn_y += 35; + if (x >= offset_x && x < offset_x + 140 && y >= btn_y && y < btn_y + 30) disp_sel_res = 3; + if (x >= right_x && x < right_x + 140 && y >= btn_y && y < btn_y + 30) disp_sel_color = 3; + + btn_y += 35; + if (x >= offset_x && x < offset_x + 140 && y >= btn_y && y < btn_y + 30) disp_sel_res = 4; + if (x >= right_x && x < right_x + 140 && y >= btn_y && y < btn_y + 30) disp_sel_color = 4; + + btn_y += 35; + if (x >= offset_x && x < offset_x + 140 && y >= btn_y && y < btn_y + 30) disp_sel_res = 5; + + // Custom Inputs + btn_y += 35; + if (x >= offset_x && x < offset_x + 60 && y >= btn_y && y < btn_y + 25) { + focused_field = 3; disp_sel_res = 5; + int len = 0; while (custom_res_w[len]) len++; input_cursor = len; + } + if (x >= offset_x + 80 && x < offset_x + 140 && y >= btn_y && y < btn_y + 25) { + focused_field = 4; disp_sel_res = 5; + int len = 0; while (custom_res_h[len]) len++; input_cursor = len; + } + + btn_y = offset_y + 320; + if (x >= offset_x && x < offset_x + 300 && y >= btn_y && y < btn_y + 35) { + int w = 1024, h = 768; + if (disp_sel_res == 0) { w = 640; h = 480; } + else if (disp_sel_res == 1) { w = 800; h = 600; } + else if (disp_sel_res >= 2 && disp_sel_res <= 4) { + w = dyn_res_w[disp_sel_res - 2]; + h = dyn_res_h[disp_sel_res - 2]; + } else if (disp_sel_res == 5) { + extern int atoi(const char *str); + int cw = atoi(custom_res_w); + int ch = atoi(custom_res_h); + if (cw >= 320 && ch >= 200) { w = cw; h = ch; } + } + + int bpp = 32, mode = 0; + if (disp_sel_color == 1) { bpp = 16; } + if (disp_sel_color == 2) { bpp = 8; mode = 0; } + if (disp_sel_color == 3) { bpp = 8; mode = 1; } + if (disp_sel_color == 4) { bpp = 8; mode = 2; } + + sys_system(47 /*SET_RESOLUTION*/, w, h, bpp, mode); + } } } @@ -769,13 +954,15 @@ static void control_panel_handle_key(char c, bool pressed) { if (!pressed) return; if (focused_field < 0) return; - if (current_view == VIEW_WALLPAPER) { + if (current_view == VIEW_WALLPAPER || current_view == VIEW_DISPLAY) { char *focused_buffer = NULL; int max_len = 3; - if (focused_field == 0) focused_buffer = rgb_r; - else if (focused_field == 1) focused_buffer = rgb_g; - else if (focused_field == 2) focused_buffer = rgb_b; + if (focused_field == 0 && current_view == VIEW_WALLPAPER) focused_buffer = rgb_r; + else if (focused_field == 1 && current_view == VIEW_WALLPAPER) focused_buffer = rgb_g; + else if (focused_field == 2 && current_view == VIEW_WALLPAPER) focused_buffer = rgb_b; + else if (focused_field == 3 && current_view == VIEW_DISPLAY) { focused_buffer = custom_res_w; max_len = 5; } + else if (focused_field == 4 && current_view == VIEW_DISPLAY) { focused_buffer = custom_res_h; max_len = 5; } else return; if (c == '\b') { diff --git a/src/kernel/vga.c b/src/kernel/vga.c new file mode 100644 index 0000000..78cca4f --- /dev/null +++ b/src/kernel/vga.c @@ -0,0 +1,84 @@ +// Copyright (c) 2023-2026 Chris (boreddevnl) +// This software is released under the GNU General Public License v3.0. See LICENSE file for details. +// This header needs to maintain in any file it is present in, as per the GPL license terms. +#include "vga.h" +#include "io.h" +#include "pci.h" +#include "platform.h" + +extern void serial_write(const char *str); +extern void serial_write_num(uint32_t n); + +static void vga_write_register(uint16_t index, uint16_t data) { + outw(VBE_DISPI_IOPORT_INDEX, index); + outw(VBE_DISPI_IOPORT_DATA, data); +} + +bool vga_set_mode(uint16_t width, uint16_t height, uint16_t bpp, void **out_framebuffer) { + // 1. Locate the BGA PCI device to get the physical framebuffer address + pci_device_t bga_dev; + // Standard VGA compatible controller is Class 0x03, Subclass 0x00 + if (!pci_find_device_by_class(0x03, 0x00, &bga_dev)) { + serial_write("[VGA] Error: VGA compatible controller not found via PCI!\n"); + return false; + } + + // Read BAR0 (offset 0x10) + uint32_t bar0 = pci_read_config(bga_dev.bus, bga_dev.device, bga_dev.function, 0x10); + // physical address is bar0 with the lower 4 bits masked out + uint32_t phys_base = bar0 & 0xFFFFFFF0; + + if (phys_base == 0) { + serial_write("[VGA] Error: Invalid BAR0 physical base.\n"); + return false; + } + + serial_write("[VGA] Found VGA Controller. LFB Phys Base: "); + serial_write_num(phys_base); + serial_write("\n"); + + // Map physical to virtual using p2v + void *vram_ptr = (void *)p2v(phys_base); + + // 2. Disable BGA extensions first + vga_write_register(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED); + + // 3. Set resolution and BPP + vga_write_register(VBE_DISPI_INDEX_XRES, width); + vga_write_register(VBE_DISPI_INDEX_YRES, height); + vga_write_register(VBE_DISPI_INDEX_BPP, bpp); + + // 4. Re-enable BGA and Linear Framebuffer + vga_write_register(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED); + + if (out_framebuffer) { + *out_framebuffer = vram_ptr; + } + + return true; +} + +void vga_set_palette_grayscale(void) { + outb(0x03C8, 0); // Palette index + for (int i = 0; i < 256; i++) { + // VGA palette uses 6 bits per channel (0-63) + uint8_t val = i >> 2; + outb(0x03C9, val); // R + outb(0x03C9, val); // G + outb(0x03C9, val); // B + } +} + +void vga_set_palette_standard(void) { + outb(0x03C8, 0); // Palette index + for (int i = 0; i < 256; i++) { + // Standard 3:3:2 RGB mapping to 6-bit per channel + uint8_t r = (i >> 5) & 0x7; + uint8_t g = (i >> 2) & 0x7; + uint8_t b = i & 0x3; + + outb(0x03C9, (r * 63) / 7); + outb(0x03C9, (g * 63) / 7); + outb(0x03C9, (b * 63) / 3); + } +} diff --git a/src/kernel/vga.h b/src/kernel/vga.h new file mode 100644 index 0000000..5ed9f85 --- /dev/null +++ b/src/kernel/vga.h @@ -0,0 +1,36 @@ +// Copyright (c) 2023-2026 Chris (boreddevnl) +// This software is released under the GNU General Public License v3.0. See LICENSE file for details. +// This header needs to maintain in any file it is present in, as per the GPL license terms. +#ifndef VGA_H +#define VGA_H + +#include +#include + +#define VBE_DISPI_IOPORT_INDEX 0x01CE +#define VBE_DISPI_IOPORT_DATA 0x01CF + +#define VBE_DISPI_INDEX_ID 0 +#define VBE_DISPI_INDEX_XRES 1 +#define VBE_DISPI_INDEX_YRES 2 +#define VBE_DISPI_INDEX_BPP 3 +#define VBE_DISPI_INDEX_ENABLE 4 +#define VBE_DISPI_INDEX_BANK 5 +#define VBE_DISPI_INDEX_VIRT_WIDTH 6 +#define VBE_DISPI_INDEX_VIRT_HEIGHT 7 +#define VBE_DISPI_INDEX_X_OFFSET 8 +#define VBE_DISPI_INDEX_Y_OFFSET 9 + +#define VBE_DISPI_DISABLED 0x00 +#define VBE_DISPI_ENABLED 0x01 +#define VBE_DISPI_LFB_ENABLED 0x40 + +#define COLOR_MODE_NORMAL 0 +#define COLOR_MODE_GRAYSCALE 1 +#define COLOR_MODE_MONOCHROME 2 + +bool vga_set_mode(uint16_t width, uint16_t height, uint16_t bpp, void **out_framebuffer); +void vga_set_palette_grayscale(void); +void vga_set_palette_standard(void); + +#endif