From 641221c2bb088248b7041031d4ff03bfe3e520d2 Mon Sep 17 00:00:00 2001 From: pjht Date: Fri, 6 Sep 2019 15:36:36 -0500 Subject: [PATCH] Upgrade ANSI parser --- vga_drv/cansid.c | 114 ------------ vga_drv/cansid.h | 26 --- vga_drv/vga.c | 81 +++------ vga_drv/vga.h | 9 +- vga_drv/vtconsole.c | 422 ++++++++++++++++++++++++++++++++++++++++++++ vga_drv/vtconsole.h | 91 ++++++++++ 6 files changed, 538 insertions(+), 205 deletions(-) delete mode 100644 vga_drv/cansid.c delete mode 100644 vga_drv/cansid.h create mode 100755 vga_drv/vtconsole.c create mode 100755 vga_drv/vtconsole.h diff --git a/vga_drv/cansid.c b/vga_drv/cansid.c deleted file mode 100644 index 765a510..0000000 --- a/vga_drv/cansid.c +++ /dev/null @@ -1,114 +0,0 @@ -#include -#include - -#include "cansid.h" - -#define ESC '\x1B' - -struct cansid_state cansid_init(void) -{ - struct cansid_state rv = { - .state = CANSID_ESC, - .style = 0x0F, - .next_style = 0x0F - }; - return rv; -} - -static inline unsigned char cansid_convert_color(unsigned char color) -{ - const unsigned char lookup_table[8] = { 0, 4, 2, 6, 1, 5, 3, 7 }; - return lookup_table[(int)color]; -} - -struct color_char cansid_process(struct cansid_state *state, char x) -{ - struct color_char rv = { - .style = state->style, - .ascii = '\0' - }; - switch (state->state) { - case CANSID_ESC: - if (x == ESC) - state->state = CANSID_BRACKET; - else { - rv.ascii = x; - } - break; - case CANSID_BRACKET: - if (x == '[') - state->state = CANSID_PARSE; - else { - state->state = CANSID_ESC; - rv.ascii = x; - } - break; - case CANSID_PARSE: - if (x == '3') { - state->state = CANSID_FGCOLOR; - } else if (x == '4') { - state->state = CANSID_BGCOLOR; - } else if (x == '0') { - state->state = CANSID_ENDVAL; - state->next_style = 0x0F; - } else if (x == '1') { - state->state = CANSID_ENDVAL; - state->next_style |= (1 << 3); - } else if (x == '=') { - state->state = CANSID_EQUALS; - } else { - state->state = CANSID_ESC; - state->next_style = state->style; - rv.ascii = x; - } - break; - case CANSID_BGCOLOR: - if (x >= '0' && x <= '7') { - state->state = CANSID_ENDVAL; - state->next_style &= 0x1F; - state->next_style |= cansid_convert_color(x - '0') << 4; - } else { - state->state = CANSID_ESC; - state->next_style = state->style; - rv.ascii = x; - } - break; - case CANSID_FGCOLOR: - if (x >= '0' && x <= '7') { - state->state = CANSID_ENDVAL; - state->next_style &= 0xF8; - state->next_style |= cansid_convert_color(x - '0'); - } else { - state->state = CANSID_ESC; - state->next_style = state->style; - rv.ascii = x; - } - break; - case CANSID_EQUALS: - if (x == '1') { - state->state = CANSID_ENDVAL; - state->next_style &= ~(1 << 3); - } else { - state->state = CANSID_ESC; - state->next_style = state->style; - rv.ascii = x; - } - break; - case CANSID_ENDVAL: - if (x == ';') { - state->state = CANSID_PARSE; - } else if (x == 'm') { - // Finish and apply styles - state->state = CANSID_ESC; - state->style = state->next_style; - } else { - state->state = CANSID_ESC; - state->next_style = state->style; - rv.ascii = x; - } - break; - default: - break; - } - return rv; -} diff --git a/vga_drv/cansid.h b/vga_drv/cansid.h deleted file mode 100644 index 069e412..0000000 --- a/vga_drv/cansid.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef CANSID_H -#define CANSID_H - -struct cansid_state { - enum { - CANSID_ESC, - CANSID_BRACKET, - CANSID_PARSE, - CANSID_BGCOLOR, - CANSID_FGCOLOR, - CANSID_EQUALS, - CANSID_ENDVAL, - } state; - unsigned char style; - unsigned char next_style; -}; - -struct color_char { - unsigned char style; - unsigned char ascii; -}; - -struct cansid_state cansid_init(void); -struct color_char cansid_process(struct cansid_state *state, char x); - -#endif diff --git a/vga_drv/vga.c b/vga_drv/vga.c index 8f2ca25..b6f5a19 100644 --- a/vga_drv/vga.c +++ b/vga_drv/vga.c @@ -3,37 +3,39 @@ #include "ports.h" #include #include -#include "cansid.h" +#include "vtconsole.h" #define xy_to_indx(x,y) ((x+(y*width))*2) +static vga_color colors[]={VGA_BLACK,VGA_RED,VGA_GREEN,VGA_BROWN, + VGA_BLUE,VGA_PURPLE,VGA_CYAN,VGA_WHITE}; static char* screen; static int width; static int height; -static int x; -static int y; -static vga_colors fg_color; -static vga_colors bg_color; -static char* scroll_buf[0xfa0]; -static struct cansid_state state; +static vtconsole_t* console; -static void set_char(int x,int y,struct color_char ch) { - screen[xy_to_indx(x,y)]=ch.ascii; - screen[xy_to_indx(x,y)+1]=ch.style; +static void set_char(int x,int y,char ch,char style) { + screen[xy_to_indx(x,y)]=ch; + screen[xy_to_indx(x,y)+1]=style; +} + +static void vt_set_char(struct vtconsole* vtc, vtcell_t* cell, int x, int y) { + vga_color fg_color=colors[cell->attr.fg]; + vga_color bg_color=colors[cell->attr.bg]; + char style=(fg_color&0xF)|((bg_color&0xF)<<4); + style=style|((cell->attr.bright&0x1)<<7); + set_char(x,y,cell->c,style); } void vga_clear() { for (int y=0;yx+(cur->y*width)); port_byte_out(0x3D4,0xF); port_byte_out(0x3D5,pos&0xFF); port_byte_out(0x3D4,0xE); @@ -41,56 +43,21 @@ static void set_cursor(int x,int y) { } void vga_init(text_fb_info framebuffer_info) { - x=0; - y=0; - fg_color=VGA_WHITE; - bg_color=VGA_BLACK; - state=cansid_init(); screen=framebuffer_info.address; width=framebuffer_info.width; height=framebuffer_info.height; + console=vtconsole(width,height,vt_set_char,set_cursor); port_byte_out(0x3D4,0xA); port_byte_out(0x3D5,(port_byte_in(0x3D5)&0xC0)|14); port_byte_out(0x3D4,0xB); port_byte_out(0x3D5,(port_byte_in(0x3D5)&0xE0)|15); - set_cursor(0,0); + vtcursor_t cur; + cur.x=0; + cur.y=0; + set_cursor(NULL,&cur); vga_clear(); } void vga_write_string(const char* string) { - for (size_t i=0;i +#include +#define min(a, b) ((a) < (b) ? (a) : (b)) + + +/* --- Constructor/Destructor ----------------------------------------------- */ + +vtconsole_t *vtconsole(int width, int height, vtc_paint_handler_t on_paint, vtc_cursor_handler_t on_move) +{ + vtconsole_t *vtc = malloc(sizeof(vtconsole_t)); + + vtc->width = width; + vtc->height = height; + + vtc->ansiparser = (vtansi_parser_t){VTSTATE_ESC, {{0, 0}}, 0}; + vtc->attr = VTC_DEFAULT_ATTR; + + vtc->buffer = malloc(width * height * sizeof(vtcell_t)); + + vtc->cursor = (vtcursor_t){0, 0}; + + vtc->on_paint = on_paint; + vtc->on_move = on_move; + + vtconsole_clear(vtc, 0, 0, width, height - 1); + + return vtc; +} + +void vtconsole_delete(vtconsole_t *vtc) +{ + free(vtc->buffer); + free(vtc); +} + +/* --- Internal methodes ---------------------------------------------------- */ + +void vtconsole_clear(vtconsole_t *vtc, int fromx, int fromy, int tox, int toy) +{ + for (int i = fromx + fromy * vtc->width; i < tox + toy * vtc->width; i++) + { + vtcell_t *cell = &vtc->buffer[i]; + + cell->attr = VTC_DEFAULT_ATTR; + cell->c = ' '; + + if (vtc->on_paint) + { + vtc->on_paint(vtc, cell, i % vtc->width, i / vtc->width); + } + } +} + +void vtconsole_scroll(vtconsole_t *vtc, int lines) +{ + if (lines == 0) return; + + lines = lines > vtc->height ? vtc->height : lines; + + // Scroll the screen by number of $lines. + for (int i = 0; i < ((vtc->width * vtc->height) - (vtc->width * lines)); i++) + { + vtc->buffer[i] = vtc->buffer[i + (vtc->width * lines)]; + + if (vtc->on_paint) + { + vtc->on_paint(vtc, &vtc->buffer[i], i % vtc->width, i / vtc->width); + } + } + + // Clear the last $lines. + for (int i = ((vtc->width * vtc->height) - (vtc->width * lines)); i < vtc->width * vtc->height; i++) + { + vtcell_t *cell = &vtc->buffer[i]; + cell->attr = VTC_DEFAULT_ATTR; + cell->c = ' '; + + if (vtc->on_paint) + { + vtc->on_paint(vtc, &vtc->buffer[i], i % vtc->width, i / vtc->width); + } + } + + // Move the cursor up $lines + if (vtc->cursor.y > 0) + { + vtc->cursor.y -= lines; + + if (vtc->cursor.y < 0) vtc->cursor.y = 0; + + if (vtc->on_move) + { + vtc->on_move(vtc, &vtc->cursor); + } + } +} + +// Append a new line +void vtconsole_newline(vtconsole_t *vtc) +{ + vtc->cursor.x = 0; + vtc->cursor.y++; + + if (vtc->cursor.y == vtc->height) + { + vtconsole_scroll(vtc, 1); + } + + if (vtc->on_move) + { + vtc->on_move(vtc, &vtc->cursor); + } +} + +// Append character to the console buffer. +void vtconsole_append(vtconsole_t *vtc, char c) +{ + if (c == '\n') + { + vtconsole_newline(vtc); + } + else if (c == '\r') + { + vtc->cursor.x = 0; + + if (vtc->on_move) + { + vtc->on_move(vtc, &vtc->cursor); + } + } + else if (c == '\t') + { + int n = 8 - (vtc->cursor.x % 8); + + for (int i = 0; i < n; i++) + { + vtconsole_append(vtc, ' '); + } + } + else if (c == '\b') + { + if (vtc->cursor.x > 0) + { + vtc->cursor.x--; + } + else + { + vtc->cursor.y--; + vtc->cursor.x = vtc->width - 1; + } + + if (vtc->on_move) + { + vtc->on_move(vtc, &vtc->cursor); + } + } + else + { + if (vtc->cursor.x >= vtc->width) + vtconsole_newline(vtc); + + vtcell_t *cell = &vtc->buffer[vtc->cursor.x + vtc->cursor.y * vtc->width]; + cell->c = c; + cell->attr = vtc->attr; + + if (vtc->on_paint) + { + vtc->on_paint(vtc, cell, vtc->cursor.x, vtc->cursor.y); + } + + vtc->cursor.x++; + + if (vtc->on_move) + { + vtc->on_move(vtc, &vtc->cursor); + } + } +} + +// Moves the cursor to row n, column m. The values are 1-based, +void vtconsole_csi_cup(vtconsole_t *vtc, vtansi_arg_t *stack, int count) +{ + if (count == 1 && stack[0].empty) + { + vtc->cursor.x = 0; + vtc->cursor.y = 0; + } + else if (count == 2) + { + if (stack[0].empty) + { + vtc->cursor.y = 0; + } + else + { + vtc->cursor.y = min(stack[0].value - 1, vtc->height - 1); + } + + if (stack[1].empty) + { + vtc->cursor.y = 0; + } + else + { + vtc->cursor.x = min(stack[1].value - 1, vtc->width - 1); + } + } + + if (vtc->on_move) + { + vtc->on_move(vtc, &vtc->cursor); + } +} + +// Clears part of the screen. +void vtconsole_csi_ed(vtconsole_t *vtc, vtansi_arg_t *stack, int count) +{ + (void)(count); + + vtcursor_t cursor = vtc->cursor; + + if (stack[0].empty) + { + vtconsole_clear(vtc, cursor.x, cursor.y, vtc->width, vtc->height - 1); + } + else + { + int attr = stack[0].value; + + if (attr == 0) + vtconsole_clear(vtc, cursor.x, cursor.y, vtc->width, vtc->height - 1); + else if (attr == 1) + vtconsole_clear(vtc, 0, 0, cursor.x, cursor.y); + else if (attr == 2) + vtconsole_clear(vtc, 0, 0, vtc->width, vtc->height - 1); + } +} + +// Erases part of the line. +void vtconsole_csi_el(vtconsole_t *vtc, vtansi_arg_t *stack, int count) +{ + (void)(count); + + vtcursor_t cursor = vtc->cursor; + + if (stack[0].empty) + { + vtconsole_clear(vtc, cursor.x, cursor.y, vtc->width, cursor.y); + } + else + { + int attr = stack[0].value; + + if (attr == 0) + vtconsole_clear(vtc, cursor.x, cursor.y, vtc->width, cursor.y); + else if (attr == 1) + vtconsole_clear(vtc, 0, cursor.y, cursor.x, cursor.y); + else if (attr == 2) + vtconsole_clear(vtc, 0, cursor.y, vtc->width, cursor.y); + } +} + +// Sets the appearance of the following characters +void vtconsole_csi_sgr(vtconsole_t *vtc, vtansi_arg_t *stack, int count) +{ + for (int i = 0; i < count; i++) + { + if (stack[i].empty || stack[i].value == 0) + { + vtc->attr = VTC_DEFAULT_ATTR; + } + else + { + int attr = stack[i].value; + + if (attr == 1) // Increased intensity + { + vtc->attr.bright = true; + } + else if (attr >= 30 && attr <= 37) // Set foreground color + { + vtc->attr.fg = attr - 30; + } + else if (attr >= 40 && attr <= 47) // Set background color + { + vtc->attr.bg = attr - 40; + } + } + } +} + +// Process ANSI escape sequences and append character to the console buffer. +void vtconsole_process(vtconsole_t *vtc, char c) +{ + vtansi_parser_t *parser = &vtc->ansiparser; + + switch (parser->state) + { + case VTSTATE_ESC: + if (c == '\033') + { + parser->state = VTSTATE_BRACKET; + + parser->index = 0; + + parser->stack[parser->index].value = 0; + parser->stack[parser->index].empty = true; + } + else + { + parser->state = VTSTATE_ESC; + vtconsole_append(vtc, c); + } + break; + + case VTSTATE_BRACKET: + if (c == '[') + { + parser->state = VTSTATE_ATTR; + } + else + { + parser->state = VTSTATE_ESC; + vtconsole_append(vtc, c); + } + break; + case VTSTATE_ATTR: + if (isdigit(c)) + { + parser->stack[parser->index].value *= 10; + parser->stack[parser->index].value += (c - '0'); + parser->stack[parser->index].empty = false; + } + else + { + if ((parser->index) < VTC_ANSI_PARSER_STACK_SIZE) + { + parser->index++; + } + + parser->stack[parser->index].value = 0; + parser->stack[parser->index].empty = true; + + parser->state = VTSTATE_ENDVAL; + } + break; + default: + break; + } + + if (parser->state == VTSTATE_ENDVAL) + { + if (c == ';') + { + parser->state = VTSTATE_ATTR; + } + else + { + switch (c) + { + case 'A': + /* Cursor up P1 rows */ + break; + case 'B': + /* Cursor down P1 rows */ + break; + + case 'C': + /* Cursor right P1 columns */ + break; + case 'D': + /* Cursor left P1 columns */ + break; + + case 'E': + /* Cursor to first column of line P1 rows down from current */ + break; + case 'F': + /* Cursor to first column of line P1 rows up from current */ + break; + + case 'G': + /* Cursor to column P1 */ + break; + case 'd': + /* Cursor left P1 columns */ + break; + + case 'H': + vtconsole_csi_cup(vtc, parser->stack, parser->index); + break; + case 'J': + vtconsole_csi_ed(vtc, parser->stack, parser->index); + break; + case 'K': + vtconsole_csi_el(vtc, parser->stack, parser->index); + break; + case 'm': + vtconsole_csi_sgr(vtc, parser->stack, parser->index); + break; + } + + parser->state = VTSTATE_ESC; + } + } +} + +/* --- Methodes ------------------------------------------------------------- */ + +void vtconsole_putchar(vtconsole_t *vtc, char c) +{ + vtconsole_process(vtc, c); +} + +void vtconsole_write(vtconsole_t *vtc, const char *buffer, unsigned int size) +{ + for (unsigned int i = 0; i < size; i++) + { + vtconsole_process(vtc, buffer[i]); + } +} diff --git a/vga_drv/vtconsole.h b/vga_drv/vtconsole.h new file mode 100755 index 0000000..a5a3760 --- /dev/null +++ b/vga_drv/vtconsole.h @@ -0,0 +1,91 @@ +#pragma once + +#include + +#define VTC_DEFAULT_FOREGROUND VTCOLOR_GREY +#define VTC_DEFAULT_BACKGROUND VTCOLOR_BLACK +#define VTC_DEFAULT_ATTR (vtattr_t){ false, VTC_DEFAULT_FOREGROUND, VTC_DEFAULT_BACKGROUND } +#define VTC_ANSI_PARSER_STACK_SIZE 8 + +struct vtconsole; + +typedef enum +{ + VTCOLOR_BLACK, + VTCOLOR_RED, + VTCOLOR_GREEN, + VTCOLOR_YELLOW, + VTCOLOR_BLUE, + VTCOLOR_MAGENTA, + VTCOLOR_CYAN, + VTCOLOR_GREY, +} vtcolor_t; + +typedef enum +{ + VTSTATE_ESC, + VTSTATE_BRACKET, + VTSTATE_ATTR, + VTSTATE_ENDVAL, +} vtansi_parser_state_t; + +typedef struct +{ + int value; + bool empty; +} vtansi_arg_t; + +typedef struct +{ + vtansi_parser_state_t state; + vtansi_arg_t stack[VTC_ANSI_PARSER_STACK_SIZE]; + int index; +} vtansi_parser_t; + +typedef struct +{ + bool bright; + vtcolor_t fg; + vtcolor_t bg; +} vtattr_t; + +typedef struct +{ + char c; + vtattr_t attr; +} vtcell_t; + +typedef struct +{ + int x; + int y; +} vtcursor_t; + +typedef void (*vtc_paint_handler_t)(struct vtconsole* vtc, vtcell_t* cell, int x, int y); +typedef void (*vtc_cursor_handler_t)(struct vtconsole* vtc, vtcursor_t* cur); + +typedef struct vtconsole +{ + + int width; + int height; + + vtattr_t attr; + vtansi_parser_t ansiparser; + + vtcell_t *buffer; + vtcursor_t cursor; + + vtc_paint_handler_t on_paint; + vtc_cursor_handler_t on_move; +} vtconsole_t; + +vtconsole_t* vtconsole(int width, int height, vtc_paint_handler_t on_paint, vtc_cursor_handler_t on_move); +void vtconsole_delete(vtconsole_t *c); + +void vtconsole_clear(vtconsole_t *vtc, int fromx, int fromy, int tox, int toy); +void vtconsole_scroll(vtconsole_t *vtc, int lines); +void vtconsole_newline(vtconsole_t *vtc); + +void vtconsole_putchar(vtconsole_t *vtc, char c); +void vtconsole_write(vtconsole_t *vtc, const char *buffer, unsigned int size);