splash.c (10313B)
1 #include "splash.h" 2 #include "decoder_PNG.h" 3 #include "decoder_BMP.h" 4 #include "decoder.h" 5 #include "vesa.h" 6 #include "ramdisk.h" 7 #include "terminal.h" 8 #include <stdlib.h> 9 #include <string.h> 10 #include <stdio.h> 11 12 /* External VESA state */ 13 extern vesa_state_t vesa_state; 14 extern ramdisk_node_t* c_ramdisk_root; 15 16 /* RTC port definitions */ 17 #define RTC_INDEX 0x70 18 #define RTC_DATA 0x71 19 #define RTC_SECONDS 0x00 20 21 /* Read a byte from I/O port */ 22 static inline uint8_t inb(uint16_t port) { 23 uint8_t ret; 24 __asm__ volatile ("inb %1, %0" : "=a"(ret) : "Nd"(port)); 25 return ret; 26 } 27 28 /* Write a byte to I/O port */ 29 static inline void outb(uint16_t port, uint8_t val) { 30 __asm__ volatile ("outb %0, %1" : : "a"(val), "Nd"(port)); 31 } 32 33 /* Read RTC register */ 34 static uint8_t read_rtc(uint8_t reg) { 35 outb(RTC_INDEX, reg); 36 return inb(RTC_DATA); 37 } 38 39 /* Convert BCD to binary */ 40 static uint8_t bcd_to_bin(uint8_t bcd) { 41 return ((bcd >> 4) * 10) + (bcd & 0x0F); 42 } 43 44 /* Get current seconds from RTC */ 45 static uint8_t get_rtc_seconds(void) { 46 uint8_t seconds = read_rtc(RTC_SECONDS); 47 /* RTC might be in BCD or binary mode, convert if needed */ 48 return bcd_to_bin(seconds); 49 } 50 51 /* RTC-based delay function */ 52 void splash_delay_ms(uint32_t ms) { 53 extern void terminal_writestring(const char*); 54 extern int snprintf(char*, size_t, const char*, ...); 55 char buf[128]; 56 57 /* For delays >= 1 second, use RTC seconds counter */ 58 if (ms >= 1000) { 59 uint32_t seconds = ms / 1000; 60 uint8_t start_sec = get_rtc_seconds(); 61 uint8_t target_sec = (start_sec + seconds) % 60; 62 63 snprintf(buf, sizeof(buf), "RTC delay: %u ms (%u sec), start=%u, target=%u\n", 64 ms, seconds, start_sec, target_sec); 65 terminal_writestring(buf); 66 67 /* Wait until target second is reached */ 68 while (1) { 69 uint8_t current_sec = get_rtc_seconds(); 70 71 /* Handle wraparound at 60 seconds */ 72 if (start_sec < target_sec) { 73 /* No wraparound */ 74 if (current_sec >= target_sec) { 75 snprintf(buf, sizeof(buf), "RTC delay complete: current=%u\n", current_sec); 76 terminal_writestring(buf); 77 break; 78 } 79 } else { 80 /* Wraparound case */ 81 if (current_sec >= target_sec && current_sec < start_sec) { 82 snprintf(buf, sizeof(buf), "RTC delay complete (wrap): current=%u\n", current_sec); 83 terminal_writestring(buf); 84 break; 85 } 86 } 87 88 /* Small pause to avoid hammering the RTC */ 89 for (volatile int i = 0; i < 1000; i++) { 90 __asm__ volatile ("nop"); 91 } 92 } 93 } else { 94 /* For sub-second delays, use busy loop */ 95 volatile uint32_t iterations = ms * 1000; 96 for (volatile uint32_t i = 0; i < iterations; i++) { 97 __asm__ volatile ("nop"); 98 } 99 } 100 } 101 102 /* Draw an image centered on the screen with VESA */ 103 static void draw_image_centered(image_t* img) { 104 if (!img || !vesa_state.active || !vesa_state.framebuffer) { 105 return; 106 } 107 108 uint32_t screen_width = vesa_state.current_mode.width; 109 uint32_t screen_height = vesa_state.current_mode.height; 110 111 /* Calculate centered position */ 112 int x_offset = (screen_width - img->width) / 2; 113 int y_offset = (screen_height - img->height) / 2; 114 115 /* Ensure we don't draw off-screen */ 116 if (x_offset < 0) x_offset = 0; 117 if (y_offset < 0) y_offset = 0; 118 119 /* Get framebuffer properties */ 120 uint8_t* fb = vesa_state.framebuffer; 121 uint16_t pitch = vesa_state.current_mode.pitch; 122 uint8_t bytes_per_pixel = vesa_state.current_mode.bpp / 8; 123 124 /* Draw the image */ 125 for (uint32_t y = 0; y < img->height; y++) { 126 uint32_t screen_y = y_offset + y; 127 if (screen_y >= screen_height) break; 128 129 for (uint32_t x = 0; x < img->width; x++) { 130 uint32_t screen_x = x_offset + x; 131 if (screen_x >= screen_width) continue; 132 133 uint8_t r, g, b, a; 134 image_get_pixel(img, x, y, &r, &g, &b, &a); 135 136 /* Calculate framebuffer position */ 137 uint8_t* pixel = fb + screen_y * pitch + screen_x * bytes_per_pixel; 138 139 /* Write pixel based on framebuffer format */ 140 if (bytes_per_pixel == 3) { 141 /* 24-bit RGB */ 142 pixel[0] = b; 143 pixel[1] = g; 144 pixel[2] = r; 145 } else if (bytes_per_pixel == 4) { 146 /* 32-bit RGBA or RGBX */ 147 pixel[0] = b; 148 pixel[1] = g; 149 pixel[2] = r; 150 pixel[3] = 0; /* Reserved/Alpha */ 151 } 152 } 153 } 154 } 155 156 /* Clear screen to black */ 157 static void clear_screen_black(void) { 158 if (!vesa_state.active || !vesa_state.framebuffer) { 159 return; 160 } 161 162 uint32_t screen_height = vesa_state.current_mode.height; 163 uint16_t pitch = vesa_state.current_mode.pitch; 164 165 /* Clear to black */ 166 for (uint32_t y = 0; y < screen_height; y++) { 167 memset(vesa_state.framebuffer + y * pitch, 0, pitch); 168 } 169 } 170 171 /* Display a splash screen with a PNG image */ 172 int splash_show_png(const uint8_t* png_data, uint32_t png_size, uint32_t duration_ms) { 173 (void)duration_ms; /* Unused - delay is skipped */ 174 if (!png_data || png_size == 0) { 175 terminal_writestring("ERROR: Invalid PNG data\n"); 176 return -1; 177 } 178 179 /* Decode the PNG */ 180 terminal_writestring("Decoding PNG splash screen...\n"); 181 image_t* img = png_decode(png_data, png_size); 182 if (!img) { 183 terminal_writestring("ERROR: Failed to decode PNG\n"); 184 return -1; 185 } 186 187 char size_str[32]; 188 terminal_writestring("PNG decoded: "); 189 snprintf(size_str, sizeof(size_str), "%ux%u", img->width, img->height); 190 terminal_writestring(size_str); 191 terminal_writestring(" pixels\n"); 192 193 /* Initialize VESA if not already initialized */ 194 if (!vesa_state.initialized) { 195 terminal_writestring("Initializing VESA...\n"); 196 if (vesa_init() != 0) { 197 terminal_writestring("ERROR: Failed to initialize VESA\n"); 198 image_destroy(img); 199 return -1; 200 } 201 } 202 203 /* Set a suitable video mode if not already set */ 204 if (!vesa_state.active) { 205 terminal_writestring("Setting VESA mode 1024x768x32...\n"); 206 if (vesa_set_mode(1024, 768, 32) != 0) { 207 terminal_writestring("WARNING: Failed to set 1024x768x32, trying 800x600x32...\n"); 208 if (vesa_set_mode(800, 600, 32) != 0) { 209 terminal_writestring("ERROR: Failed to set video mode\n"); 210 image_destroy(img); 211 return -1; 212 } 213 } 214 } 215 216 /* Clear screen to black */ 217 clear_screen_black(); 218 219 /* Draw the image centered */ 220 terminal_writestring("Displaying splash screen...\n"); 221 draw_image_centered(img); 222 223 /* Skip delay for now - timing issues */ 224 // splash_delay_ms(duration_ms); 225 226 /* Clean up */ 227 image_destroy(img); 228 terminal_writestring("Splash screen complete\n"); 229 230 return 0; 231 } 232 233 /* Display a splash screen from ramdisk file path */ 234 int splash_show_from_file(const char* filepath, uint32_t duration_ms) { 235 (void)duration_ms; /* Unused - we don't block anymore */ 236 237 if (!filepath) { 238 terminal_writestring("ERROR: Invalid file path\n"); 239 return -1; 240 } 241 242 terminal_writestring("Loading splash screen from: "); 243 terminal_writestring(filepath); 244 terminal_writestring("\n"); 245 246 /* Traverse ramdisk to find the file */ 247 ramdisk_node_t* node = ramdisk_find(c_ramdisk_root, filepath); 248 if (!node) { 249 terminal_writestring("ERROR: File not found in ramdisk\n"); 250 return -1; 251 } 252 253 if (node->type != RAMDISK_FILE) { 254 terminal_writestring("ERROR: Path is not a file\n"); 255 return -1; 256 } 257 258 char size_str[32]; 259 snprintf(size_str, sizeof(size_str), "File size: %u bytes\n", node->file.size); 260 terminal_writestring(size_str); 261 262 /* Detect file type by extension */ 263 const char* ext = filepath; 264 const char* dot = NULL; 265 while (*ext) { 266 if (*ext == '.') dot = ext; 267 ext++; 268 } 269 270 image_t* img = NULL; 271 if (dot && strcmp(dot, ".bmp") == 0) { 272 terminal_writestring("Decoding BMP splash screen...\n"); 273 img = bmp_decode(node->file.data, node->file.size); 274 } else if (dot && strcmp(dot, ".png") == 0) { 275 terminal_writestring("Decoding PNG splash screen...\n"); 276 img = png_decode(node->file.data, node->file.size); 277 } else { 278 terminal_writestring("ERROR: Unknown image format (use .bmp or .png)\n"); 279 return -1; 280 } 281 282 if (!img) { 283 terminal_writestring("ERROR: Failed to decode image\n"); 284 return -1; 285 } 286 287 snprintf(size_str, sizeof(size_str), "Image decoded: %ux%u pixels\n", img->width, img->height); 288 terminal_writestring(size_str); 289 290 /* Initialize VESA if not already initialized */ 291 if (!vesa_state.initialized) { 292 terminal_writestring("Initializing VESA...\n"); 293 if (vesa_init() != 0) { 294 terminal_writestring("ERROR: Failed to initialize VESA\n"); 295 image_destroy(img); 296 return -1; 297 } 298 } 299 300 /* Set a suitable video mode if not already set */ 301 if (!vesa_state.active) { 302 terminal_writestring("Setting VESA mode 1024x768x32...\n"); 303 if (vesa_set_mode(1024, 768, 32) != 0) { 304 terminal_writestring("WARNING: Failed to set 1024x768x32, trying 800x600x32...\n"); 305 if (vesa_set_mode(800, 600, 32) != 0) { 306 terminal_writestring("ERROR: Failed to set video mode\n"); 307 image_destroy(img); 308 return -1; 309 } 310 } 311 } 312 313 /* Clear screen to black */ 314 clear_screen_black(); 315 316 /* Draw the image centered */ 317 terminal_writestring("Displaying splash screen...\n"); 318 draw_image_centered(img); 319 320 /* Don't wait - let it stay on screen until drawn over by the desktop */ 321 /* Clean up memory but image stays in VRAM */ 322 image_destroy(img); 323 terminal_writestring("Splash screen displayed (will remain until overwritten)\n"); 324 325 return 0; 326 }