Merge branch 'ansi_console'

This commit is contained in:
Peter Weingartner 2021-10-06 21:25:23 -04:00
commit f904ed112b
5 changed files with 524 additions and 69 deletions

View file

@ -20,6 +20,8 @@ t_channel g_channels[CHAN_MAX];
void cdev_init_system() {
int i;
TRACE("cdev_init_system");
// Clear out all the channel device records...
for (i = 0; i < CDEV_DEVICES_MAX; i++) {
g_channel_devs[i].number = 0;
@ -31,13 +33,6 @@ void cdev_init_system() {
g_channels[i].number = -1;
g_channels[i].dev = -1;
}
// Pre-open a channel for the console and EVID
g_channels[0].number = 0;
g_channels[0].dev = 0;
g_channels[1].number = 1;
g_channels[1].dev = 1;
}
//
@ -54,6 +49,8 @@ short cdev_register(p_dev_chan device) {
cdev->number = device->number;
cdev->name = device->name;
cdev->init = device->init;
cdev->open = device->open;
cdev->close = device->close;
cdev->read = device->read;
cdev->readline = device->readline;
cdev->read_b = device->read_b;
@ -69,19 +66,33 @@ short cdev_register(p_dev_chan device) {
}
}
//
// Get a free channel
//
// Returns:
// A pointer to the free channel, 0 if none are available.
//
p_channel chan_alloc() {
/*
* Get a free channel
*
* Inputs:
* the device to associate with the channel
*
* Returns:
* A pointer to the free channel, 0 if none are available.
*/
p_channel chan_alloc(short dev) {
int i;
for (i = 0; i < CHAN_MAX; i++) {
if (g_channels[i].number < 0) {
g_channels[i].number = i;
return &g_channels[i];
TRACE("chan_alloc");
if ((dev == CDEV_CONSOLE) || (dev == CDEV_EVID)) {
/* For CONSOLE and EVID, the channel is always the same number as the device */
g_channels[dev].number = dev;
g_channels[dev].dev = dev;
return &g_channels[dev];
} else {
for (i = CDEV_EVID + 1; i < CHAN_MAX; i++) {
if (g_channels[i].number < 0) {
g_channels[i].number = i;
g_channels[i].dev = dev;
return &g_channels[i];
}
}
}
@ -112,6 +123,39 @@ void chan_free(p_channel chan) {
chan->dev = 0;
}
//
// Find the records for the channel and the channel's device, given the channel number
//
// Inputs:
// channel = the number of the channel to look up
// chan = pointer to the channel structure pointer to set
// cdev = pointer to the channel device structure pointer to set
//
// Returns:
// 0 on success, a negative number on error
//
short chan_get_records(short channel, p_channel * chan, p_dev_chan * cdev) {
if (channel < CHAN_MAX) {
*chan = &g_channels[channel];
if ((*chan)->number == channel) {
if ((*chan)->dev < CDEV_DEVICES_MAX) {
*cdev = &g_channel_devs[(*chan)->dev];
return 0;
} else {
log_num(LOG_ERROR, "chan_get_records 1: ", (*chan)->dev);
return DEV_ERR_BADDEV;
}
} else {
log_num(LOG_ERROR, "chan_get_records 2: ", channel);
return DEV_ERR_BADDEV;
}
} else {
return ERR_BADCHANNEL;
}
}
//
// Initialize the device
//
@ -132,37 +176,72 @@ short cdev_init(short dev) {
}
}
//
// Find the records for the channel and the channel's device, given the channel number
//
// Inputs:
// channel = the number of the channel to look up
// chan = pointer to the channel structure pointer to set
// cdev = pointer to the channel device structure pointer to set
//
// Returns:
// 0 on success, a negative number on error
//
short chan_get_records(short channel, p_channel * chan, p_dev_chan * cdev) {
if (channel < CHAN_MAX) {
*chan = &g_channels[channel];
if ((*chan)->number == channel) {
if ((*chan)->dev < CDEV_DEVICES_MAX) {
*cdev = &g_channel_devs[(*chan)->dev];
return 0;
} else {
return DEV_ERR_BADDEV;
}
/*
* Open a channel
*
* Inputs:
* dev = the device number to have a channel opened
* path = a "path" describing how the device is to be open
* mode = is the device to be read, written, both?
*
* Returns:
* the number of the channel opened, negative number on error
*/
short chan_open(short dev, uint8_t * path, short mode) {
short result;
p_channel chan;
p_dev_chan cdev;
} else {
TRACE("chan_open");
log_num(LOG_DEBUG, "dev = ", dev);
if (dev < CDEV_DEVICES_MAX) {
/* Get the device record */
cdev = &g_channel_devs[dev];
if (cdev->number != dev) {
/* Double check we have a real device */
return DEV_ERR_BADDEV;
}
/* Grab a channel */
chan = chan_alloc(dev);
if (chan == 0) {
return ERR_OUT_OF_HANDLES;
}
/* Open the channel */
result = cdev->open(chan, path, mode);
if (result == 0) {
/* Success: return the channel number */
return chan->number;
} else {
return result;
}
} else {
return ERR_BADCHANNEL;
return DEV_ERR_BADDEV;
}
}
/*
* Close a channel
*
* Inputs:
* chan = the number of the channel to close
*
* Returns:
* nothing useful
*/
short chan_close(short channel) {
p_channel chan;
p_dev_chan cdev;
if (chan_get_records(channel, &chan, &cdev) == 0) {
cdev->close(chan);
chan_free(chan);
}
return 0;
}
//
// Read bytes from the channel
//
@ -258,7 +337,8 @@ short chan_write(short channel, const uint8_t * buffer, short size) {
if (res == 0) {
return cdev->write(chan, buffer, size);
} else {
DEBUG("chan_write error\n");
log_num(LOG_ERROR, "chan_write error: ", res);
while (1) ;
return res;
}
}

View file

@ -64,6 +64,8 @@ typedef struct s_dev_chan {
short number; // The number of the device (assigned by registration)
char * name; // The name of the device
FUNC_V_2_S init; // short init() -- Initialize the device
FUNC_CBS_2_S open; // short open(t_channel * chan, uint8_t * path, short mode) -- open a channel for the device
FUNC_V_2_S close; // short close(t_channel * chan) -- called when a channel is closed
FUNC_CBS_2_S read; // short read(t_channel *, uint8_t * buffer, short size) -- Read a a buffer from the device
FUNC_CBS_2_S readline; // short readline(t_channel *, uint8_t * buffer, short size) -- Read a line of text from the device
FUNC_C_2_S read_b; // short read_b(t_channel *) -- read a single uint8_t from the device
@ -126,6 +128,30 @@ extern p_channel chan_get_record(short c);
*/
extern short cdev_init(short dev);
/*
* Open a channel
*
* Inputs:
* dev = the device number to have a channel opened
* path = a "path" describing how the device is to be open
* mode = is the device to be read, written, both?
*
* Returns:
* the number of the channel opened, negative number on error
*/
extern short chan_open(short dev, uint8_t * path, short mode);
/*
* Close a channel
*
* Inputs:
* chan = the number of the channel to close
*
* Returns:
* nothing useful
*/
extern short chan_close(short chan);
/*
* Read bytes from the channel
*

View file

@ -5,6 +5,9 @@
*
*/
#include <ctype.h>
#include <string.h>
#include "log.h"
#include "types.h"
#include "constants.h"
#include "dev/channel.h"
@ -13,6 +16,135 @@
#include "dev/kbd_mo.h"
#include "dev/text_screen_iii.h"
#define ANSI_BUFFER_SIZE 16
#define MAX_ANSI_ARGS 10
#define CON_CTRL_ANSI 0x80 /* Set to enable ANSI escape processing */
typedef void (*ansi_handler)(p_channel, short, short[]);
/*
* Structure to map an ANSI escape sequence pattern to a handler
*/
typedef struct s_ansi_seq {
char * pattern;
ansi_handler handler;
} t_ansi_seq, *p_ansi_seq;
/*
* Structure to track console state
*/
typedef struct s_console_data {
unsigned char control; /* Control flags for the console: e.g. process ANSI codes */
unsigned char ansi_buffer_count; /* Number of characters in the ANSI BUFFER */
char ansi_buffer[ANSI_BUFFER_SIZE]; /* Used to keep track of characters in the ANSI escape sequences */
} t_console_data, *p_console_data;
/*
* Forwards
*/
extern void ansi_cuu(p_channel chan, short arg_count, short args[]);
extern void ansi_cuf(p_channel chan, short arg_count, short args[]);
extern void ansi_cub(p_channel chan, short arg_count, short args[]);
extern void ansi_cud(p_channel chan, short arg_count, short args[]);
/*
* Console variables and constants
*/
/*
* ANSI escape sequences:
* # is a placeholder for any number of numerals
*
*/
const t_ansi_seq ansi_sequence[] = {
{ "\x27[#A", ansi_cuu },
{ "\x27[#B", ansi_cuf },
{ "\x27[#C", ansi_cub },
{ "\x27[#D", ansi_cud },
{ 0, 0 }
};
/*
* ANSI Handler: cursor up
*/
void ansi_cuu(p_channel chan, short arg_count, short args[]) {
unsigned short x, y;
short delta = 1;
TRACE("ansi_cuu");
if (arg_count > 0) {
delta = args[0];
}
if (delta == 0) delta = 1;
text_get_xy(chan->dev, &x, &y);
y -= delta;
text_set_xy(chan->dev, x, y);
}
/*
* ANSI Handler: cursor forward
*/
void ansi_cuf(p_channel chan, short arg_count, short args[]) {
unsigned short x, y;
short delta = 1;
TRACE("ansi_cuf");
if (arg_count > 0) {
delta = args[0];
}
if (delta == 0) delta = 1;
text_get_xy(chan->dev, &x, &y);
x += delta;
text_set_xy(chan->dev, x, y);
}
/*
* ANSI Handler: cursor back
*/
void ansi_cub(p_channel chan, short arg_count, short args[]) {
unsigned short x, y;
short delta = 1;
TRACE("ansi_cub");
if (arg_count > 0) {
delta = args[0];
}
if (delta == 0) delta = 1;
text_get_xy(chan->dev, &x, &y);
x -= delta;
text_set_xy(chan->dev, x, y);
}
/*
* ANSI Handler: cursor down
*/
void ansi_cud(p_channel chan, short arg_count, short args[]) {
unsigned short x, y;
short delta = 1;
TRACE("ansi_cud");
if (arg_count > 0) {
delta = args[0];
}
if (delta == 0) delta = 1;
text_get_xy(chan->dev, &x, &y);
y += delta;
text_set_xy(chan->dev, x, y);
}
//
// Initialize the console... nothing needs to happen here
@ -21,17 +153,205 @@ short con_init() {
return 0;
}
//
// Send a uint8_t to the console screen
//
short con_write_b(p_channel chan, uint8_t b) {
text_put_raw(chan->dev, (char)b);
/*
* Open the consolde device for the given channel
*
* Inputs:
* chan = the channel record for this console device
* path = unused
* mode = unused
*
* Returns
* 0 on success, negative number on failure
*/
short con_open(p_channel chan, uint8_t * path, short mode) {
int i;
p_console_data con_data;
TRACE("con_open");
/* Initialize the console data for this channel */
con_data = &(chan->data);
con_data->control = CON_CTRL_ANSI;
con_data->ansi_buffer_count = 0;
for (i = 0; i < ANSI_BUFFER_SIZE; i++) {
con_data->ansi_buffer[i] = 0;
}
return 0;
}
//
// Attempt to read from the keyboard.
//
/*
* Flush the output to the console...
*
* Really only does something if the console is set to process ANSI escape codes
*
*/
short con_flush(p_channel chan) {
int i;
p_console_data con_data;
con_data = &(chan->data);
if (con_data->control & CON_CTRL_ANSI) {
for (i = 0; i < con_data->ansi_buffer_count; i++) {
text_put_raw(chan->dev, con_data->ansi_buffer[i]);
}
}
return 0;
}
/*
* Close the console channel... just flush the data
*
* Inputs:
* chan = the channel record for this console device
*/
short con_close(p_channel chan) {
con_flush(chan);
return 0;
}
/*
* Return true if a character is an initial character for an ANSI escape code
*
* Inputs:
* c = the character to test
*
* Returns:
* 0 if the character is not an ANSI initial, 1 if it is
*/
short ansi_start_code(char c) {
switch (c) {
case '\x27':
return 1;
default:
return 0;
}
}
/*
* Attempt to match a pattern to what's in the buffer
* Calls the sequence's handler if the pattern matches
*
* Inputs:
* chan = pointer to the channel record
* con_data = pointer to the console data record for this channel
* sequence = pointer to the ANSI sequence mapping to check
*
* Returns
* 0 if not a match, 1 if it is a match
*/
short ansi_match_pattern(p_channel chan, p_console_data con_data, p_ansi_seq sequence) {
short arg_idx = 0;
short arg[MAX_ANSI_ARGS];
short i, j;
char * pattern = sequence->pattern;
char * buffer = con_data->ansi_buffer;
short buffer_count = con_data->ansi_buffer_count;
for (i = 0, j = 0; i < strlen(pattern); i++) {
if (buffer[j] == pattern[i]) {
/* Character matches... advance to the next character in the buffer and pattern */
j++;
} else if (pattern[i] == '#') {
/* Parse a number of decimal digits */
arg[arg_idx] = 0;
while (isdigit(buffer[j]) && (j < buffer_count)) {
arg[arg_idx] = arg[arg_idx] * 10 + (buffer[j] - '0');
j++;
}
if (j == buffer_count) {
/* This pattern does not match */
return 0;
} else {
/* We've read a number */
arg_idx++;
}
} else {
return 0;
}
}
if (i == strlen(pattern)) {
/* The pattern has been completely matched */
/* Clear the buffer */
for (i = 0; i < buffer_count; i++) {
buffer[i] = 0;
}
con_data->ansi_buffer_count = 0;
sequence->handler(chan, arg_idx, arg);
return 1;
}
return 0;
}
/*
* Attempt to match what's in the buffer to known ANSI escape sequences
*
* Inputs:
* chan = pointer to the channel record
* con_data = pointer to the console data record for this channel
*/
void ansi_match_buffer(p_channel chan, p_console_data con_data) {
short arg[MAX_ANSI_ARGS];
short arg_count = 0;
int i;
for (i = 0; i < MAX_ANSI_ARGS; i++) {
arg[i] = 0;
}
for (i = 0; ansi_sequence[i].pattern; i++) {
if (ansi_match_pattern(chan, con_data, &ansi_sequence[i])) {
return;
}
}
/* If not currently matching, but the buffer is full, we need to flush */
if (con_data->ansi_buffer_count == ANSI_BUFFER_SIZE) {
con_flush(chan);
}
}
/*
* Send a byte to the console screen
*/
short con_write_b(p_channel chan, uint8_t b) {
p_console_data con_data;
/* Check to see if we need to process ANSI codes */
con_data = &(chan->data);
if (con_data->control & CON_CTRL_ANSI) {
/* ANSI codes are to be processed */
if ((con_data->ansi_buffer_count > 0) || ansi_start_code(b)) {
/* We're working on a sequence: add the character to the buffer */
con_data->ansi_buffer[con_data->ansi_buffer_count++] = b;
/* Attempt to match the buffer to an escape sequence */
ansi_match_buffer(chan, con_data);
} else {
/* We're not working on a sequence: just print the character */
text_put_raw(chan->dev, (char)b);
}
} else {
/* Not processing ANSI codes... just pass it to the text driver */
text_put_raw(chan->dev, (char)b);
}
return 0;
}
/*
* Attempt to read from the keyboard.
*/
short con_read_b(p_channel chan) {
char c;
do {
@ -128,12 +448,14 @@ short con_readline(p_channel chan, uint8_t * buffer, short size) {
short con_write(p_channel chan, const uint8_t * buffer, short size) {
int i;
TRACE("con_write");
for (i = 0; i < size; i++) {
char c = (char)buffer[i];
if (c == 0) {
break;
} else {
text_put_raw(chan->dev, c);
con_write_b(chan, c);
}
}
@ -149,13 +471,6 @@ short con_status(p_channel chan) {
return CDEV_STAT_READABLE | CDEV_STAT_WRITABLE;
}
//
// Flush the output to the console... this does nothing...
//
short con_flush(p_channel chan) {
return 0;
}
//
// We can't seek on the console... just return 0
//
@ -171,11 +486,14 @@ short con_ioctrl(p_channel chan, short command, uint8_t * buffer, short size) {
// Install the console device driver
//
short con_install() {
short result;
t_dev_chan dev;
dev.name = "CONSOLE";
dev.number = CDEV_CONSOLE;
dev.init = con_init;
dev.open = con_open;
dev.close = con_close;
dev.read = con_read;
dev.readline = con_readline;
dev.read_b = con_read_b;
@ -186,11 +504,16 @@ short con_install() {
dev.status = con_status;
dev.ioctrl = con_ioctrl;
cdev_register(&dev);
result = cdev_register(&dev);
if (result) {
return result;
}
dev.name = "EVID";
dev.number = CDEV_EVID;
dev.init = con_init;
dev.open = con_open;
dev.close = con_close;
dev.read = con_read;
dev.readline = con_readline;
dev.read_b = con_read_b;
@ -201,5 +524,15 @@ short con_install() {
dev.status = con_status;
dev.ioctrl = con_ioctrl;
return cdev_register(&dev);
result = cdev_register(&dev);
if (result) {
return result;
}
/* Pre-open the console and EVID channels */
chan_open(CDEV_CONSOLE, 0, 0);
// chan_open(CDEV_EVID, 0, 0);
return result;
}

View file

@ -199,6 +199,21 @@ void text_set_xy(short screen, unsigned short x, unsigned short y) {
}
}
/*
* Get the position of the cursor on the screen.
*
* Inputs:
* screen = the screen number 0 for channel A, 1 for channel B
* x = pointer to the location to store the column (0 is left most)
* y = pointer to the location to store the row (0 is right most)
*/
void text_get_xy(short screen, unsigned short * x, unsigned short * y) {
p_text_channel chan = &text_channel[screen];
*x = chan->x;
*y = chan->y;
}
/*
* Compute the size information for the text screen based on the current settings in VICKY
* These settings are needed to correctly position text on the screen.

View file

@ -35,6 +35,16 @@ extern void text_set_cursor(short screen, short color, char character, short rat
*/
extern void text_set_xy(short screen, unsigned short x, unsigned short y);
/*
* Get the position of the cursor on the screen.
*
* Inputs:
* screen = the screen number 0 for channel A, 1 for channel B
* x = pointer to the location to store the column (0 is left most)
* y = pointer to the location to store the row (0 is right most)
*/
extern void text_get_xy(short screen, unsigned short * x, unsigned short * y);
/*
* Compute the size information for the text screen based on the current settings in VICKY
* These settings are needed to correctly position text on the screen.
@ -53,15 +63,6 @@ extern void text_setsizes(short screen);
*/
extern void text_put_raw(short screen, char c);
/*
* Send a character to the screen... but handle ANSI escape codes and process accordingly.
*
* Inputs:
* screen = the screen number 0 for channel A, 1 for channel B
* c = the character to print
*/
void text_put_ansi(short screen, char c);
/*
* Set the foreground and background color for printing
*