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/fmtout.h>
16 #include <geekos/screen.h>
17 #include <geekos/vm_cons.h>
20 * Information sources for VT100 and ANSI escape sequences:
21 * - http://www.lns.cornell.edu/~pvhp/dcl/vt100.html
22 * - http://en.wikipedia.org/wiki/ANSI_escape_code
25 /* ----------------------------------------------------------------------
26 * Private functions and data
27 * ---------------------------------------------------------------------- */
29 #define ESC ((char) 0x1B)
30 #define DEFAULT_ATTRIBUTE ATTRIB(BLACK, GRAY)
33 S_NORMAL, /* Normal state - output is echoed verbatim */
34 S_ESC, /* Saw ESC character - begin output escape sequence */
35 S_ESC2, /* Saw '[' character - continue output escape sequence */
36 S_ARG, /* Scanning a numeric argument */
40 #define MAXARGS 8 /* Max args that can be passed to esc sequence */
42 struct Console_State {
43 /* Current state information */
48 /* Working variables for processing escape sequences. */
54 static struct Console_State s_cons;
56 #define NUM_SCREEN_DWORDS ((NUMROWS * NUMCOLS * 2) / 4)
57 #define NUM_SCROLL_DWORDS (((NUMROWS-1) * NUMCOLS * 2) / 4)
58 #define NUM_DWORDS_PER_LINE ((NUMCOLS*2)/4)
59 #define FILL_DWORD (0x00200020 | (s_cons.currentAttr<<24) | (s_cons.currentAttr<<8))
62 * Scroll the display one line.
63 * We speed things up by copying 4 bytes at a time.
65 static void Scroll(void)
68 int i, n = NUM_SCROLL_DWORDS;
69 uint_t fill = FILL_DWORD;
71 /* Move lines 1..NUMROWS-1 up one position. */
72 for (v = (uint_t*)VIDMEM, i = 0; i < n; ++i) {
73 *v = *(v + NUM_DWORDS_PER_LINE);
77 /* Clear out last line. */
78 for (v = (uint_t*)VIDMEM + n, i = 0; i < NUM_DWORDS_PER_LINE; ++i)
83 * Clear current cursor position to end of line using
86 static void Clear_To_EOL(void)
88 int n = (NUMCOLS - s_cons.col);
89 uchar_t* v = VIDMEM + s_cons.row*(NUMCOLS*2) + s_cons.col*2;
92 *v++ = s_cons.currentAttr;
97 * Move to the beginning of the next line, scrolling
100 static void Newline(void)
104 if (s_cons.row == NUMROWS) {
106 s_cons.row = NUMROWS - 1;
111 * Write the graphic representation of given character to the screen
112 * at current position, with current attribute, scrolling if
115 static void Put_Graphic_Char(int c)
117 uchar_t* v = VIDMEM + s_cons.row*(NUMCOLS*2) + s_cons.col*2;
119 /* Put character at current position */
121 *v = s_cons.currentAttr;
123 if (s_cons.col < NUMCOLS - 1)
130 * Put one character to the screen using the current cursor position
131 * and attribute, scrolling if needed. The caller should update
132 * the cursor position once all characters have been written.
134 static void Output_Literal_Character(int c)
145 numSpaces = TABWIDTH - (s_cons.col % TABWIDTH);
146 while (numSpaces-- > 0)
147 Put_Graphic_Char(' ');
157 * When compiled with --enable-port-e9-hack, Bochs will send writes
158 * to port E9 to the console. This helps tremendously with debugging,
159 * because it allows debug Print() statements to be visible after
167 * Move the cursor to a new position, stopping at the screen borders.
169 static void Move_Cursor(int row, int col)
173 else if (row >= NUMROWS)
178 else if (col >= NUMCOLS)
186 * Table mapping ANSI colors to VGA text mode colors.
188 static const uchar_t s_ansiToVgaColor[] = {
189 BLACK,RED,GREEN,AMBER,BLUE,MAGENTA,CYAN,GRAY
193 * Update the attributes specified by the arguments
194 * of the escape sequence.
196 static void Update_Attributes(void)
199 int attr = s_cons.currentAttr & ~(BRIGHT);
201 for (i = 0; i < s_cons.numArgs; ++i) {
202 int value = s_cons.argList[i];
204 attr = DEFAULT_ATTRIBUTE;
207 else if (value >= 30 && value <= 37)
208 attr = (attr & ~0x7) | s_ansiToVgaColor[value - 30];
209 else if (value >= 40 && value <= 47)
210 attr = (attr & ~(0x7 << 4)) | (s_ansiToVgaColor[value - 40] << 4);
212 s_cons.currentAttr = attr;
215 /* Reset to cancel or finish processing an escape sequence. */
216 static void Reset(void)
218 s_cons.state = S_NORMAL;
222 /* Start an escape sequence. */
223 static void Start_Escape(void)
225 s_cons.state = S_ESC;
229 /* Start a numeric argument to an escape sequence. */
230 static void Start_Arg(int argNum)
232 KASSERT(s_cons.numArgs == argNum);
234 s_cons.state = S_ARG;
235 if (argNum < MAXARGS)
236 s_cons.argList[argNum] = 0;
239 /* Save current cursor position. */
240 static void Save_Cursor(void)
242 s_cons.saveRow = s_cons.row;
243 s_cons.saveCol = s_cons.col;
246 /* Restore saved cursor position. */
247 static void Restore_Cursor(void)
249 s_cons.row = s_cons.saveRow;
250 s_cons.col = s_cons.saveCol;
253 /* Add a digit to current numeric argument. */
254 static void Add_Digit(int c)
257 if (s_cons.numArgs < MAXARGS) {
258 int argNum = s_cons.numArgs - 1;
259 s_cons.argList[argNum] *= 10;
260 s_cons.argList[argNum] += (c - '0');
265 * Get a numeric argument.
266 * Returns zero if that argument was not actually specified.
268 static int Get_Arg(int argNum)
270 return argNum < s_cons.numArgs ? s_cons.argList[argNum] : 0;
274 * The workhorse output function.
275 * Depending on the current console output state,
276 * does literal character output or processes part of
277 * an escape sequence.
279 static void Put_Char_Imp(int c)
282 switch (s_cons.state) {
287 Output_Literal_Character(c);
292 s_cons.state = S_ESC2;
301 } else if (c == ';') {
302 /* Special case: for "n;m" commands, "n" is implicitly 1 if omitted */
307 s_cons.state = S_CMD;
316 Start_Arg(s_cons.numArgs);
318 s_cons.state = S_CMD;
325 case 'K': Clear_To_EOL(); break;
326 case 's': Save_Cursor(); break;
327 case 'u': Restore_Cursor(); break;
328 case 'A': Move_Cursor(s_cons.row - Get_Arg(0), s_cons.col); break;
329 case 'B': Move_Cursor(s_cons.row + Get_Arg(0), s_cons.col); break;
330 case 'C': Move_Cursor(s_cons.row, s_cons.col + Get_Arg(0)); break;
331 case 'D': Move_Cursor(s_cons.row, s_cons.col - Get_Arg(0)); break;
332 case 'm': Update_Attributes(); break;
334 if (s_cons.numArgs == 2) Move_Cursor(Get_Arg(0)-1, Get_Arg(1)-1); break;
336 if (s_cons.numArgs == 1 && Get_Arg(0) == 2) {
352 * Update the location of the hardware cursor.
354 static void Update_Cursor(void)
357 * The cursor location is a character offset from the beginning
358 * of page memory (I think).
360 uint_t characterPos = (s_cons.row * NUMCOLS) + s_cons.col;
364 * Save original contents of CRT address register.
365 * It is considered good programming practice to restore
366 * it to its original value after modifying it.
368 origAddr = In_Byte(CRT_ADDR_REG);
371 /* Set the high cursor location byte */
372 Out_Byte(CRT_ADDR_REG, CRT_CURSOR_LOC_HIGH_REG);
374 Out_Byte(CRT_DATA_REG, (characterPos>>8) & 0xff);
377 /* Set the low cursor location byte */
378 Out_Byte(CRT_ADDR_REG, CRT_CURSOR_LOC_LOW_REG);
380 Out_Byte(CRT_DATA_REG, characterPos & 0xff);
383 /* Restore contents of the CRT address register */
384 Out_Byte(CRT_ADDR_REG, origAddr);
387 /* ----------------------------------------------------------------------
389 * ---------------------------------------------------------------------- */
393 * Clear the screen using the current attribute.
395 void Clear_Screen(void)
397 uint_t* v = (uint_t*)VIDMEM;
399 uint_t fill = FILL_DWORD;
401 bool iflag = Begin_Int_Atomic();
403 for (i = 0; i < NUM_SCREEN_DWORDS; ++i)
406 End_Int_Atomic(iflag);
410 * Get current cursor position.
412 void Get_Cursor(int* row, int* col)
414 bool iflag = Begin_Int_Atomic();
417 End_Int_Atomic(iflag);
421 * Set the current cursor position.
422 * Return true if successful, or false if the specified
423 * cursor position is invalid.
425 bool Put_Cursor(int row, int col)
429 if (row < 0 || row >= NUMROWS || col < 0 || col >= NUMCOLS)
432 iflag = Begin_Int_Atomic();
436 End_Int_Atomic(iflag);
442 * Get the current character attribute.
444 uchar_t Get_Current_Attr(void)
446 return s_cons.currentAttr;
450 * Set the current character attribute.
452 void Set_Current_Attr(uchar_t attrib)
454 bool iflag = Begin_Int_Atomic();
455 s_cons.currentAttr = attrib;
456 End_Int_Atomic(iflag);
460 * Write a single character to the screen at current position
461 * using current attribute, handling scrolling, special characters, etc.
465 bool iflag = Begin_Int_Atomic();
468 End_Int_Atomic(iflag);
472 * Write a string of characters to the screen at current cursor
473 * position using current attribute.
475 void Put_String(const char* s)
477 bool iflag = Begin_Int_Atomic();
481 End_Int_Atomic(iflag);
485 * Write a buffer of characters at current cursor position
486 * using current attribute.
488 void Put_Buf(const char* buf, ulong_t length)
490 bool iflag = Begin_Int_Atomic();
492 Put_Char_Imp(*buf++);
496 End_Int_Atomic(iflag);
499 /* Support for Print(). */
500 static void Print_Emit(struct Output_Sink *o, int ch) { Put_Char_Imp(ch); }
501 static void Print_Finish(struct Output_Sink *o) { Update_Cursor(); }
502 static struct Output_Sink s_outputSink;
505 * Print to console using printf()-style formatting.
506 * Calls into Format_Output in common library.
509 static __inline__ void PrintInternal(const char * format, va_list ap) {
510 Format_Output(&s_outputSink, format, ap);
514 void Print(const char *fmt, ...)
518 bool iflag = Begin_Int_Atomic();
521 PrintInternal(fmt, args);
524 End_Int_Atomic(iflag);
527 void PrintList(const char * fmt, va_list ap) {
528 bool iflag = Begin_Int_Atomic();
529 PrintInternal(fmt, ap);
530 End_Int_Atomic(iflag);
535 * Initialize the screen module.
537 void Init_Screen(void)
539 bool iflag = Begin_Int_Atomic();
541 s_outputSink.Emit = &Print_Emit;
542 s_outputSink.Finish = &Print_Finish;
544 s_cons.row = s_cons.col = 0;
545 s_cons.currentAttr = DEFAULT_ATTRIBUTE;
548 End_Int_Atomic(iflag);
549 Print("Screen Inited\n");