A toy operating system written in C.
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.
 
 
 
 
freywaros/src/kernel/vga.c

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++);
}
}