#include "console.h" #include #define ROWS 25 #define COLUMNS 80 typedef struct { u8 character; u8 color; } vga_char_t; static vga_char_t *vga_buffer = (vga_char_t *)0xb8000; static u8 cursor_row = 0; static u8 cursor_col = 0; void console_putchar(char c, u8 color) { switch (c) { case '\n': { cursor_row += 1; cursor_col = 0; break; } case '\r': { cursor_col = 0; break; } case '\t': { u8 remainder = 4 - (cursor_col % 4); cursor_col += remainder; break; } case '\b': { if (cursor_col > 0) { cursor_col -= 1; } else if (cursor_row > 0) { cursor_row -= 1; cursor_col = 0; for (int col = COLUMNS - 1; col >= 0; col--) { vga_char_t cell = vga_buffer[cursor_row * COLUMNS + col]; if (cell.character != ' ') { cursor_col = col; break; } } } vga_buffer[cursor_row * COLUMNS + cursor_col] = (vga_char_t){ .character = ' ', .color = VGA_DEFAULT_COLOR, }; break; } default: { vga_buffer[COLUMNS * cursor_row + cursor_col] = (vga_char_t){ .character = c, .color = color, }; cursor_col += 1; break; } } if (cursor_col >= COLUMNS) { cursor_col = 0; cursor_row += 1; } if (cursor_row >= ROWS) { for (size_t row = 1; row < ROWS; row++) { for (size_t col = 0; col < COLUMNS; col++) { vga_buffer[COLUMNS * (row - 1) + col] = vga_buffer[COLUMNS * row + col]; } } for (size_t col = 0; col < COLUMNS; col++) { vga_buffer[COLUMNS * (ROWS - 1) + col] = (vga_char_t){ .character = ' ', .color = VGA_DEFAULT_COLOR, }; }; cursor_row = ROWS - 1; } } void console_clear() { for (size_t row = 0; row < ROWS; row++) { for (size_t col = 0; col < COLUMNS; col++) { vga_buffer[COLUMNS * row + col] = (vga_char_t){ .character = ' ', .color = VGA_DEFAULT_COLOR, }; } } cursor_row = 0; cursor_col = 0; } // Supported formats: // - `d`: i32 (decimal) // - `u`: u32 (decimal) // - `x`: u32 (hex) // - `c`: char (ascii) // - `s`: char* (ascii string) void kvprintf(const char *fmt, va_list args) { bool should_format = false; for (size_t i = 0; fmt[i] != '\0'; i++) { if (should_format) { should_format = false; if (fmt[i] == '%') { console_putchar('%', VGA_DEFAULT_COLOR); } else if (fmt[i] == 's') { const char *str = va_arg(args, const char *); for (size_t j = 0; str[j] != '\0'; j++) { console_putchar(str[j], VGA_DEFAULT_COLOR); } } else if (fmt[i] == 'c') { char character = (char)va_arg(args, u32); console_putchar(character, VGA_DEFAULT_COLOR); } else if (fmt[i] == 'd') { u32 val = va_arg(args, u32); char buf[ITOA32_BUFSIZE]; itoa32(val, buf); for (size_t j = 0; buf[j] != '\0'; j++) { console_putchar(buf[j], VGA_DEFAULT_COLOR); } } else if (fmt[i] == 'u') { u32 val = va_arg(args, u32); char buf[UITOA32_BUFSIZE]; uitoa32(val, buf); for (size_t j = 0; buf[j] != '\0'; j++) { console_putchar(buf[j], VGA_DEFAULT_COLOR); } } else if (fmt[i] == 'x') { u32 val = va_arg(args, u32); char buf[UITOA32_HEX_BUFSIZE]; uitoa32_hex(val, buf); for (size_t j = 0; buf[j] != '\0'; j++) { console_putchar(buf[j], VGA_DEFAULT_COLOR); } } else if (fmt[i] == 'D') { i64 val = va_arg(args, i64); char buf[ITOA64_BUFSIZE]; itoa64(val, buf); for (size_t j = 0; buf[j] != '\0'; j++) { console_putchar(buf[j], VGA_DEFAULT_COLOR); } } else if (fmt[i] == 'U') { u64 val = va_arg(args, u64); char buf[UITOA64_BUFSIZE]; uitoa64(val, buf); for (size_t j = 0; buf[j] != '\0'; j++) console_putchar(buf[j], VGA_DEFAULT_COLOR); } else if (fmt[i] == 'X') { u64 val = va_arg(args, u64); char buf[UITOA64_HEX_BUFSIZE]; uitoa64_hex(val, buf); for (size_t j = 0; buf[j] != '\0'; j++) console_putchar(buf[j], VGA_DEFAULT_COLOR); } else { console_putchar(fmt[i], VGA_DEFAULT_COLOR); } } else if (fmt[i] == '%') { should_format = true; } else { console_putchar(fmt[i], VGA_DEFAULT_COLOR); } } } // Supported formats: // - `d`: i32 (decimal) // - `u`: u32 (decimal) // - `x`: u32 (hex) // - `c`: char (ascii) // - `s`: char* (ascii string) // // ```c // printf( // "The answer is %d is located at offset %x in file %s", // 42, // 0x2000 // "hitchhiker.txt" // ); // ``` void kprintf(const char *fmt, ...) { va_list args; va_start(args, fmt); kvprintf(fmt, args); va_end(args); }