X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?p=palacios.git;a=blobdiff_plain;f=geekos%2Fsrc%2Fgeekos%2Fscreen.c;fp=geekos%2Fsrc%2Fgeekos%2Fscreen.c;h=c952b42e8da6089e7ca4625e23a1d90a8de5f91a;hp=0000000000000000000000000000000000000000;hb=ddc16b0737cf58f7aa90a69c6652cdf4090aec51;hpb=626595465a2c6987606a6bc697df65130ad8c2d3 diff --git a/geekos/src/geekos/screen.c b/geekos/src/geekos/screen.c new file mode 100644 index 0000000..c952b42 --- /dev/null +++ b/geekos/src/geekos/screen.c @@ -0,0 +1,571 @@ +/* + * GeekOS text screen output + * Copyright (c) 2001,2003,2004 David H. Hovemeyer + * $Revision: 1.3 $ + * + * This is free software. You are permitted to use, + * redistribute, and modify it as specified in the file "COPYING". + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * Information sources for VT100 and ANSI escape sequences: + * - http://www.lns.cornell.edu/~pvhp/dcl/vt100.html + * - http://en.wikipedia.org/wiki/ANSI_escape_code + */ + +/* ---------------------------------------------------------------------- + * Private functions and data + * ---------------------------------------------------------------------- */ + +#define ESC ((char) 0x1B) +#define DEFAULT_ATTRIBUTE ATTRIB(BLACK, GRAY) + +enum State { + S_NORMAL, /* Normal state - output is echoed verbatim */ + S_ESC, /* Saw ESC character - begin output escape sequence */ + S_ESC2, /* Saw '[' character - continue output escape sequence */ + S_ARG, /* Scanning a numeric argument */ + S_CMD, /* Command */ +}; + +#define MAXARGS 8 /* Max args that can be passed to esc sequence */ + +struct Console_State { + /* Current state information */ + int row, col; + int saveRow, saveCol; + uchar_t currentAttr; + + /* Working variables for processing escape sequences. */ + enum State state; + int argList[MAXARGS]; + int numArgs; +}; + +static struct Console_State s_cons; + +#define NUM_SCREEN_DWORDS ((NUMROWS * NUMCOLS * 2) / 4) +#define NUM_SCROLL_DWORDS (((NUMROWS-1) * NUMCOLS * 2) / 4) +#define NUM_DWORDS_PER_LINE ((NUMCOLS*2)/4) +#define FILL_DWORD (0x00200020 | (s_cons.currentAttr<<24) | (s_cons.currentAttr<<8)) + +/* + * Scroll the display one line. + * We speed things up by copying 4 bytes at a time. + */ +static void Scroll(void) +{ + uint_t* v; + int i, n = NUM_SCROLL_DWORDS; + uint_t fill = FILL_DWORD; + + /* Move lines 1..NUMROWS-1 up one position. */ + for (v = (uint_t*)VIDMEM, i = 0; i < n; ++i) { + *v = *(v + NUM_DWORDS_PER_LINE); + ++v; + } + + /* Clear out last line. */ + for (v = (uint_t*)VIDMEM + n, i = 0; i < NUM_DWORDS_PER_LINE; ++i) + *v++ = fill; +} + +/* + * Clear current cursor position to end of line using + * current attribute. + */ +static void Clear_To_EOL(void) +{ + int n = (NUMCOLS - s_cons.col); + uchar_t* v = VIDMEM + s_cons.row*(NUMCOLS*2) + s_cons.col*2; + while (n-- > 0) { + *v++ = ' '; + *v++ = s_cons.currentAttr; + } +} + +/* + * Move to the beginning of the next line, scrolling + * if necessary. + */ +static void Newline(void) +{ + ++s_cons.row; + s_cons.col = 0; + if (s_cons.row == NUMROWS) { + Scroll(); + s_cons.row = NUMROWS - 1; + } +} + +/* + * Write the graphic representation of given character to the screen + * at current position, with current attribute, scrolling if + * necessary. + */ +static void Put_Graphic_Char(int c) +{ + uchar_t* v = VIDMEM + s_cons.row*(NUMCOLS*2) + s_cons.col*2; + + /* Put character at current position */ + *v++ = (uchar_t) c; + *v = s_cons.currentAttr; + + if (s_cons.col < NUMCOLS - 1) + ++s_cons.col; + else + Newline(); +} + +/* + * Put one character to the screen using the current cursor position + * and attribute, scrolling if needed. The caller should update + * the cursor position once all characters have been written. + */ +static void Output_Literal_Character(int c) +{ + int numSpaces; + + switch (c) { + case '\n': + Clear_To_EOL(); + Newline(); + break; + + case '\t': + numSpaces = TABWIDTH - (s_cons.col % TABWIDTH); + while (numSpaces-- > 0) + Put_Graphic_Char(' '); + break; + + default: + Put_Graphic_Char(c); + break; + } + +#ifndef NDEBUG + /* + * When compiled with --enable-port-e9-hack, Bochs will send writes + * to port E9 to the console. This helps tremendously with debugging, + * because it allows debug Print() statements to be visible after + * Bochs has exited. + */ + Out_Byte(0xE9, c); +#endif +} + +/* + * Move the cursor to a new position, stopping at the screen borders. + */ +static void Move_Cursor(int row, int col) +{ + if (row < 0) + row = 0; + else if (row >= NUMROWS) + row = NUMROWS - 1; + + if (col < 0) + col = 0; + else if (col >= NUMCOLS) + col = NUMCOLS - 1; + + s_cons.row = row; + s_cons.col = col; +} + +/* + * Table mapping ANSI colors to VGA text mode colors. + */ +static const uchar_t s_ansiToVgaColor[] = { + BLACK,RED,GREEN,AMBER,BLUE,MAGENTA,CYAN,GRAY +}; + +/* + * Update the attributes specified by the arguments + * of the escape sequence. + */ +static void Update_Attributes(void) +{ + int i; + int attr = s_cons.currentAttr & ~(BRIGHT); + + for (i = 0; i < s_cons.numArgs; ++i) { + int value = s_cons.argList[i]; + if (value == 0) + attr = DEFAULT_ATTRIBUTE; + else if (value == 1) + attr |= BRIGHT; + else if (value >= 30 && value <= 37) + attr = (attr & ~0x7) | s_ansiToVgaColor[value - 30]; + else if (value >= 40 && value <= 47) + attr = (attr & ~(0x7 << 4)) | (s_ansiToVgaColor[value - 40] << 4); + } + s_cons.currentAttr = attr; +} + +/* Reset to cancel or finish processing an escape sequence. */ +static void Reset(void) +{ + s_cons.state = S_NORMAL; + s_cons.numArgs = 0; +} + +/* Start an escape sequence. */ +static void Start_Escape(void) +{ + s_cons.state = S_ESC; + s_cons.numArgs = 0; +} + +/* Start a numeric argument to an escape sequence. */ +static void Start_Arg(int argNum) +{ + KASSERT(s_cons.numArgs == argNum); + s_cons.numArgs++; + s_cons.state = S_ARG; + if (argNum < MAXARGS) + s_cons.argList[argNum] = 0; +} + +/* Save current cursor position. */ +static void Save_Cursor(void) +{ + s_cons.saveRow = s_cons.row; + s_cons.saveCol = s_cons.col; +} + +/* Restore saved cursor position. */ +static void Restore_Cursor(void) +{ + s_cons.row = s_cons.saveRow; + s_cons.col = s_cons.saveCol; +} + +/* Add a digit to current numeric argument. */ +static void Add_Digit(int c) +{ + KASSERT(ISDIGIT(c)); + if (s_cons.numArgs < MAXARGS) { + int argNum = s_cons.numArgs - 1; + s_cons.argList[argNum] *= 10; + s_cons.argList[argNum] += (c - '0'); + } +} + +/* + * Get a numeric argument. + * Returns zero if that argument was not actually specified. + */ +static int Get_Arg(int argNum) +{ + return argNum < s_cons.numArgs ? s_cons.argList[argNum] : 0; +} + +/* + * The workhorse output function. + * Depending on the current console output state, + * does literal character output or processes part of + * an escape sequence. + */ +static void Put_Char_Imp(int c) +{ +again: + switch (s_cons.state) { + case S_NORMAL: + if (c == ESC) + Start_Escape(); + else + Output_Literal_Character(c); + break; + + case S_ESC: + if (c == '[') + s_cons.state = S_ESC2; + else + Reset(); + break; + + case S_ESC2: + if (ISDIGIT(c)) { + Start_Arg(0); + goto again; + } else if (c == ';') { + /* Special case: for "n;m" commands, "n" is implicitly 1 if omitted */ + Start_Arg(0); + Add_Digit('1'); + Start_Arg(1); + } else { + s_cons.state = S_CMD; + goto again; + } + break; + + case S_ARG: + if (ISDIGIT(c)) + Add_Digit(c); + else if (c == ';') + Start_Arg(s_cons.numArgs); + else { + s_cons.state = S_CMD; + goto again; + } + break; + + case S_CMD: + switch (c) { + case 'K': Clear_To_EOL(); break; + case 's': Save_Cursor(); break; + case 'u': Restore_Cursor(); break; + case 'A': Move_Cursor(s_cons.row - Get_Arg(0), s_cons.col); break; + case 'B': Move_Cursor(s_cons.row + Get_Arg(0), s_cons.col); break; + case 'C': Move_Cursor(s_cons.row, s_cons.col + Get_Arg(0)); break; + case 'D': Move_Cursor(s_cons.row, s_cons.col - Get_Arg(0)); break; + case 'm': Update_Attributes(); break; + case 'f': case 'H': + if (s_cons.numArgs == 2) Move_Cursor(Get_Arg(0)-1, Get_Arg(1)-1); break; + case 'J': + if (s_cons.numArgs == 1 && Get_Arg(0) == 2) { + Clear_Screen(); + Put_Cursor(0, 0); + } + break; + default: break; + } + Reset(); + break; + + default: + KASSERT(false); + } +} + +/* + * Update the location of the hardware cursor. + */ +static void Update_Cursor(void) +{ + /* + * The cursor location is a character offset from the beginning + * of page memory (I think). + */ + uint_t characterPos = (s_cons.row * NUMCOLS) + s_cons.col; + uchar_t origAddr; + + /* + * Save original contents of CRT address register. + * It is considered good programming practice to restore + * it to its original value after modifying it. + */ + origAddr = In_Byte(CRT_ADDR_REG); + IO_Delay(); + + /* Set the high cursor location byte */ + Out_Byte(CRT_ADDR_REG, CRT_CURSOR_LOC_HIGH_REG); + IO_Delay(); + Out_Byte(CRT_DATA_REG, (characterPos>>8) & 0xff); + IO_Delay(); + + /* Set the low cursor location byte */ + Out_Byte(CRT_ADDR_REG, CRT_CURSOR_LOC_LOW_REG); + IO_Delay(); + Out_Byte(CRT_DATA_REG, characterPos & 0xff); + IO_Delay(); + + /* Restore contents of the CRT address register */ + Out_Byte(CRT_ADDR_REG, origAddr); +} + +/* ---------------------------------------------------------------------- + * Public functions + * ---------------------------------------------------------------------- */ + +/* + * Initialize the screen module. + */ +void Init_Screen(void) +{ + bool iflag = Begin_Int_Atomic(); + + s_cons.row = s_cons.col = 0; + s_cons.currentAttr = DEFAULT_ATTRIBUTE; + Clear_Screen(); + +#if 0 + { + unsigned int z = (unsigned int)&Print_Emit; + int i; + Put_Char(' '); + Put_Char('0'); + Put_Char('x'); + + for (i = 0; i < 8; i++) { + int j = z & 0xf0000000; + + j = j >> 28; + j &= 0x0000000f; + + if (j > 9) { + j += 55; + } else { + j += 48; + } + + Put_Char(j); + + z = z << 4; + } + } + +#endif + + End_Int_Atomic(iflag); + Print("Screen Inited\n"); +} + +/* + * Clear the screen using the current attribute. + */ +void Clear_Screen(void) +{ + uint_t* v = (uint_t*)VIDMEM; + int i; + uint_t fill = FILL_DWORD; + + bool iflag = Begin_Int_Atomic(); + + for (i = 0; i < NUM_SCREEN_DWORDS; ++i) + *v++ = fill; + + End_Int_Atomic(iflag); +} + +/* + * Get current cursor position. + */ +void Get_Cursor(int* row, int* col) +{ + bool iflag = Begin_Int_Atomic(); + *row = s_cons.row; + *col = s_cons.col; + End_Int_Atomic(iflag); +} + +/* + * Set the current cursor position. + * Return true if successful, or false if the specified + * cursor position is invalid. + */ +bool Put_Cursor(int row, int col) +{ + bool iflag; + + if (row < 0 || row >= NUMROWS || col < 0 || col >= NUMCOLS) + return false; + + iflag = Begin_Int_Atomic(); + s_cons.row = row; + s_cons.col = col; + Update_Cursor(); + End_Int_Atomic(iflag); + + return true; +} + +/* + * Get the current character attribute. + */ +uchar_t Get_Current_Attr(void) +{ + return s_cons.currentAttr; +} + +/* + * Set the current character attribute. + */ +void Set_Current_Attr(uchar_t attrib) +{ + bool iflag = Begin_Int_Atomic(); + s_cons.currentAttr = attrib; + End_Int_Atomic(iflag); +} + +/* + * Write a single character to the screen at current position + * using current attribute, handling scrolling, special characters, etc. + */ +void Put_Char(int c) +{ + bool iflag = Begin_Int_Atomic(); + Put_Char_Imp(c); + Update_Cursor(); + End_Int_Atomic(iflag); +} + +/* + * Write a string of characters to the screen at current cursor + * position using current attribute. + */ +void Put_String(const char* s) +{ + bool iflag = Begin_Int_Atomic(); + while (*s != '\0') + Put_Char_Imp(*s++); + Update_Cursor(); + End_Int_Atomic(iflag); +} + +/* + * Write a buffer of characters at current cursor position + * using current attribute. + */ +void Put_Buf(const char* buf, ulong_t length) +{ + bool iflag = Begin_Int_Atomic(); + while (length > 0) { + Put_Char_Imp(*buf++); + --length; + } + Update_Cursor(); + End_Int_Atomic(iflag); +} + +/* Support for Print(). */ +static void Print_Emit(struct Output_Sink *o, int ch) { Put_Char_Imp(ch); } +static void Print_Finish(struct Output_Sink *o) { Update_Cursor(); } +static struct Output_Sink s_outputSink = { &Print_Emit, &Print_Finish }; + +/* + * Print to console using printf()-style formatting. + * Calls into Format_Output in common library. + */ + +static __inline__ void PrintInternal(const char * format, va_list ap) { + Format_Output(&s_outputSink, format, ap); +} + +void Print(const char *fmt, ...) +{ + va_list args; + bool iflag = Begin_Int_Atomic(); + + va_start(args, fmt); + PrintInternal(fmt, args); + va_end(args); + + End_Int_Atomic(iflag); +} + + +void PrintList(const char * fmt, va_list ap) { + bool iflag = Begin_Int_Atomic(); + PrintInternal(fmt, ap); + End_Int_Atomic(iflag); +}