diff --git a/README.md b/README.md index 9bba99d..cf1da20 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ - [x] UART отладка - [x] Драйвер таймера - [x] Драйвер клавиатуры -- [ ] Нормальный драйвер VGA (скролл итд) +- [x] Нормальный драйвер VGA (скролл итд) - [ ] Нормальный драйвер клавиатуры (Caps Lock, Shift, Numpad итд) - [ ] Драйвер PC Speaker - [ ] Screen buffers (как tty в Linux) diff --git a/drivers/vga.c b/drivers/vga.c index 97e6a22..badfb6b 100644 --- a/drivers/vga.c +++ b/drivers/vga.c @@ -1,4 +1,6 @@ #include "vga.h" +#include "../kernel/io.h" +#include #define VGA_WIDTH 80 #define VGA_HEIGHT 25 @@ -9,6 +11,34 @@ static int cursor_x = 0; static int cursor_y = 0; static char current_color = 0x07; // По умолчанию серый на черном +// Локальная функция для сдвига экрана вверх на одну строку +static void vga_scroll(void) { + // Копируем строки с 1-й по 24-ю на место строк с 0-й по 23-ю + // Каждая строка занимает VGA_WIDTH * 2 байт (символ + атрибут) + for (int i = 0; i < (VGA_HEIGHT - 1) * VGA_WIDTH * 2; i++) { + video_mem[i] = video_mem[i + VGA_WIDTH * 2]; + } + + // Очищаем самую нижнюю строку (заполняем пробелами с текущим цветом) + int last_row_start = (VGA_HEIGHT - 1) * VGA_WIDTH * 2; + for (int i = 0; i < VGA_WIDTH; i++) { + video_mem[last_row_start + i * 2] = ' '; + video_mem[last_row_start + i * 2 + 1] = current_color; + } +} + +void vga_update_cursor(int x, int y) { + // Вычисляем плоский индекс в видеопамяти + uint16_t pos = y * VGA_WIDTH + x; + + // Передаем контроллеру VGA младший байт позиции (регистр 15) + outb(0x3D4, 0x0F); + outb(0x3D5, (uint8_t)(pos & 0xFF)); + + // Передаем старший байт позиции (регистр 14) + outb(0x3D4, 0x0E); + outb(0x3D5, (uint8_t)((pos >> 8) & 0xFF)); +} void vga_init(void) { cursor_x = 0; cursor_y = 0; @@ -17,6 +47,7 @@ void vga_init(void) { video_mem[i * 2] = ' '; video_mem[i * 2 + 1] = current_color; } + vga_update_cursor(0,0); } void vga_set_color(char fg, char bg) { @@ -24,11 +55,26 @@ void vga_set_color(char fg, char bg) { } void vga_putchar(char c) { + // Обработка бэкспейса (стирание символа) + if (c == '\b') { + if (cursor_x > 0) { + cursor_x--; + } else if (cursor_y > 0) { + cursor_y--; + cursor_x = VGA_WIDTH - 1; + } + int index = (cursor_y * VGA_WIDTH + cursor_x) * 2; + video_mem[index] = ' '; + video_mem[index + 1] = current_color; + vga_update_cursor(cursor_x, cursor_y); + return; // Выходим, чтобы не двигать курсор вперед + } + // Обработка переноса строки if (c == '\n') { cursor_x = 0; cursor_y++; - } else { + } else if (c >= ' ') { // Печатаем только видимые символы int index = (cursor_y * VGA_WIDTH + cursor_x) * 2; video_mem[index] = c; video_mem[index + 1] = current_color; @@ -41,10 +87,12 @@ void vga_putchar(char c) { cursor_y++; } - // Если экран кончился — просто сбрасываем наверх (примитивный скролл) + // Если экран кончился — скроллим вверх, пока курсор не встанет на последнюю строку if (cursor_y >= VGA_HEIGHT) { - vga_init(); + vga_scroll(); + cursor_y = VGA_HEIGHT - 1; } + vga_update_cursor(cursor_x, cursor_y); } void vga_print(const char *str) { @@ -54,6 +102,9 @@ void vga_print(const char *str) { } void vga_println(const char *str) { - vga_print(str); + for (int i = 0; str[i] != '\0'; i++) { + vga_putchar(str[i]); + } vga_putchar('\n'); } + diff --git a/drivers/vga.h b/drivers/vga.h index c3a8756..9b978bd 100644 --- a/drivers/vga.h +++ b/drivers/vga.h @@ -25,4 +25,7 @@ void vga_print(const char *str); // Вывод строки с автоматическим переносом на новую линию void vga_println(const char *str); +// Обновление курсора +void vga_update_cursor(int x, int y); + #endif