Finish initial ANSI Escape Sequence support - basic CSI sequences (cursor moving, line erasing) work

This commit is contained in:
Jessica Creighton 2019-04-03 16:44:48 -04:00
parent 94c1eb2279
commit bc6f94e353
2 changed files with 365 additions and 312 deletions

View File

@ -1,294 +1,332 @@
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include <kernel/tty.h> #include "kernel/tty.h"
#include <kernel/utils.h> #include "kernel/utils.h"
static const size_t TERM_WIDTH=80; static const size_t TERM_WIDTH=80;
static const size_t TERM_HEIGHT=25; static const size_t TERM_HEIGHT=25;
static size_t terminal_row; static size_t terminal_row;
static size_t terminal_column; static size_t terminal_column;
static uint8_t current_color; static uint8_t current_color;
static uint16_t* term_buffer; static uint16_t* term_buffer;
static uint16_t* const vga_buffer = (uint16_t*) 0xB8000; static uint16_t* const vga_buffer = (uint16_t*) 0xB8000;
void screen_initialize(void) { void screen_initialize(void) {
terminal_row = 0; terminal_row = 0;
terminal_column = 0; terminal_column = 0;
current_color = vga_color_set(LIGHT_GREY, BLACK); current_color = vga_color_set(LIGHT_GREY, BLACK);
term_buffer = vga_buffer; term_buffer = vga_buffer;
for (size_t y = 0; y < TERM_HEIGHT; y++) { for (size_t y = 0; y < TERM_HEIGHT; y++) {
for(size_t x = 0; x < TERM_WIDTH; x++) { for(size_t x = 0; x < TERM_WIDTH; x++) {
const size_t offset = y * TERM_WIDTH + x; const size_t offset = y * TERM_WIDTH + x;
term_buffer[offset] = vga_entry(' ', current_color); term_buffer[offset] = vga_entry(' ', current_color);
} }
} }
} }
void term_setcolor(enum vga_colors color) { void term_setcolor(enum vga_colors color) {
current_color = color; current_color = color;
} }
void term_putentryat (char c, uint8_t color, size_t x, size_t y) { void term_putentryat (char c, uint8_t color, size_t x, size_t y) {
const size_t offset = y * TERM_WIDTH + x; const size_t offset = y * TERM_WIDTH + x;
term_buffer[offset] = vga_entry(c, color); term_buffer[offset] = vga_entry(c, color);
} }
void term_putchar(char c) { void term_putchar(char c) {
unsigned char uc = c; unsigned char uc = c;
//Handle escaped characters, such as newline, and crtn. //Handle escaped characters, such as newline, and crtn.
switch (uc) { switch (uc) {
case '\n': case '\n':
terminal_column = 0; terminal_column = 0;
terminal_row += 1; terminal_row += 1;
break; break;
default: default:
term_putentryat(uc, current_color, terminal_column, terminal_row); term_putentryat(uc, current_color, terminal_column, terminal_row);
break; if (++terminal_column == TERM_WIDTH) {
} terminal_column = 0;
if (++terminal_row == TERM_HEIGHT) {
if(++terminal_column == TERM_WIDTH) term_scroll(false);
{ terminal_row = 0;
terminal_column = 0; }
if(++terminal_row == TERM_HEIGHT){ }
term_scroll(false); break;
terminal_row = 0; }
}
} }
}
struct csi_sequence parse_csi(const char* data, size_t size) {
void term_write(const char* data, size_t size) { enum State { PARAMETER, INTERMEDIATE, FINAL, INVALID };
for(size_t i = 0; i < size; i++) { enum State state = PARAMETER;
//Begin handling ANSI escape codes.
if(data[i] == 0x1B) { //The current character is ESC - the start of ANSI codes. struct csi_sequence sequence = {
term_writes("ANSI Code encountered: "); .parameter = NULL,
.parameter_len = 0,
bool string_terminated = false; //Flag used in some of the escape codes .intermediate = NULL,
switch(data[i+1]) { .intermediate_len = 0,
case 0x9b/*[*/: //CSI - Control Sequence Introducer (The most common one, hence it comes first) .final = NULL,
char* controlSequence; .valid = false
if(data[i+2] == NULL) { };
controlSequence = '\0';
for(size_t j = 0; j < (strlen(data) - (i+2); j++) { for (size_t j = 0; j < size; j++) {
if(data[j] == ' ') uint8_t c = data[j];
break; if (state == PARAMETER && (c >= 0x30 && c <= 0x3F)) {
controlSequence += data[j]; if (!sequence.parameter)
} sequence.parameter = data + j;
handleControlSequence(controlSequence); //Send it off to our handler function to keep this part clean sequence.parameter_len++;
break; } else if (c >= 0x20 && c <= 0x2F) {
if (!sequence.intermediate)
//Single shifts are not handled, so just print them and exit sequence.intermediate = data + j;
case 0x8e/*N*/: //SS2 - Single Shift Two sequence.intermediate_len++;
term_writes("Single Shift Two\n"); state = INTERMEDIATE;
break; } else if (c >= 0x40 && c <= 0x7F) {
case 0x8f/*O*/: //SS3 - Single Shift Three sequence.final = data + j;
term_writes("Single Shift Three\n"); sequence.valid = true;
break; state = FINAL;
break;
//Control Strings } else {
case 0x90/*P*/: //DCS - Device Control String // Parameter found in intermediate byte location, or byte out of
term_writes("Device Control String"); // range
string_terminated = false; state = INVALID;
break; break;
case 0x9c/*\*/: //ST - String Terminator }
term_writes("String Terminator\n"); }
string_terminated = true; return sequence;
break; }
case 0x9d/*]*/: //OSC - Operating System Command
term_writes("Operating System Command\n"); void term_write(const char* data, size_t size) {
string_terminated = false; for(size_t i = 0; i < size; i++) {
break; //Begin handling ANSI escape codes.
case 0x98/*X*/: //SOS - Start Of String if(data[i] == 0x1b) { //The current character is ESC - the start of ANSI codes.
term_writes("Start of String"); //term_writes("ANSI Code encountered: ");
string_terminated = false;
break; bool string_terminated = false; //Flag used in some of the escape codes
case 0x9e/*^*/: //PM - Privacy Message
term_writes("Privacy Message\n"); // TODO: Should only progress if we have at least 2 more bytes
break;
case 0x9f/*_*/: //APC - Application Program Command switch ((uint8_t)data[i + 1]) {
term_writes("Application Program Command\n"); case '[': //CSI - Control Sequence Introducer (The most common one, hence it comes first)
break; {
} 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);
term_putchar(data[i]); i += sequence.parameter_len + sequence.intermediate_len + 2; // Move past sequence
} }
} }
break;
void term_writes(const char* data) { //Single shifts are not handled, so just print them and exit
term_write(data, strlen(data)); case 'N': //SS2 - Single Shift Two
} term_writes("Single Shift Two\n");
break;
void puts(const char* string) { case 'O': //SS3 - Single Shift Three
term_write(string, strlen(string)); term_writes("Single Shift Three\n");
term_putchar('\n'); break;
}
//Control Strings
void handleControlSequence(const char* sequence) { case 'P': //DCS - Device Control String
//Check for our failsafes term_writes("Device Control String");
if(sequence) { string_terminated = false;
if(sequence != '\0') { break;
int n = 0; //Default of the flag used for a few items case '\\': //ST - String Terminator
term_writes("String Terminator\n");
bool nPresent = false; string_terminated = true;
bool terminated = false; break;
for(size_t i = 0; i < strlen(sequence); i++) { case ']': //OSC - Operating System Command
if(isDigit(sequence[i])) { term_writes("Operating System Command\n");
nPresent = true; string_terminated = false;
n = (n*10) + sequence[i]; break;
} else if(n > 80) { case 'X': //SOS - Start Of String
n = 80; term_writes("Start of String");
break; string_terminated = false;
} break;
} case '^': //PM - Privacy Message
term_writes("Privacy Message\n");
if(n == 0) break;
n = 1; case '_': //APC - Application Program Command
term_writes("Application Program Command\n");
switch(sequence[i]) { break;
case ';': }
terminated = true; } else {
int m = 0; term_putchar(data[i]);
for(size_t j = 0; j < strlen(sequence - i); j++) { }
if(sequence[j] == 'H' ||sequence[j] == 'f') }
break; }
if(isDigit(sequence[j])) { void term_writes(const char* data) {
m = (m*10) + sequence[j]; term_write(data, strlen(data));
} else if(j > 80) break; }
}
void puts(const char* string) {
if(m == 0) term_write(string, strlen(string));
m = 1; term_putchar('\n');
cursor_pos(n, m); }
break; void handleControlSequence(struct csi_sequence sequence) {
case 'A': //CUU - Cursor Up //Check for our failsafes
//cursor_pos(x, y)
if (sequence.valid) {
cursor_pos(terminal_column, terminal_row + n); int n = 0; //Default of the flag used for a few items
break;
case 'B': //CUD - Cursor Down // Parse parameters, we only care about a max 2 of number only parameters
cursor_pos(terminal_column, terminal_row - n); // for now
break; int params[2] = {0};
case 'C': //CUF - Cursor Forward int param_count = 0;
cursor_pos(terminal_column + n, terminal_row); if (sequence.parameter_len) {
break; for (size_t i = 0; i < sequence.parameter_len && param_count < 1; i++) {
case 'D': //CUB - Cursor Back char c = sequence.parameter[i];
cursor_pos(terminal_column - n, terminal_row); if (isDigit(c)) {
break; n = (n*10) + (sequence.parameter[i] - '0');
case 'E': //CNL - Cursor Next Line } else if (c == ';') {
cursor_pos(0, terminal_row + n); params[param_count++] = n;
break; }
case 'F': //CPL - Cursor Previous Line }
cursor_pos(0, terminal_row - n); params[param_count++] = n;
break; }
case 'G': //CHA - Cursor Horizontal Absolute
cursor_pos(n, terminal_row); switch (*(sequence.final)) {
case 'J': //ED - Erase in Display case 'H': case 'f': // CUP - Cursor Position
//current cursor pos = y * width + x // TODO: Check to see if we have 2 paramaters
int pos = terminal_row * 80 + terminal_column; if (params[0]) params[0]--;
if(!nPresent) { if (params[1]) params[1]--;
for(pos < (25 * 80); pos++) { set_cursor(params[0], params[1]);
vga_buffer[pos] = '\0'; break;
} case 'A': //CUU - Cursor Up
n = 0; if (!params[0]) params[0] = 1;
} else if(nPresent && n == 1) { set_cursor(terminal_column, terminal_row - params[0]);
for(pos > 0; pos--) { break;
vga_buffer[pos] = '\0'; case 'B': //CUD - Cursor Down
} if (!params[0]) params[0] = 1;
} else if(n == 2 || n == 3) { set_cursor(terminal_column, terminal_row + params[0]);
for(int i = 0; i < (25*80); i++) { break;
vga_buffer[0] = '\0'; case 'C': //CUF - Cursor Forward
} if (!params[0]) params[0] = 1;
} set_cursor(terminal_column + params[0], terminal_row);
break; break;
case 'K': //EL - Erase in Line case 'D': //CUB - Cursor Back
if (!params[0]) params[0] = 1;
int pos = terminal_row * 80 + terminal_column; set_cursor(terminal_column - params[0], terminal_row);
if(!nPresent) { //From cursor to end of line break;
int endPos = (terminal_row + 1) * 80 - 1; //End of line = current row + 25 columns = current row + 1 case 'E': //CNL - Cursor Next Line
for(pos < endPos; pos++) { if (!params[0]) params[0] = 1;
vga_buffer[pos] = '\0'; set_cursor(0, terminal_row + params[0]);
} break;
} else if(nPresent && n == 1) { //From cursor to start of line case 'F': //CPL - Cursor Previous Line
int endPos = terminal_row * 80; //Start of line = end of previous line + 1 == current line if (!params[0]) params[0] = 1;
for(pos > endPos; pos--) { set_cursor(0, terminal_row - params[0]);
vga_buffer[pos] = '\0'; break;
} case 'G': //CHA - Cursor Horizontal Absolute
} else if(nPresent && n == 2) { //Entire current line if (params[0]) params[0]--;
pos = terminal_row * 80; set_cursor(params[0], terminal_row);
int endPos = (terminal_row + 1) * 80 - 1; break;
for(pos < endPos; pos++) { case 'J': //ED - Erase in Display
vga_buffer[pos] = '\0'; //current cursor pos = y * width + x
} {
} int pos = terminal_row * 80 + terminal_column;
break; if (params[0] == 0) { // Clear from cursor to end of screen
case 'S': //SU - Scroll Up for(; pos < (25 * 80); pos++) {
term_scroll(true); vga_buffer[pos] = '\0';
break; }
case 'T': //SD - Scroll Down } else if(params[0] == 1) { // Clear from cursor to beginning
term_scroll(false); for(; pos > 0; pos--) {
break; 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';
} }
}
bool isDigit(const char c) { }
return c == '0' || c == '1' || c == '2' || c == '3' || break;
c == '4' || c == '5' || c == '6' || c == '7' || case 'K': //EL - Erase in Line
c == '8' || c == '9'; {
} int pos = terminal_row * 80 + terminal_column;
if(params[0] == 0) { // From cursor to end of line
void term_scroll(bool down) { int endPos = (terminal_row + 1) * 80 - 1; // End of line = current row + 25 columns = current row + 1
for(; pos < endPos; pos++) {
int current_pos; vga_buffer[pos] = '\0';
if(down) { }
current_pos = 25 * 80; //Start of the last line. } else if (params[0] == 1) { // From cursor to start of line
} else { int endPos = terminal_row * 80; // Start of line = end of previous line + 1 == current line
current_pos = 160; //Start of the second line. for(; pos > endPos; pos--) {
} vga_buffer[pos] = '\0';
}
unsigned char* term_buffer = (unsigned char*) vga_buffer; } else if(params[0] == 2) { // Entire current line
pos = terminal_row * 80;
if(down) { //To scroll down, move every character into the one below it, or "pull" every character down int endPos = (terminal_row + 1) * 80 - 1;
while(current_pos > 80) { for(; pos < endPos; pos++) {
term_buffer[current_pos + 160] = vga_buffer[current_pos]; vga_buffer[pos] = '\0';
term_buffer[current_pos + 159] = vga_buffer[current_pos - 1]; }
current_pos -= 2; }
} else { }
while (current_pos <= 4000) { break;
term_buffer[current_pos - 160 /*The character immediately below it*/] = vga_buffer[current_pos]; //Move each character up a line case 'S': //SU - Scroll Up
term_buffer[current_pos - 159 /*The color of the character below*/] = vga_buffer[current_pos + 1]; //As well as its color term_scroll(true);
current_pos += 2; //Move to next char break;
} case 'T': //SD - Scroll Down
} term_scroll(false);
break;
if(down) { }
current_pos = 0; //Start of first line }
for(current_pos < 80; current_pos++) {
term_buffer[current_pos] = '\0'; }
current_pos += 2;
} else { bool isDigit(char c) {
; //Start of the last line return c >= '0' && c <= '9';
//Wipe out the bottom line }
for(current_pos = 3840; current_pos <= 3920; i++) {
term_buffer[current_pos] = 0; void set_cursor(int n, int m) {
current_pos += 2; terminal_column = n;
} terminal_row = m;
} }
terminal_row = 24; //Start writing on the last line void term_scroll(bool down) {
terminal_column = 0;
} 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;
}

View File

@ -1,18 +1,33 @@
#pragma once #pragma once
#include <stddef.h> #include <stdbool.h>
#include <stdint.h> #include <stddef.h>
#include <vga.h> #include <stdint.h>
void term_setcolor(enum vga_colors); #include "vga.h"
void screen_initialize(void);
void term_putentryat(char, uint8_t, size_t, size_t); struct csi_sequence {
void term_putchar(char); const char* parameter;
void term_write(const char*, size_t); size_t parameter_len;
void term_writes(const char*); const char* intermediate;
void puts(const char*); size_t intermediate_len;
void set_cursor(int, int); const char* final;
void term_scroll(void); bool valid;
void int_to_ascii(int, char*); };
void int_to_hex(int, char*);
void empty_string(char*);
void term_setcolor(enum vga_colors);
void screen_initialize(void);
void term_putentryat(char, uint8_t, size_t, size_t);
void term_putchar(char);
void term_write(const char*, size_t);
void term_writes(const char*);
void puts(const char*);
void handleControlSequence(struct csi_sequence);
void set_cursor(int, int);
void term_scroll(bool);
void int_to_ascii(int, char*);
void int_to_hex(int, char*);
void empty_string(char*);
bool isDigit(char c);