110 lines
3.6 KiB
C
110 lines
3.6 KiB
C
#include "vga.h"
|
||
#include "../kernel/io.h"
|
||
#include <stdint.h>
|
||
|
||
#define VGA_WIDTH 80
|
||
#define VGA_HEIGHT 25
|
||
#define VGA_ADDRESS 0xB8000
|
||
|
||
static volatile char *video_mem = (volatile char*)VGA_ADDRESS;
|
||
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;
|
||
// Заполняем весь экран пробелами
|
||
for (int i = 0; i < VGA_WIDTH * VGA_HEIGHT; i++) {
|
||
video_mem[i * 2] = ' ';
|
||
video_mem[i * 2 + 1] = current_color;
|
||
}
|
||
vga_update_cursor(0,0);
|
||
}
|
||
|
||
void vga_set_color(char fg, char bg) {
|
||
current_color = (bg << 4) | (fg & 0x0F);
|
||
}
|
||
|
||
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 if (c >= ' ') { // Печатаем только видимые символы
|
||
int index = (cursor_y * VGA_WIDTH + cursor_x) * 2;
|
||
video_mem[index] = c;
|
||
video_mem[index + 1] = current_color;
|
||
cursor_x++;
|
||
}
|
||
|
||
// Если вышли за правый край — перенос
|
||
if (cursor_x >= VGA_WIDTH) {
|
||
cursor_x = 0;
|
||
cursor_y++;
|
||
}
|
||
|
||
// Если экран кончился — скроллим вверх, пока курсор не встанет на последнюю строку
|
||
if (cursor_y >= VGA_HEIGHT) {
|
||
vga_scroll();
|
||
cursor_y = VGA_HEIGHT - 1;
|
||
}
|
||
vga_update_cursor(cursor_x, cursor_y);
|
||
}
|
||
|
||
void vga_print(const char *str) {
|
||
for (int i = 0; str[i] != '\0'; i++) {
|
||
vga_putchar(str[i]);
|
||
}
|
||
}
|
||
|
||
void vga_println(const char *str) {
|
||
for (int i = 0; str[i] != '\0'; i++) {
|
||
vga_putchar(str[i]);
|
||
}
|
||
vga_putchar('\n');
|
||
}
|
||
|