limine/common/drivers/vga_textmode.c
2022-02-03 10:38:43 +01:00

321 lines
8.0 KiB
C

#if bios == 1
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <drivers/vga_textmode.h>
#include <sys/cpu.h>
#include <lib/real.h>
#include <lib/libc.h>
#include <lib/blib.h>
#include <lib/term.h>
#include <mm/pmm.h>
#define VIDEO_BOTTOM ((VD_ROWS * VD_COLS) - 1)
#define VD_COLS (80 * 2)
#define VD_ROWS 25
static volatile uint8_t *video_mem = (uint8_t *)0xb8000;
static uint8_t *back_buffer = NULL;
static uint8_t *front_buffer = NULL;
static struct context {
size_t cursor_offset;
#define cursor_offset context.cursor_offset
bool cursor_status;
#define cursor_status context.cursor_status
uint8_t text_palette;
#define text_palette context.text_palette
bool scroll_enabled;
#define scroll_enabled context.scroll_enabled
uint8_t saved_state_text_palette;
#define saved_state_text_palette context.saved_state_text_palette
size_t saved_state_cursor_offset;
#define saved_state_cursor_offset context.saved_state_cursor_offset
} context;
static size_t old_cursor_offset = 0;
static void draw_cursor(void) {
uint8_t pal = back_buffer[cursor_offset + 1];
video_mem[cursor_offset + 1] = ((pal & 0xf0) >> 4) | ((pal & 0x0f) << 4);
}
void text_save_state(void) {
saved_state_text_palette = text_palette;
saved_state_cursor_offset = cursor_offset;
}
void text_restore_state(void) {
text_palette = saved_state_text_palette;
cursor_offset = saved_state_cursor_offset;
}
void text_swap_palette(void) {
text_palette = (text_palette << 4) | (text_palette >> 4);
}
bool text_scroll_disable(void) {
bool ret = scroll_enabled;
scroll_enabled = false;
return ret;
}
void text_scroll_enable(void) {
scroll_enabled = true;
}
void text_scroll(void) {
// move the text up by one row
for (size_t i = term_context.scroll_top_margin * VD_COLS;
i < (term_context.scroll_bottom_margin - 1) * VD_COLS; i++) {
back_buffer[i] = back_buffer[i + VD_COLS];
}
// clear the last line of the screen
for (size_t i = (term_context.scroll_bottom_margin - 1) * VD_COLS;
i < term_context.scroll_bottom_margin * VD_COLS; i += 2) {
back_buffer[i] = ' ';
back_buffer[i + 1] = text_palette;
}
}
void text_revscroll(void) {
// move the text up by one row
for (size_t i = (term_context.scroll_bottom_margin - 1) * VD_COLS - 2; ; i--) {
back_buffer[i + VD_COLS] = back_buffer[i];
if (i == term_context.scroll_top_margin * VD_COLS) {
break;
}
}
// clear the first line of the screen
for (size_t i = term_context.scroll_top_margin * VD_COLS;
i < (term_context.scroll_top_margin + 1) * VD_COLS; i += 2) {
back_buffer[i] = ' ';
back_buffer[i + 1] = text_palette;
}
}
void text_clear(bool move) {
for (size_t i = 0; i < VIDEO_BOTTOM; i += 2) {
back_buffer[i] = ' ';
back_buffer[i + 1] = text_palette;
}
if (move)
cursor_offset = 0;
}
void text_enable_cursor(void) {
cursor_status = true;
}
bool text_disable_cursor(void) {
bool ret = cursor_status;
cursor_status = false;
return ret;
}
uint64_t text_context_size(void) {
uint64_t ret = 0;
ret += sizeof(struct context);
ret += VD_ROWS * VD_COLS; // front buffer
return ret;
}
void text_context_save(uint64_t ptr) {
memcpy32to64(ptr, (uint64_t)(uintptr_t)&context, sizeof(struct context));
ptr += sizeof(struct context);
memcpy32to64(ptr, (uint64_t)(uintptr_t)front_buffer, VD_ROWS * VD_COLS);
}
void text_context_restore(uint64_t ptr) {
memcpy32to64((uint64_t)(uintptr_t)&context, ptr, sizeof(struct context));
ptr += sizeof(struct context);
memcpy32to64((uint64_t)(uintptr_t)front_buffer, ptr, VD_ROWS * VD_COLS);
for (size_t i = 0; i < VD_ROWS * VD_COLS; i++) {
video_mem[i] = front_buffer[i];
back_buffer[i] = front_buffer[i];
}
if (cursor_status) {
draw_cursor();
old_cursor_offset = cursor_offset;
}
}
void text_full_refresh(void) {
for (size_t i = 0; i < VD_ROWS * VD_COLS; i++) {
video_mem[i] = front_buffer[i];
back_buffer[i] = front_buffer[i];
}
if (cursor_status) {
draw_cursor();
old_cursor_offset = cursor_offset;
}
}
void init_vga_textmode(size_t *_rows, size_t *_cols, bool managed) {
if (current_video_mode != -1) {
struct rm_regs r = {0};
r.eax = 0x0003;
rm_int(0x10, &r, &r);
current_video_mode = -1;
}
if (back_buffer == NULL) {
back_buffer = ext_mem_alloc(VD_ROWS * VD_COLS);
} else {
memset(back_buffer, 0, VD_ROWS * VD_COLS);
}
if (front_buffer == NULL) {
front_buffer = ext_mem_alloc(VD_ROWS * VD_COLS);
} else {
memset(front_buffer, 0, VD_ROWS * VD_COLS);
}
cursor_offset = 0;
cursor_status = true;
text_palette = 0x07;
scroll_enabled = true;
text_clear(false);
*_rows = VD_ROWS;
*_cols = VD_COLS / 2;
// VGA cursor code taken from: https://wiki.osdev.org/Text_Mode_Cursor
if (!managed) {
text_disable_cursor();
outb(0x3d4, 0x0a);
outb(0x3d5, (inb(0x3d5) & 0xc0) | 14);
outb(0x3d4, 0x0b);
outb(0x3d5, (inb(0x3d5) & 0xe0) | 15);
outb(0x3d4, 0x0f);
outb(0x3d5, 0);
outb(0x3d4, 0x0e);
outb(0x3d5, 0);
struct rm_regs r = {0};
r.eax = 0x0200;
rm_int(0x10, &r, &r);
} else {
outb(0x3d4, 0x0a);
outb(0x3d5, 0x20);
}
text_double_buffer_flush();
}
void text_double_buffer_flush(void) {
if (cursor_status) {
draw_cursor();
}
if (cursor_offset != old_cursor_offset || cursor_status == false) {
video_mem[old_cursor_offset + 1] = back_buffer[old_cursor_offset + 1];
}
for (size_t i = 0; i < VD_ROWS * VD_COLS; i++) {
if (back_buffer[i] == front_buffer[i]) {
continue;
}
if (cursor_status && i == cursor_offset + 1) {
continue;
}
front_buffer[i] = back_buffer[i];
video_mem[i] = back_buffer[i];
}
if (cursor_status) {
old_cursor_offset = cursor_offset;
}
}
void text_get_cursor_pos(size_t *x, size_t *y) {
*x = (cursor_offset % VD_COLS) / 2;
*y = cursor_offset / VD_COLS;
}
void text_move_character(size_t new_x, size_t new_y, size_t old_x, size_t old_y) {
if (old_x >= VD_COLS / 2 || old_y >= VD_ROWS
|| new_x >= VD_COLS / 2 || new_y >= VD_ROWS) {
return;
}
back_buffer[new_y * VD_COLS + new_x * 2] = back_buffer[old_y * VD_COLS + old_x * 2];
}
void text_set_cursor_pos(size_t x, size_t y) {
if (x >= VD_COLS / 2) {
if ((int)x < 0) {
x = 0;
} else {
x = VD_COLS / 2 - 1;
}
}
if (y >= VD_ROWS) {
if ((int)y < 0) {
y = 0;
} else {
y = VD_ROWS - 1;
}
}
cursor_offset = y * VD_COLS + x * 2;
}
static uint8_t ansi_colours[] = { 0, 4, 2, 6, 1, 5, 3, 7 };
void text_set_text_fg(size_t fg) {
text_palette = (text_palette & 0xf0) | ansi_colours[fg];
}
void text_set_text_bg(size_t bg) {
text_palette = (text_palette & 0x0f) | (ansi_colours[bg] << 4);
}
void text_set_text_fg_bright(size_t fg) {
text_palette = (text_palette & 0xf0) | (ansi_colours[fg] | (1 << 3));
}
void text_set_text_bg_bright(size_t bg) {
text_palette = (text_palette & 0x0f) | ((ansi_colours[bg] | (1 << 3)) << 4);
}
void text_set_text_fg_default(void) {
text_palette = (text_palette & 0xf0) | 7;
}
void text_set_text_bg_default(void) {
text_palette &= 0x0f;
}
void text_putchar(uint8_t c) {
back_buffer[cursor_offset] = c;
back_buffer[cursor_offset + 1] = text_palette;
if (cursor_offset / VD_COLS == term_context.scroll_bottom_margin - 1
&& cursor_offset % VD_COLS == VD_COLS - 2) {
if (scroll_enabled) {
text_scroll();
cursor_offset -= cursor_offset % VD_COLS;
}
} else if (cursor_offset >= (VIDEO_BOTTOM - 1)) {
cursor_offset -= cursor_offset % VD_COLS;
} else {
cursor_offset += 2;
}
}
#endif