Upgrade ANSI parser

This commit is contained in:
pjht 2019-09-06 15:36:36 -05:00
parent c5504b80dc
commit 641221c2bb
6 changed files with 538 additions and 205 deletions

View File

@ -1,114 +0,0 @@
#include <stddef.h>
#include <stdint.h>
#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;
}

View File

@ -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

View File

@ -3,37 +3,39 @@
#include "ports.h"
#include <string.h>
#include <stddef.h>
#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;y<height;y++) {
for (int x=0;x<width;x++) {
struct color_char ch;
ch.ascii=' ';
ch.style=0;
set_char(x,y,ch);
set_char(x,y,' ',0);
}
}
}
static void set_cursor(int x,int y) {
int pos=(x+(y*width));
static void set_cursor(struct vtconsole* vtc, vtcursor_t* cur) {
int pos=(cur->x+(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<strlen(string);i++) {
char c=string[i];
if (c=='\n') {
x=0;
y++;
} else {
struct color_char ch=cansid_process(&state,c);
if (ch.ascii) {
set_char(x,y,ch);
x++;
}
}
if (x==width) {
x=0;
y++;
}
if (y==height) {
x=0;
y=24;
memcpy(scroll_buf,&screen[xy_to_indx(0,1)],xy_to_indx(0,24));
vga_clear();
memcpy(screen,scroll_buf,xy_to_indx(0,25));
}
}
set_cursor(x,y);
}
void vga_backspace() {
if (x!=0) {
x--;
struct color_char ch;
ch.ascii=' ';
ch.style=0;
set_char(x,y,ch);
}
vtconsole_write(console,string,strlen(string));
}

View File

@ -10,15 +10,8 @@ typedef enum {
VGA_PURPLE=5,
VGA_BROWN=6,
VGA_GRAY=7,
VGA_DARK_GRAY=8,
VGA_LIGHT_BLUE=9,
VGA_LIGHT_GREEN=10,
VGA_LIGHT_CYAN=11,
VGA_LIGHT_RED=12,
VGA_LIGHT_PURPLE=13,
VGA_YELLOW=14,
VGA_WHITE=15
} vga_colors;
} vga_color;
void vga_init();

422
vga_drv/vtconsole.c Executable file
View File

@ -0,0 +1,422 @@
#include "vtconsole.h"
#include <stdlib.h>
#include <ctype.h>
#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]);
}
}

91
vga_drv/vtconsole.h Executable file
View File

@ -0,0 +1,91 @@
#pragma once
#include <stdbool.h>
#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);