#include "src/kernel/vga.h" #include "src/kernel/panic.h" #include "src/kernel/util.h" #include "src/lib/memory.h" static uint16_t *vga = (uint16_t *)0xFFFFFFFF800B8000; static uint16_t color = (uint16_t)0x0F << 8; static uint16_t offset = 0; static void set_char(char c) { vga[offset] = color | (uint16_t)c; } static void set_cursor() { outb(0x3D4, 0x0F); outb(0x3D5, offset & 0xFF); outb(0x3D4, 0x0E); outb(0x3D5, (offset >> 8) & 0xFF); } static void scroll() { memory_move(vga + VGA_WIDTH, 2 * VGA_WIDTH * (VGA_HEIGHT - 1), vga); for (offset = VGA_WIDTH * (VGA_HEIGHT - 1); offset < VGA_WIDTH * VGA_HEIGHT; offset++) { set_char(' '); } offset = VGA_WIDTH * (VGA_HEIGHT - 1); } static void advance() { offset++; if (offset >= VGA_WIDTH * VGA_HEIGHT) { scroll(); } } static void clear() { uint16_t *ptr = vga; uint16_t fill = 0x0F20; uint32_t count = VGA_WIDTH * VGA_HEIGHT; __asm__ volatile("rep stosw" : "=D"(ptr), "=c"(count) : "D"(ptr), "a"(fill), "c"(count) : "memory"); offset = 0; set_cursor(); } typedef enum { PARSE_NORMAL, PARSE_ESC, PARSE_CSI, } parse_state_t; static parse_state_t parser_state = PARSE_NORMAL; static char parser_csi_param[8]; static uint8_t parser_csi_len; static void on_char_received(char c) { switch (parser_state) { case PARSE_NORMAL: switch (c) { case '\x1B': parser_state = PARSE_ESC; break; case '\b': case '\x7F': if (offset > 0) { offset--; set_cursor(); } break; case '\r': offset -= offset % VGA_WIDTH; set_cursor(); break; case '\n': if (offset / VGA_WIDTH == VGA_HEIGHT - 1) { scroll(); } else { offset += VGA_WIDTH; } offset -= offset % VGA_WIDTH; set_cursor(); break; default: if (c >= '\x01' && c <= '\x1A') { set_char('^'); advance(); set_char(c + 'A' - 1); advance(); set_cursor(); } else if (c >= ' ') { set_char(c); advance(); set_cursor(); } break; } break; case PARSE_ESC: if (c == '[') { parser_state = PARSE_CSI; parser_csi_len = 0; } else { parser_state = PARSE_NORMAL; } break; case PARSE_CSI: if ((c >= '0' && c <= '9') || c == ';') { if (parser_csi_len < sizeof(parser_csi_param) - 1) { parser_csi_param[parser_csi_len++] = c; } } else { parser_state = PARSE_NORMAL; parser_csi_param[parser_csi_len] = '\0'; switch (c) { case 'C': if (offset < VGA_WIDTH * VGA_HEIGHT - 1) { offset++; } set_cursor(); break; case 'D': if (offset > 0) { offset--; } set_cursor(); break; case 'H': offset -= offset % VGA_WIDTH; set_cursor(); break; case 'F': offset -= offset % VGA_WIDTH; offset += VGA_WIDTH - 1; set_cursor(); break; } } break; } } static uint64_t stream_write(__attribute__((unused)) stream_t *self, const char *from, uint64_t bytes) { uint64_t left = bytes; while (left--) { on_char_received(*from++); } return bytes; } static uint64_t stream_read(__attribute__((unused)) stream_t *self, __attribute__((unused)) uint64_t max, __attribute__((unused)) char *to) { return 0; } static uint64_t stream_truncate(__attribute__((unused)) stream_t *self, __attribute__((unused)) uint64_t size) { return size; } static void stream_close(__attribute__((unused)) stream_t *self) { } static stream_t stream = {stream_write, stream_read, stream_truncate, stream_close}; stream_t *vga_init() { clear(); return &stream; } void vga_set_string(uint8_t row, uint8_t col, const char *str, uint8_t c) { ASSERT(row < VGA_HEIGHT && col < VGA_WIDTH, "vga_set_string: invalid coordinates") color = (uint16_t)((uint16_t)c << 8); offset = row * VGA_WIDTH + col; while (*str) { on_char_received(*str++); } }