360 lines
10 KiB
C
360 lines
10 KiB
C
#if defined (BIOS)
|
|
|
|
#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/misc.h>
|
|
#include <lib/term.h>
|
|
#include <mm/pmm.h>
|
|
|
|
#define VIDEO_BOTTOM ((VD_ROWS * VD_COLS) - 1)
|
|
|
|
static void draw_cursor(struct textmode_context *ctx) {
|
|
uint8_t pal = ctx->back_buffer[ctx->cursor_offset + 1];
|
|
ctx->video_mem[ctx->cursor_offset + 1] = ((pal & 0xf0) >> 4) | ((pal & 0x0f) << 4);
|
|
}
|
|
|
|
static void text_save_state(struct term_context *_ctx) {
|
|
struct textmode_context *ctx = (void *)_ctx;
|
|
ctx->saved_state_text_palette = ctx->text_palette;
|
|
ctx->saved_state_cursor_offset = ctx->cursor_offset;
|
|
}
|
|
|
|
static void text_restore_state(struct term_context *_ctx) {
|
|
struct textmode_context *ctx = (void *)_ctx;
|
|
ctx->text_palette = ctx->saved_state_text_palette;
|
|
ctx->cursor_offset = ctx->saved_state_cursor_offset;
|
|
}
|
|
|
|
static void text_swap_palette(struct term_context *_ctx) {
|
|
struct textmode_context *ctx = (void *)_ctx;
|
|
ctx->text_palette = (ctx->text_palette << 4) | (ctx->text_palette >> 4);
|
|
}
|
|
|
|
static void text_scroll(struct term_context *_ctx) {
|
|
struct textmode_context *ctx = (void *)_ctx;
|
|
|
|
// move the text up by one row
|
|
for (size_t i = _ctx->scroll_top_margin * VD_COLS;
|
|
i < (_ctx->scroll_bottom_margin - 1) * VD_COLS; i++) {
|
|
ctx->back_buffer[i] = ctx->back_buffer[i + VD_COLS];
|
|
}
|
|
// clear the last line of the screen
|
|
for (size_t i = (_ctx->scroll_bottom_margin - 1) * VD_COLS;
|
|
i < _ctx->scroll_bottom_margin * VD_COLS; i += 2) {
|
|
ctx->back_buffer[i] = ' ';
|
|
ctx->back_buffer[i + 1] = ctx->text_palette;
|
|
}
|
|
}
|
|
|
|
static void text_revscroll(struct term_context *_ctx) {
|
|
struct textmode_context *ctx = (void *)_ctx;
|
|
|
|
// move the text up by one row
|
|
for (size_t i = (_ctx->scroll_bottom_margin - 1) * VD_COLS - 2; ; i--) {
|
|
ctx->back_buffer[i + VD_COLS] = ctx->back_buffer[i];
|
|
if (i == _ctx->scroll_top_margin * VD_COLS) {
|
|
break;
|
|
}
|
|
}
|
|
// clear the first line of the screen
|
|
for (size_t i = _ctx->scroll_top_margin * VD_COLS;
|
|
i < (_ctx->scroll_top_margin + 1) * VD_COLS; i += 2) {
|
|
ctx->back_buffer[i] = ' ';
|
|
ctx->back_buffer[i + 1] = ctx->text_palette;
|
|
}
|
|
}
|
|
|
|
static void text_clear(struct term_context *_ctx, bool move) {
|
|
struct textmode_context *ctx = (void *)_ctx;
|
|
|
|
for (size_t i = 0; i < VIDEO_BOTTOM; i += 2) {
|
|
ctx->back_buffer[i] = ' ';
|
|
ctx->back_buffer[i + 1] = ctx->text_palette;
|
|
}
|
|
if (move) {
|
|
ctx->cursor_offset = 0;
|
|
}
|
|
}
|
|
|
|
static void text_enable_cursor(struct term_context *_ctx) {
|
|
struct textmode_context *ctx = (void *)_ctx;
|
|
|
|
ctx->cursor_status = true;
|
|
}
|
|
|
|
static bool text_disable_cursor(struct term_context *_ctx) {
|
|
struct textmode_context *ctx = (void *)_ctx;
|
|
|
|
bool ret = ctx->cursor_status;
|
|
ctx->cursor_status = false;
|
|
return ret;
|
|
}
|
|
|
|
static void text_full_refresh(struct term_context *_ctx) {
|
|
struct textmode_context *ctx = (void *)_ctx;
|
|
|
|
for (size_t i = 0; i < VD_ROWS * VD_COLS; i++) {
|
|
ctx->video_mem[i] = ctx->front_buffer[i];
|
|
ctx->back_buffer[i] = ctx->front_buffer[i];
|
|
}
|
|
|
|
if (ctx->cursor_status) {
|
|
draw_cursor(ctx);
|
|
ctx->old_cursor_offset = ctx->cursor_offset;
|
|
}
|
|
}
|
|
|
|
static void text_double_buffer_flush(struct term_context *_ctx) {
|
|
struct textmode_context *ctx = (void *)_ctx;
|
|
|
|
if (ctx->cursor_status) {
|
|
draw_cursor(ctx);
|
|
}
|
|
|
|
if (ctx->cursor_offset != ctx->old_cursor_offset || ctx->cursor_status == false) {
|
|
ctx->video_mem[ctx->old_cursor_offset + 1] = ctx->back_buffer[ctx->old_cursor_offset + 1];
|
|
}
|
|
|
|
for (size_t i = 0; i < VD_ROWS * VD_COLS; i++) {
|
|
if (ctx->back_buffer[i] == ctx->front_buffer[i]) {
|
|
continue;
|
|
}
|
|
|
|
if (ctx->cursor_status && i == ctx->cursor_offset + 1) {
|
|
continue;
|
|
}
|
|
|
|
ctx->front_buffer[i] = ctx->back_buffer[i];
|
|
ctx->video_mem[i] = ctx->back_buffer[i];
|
|
}
|
|
|
|
if (ctx->cursor_status) {
|
|
ctx->old_cursor_offset = ctx->cursor_offset;
|
|
}
|
|
}
|
|
|
|
static void text_get_cursor_pos(struct term_context *_ctx, size_t *x, size_t *y) {
|
|
struct textmode_context *ctx = (void *)_ctx;
|
|
|
|
*x = (ctx->cursor_offset % VD_COLS) / 2;
|
|
*y = ctx->cursor_offset / VD_COLS;
|
|
}
|
|
|
|
static void text_move_character(struct term_context *_ctx, size_t new_x, size_t new_y, size_t old_x, size_t old_y) {
|
|
struct textmode_context *ctx = (void *)_ctx;
|
|
|
|
if (old_x >= VD_COLS / 2 || old_y >= VD_ROWS
|
|
|| new_x >= VD_COLS / 2 || new_y >= VD_ROWS) {
|
|
return;
|
|
}
|
|
|
|
ctx->back_buffer[new_y * VD_COLS + new_x * 2] = ctx->back_buffer[old_y * VD_COLS + old_x * 2];
|
|
}
|
|
|
|
static void text_set_cursor_pos(struct term_context *_ctx, size_t x, size_t y) {
|
|
struct textmode_context *ctx = (void *)_ctx;
|
|
|
|
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;
|
|
}
|
|
}
|
|
ctx->cursor_offset = y * VD_COLS + x * 2;
|
|
}
|
|
|
|
static uint8_t ansi_colours[] = { 0, 4, 2, 6, 1, 5, 3, 7 };
|
|
|
|
static void text_set_text_fg(struct term_context *_ctx, size_t fg) {
|
|
struct textmode_context *ctx = (void *)_ctx;
|
|
ctx->text_palette = (ctx->text_palette & 0xf0) | ansi_colours[fg];
|
|
}
|
|
|
|
static void text_set_text_bg(struct term_context *_ctx, size_t bg) {
|
|
struct textmode_context *ctx = (void *)_ctx;
|
|
ctx->text_palette = (ctx->text_palette & 0x0f) | (ansi_colours[bg] << 4);
|
|
}
|
|
|
|
static void text_set_text_fg_bright(struct term_context *_ctx, size_t fg) {
|
|
struct textmode_context *ctx = (void *)_ctx;
|
|
ctx->text_palette = (ctx->text_palette & 0xf0) | (ansi_colours[fg] | (1 << 3));
|
|
}
|
|
|
|
static void text_set_text_bg_bright(struct term_context *_ctx, size_t bg) {
|
|
struct textmode_context *ctx = (void *)_ctx;
|
|
ctx->text_palette = (ctx->text_palette & 0x0f) | ((ansi_colours[bg] | (1 << 3)) << 4);
|
|
}
|
|
|
|
static void text_set_text_fg_rgb(struct term_context *ctx, uint32_t n) {
|
|
(void)ctx;
|
|
(void)n;
|
|
}
|
|
|
|
static void text_set_text_bg_rgb(struct term_context *ctx, uint32_t n) {
|
|
(void)ctx;
|
|
(void)n;
|
|
}
|
|
|
|
static void text_set_text_fg_default(struct term_context *_ctx) {
|
|
struct textmode_context *ctx = (void *)_ctx;
|
|
ctx->text_palette = (ctx->text_palette & 0xf0) | 7;
|
|
}
|
|
|
|
static void text_set_text_bg_default(struct term_context *_ctx) {
|
|
struct textmode_context *ctx = (void *)_ctx;
|
|
ctx->text_palette &= 0x0f;
|
|
}
|
|
|
|
static void text_putchar(struct term_context *_ctx, uint8_t c) {
|
|
struct textmode_context *ctx = (void *)_ctx;
|
|
|
|
ctx->back_buffer[ctx->cursor_offset] = c;
|
|
ctx->back_buffer[ctx->cursor_offset + 1] = ctx->text_palette;
|
|
if (ctx->cursor_offset / VD_COLS == _ctx->scroll_bottom_margin - 1
|
|
&& ctx->cursor_offset % VD_COLS == VD_COLS - 2) {
|
|
if (_ctx->scroll_enabled) {
|
|
text_scroll(_ctx);
|
|
ctx->cursor_offset -= ctx->cursor_offset % VD_COLS;
|
|
}
|
|
} else if (ctx->cursor_offset >= (VIDEO_BOTTOM - 1)) {
|
|
ctx->cursor_offset -= ctx->cursor_offset % VD_COLS;
|
|
} else {
|
|
ctx->cursor_offset += 2;
|
|
}
|
|
}
|
|
|
|
static void text_deinit(struct term_context *_ctx, void (*_free)(void *, size_t)) {
|
|
struct textmode_context *ctx = (void *)_ctx;
|
|
|
|
if (ctx->back_buffer != NULL) {
|
|
_free(ctx->back_buffer, VD_ROWS * VD_COLS);
|
|
ctx->back_buffer = NULL;
|
|
}
|
|
|
|
if (ctx->front_buffer != NULL) {
|
|
_free(ctx->front_buffer, VD_ROWS * VD_COLS);
|
|
ctx->front_buffer = NULL;
|
|
}
|
|
}
|
|
|
|
static struct textmode_context term_local_struct;
|
|
|
|
void vga_textmode_init(bool managed) {
|
|
if (term != NULL) {
|
|
term->deinit(term, pmm_free);
|
|
term = NULL;
|
|
}
|
|
|
|
if (quiet) {
|
|
return;
|
|
}
|
|
|
|
if (current_video_mode != 0x3) {
|
|
struct rm_regs r = {0};
|
|
r.eax = 0x0003;
|
|
rm_int(0x10, &r, &r);
|
|
|
|
current_video_mode = 0x3;
|
|
}
|
|
|
|
struct textmode_context *ctx = &term_local_struct;
|
|
term = &term_local_struct.term;
|
|
|
|
if (ctx->back_buffer == NULL) {
|
|
ctx->back_buffer = ext_mem_alloc(VD_ROWS * VD_COLS);
|
|
} else {
|
|
memset(ctx->back_buffer, 0, VD_ROWS * VD_COLS);
|
|
}
|
|
if (ctx->front_buffer == NULL) {
|
|
ctx->front_buffer = ext_mem_alloc(VD_ROWS * VD_COLS);
|
|
} else {
|
|
memset(ctx->front_buffer, 0, VD_ROWS * VD_COLS);
|
|
}
|
|
|
|
ctx->cursor_offset = 0;
|
|
ctx->cursor_status = true;
|
|
ctx->text_palette = 0x07;
|
|
|
|
ctx->video_mem = (volatile uint8_t *)0xb8000;
|
|
|
|
text_clear(term, false);
|
|
|
|
// VGA cursor code taken from: https://wiki.osdev.org/Text_Mode_Cursor
|
|
|
|
if (!managed) {
|
|
text_disable_cursor(term);
|
|
|
|
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(term);
|
|
|
|
if (managed && serial) {
|
|
term->cols = 80;
|
|
term->rows = 24;
|
|
} else {
|
|
term->cols = 80;
|
|
term->rows = 25;
|
|
}
|
|
|
|
term->raw_putchar = text_putchar;
|
|
term->clear = text_clear;
|
|
term->enable_cursor = text_enable_cursor;
|
|
term->disable_cursor = text_disable_cursor;
|
|
term->set_cursor_pos = text_set_cursor_pos;
|
|
term->get_cursor_pos = text_get_cursor_pos;
|
|
term->set_text_fg = text_set_text_fg;
|
|
term->set_text_bg = text_set_text_bg;
|
|
term->set_text_fg_bright = text_set_text_fg_bright;
|
|
term->set_text_bg_bright = text_set_text_bg_bright;
|
|
term->set_text_fg_rgb = text_set_text_fg_rgb;
|
|
term->set_text_bg_rgb = text_set_text_bg_rgb;
|
|
term->set_text_fg_default = text_set_text_fg_default;
|
|
term->set_text_bg_default = text_set_text_bg_default;
|
|
term->move_character = text_move_character;
|
|
term->scroll = text_scroll;
|
|
term->revscroll = text_revscroll;
|
|
term->swap_palette = text_swap_palette;
|
|
term->save_state = text_save_state;
|
|
term->restore_state = text_restore_state;
|
|
term->double_buffer_flush = text_double_buffer_flush;
|
|
term->full_refresh = text_full_refresh;
|
|
term->deinit = text_deinit;
|
|
|
|
term->in_bootloader = true;
|
|
|
|
term_context_reinit(term);
|
|
term->full_refresh(term);
|
|
|
|
term_backend = TEXTMODE;
|
|
}
|
|
|
|
#endif
|