2019-04-01 01:18:48 +00:00
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include <kernel/tty.h>
|
2019-04-01 11:21:00 +00:00
|
|
|
#include <kernel/utils.h>
|
2019-04-01 01:18:48 +00:00
|
|
|
|
|
|
|
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;
|
2019-04-01 11:21:00 +00:00
|
|
|
static uint16_t* const vga_buffer = (uint16_t*) 0xB8000;
|
2019-04-01 01:18:48 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-04-01 11:21:00 +00:00
|
|
|
void term_putentryat (char c, uint8_t color, size_t x, size_t y) {
|
2019-04-01 01:18:48 +00:00
|
|
|
const size_t offset = y * TERM_WIDTH + x;
|
|
|
|
term_buffer[offset] = vga_entry(c, color);
|
|
|
|
}
|
|
|
|
|
|
|
|
void term_putchar(char c) {
|
|
|
|
unsigned char uc = c;
|
2019-04-01 11:46:40 +00:00
|
|
|
|
2019-04-02 21:46:37 +00:00
|
|
|
//Handle escaped characters, such as newline, and crtn.
|
2019-04-01 11:46:40 +00:00
|
|
|
switch (uc) {
|
|
|
|
case '\n':
|
|
|
|
terminal_column = 0;
|
|
|
|
terminal_row += 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
term_putentryat(uc, current_color, terminal_column, terminal_row);
|
|
|
|
break;
|
|
|
|
}
|
2019-04-01 01:18:48 +00:00
|
|
|
|
|
|
|
if(++terminal_column == TERM_WIDTH)
|
2019-04-01 11:21:00 +00:00
|
|
|
{
|
2019-04-01 01:18:48 +00:00
|
|
|
terminal_column = 0;
|
|
|
|
if(++terminal_row == TERM_HEIGHT){
|
2019-04-02 21:46:37 +00:00
|
|
|
term_scroll(false);
|
2019-04-01 01:18:48 +00:00
|
|
|
terminal_row = 0;
|
|
|
|
}
|
2019-04-01 11:21:00 +00:00
|
|
|
}
|
2019-04-01 01:18:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void term_write(const char* data, size_t size) {
|
2019-04-02 21:46:37 +00:00
|
|
|
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
|
|
|
|
switch(data[i+1]) {
|
|
|
|
case 0x9b/*[*/: //CSI - Control Sequence Introducer (The most common one, hence it comes first)
|
|
|
|
char* controlSequence;
|
|
|
|
if(data[i+2] == NULL) {
|
|
|
|
controlSequence = '\0';
|
|
|
|
for(size_t j = 0; j < (strlen(data) - (i+2); j++) {
|
|
|
|
if(data[j] == ' ')
|
|
|
|
break;
|
|
|
|
controlSequence += data[j];
|
|
|
|
}
|
|
|
|
handleControlSequence(controlSequence); //Send it off to our handler function to keep this part clean
|
|
|
|
break;
|
|
|
|
|
|
|
|
//Single shifts are not handled, so just print them and exit
|
|
|
|
case 0x8e/*N*/: //SS2 - Single Shift Two
|
|
|
|
term_writes("Single Shift Two\n");
|
|
|
|
break;
|
|
|
|
case 0x8f/*O*/: //SS3 - Single Shift Three
|
|
|
|
term_writes("Single Shift Three\n");
|
|
|
|
break;
|
|
|
|
|
|
|
|
//Control Strings
|
|
|
|
case 0x90/*P*/: //DCS - Device Control String
|
|
|
|
term_writes("Device Control String");
|
|
|
|
string_terminated = false;
|
|
|
|
break;
|
|
|
|
case 0x9c/*\*/: //ST - String Terminator
|
|
|
|
term_writes("String Terminator\n");
|
|
|
|
string_terminated = true;
|
|
|
|
break;
|
|
|
|
case 0x9d/*]*/: //OSC - Operating System Command
|
|
|
|
term_writes("Operating System Command\n");
|
|
|
|
string_terminated = false;
|
|
|
|
break;
|
|
|
|
case 0x98/*X*/: //SOS - Start Of String
|
|
|
|
term_writes("Start of String");
|
|
|
|
string_terminated = false;
|
|
|
|
break;
|
|
|
|
case 0x9e/*^*/: //PM - Privacy Message
|
|
|
|
term_writes("Privacy Message\n");
|
|
|
|
break;
|
|
|
|
case 0x9f/*_*/: //APC - Application Program Command
|
|
|
|
term_writes("Application Program Command\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-04-01 01:18:48 +00:00
|
|
|
term_putchar(data[i]);
|
2019-04-02 21:46:37 +00:00
|
|
|
}
|
2019-04-01 01:18:48 +00:00
|
|
|
}
|
|
|
|
|
2019-04-01 11:46:40 +00:00
|
|
|
void term_writes(const char* data) {
|
|
|
|
term_write(data, strlen(data));
|
|
|
|
}
|
|
|
|
|
2019-04-01 11:21:00 +00:00
|
|
|
void puts(const char* string) {
|
|
|
|
term_write(string, strlen(string));
|
|
|
|
term_putchar('\n');
|
|
|
|
}
|
|
|
|
|
2019-04-02 21:46:37 +00:00
|
|
|
void handleControlSequence(const char* sequence) {
|
|
|
|
//Check for our failsafes
|
|
|
|
if(sequence) {
|
|
|
|
if(sequence != '\0') {
|
|
|
|
int n = 0; //Default of the flag used for a few items
|
|
|
|
|
|
|
|
bool nPresent = false;
|
|
|
|
bool terminated = false;
|
|
|
|
for(size_t i = 0; i < strlen(sequence); i++) {
|
|
|
|
if(isDigit(sequence[i])) {
|
|
|
|
nPresent = true;
|
|
|
|
n = (n*10) + sequence[i];
|
|
|
|
} else if(n > 80) {
|
|
|
|
n = 80;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(n == 0)
|
|
|
|
n = 1;
|
|
|
|
|
|
|
|
switch(sequence[i]) {
|
|
|
|
case ';':
|
|
|
|
terminated = true;
|
|
|
|
int m = 0;
|
|
|
|
for(size_t j = 0; j < strlen(sequence - i); j++) {
|
|
|
|
if(sequence[j] == 'H' ||sequence[j] == 'f')
|
|
|
|
break;
|
|
|
|
|
|
|
|
if(isDigit(sequence[j])) {
|
|
|
|
m = (m*10) + sequence[j];
|
|
|
|
} else if(j > 80) break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(m == 0)
|
|
|
|
m = 1;
|
|
|
|
cursor_pos(n, m);
|
|
|
|
|
|
|
|
break;
|
|
|
|
case 'A': //CUU - Cursor Up
|
|
|
|
//cursor_pos(x, y)
|
|
|
|
|
|
|
|
cursor_pos(terminal_column, terminal_row + n);
|
|
|
|
break;
|
|
|
|
case 'B': //CUD - Cursor Down
|
|
|
|
cursor_pos(terminal_column, terminal_row - n);
|
|
|
|
break;
|
|
|
|
case 'C': //CUF - Cursor Forward
|
|
|
|
cursor_pos(terminal_column + n, terminal_row);
|
|
|
|
break;
|
|
|
|
case 'D': //CUB - Cursor Back
|
|
|
|
cursor_pos(terminal_column - n, terminal_row);
|
|
|
|
break;
|
|
|
|
case 'E': //CNL - Cursor Next Line
|
|
|
|
cursor_pos(0, terminal_row + n);
|
|
|
|
break;
|
|
|
|
case 'F': //CPL - Cursor Previous Line
|
|
|
|
cursor_pos(0, terminal_row - n);
|
|
|
|
break;
|
|
|
|
case 'G': //CHA - Cursor Horizontal Absolute
|
|
|
|
cursor_pos(n, terminal_row);
|
|
|
|
case 'J': //ED - Erase in Display
|
|
|
|
//current cursor pos = y * width + x
|
|
|
|
int pos = terminal_row * 80 + terminal_column;
|
|
|
|
if(!nPresent) {
|
|
|
|
for(pos < (25 * 80); pos++) {
|
|
|
|
vga_buffer[pos] = '\0';
|
|
|
|
}
|
|
|
|
n = 0;
|
|
|
|
} else if(nPresent && n == 1) {
|
|
|
|
for(pos > 0; pos--) {
|
|
|
|
vga_buffer[pos] = '\0';
|
|
|
|
}
|
|
|
|
} else if(n == 2 || 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(!nPresent) { //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(nPresent && n == 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(nPresent && n == 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(const char c) {
|
|
|
|
return c == '0' || c == '1' || c == '2' || c == '3' ||
|
|
|
|
c == '4' || c == '5' || c == '6' || c == '7' ||
|
|
|
|
c == '8' || c == '9';
|
|
|
|
}
|
|
|
|
|
|
|
|
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.
|
|
|
|
}
|
2019-04-01 11:21:00 +00:00
|
|
|
|
|
|
|
unsigned char* term_buffer = (unsigned char*) vga_buffer;
|
2019-04-02 21:46:37 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
2019-04-01 11:21:00 +00:00
|
|
|
}
|
|
|
|
|
2019-04-02 21:46:37 +00:00
|
|
|
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; i++) {
|
|
|
|
term_buffer[current_pos] = 0;
|
|
|
|
current_pos += 2;
|
|
|
|
}
|
2019-04-01 11:21:00 +00:00
|
|
|
}
|
|
|
|
|
2019-04-02 21:46:37 +00:00
|
|
|
terminal_row = 24; //Start writing on the last line
|
|
|
|
terminal_column = 0;
|
2019-04-01 01:18:48 +00:00
|
|
|
}
|