191 lines
3.8 KiB
C
191 lines
3.8 KiB
C
/*
|
|
* AsterOS Kernel
|
|
* Autor: Pavel Kalaš
|
|
* Rok: 2026
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* Tento ovladac realizuje textovou konzoli nad VGA pameti na adrese 0xB8000.
|
|
* Ridi pozici kurzoru, barvy znaku, zpracovani ridicich znaku
|
|
* a automaticky scroll, kdyz vystup presahne posledni radek obrazovky.
|
|
*/
|
|
|
|
#include "display.h"
|
|
|
|
#define VGA_WIDTH 80
|
|
#define VGA_HEIGHT 25
|
|
#define VGA_CONTENT_HEIGHT (VGA_HEIGHT - 1)
|
|
|
|
static volatile u16 *const vga = (u16 *)0xB8000;
|
|
static u8 color = 0x0F;
|
|
static usize row = 0;
|
|
static usize col = 0;
|
|
|
|
static inline void outb(u16 port, u8 value) {
|
|
__asm__ volatile ("outb %0, %1" : : "a"(value), "Nd"(port));
|
|
}
|
|
|
|
static void update_hw_cursor(void) {
|
|
u16 pos = (u16)(row * VGA_WIDTH + col);
|
|
|
|
outb(0x3D4, 0x0F);
|
|
outb(0x3D5, (u8)(pos & 0xFF));
|
|
outb(0x3D4, 0x0E);
|
|
outb(0x3D5, (u8)((pos >> 8) & 0xFF));
|
|
}
|
|
|
|
static inline u16 vga_entry(char c, u8 clr) {
|
|
return (u16)c | ((u16)clr << 8);
|
|
}
|
|
|
|
static void scroll_if_needed(void) {
|
|
usize r;
|
|
usize c;
|
|
|
|
if (row < VGA_CONTENT_HEIGHT) {
|
|
return;
|
|
}
|
|
|
|
for (r = 1; r < VGA_CONTENT_HEIGHT; ++r) {
|
|
for (c = 0; c < VGA_WIDTH; ++c) {
|
|
vga[(r - 1) * VGA_WIDTH + c] = vga[r * VGA_WIDTH + c];
|
|
}
|
|
}
|
|
|
|
for (c = 0; c < VGA_WIDTH; ++c) {
|
|
vga[(VGA_CONTENT_HEIGHT - 1) * VGA_WIDTH + c] = vga_entry(' ', color);
|
|
}
|
|
|
|
row = VGA_CONTENT_HEIGHT - 1;
|
|
}
|
|
|
|
void display_set_color(u8 fg, u8 bg) {
|
|
color = (u8)((bg << 4) | (fg & 0x0F));
|
|
}
|
|
|
|
void display_clear(void) {
|
|
usize i;
|
|
|
|
for (i = 0; i < VGA_WIDTH * VGA_HEIGHT; ++i) {
|
|
vga[i] = vga_entry(' ', color);
|
|
}
|
|
|
|
row = 0;
|
|
col = 0;
|
|
update_hw_cursor();
|
|
}
|
|
|
|
void display_init(void) {
|
|
display_set_color(0x0F, 0x00);
|
|
display_clear();
|
|
}
|
|
|
|
void display_putc(char c) {
|
|
if (c == '\n') {
|
|
col = 0;
|
|
++row;
|
|
scroll_if_needed();
|
|
update_hw_cursor();
|
|
return;
|
|
}
|
|
|
|
if (c == '\r') {
|
|
col = 0;
|
|
update_hw_cursor();
|
|
return;
|
|
}
|
|
|
|
if (c == '\b') {
|
|
if (col > 0) {
|
|
--col;
|
|
} else if (row > 0) {
|
|
--row;
|
|
col = VGA_WIDTH - 1;
|
|
}
|
|
|
|
vga[row * VGA_WIDTH + col] = vga_entry(' ', color);
|
|
update_hw_cursor();
|
|
return;
|
|
}
|
|
|
|
if (c == '\t') {
|
|
col = (col + 4) & ~3ULL;
|
|
if (col >= VGA_WIDTH) {
|
|
col = 0;
|
|
++row;
|
|
scroll_if_needed();
|
|
}
|
|
update_hw_cursor();
|
|
return;
|
|
}
|
|
|
|
vga[row * VGA_WIDTH + col] = vga_entry(c, color);
|
|
++col;
|
|
|
|
if (col >= VGA_WIDTH) {
|
|
col = 0;
|
|
++row;
|
|
scroll_if_needed();
|
|
}
|
|
|
|
update_hw_cursor();
|
|
}
|
|
|
|
void display_write(const char *text) {
|
|
while (*text) {
|
|
display_putc(*text++);
|
|
}
|
|
}
|
|
|
|
void display_get_cursor(usize *out_row, usize *out_col) {
|
|
if (out_row) {
|
|
*out_row = row;
|
|
}
|
|
if (out_col) {
|
|
*out_col = col;
|
|
}
|
|
}
|
|
|
|
void display_set_cursor(usize new_row, usize new_col) {
|
|
if (new_row >= VGA_HEIGHT) {
|
|
new_row = VGA_HEIGHT - 1;
|
|
}
|
|
if (new_col >= VGA_WIDTH) {
|
|
new_col = VGA_WIDTH - 1;
|
|
}
|
|
|
|
row = new_row;
|
|
col = new_col;
|
|
update_hw_cursor();
|
|
}
|
|
|
|
void display_fill_row(usize target_row, char ch, u8 fg, u8 bg) {
|
|
usize c;
|
|
u8 clr;
|
|
|
|
if (target_row >= VGA_HEIGHT) {
|
|
return;
|
|
}
|
|
|
|
clr = (u8)((bg << 4) | (fg & 0x0F));
|
|
for (c = 0; c < VGA_WIDTH; ++c) {
|
|
vga[target_row * VGA_WIDTH + c] = vga_entry(ch, clr);
|
|
}
|
|
}
|
|
|
|
void display_write_at(usize target_row, usize target_col, const char *text, u8 fg, u8 bg) {
|
|
usize c = target_col;
|
|
u8 clr;
|
|
|
|
if (!text || target_row >= VGA_HEIGHT || target_col >= VGA_WIDTH) {
|
|
return;
|
|
}
|
|
|
|
clr = (u8)((bg << 4) | (fg & 0x0F));
|
|
while (*text && c < VGA_WIDTH) {
|
|
vga[target_row * VGA_WIDTH + c] = vga_entry(*text++, clr);
|
|
++c;
|
|
}
|
|
}
|