Curle
8f0de18b37
The current system of separating x86 and x86_64 is no longer feasible with UEFI implementation. UEFI must have its own entry point. To this end, some optimisations have been implemented in the ASM files. Everything related to setup has been removed, leaving only the IDT and GDT utility functions. The linker may be required for the other files, though.
349 lines
9.5 KiB
C
349 lines
9.5 KiB
C
/************************
|
|
*** Team Kitty, 2019 ***
|
|
*** Sync ***
|
|
***********************/
|
|
|
|
/* This file provides all of the functionality
|
|
* needed to interact with the Text-Mode VGA
|
|
* buffer, which will soon be defunct due to
|
|
* EFI and graphics.
|
|
*
|
|
* This file will be left for the forseeable
|
|
* future, because it allows for easy debugging.
|
|
* 17/07/19 Curle
|
|
*/
|
|
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
//#include <string.h>
|
|
|
|
#include "kernel/tty.h"
|
|
#include "kernel/utils.h"
|
|
|
|
static const size_t TERM_WIDTH = 80;
|
|
static const size_t TERM_HEIGHT = 25;
|
|
|
|
static size_t terminal_row;
|
|
static size_t terminal_column;
|
|
|
|
static uint8_t current_color;
|
|
static uint16_t* term_buffer;
|
|
volatile uint16_t* vga_buffer = (uint16_t*)0xB8000;
|
|
|
|
void screen_initialize(void) {
|
|
|
|
terminal_row = 0;
|
|
terminal_column = 0;
|
|
current_color = vga_color_set(LIGHT_GREY, BLACK);
|
|
term_buffer = vga_buffer;
|
|
|
|
for (size_t y = 0; y < TERM_HEIGHT; y++) {
|
|
for (size_t x = 0; x < TERM_WIDTH; x++) {
|
|
const size_t offset = y * TERM_WIDTH + x;
|
|
term_buffer[offset] = vga_entry(' ', current_color);
|
|
}
|
|
}
|
|
}
|
|
|
|
void term_setcolor(enum vga_colors color) { current_color = color; }
|
|
|
|
void term_putentryat(char c, uint8_t color, size_t x, size_t y) {
|
|
const size_t offset = y * TERM_WIDTH + x;
|
|
term_buffer[offset] = vga_entry(c, color);
|
|
}
|
|
|
|
void term_putchar(char c) {
|
|
unsigned char uc = c;
|
|
|
|
// Handle escaped characters, such as newline, and crtn.
|
|
switch (uc) {
|
|
case '\n':
|
|
terminal_column = 0;
|
|
terminal_row += 1;
|
|
break;
|
|
default:
|
|
term_putentryat(uc, current_color, terminal_column, terminal_row);
|
|
if (++terminal_column == TERM_WIDTH) {
|
|
terminal_column = 0;
|
|
if (++terminal_row == TERM_HEIGHT) {
|
|
term_scroll(false);
|
|
terminal_row = 0;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
struct csi_sequence parse_csi(const char* data, size_t size) {
|
|
enum State { PARAMETER, INTERMEDIATE, FINAL, INVALID };
|
|
enum State state = PARAMETER;
|
|
|
|
struct csi_sequence sequence = {.parameter = NULL,
|
|
.parameter_len = 0,
|
|
.intermediate = NULL,
|
|
.intermediate_len = 0,
|
|
.final = NULL,
|
|
.valid = false};
|
|
|
|
for (size_t j = 0; j < size; j++) {
|
|
uint8_t c = data[j];
|
|
if (state == PARAMETER && (c >= 0x30 && c <= 0x3F)) {
|
|
if (!sequence.parameter)
|
|
sequence.parameter = data + j;
|
|
sequence.parameter_len++;
|
|
} else if (c >= 0x20 && c <= 0x2F) {
|
|
if (!sequence.intermediate)
|
|
sequence.intermediate = data + j;
|
|
sequence.intermediate_len++;
|
|
state = INTERMEDIATE;
|
|
} else if (c >= 0x40 && c <= 0x7F) {
|
|
sequence.final = data + j;
|
|
sequence.valid = true;
|
|
state = FINAL;
|
|
break;
|
|
} else {
|
|
// Parameter found in intermediate byte location, or byte out of
|
|
// range
|
|
state = INVALID;
|
|
break;
|
|
}
|
|
}
|
|
return sequence;
|
|
}
|
|
|
|
void term_write(const char* data, size_t size) {
|
|
for (size_t i = 0; i < size; i++) {
|
|
// Begin handling ANSI escape codes.
|
|
if (data[i] == 0x1b) { // The current character is ESC - the start of ANSI codes.
|
|
// term_writes("ANSI Code encountered: ");
|
|
|
|
bool string_terminated = false; // Flag used in some of the escape codes
|
|
|
|
// TODO: Should only progress if we have at least 2 more bytes
|
|
|
|
switch ((uint8_t)data[i + 1]) {
|
|
case '[': // CSI - Control Sequence Introducer (The most common one, hence it comes first)
|
|
{
|
|
struct csi_sequence sequence = parse_csi(data + i + 2, size - (i + 2));
|
|
if (sequence.valid) {
|
|
// Send it off to our handler function to keep this part clean
|
|
handleControlSequence(sequence);
|
|
i += sequence.parameter_len + sequence.intermediate_len + 2; // Move past sequence
|
|
}
|
|
} break;
|
|
// Single shifts are not handled, so just print them and exit
|
|
case 'N': // SS2 - Single Shift Two
|
|
term_writes("Single Shift Two\n");
|
|
break;
|
|
case 'O': // SS3 - Single Shift Three
|
|
term_writes("Single Shift Three\n");
|
|
break;
|
|
|
|
// Control Strings
|
|
case 'P': // DCS - Device Control String
|
|
term_writes("Device Control String");
|
|
string_terminated = false;
|
|
break;
|
|
case '\\': // ST - String Terminator
|
|
term_writes("String Terminator\n");
|
|
string_terminated = true;
|
|
break;
|
|
case ']': // OSC - Operating System Command
|
|
term_writes("Operating System Command\n");
|
|
string_terminated = false;
|
|
break;
|
|
case 'X': // SOS - Start Of String
|
|
term_writes("Start of String");
|
|
string_terminated = false;
|
|
break;
|
|
case '^': // PM - Privacy Message
|
|
term_writes("Privacy Message\n");
|
|
break;
|
|
case '_': // APC - Application Program Command
|
|
term_writes("Application Program Command\n");
|
|
break;
|
|
}
|
|
} else {
|
|
term_putchar(data[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void term_writes(const char* data) { term_write(data, strlen(data)); }
|
|
|
|
void puts(const char* string) {
|
|
term_write(string, strlen(string));
|
|
term_putchar('\n');
|
|
}
|
|
|
|
void handleControlSequence(struct csi_sequence sequence) {
|
|
// Check for our failsafes
|
|
|
|
if (sequence.valid) {
|
|
int n = 0; // Default of the flag used for a few items
|
|
|
|
// Parse parameters, we only care about a max 2 of number only parameters
|
|
// for now
|
|
int params[2] = {0};
|
|
int param_count = 0;
|
|
if (sequence.parameter_len) {
|
|
for (size_t i = 0; i < sequence.parameter_len && param_count < 1; i++) {
|
|
char c = sequence.parameter[i];
|
|
if (isDigit(c)) {
|
|
n = (n * 10) + (sequence.parameter[i] - '0');
|
|
} else if (c == ';') {
|
|
params[param_count++] = n;
|
|
}
|
|
}
|
|
params[param_count++] = n;
|
|
}
|
|
|
|
switch (*(sequence.final)) {
|
|
case 'H':
|
|
case 'f': // CUP - Cursor Position
|
|
// TODO: Check to see if we have 2 paramaters
|
|
if (params[0])
|
|
params[0]--;
|
|
if (params[1])
|
|
params[1]--;
|
|
set_cursor(params[0], params[1]);
|
|
break;
|
|
case 'A': // CUU - Cursor Up
|
|
if (!params[0])
|
|
params[0] = 1;
|
|
set_cursor(terminal_column, terminal_row - params[0]);
|
|
break;
|
|
case 'B': // CUD - Cursor Down
|
|
if (!params[0])
|
|
params[0] = 1;
|
|
set_cursor(terminal_column, terminal_row + params[0]);
|
|
break;
|
|
case 'C': // CUF - Cursor Forward
|
|
if (!params[0])
|
|
params[0] = 1;
|
|
set_cursor(terminal_column + params[0], terminal_row);
|
|
break;
|
|
case 'D': // CUB - Cursor Back
|
|
if (!params[0])
|
|
params[0] = 1;
|
|
set_cursor(terminal_column - params[0], terminal_row);
|
|
break;
|
|
case 'E': // CNL - Cursor Next Line
|
|
if (!params[0])
|
|
params[0] = 1;
|
|
set_cursor(0, terminal_row + params[0]);
|
|
break;
|
|
case 'F': // CPL - Cursor Previous Line
|
|
if (!params[0])
|
|
params[0] = 1;
|
|
set_cursor(0, terminal_row - params[0]);
|
|
break;
|
|
case 'G': // CHA - Cursor Horizontal Absolute
|
|
if (params[0])
|
|
params[0]--;
|
|
set_cursor(params[0], terminal_row);
|
|
break;
|
|
case 'J': // ED - Erase in Display
|
|
{
|
|
// current cursor pos = y * width + x
|
|
int pos = terminal_row * 80 + terminal_column;
|
|
if (params[0] == 0) { // Clear from cursor to end of screen
|
|
for (; pos < (25 * 80); pos++) {
|
|
vga_buffer[pos] = '\0';
|
|
}
|
|
} else if (params[0] == 1) { // Clear from cursor to beginning
|
|
for (; pos > 0; pos--) {
|
|
vga_buffer[pos] = '\0';
|
|
}
|
|
} else if (params[0] == 2 || params[0] == 3) { // Clear entire screen
|
|
// TODO: Support scrollback buffer? (n = 3)
|
|
for (int i = 0; i < (25 * 80); i++) {
|
|
vga_buffer[0] = '\0';
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 'K': // EL - Erase in Line
|
|
{
|
|
int pos = terminal_row * 80 + terminal_column;
|
|
if (params[0] == 0) { // From cursor to end of line
|
|
int endPos = (terminal_row + 1) * 80 - 1; // End of line = current row + 25 columns = current row + 1
|
|
for (; pos < endPos; pos++) {
|
|
vga_buffer[pos] = '\0';
|
|
}
|
|
} else if (params[0] == 1) { // From cursor to start of line
|
|
int endPos = terminal_row * 80; // Start of line = end of previous line + 1 == current line
|
|
for (; pos > endPos; pos--) {
|
|
vga_buffer[pos] = '\0';
|
|
}
|
|
} else if (params[0] == 2) { // Entire current line
|
|
pos = terminal_row * 80;
|
|
int endPos = (terminal_row + 1) * 80 - 1;
|
|
for (; pos < endPos; pos++) {
|
|
vga_buffer[pos] = '\0';
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 'S': // SU - Scroll Up
|
|
term_scroll(true);
|
|
break;
|
|
case 'T': // SD - Scroll Down
|
|
term_scroll(false);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool isDigit(char c) { return c >= '0' && c <= '9'; }
|
|
|
|
void set_cursor(int n, int m) {
|
|
terminal_column = n;
|
|
terminal_row = m;
|
|
}
|
|
|
|
void term_scroll(bool down) {
|
|
|
|
int current_pos;
|
|
if (down) {
|
|
current_pos = 25 * 80; // Start of the last line.
|
|
} else {
|
|
current_pos = 160; // Start of the second line.
|
|
}
|
|
|
|
unsigned char* term_buffer = (unsigned char*)vga_buffer;
|
|
|
|
if (down) { // To scroll down, move every character into the one below it, or "pull" every character down
|
|
while (current_pos > 80) {
|
|
term_buffer[current_pos + 160] = vga_buffer[current_pos];
|
|
term_buffer[current_pos + 159] = vga_buffer[current_pos - 1];
|
|
current_pos -= 2;
|
|
}
|
|
} else {
|
|
while (current_pos <= 4000) {
|
|
term_buffer[current_pos - 160 /*The character immediately below it*/] =
|
|
vga_buffer[current_pos]; // Move each character up a line
|
|
term_buffer[current_pos - 159 /*The color of the character below*/] =
|
|
vga_buffer[current_pos + 1]; // As well as its color
|
|
current_pos += 2; // Move to next char
|
|
}
|
|
}
|
|
|
|
if (down) {
|
|
current_pos = 0; // Start of first line
|
|
for (; current_pos < 80; current_pos++) {
|
|
term_buffer[current_pos] = '\0';
|
|
current_pos += 2;
|
|
}
|
|
} else {
|
|
; // Start of the last line
|
|
// Wipe out the bottom line
|
|
for (current_pos = 3840; current_pos <= 3920; current_pos += 2) {
|
|
term_buffer[current_pos] = 0;
|
|
}
|
|
}
|
|
terminal_row = 24; // Start writing on the last line
|
|
terminal_column = 0;
|
|
}
|