You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
179 lines
4.0 KiB
179 lines
4.0 KiB
#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++);
|
|
}
|
|
}
|
|
|