2 * GeekOS text screen output
3 * Copyright (c) 2001,2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
6 * This is free software. You are permitted to use,
7 * redistribute, and modify it as specified in the file "COPYING".
11 #include <geekos/kassert.h>
12 #include <geekos/ktypes.h>
13 #include <geekos/io.h>
14 #include <geekos/int.h>
15 #include <geekos/screen.h>
16 #include <geekos/debug.h>
19 * Information sources for VT100 and ANSI escape sequences:
20 * - http://www.lns.cornell.edu/~pvhp/dcl/vt100.html
21 * - http://en.wikipedia.org/wiki/ANSI_escape_code
24 /* ----------------------------------------------------------------------
25 * Private functions and data
26 * ---------------------------------------------------------------------- */
28 #define ESC ((char) 0x1B)
29 #define DEFAULT_ATTRIBUTE ATTRIB(BLACK, GRAY)
32 S_NORMAL, /* Normal state - output is echoed verbatim */
33 S_ESC, /* Saw ESC character - begin output escape sequence */
34 S_ESC2, /* Saw '[' character - continue output escape sequence */
35 S_ARG, /* Scanning a numeric argument */
39 #define MAXARGS 8 /* Max args that can be passed to esc sequence */
41 struct Console_State {
42 /* Current state information */
47 /* Working variables for processing escape sequences. */
53 static struct Console_State s_cons;
55 #define NUM_SCREEN_DWORDS ((NUMROWS * NUMCOLS * 2) / 4)
56 #define NUM_SCROLL_DWORDS (((NUMROWS-1) * NUMCOLS * 2) / 4)
57 #define NUM_DWORDS_PER_LINE ((NUMCOLS*2)/4)
58 #define FILL_DWORD (0x00200020 | (s_cons.currentAttr<<24) | (s_cons.currentAttr<<8))
61 * Scroll the display one line.
62 * We speed things up by copying 4 bytes at a time.
64 static void Scroll(void)
67 int i, n = NUM_SCROLL_DWORDS;
68 uint_t fill = FILL_DWORD;
70 /* Move lines 1..NUMROWS-1 up one position. */
71 for (v = (uint_t*)VIDMEM, i = 0; i < n; ++i) {
72 *v = *(v + NUM_DWORDS_PER_LINE);
76 /* Clear out last line. */
77 for (v = (uint_t*)VIDMEM + n, i = 0; i < NUM_DWORDS_PER_LINE; ++i)
82 * Clear current cursor position to end of line using
85 static void Clear_To_EOL(void)
87 int n = (NUMCOLS - s_cons.col);
88 uchar_t* v = VIDMEM + s_cons.row*(NUMCOLS*2) + s_cons.col*2;
91 *v++ = s_cons.currentAttr;
96 * Move to the beginning of the next line, scrolling
99 static void Newline(void)
103 if (s_cons.row == NUMROWS) {
105 s_cons.row = NUMROWS - 1;
110 * Write the graphic representation of given character to the screen
111 * at current position, with current attribute, scrolling if
114 static void Put_Graphic_Char(int c)
116 uchar_t* v = VIDMEM + s_cons.row*(NUMCOLS*2) + s_cons.col*2;
118 /* Put character at current position */
120 *v = s_cons.currentAttr;
122 if (s_cons.col < NUMCOLS - 1)
129 * Put one character to the screen using the current cursor position
130 * and attribute, scrolling if needed. The caller should update
131 * the cursor position once all characters have been written.
133 static void Output_Literal_Character(int c)
144 numSpaces = TABWIDTH - (s_cons.col % TABWIDTH);
145 while (numSpaces-- > 0)
146 Put_Graphic_Char(' ');
156 * When compiled with --enable-port-e9-hack, Bochs will send writes
157 * to port E9 to the console. This helps tremendously with debugging,
158 * because it allows debug Print() statements to be visible after
166 * Move the cursor to a new position, stopping at the screen borders.
168 static void Move_Cursor(int row, int col)
172 else if (row >= NUMROWS)
177 else if (col >= NUMCOLS)
185 * Table mapping ANSI colors to VGA text mode colors.
187 static const uchar_t s_ansiToVgaColor[] = {
188 BLACK,RED,GREEN,AMBER,BLUE,MAGENTA,CYAN,GRAY
192 * Update the attributes specified by the arguments
193 * of the escape sequence.
195 static void Update_Attributes(void)
198 int attr = s_cons.currentAttr & ~(BRIGHT);
200 for (i = 0; i < s_cons.numArgs; ++i) {
201 int value = s_cons.argList[i];
203 attr = DEFAULT_ATTRIBUTE;
206 else if (value >= 30 && value <= 37)
207 attr = (attr & ~0x7) | s_ansiToVgaColor[value - 30];
208 else if (value >= 40 && value <= 47)
209 attr = (attr & ~(0x7 << 4)) | (s_ansiToVgaColor[value - 40] << 4);
211 s_cons.currentAttr = attr;
214 /* Reset to cancel or finish processing an escape sequence. */
215 static void Reset(void)
217 s_cons.state = S_NORMAL;
221 /* Start an escape sequence. */
222 static void Start_Escape(void)
224 s_cons.state = S_ESC;
228 /* Start a numeric argument to an escape sequence. */
229 static void Start_Arg(int argNum)
231 KASSERT(s_cons.numArgs == argNum);
233 s_cons.state = S_ARG;
234 if (argNum < MAXARGS)
235 s_cons.argList[argNum] = 0;
238 /* Save current cursor position. */
239 static void Save_Cursor(void)
241 s_cons.saveRow = s_cons.row;
242 s_cons.saveCol = s_cons.col;
245 /* Restore saved cursor position. */
246 static void Restore_Cursor(void)
248 s_cons.row = s_cons.saveRow;
249 s_cons.col = s_cons.saveCol;
252 /* Add a digit to current numeric argument. */
253 static void Add_Digit(int c)
256 if (s_cons.numArgs < MAXARGS) {
257 int argNum = s_cons.numArgs - 1;
258 s_cons.argList[argNum] *= 10;
259 s_cons.argList[argNum] += (c - '0');
264 * Get a numeric argument.
265 * Returns zero if that argument was not actually specified.
267 static int Get_Arg(int argNum)
269 return argNum < s_cons.numArgs ? s_cons.argList[argNum] : 0;
273 * The workhorse output function.
274 * Depending on the current console output state,
275 * does literal character output or processes part of
276 * an escape sequence.
278 static void Put_Char_Imp(int c)
281 switch (s_cons.state) {
286 Output_Literal_Character(c);
291 s_cons.state = S_ESC2;
300 } else if (c == ';') {
301 /* Special case: for "n;m" commands, "n" is implicitly 1 if omitted */
306 s_cons.state = S_CMD;
315 Start_Arg(s_cons.numArgs);
317 s_cons.state = S_CMD;
324 case 'K': Clear_To_EOL(); break;
325 case 's': Save_Cursor(); break;
326 case 'u': Restore_Cursor(); break;
327 case 'A': Move_Cursor(s_cons.row - Get_Arg(0), s_cons.col); break;
328 case 'B': Move_Cursor(s_cons.row + Get_Arg(0), s_cons.col); break;
329 case 'C': Move_Cursor(s_cons.row, s_cons.col + Get_Arg(0)); break;
330 case 'D': Move_Cursor(s_cons.row, s_cons.col - Get_Arg(0)); break;
331 case 'm': Update_Attributes(); break;
333 if (s_cons.numArgs == 2) Move_Cursor(Get_Arg(0)-1, Get_Arg(1)-1); break;
335 if (s_cons.numArgs == 1 && Get_Arg(0) == 2) {
351 * Update the location of the hardware cursor.
353 static void Update_Cursor(void)
356 * The cursor location is a character offset from the beginning
357 * of page memory (I think).
359 uint_t characterPos = (s_cons.row * NUMCOLS) + s_cons.col;
363 * Save original contents of CRT address register.
364 * It is considered good programming practice to restore
365 * it to its original value after modifying it.
367 origAddr = In_Byte(CRT_ADDR_REG);
370 /* Set the high cursor location byte */
371 Out_Byte(CRT_ADDR_REG, CRT_CURSOR_LOC_HIGH_REG);
373 Out_Byte(CRT_DATA_REG, (characterPos>>8) & 0xff);
376 /* Set the low cursor location byte */
377 Out_Byte(CRT_ADDR_REG, CRT_CURSOR_LOC_LOW_REG);
379 Out_Byte(CRT_DATA_REG, characterPos & 0xff);
382 /* Restore contents of the CRT address register */
383 Out_Byte(CRT_ADDR_REG, origAddr);
386 /* ----------------------------------------------------------------------
388 * ---------------------------------------------------------------------- */
391 * Initialize the screen module.
393 void Init_Screen(void)
395 bool iflag = Begin_Int_Atomic();
397 s_cons.row = s_cons.col = 0;
398 s_cons.currentAttr = DEFAULT_ATTRIBUTE;
403 unsigned int z = (unsigned int)&Print_Emit;
409 for (i = 0; i < 8; i++) {
410 int j = z & 0xf0000000;
429 End_Int_Atomic(iflag);
430 Print("Screen Inited\n");
434 * Clear the screen using the current attribute.
436 void Clear_Screen(void)
438 uint_t* v = (uint_t*)VIDMEM;
440 uint_t fill = FILL_DWORD;
442 bool iflag = Begin_Int_Atomic();
444 for (i = 0; i < NUM_SCREEN_DWORDS; ++i)
447 End_Int_Atomic(iflag);
451 * Get current cursor position.
453 void Get_Cursor(int* row, int* col)
455 bool iflag = Begin_Int_Atomic();
458 End_Int_Atomic(iflag);
462 * Set the current cursor position.
463 * Return true if successful, or false if the specified
464 * cursor position is invalid.
466 bool Put_Cursor(int row, int col)
470 if (row < 0 || row >= NUMROWS || col < 0 || col >= NUMCOLS)
473 iflag = Begin_Int_Atomic();
477 End_Int_Atomic(iflag);
483 * Get the current character attribute.
485 uchar_t Get_Current_Attr(void)
487 return s_cons.currentAttr;
491 * Set the current character attribute.
493 void Set_Current_Attr(uchar_t attrib)
495 bool iflag = Begin_Int_Atomic();
496 s_cons.currentAttr = attrib;
497 End_Int_Atomic(iflag);
501 * Write a single character to the screen at current position
502 * using current attribute, handling scrolling, special characters, etc.
506 bool iflag = Begin_Int_Atomic();
509 End_Int_Atomic(iflag);
513 * Write a string of characters to the screen at current cursor
514 * position using current attribute.
516 void Put_String(const char* s)
518 bool iflag = Begin_Int_Atomic();
522 End_Int_Atomic(iflag);
526 * Write a buffer of characters at current cursor position
527 * using current attribute.
529 void Put_Buf(const char* buf, ulong_t length)
531 bool iflag = Begin_Int_Atomic();
533 Put_Char_Imp(*buf++);
537 End_Int_Atomic(iflag);
540 /* Support for Print(). */
541 static void Print_Emit(struct Output_Sink *o, int ch) { Put_Char_Imp(ch); }
542 static void Print_Finish(struct Output_Sink *o) { Update_Cursor(); }
543 static struct Output_Sink s_outputSink = { &Print_Emit, &Print_Finish };
546 * Print to console using printf()-style formatting.
547 * Calls into Format_Output in common library.
550 static __inline__ void PrintInternal(const char * format, va_list ap) {
551 Format_Output(&s_outputSink, format, ap);
554 void Print(const char *fmt, ...)
557 bool iflag = Begin_Int_Atomic();
560 PrintInternal(fmt, args);
563 End_Int_Atomic(iflag);
567 void PrintList(const char * fmt, va_list ap) {
568 bool iflag = Begin_Int_Atomic();
569 PrintInternal(fmt, ap);
570 End_Int_Atomic(iflag);