Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


(no commit message)
[palacios.git] / palacios / src / geekos / screen.c
1 /*
2  * GeekOS text screen output
3  * Copyright (c) 2001,2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
4  * $Revision: 1.1.1.1 $
5  * 
6  * This is free software.  You are permitted to use,
7  * redistribute, and modify it as specified in the file "COPYING".
8  */
9
10 #include <stdarg.h>
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
18 /*
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
22  */
23
24 /* ----------------------------------------------------------------------
25  * Private functions and data
26  * ---------------------------------------------------------------------- */
27
28 #define ESC ((char) 0x1B)
29 #define DEFAULT_ATTRIBUTE ATTRIB(BLACK, GRAY)
30
31 enum State {
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 */
36     S_CMD,              /* Command */
37 };
38
39 #define MAXARGS 8       /* Max args that can be passed to esc sequence */
40
41 struct Console_State {
42     /* Current state information */
43     int row, col;
44     int saveRow, saveCol;
45     uchar_t currentAttr;
46
47     /* Working variables for processing escape sequences. */
48     enum State state;
49     int argList[MAXARGS];
50     int numArgs;
51 };
52
53 static struct Console_State s_cons;
54
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))
59
60 /*
61  * Scroll the display one line.
62  * We speed things up by copying 4 bytes at a time.
63  */
64 static void Scroll(void)
65 {
66     uint_t* v;
67     int i, n = NUM_SCROLL_DWORDS;
68     uint_t fill = FILL_DWORD;
69
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);
73         ++v;
74     }
75
76     /* Clear out last line. */
77     for (v = (uint_t*)VIDMEM + n, i = 0; i < NUM_DWORDS_PER_LINE; ++i)
78         *v++ = fill;
79 }
80
81 /*
82  * Clear current cursor position to end of line using
83  * current attribute.
84  */
85 static void Clear_To_EOL(void)
86 {
87     int n = (NUMCOLS - s_cons.col);
88     uchar_t* v = VIDMEM + s_cons.row*(NUMCOLS*2) + s_cons.col*2;
89     while (n-- > 0) {
90         *v++ = ' ';
91         *v++ = s_cons.currentAttr;
92     }
93 }
94
95 /*
96  * Move to the beginning of the next line, scrolling
97  * if necessary.
98  */
99 static void Newline(void)
100 {
101     ++s_cons.row;
102     s_cons.col = 0;
103     if (s_cons.row == NUMROWS) {
104         Scroll();
105         s_cons.row = NUMROWS - 1;
106     }
107 }
108
109 /*
110  * Write the graphic representation of given character to the screen
111  * at current position, with current attribute, scrolling if
112  * necessary.
113  */
114 static void Put_Graphic_Char(int c)
115 {
116     uchar_t* v = VIDMEM + s_cons.row*(NUMCOLS*2) + s_cons.col*2;
117
118     /* Put character at current position */
119     *v++ = (uchar_t) c;
120     *v = s_cons.currentAttr;
121
122     if (s_cons.col < NUMCOLS - 1)
123         ++s_cons.col;
124     else
125         Newline();
126 }
127
128 /*
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.
132  */
133 static void Output_Literal_Character(int c)
134 {
135     int numSpaces;
136
137     switch (c) {
138     case '\n':
139         Clear_To_EOL();
140         Newline();
141         break;
142
143     case '\t':
144         numSpaces = TABWIDTH - (s_cons.col % TABWIDTH);
145         while (numSpaces-- > 0)
146             Put_Graphic_Char(' ');
147         break;
148
149     default:
150         Put_Graphic_Char(c);
151         break;
152     }
153
154 #ifndef NDEBUG
155     /*
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
159      * Bochs has exited.
160      */
161     Out_Byte(0xE9, c);
162 #endif
163 }
164
165 /*
166  * Move the cursor to a new position, stopping at the screen borders.
167  */
168 static void Move_Cursor(int row, int col)
169 {
170     if (row < 0)
171         row = 0;
172     else if (row >= NUMROWS)
173         row = NUMROWS - 1;
174
175     if (col < 0)
176         col = 0;
177     else if (col >= NUMCOLS)
178         col = NUMCOLS - 1;
179
180     s_cons.row = row;
181     s_cons.col = col;
182 }
183
184 /*
185  * Table mapping ANSI colors to VGA text mode colors.
186  */
187 static const uchar_t s_ansiToVgaColor[] = {
188     BLACK,RED,GREEN,AMBER,BLUE,MAGENTA,CYAN,GRAY
189 };
190
191 /*
192  * Update the attributes specified by the arguments
193  * of the escape sequence.
194  */
195 static void Update_Attributes(void)
196 {
197     int i;
198     int attr = s_cons.currentAttr & ~(BRIGHT);
199
200     for (i = 0; i < s_cons.numArgs; ++i) {
201         int value = s_cons.argList[i];
202         if (value == 0)
203             attr = DEFAULT_ATTRIBUTE;
204         else if (value == 1)
205             attr |= BRIGHT;
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);
210     }
211     s_cons.currentAttr = attr;
212 }
213
214 /* Reset to cancel or finish processing an escape sequence. */
215 static void Reset(void)
216 {
217     s_cons.state = S_NORMAL;
218     s_cons.numArgs = 0;
219 }
220
221 /* Start an escape sequence. */
222 static void Start_Escape(void)
223 {
224     s_cons.state = S_ESC;
225     s_cons.numArgs = 0;
226 }
227
228 /* Start a numeric argument to an escape sequence. */
229 static void Start_Arg(int argNum)
230 {
231     KASSERT(s_cons.numArgs == argNum);
232     s_cons.numArgs++;
233     s_cons.state = S_ARG;
234     if (argNum < MAXARGS)
235         s_cons.argList[argNum] = 0;
236 }
237
238 /* Save current cursor position. */
239 static void Save_Cursor(void)
240 {
241     s_cons.saveRow = s_cons.row;
242     s_cons.saveCol = s_cons.col;
243 }
244
245 /* Restore saved cursor position. */
246 static void Restore_Cursor(void)
247 {
248     s_cons.row = s_cons.saveRow;
249     s_cons.col = s_cons.saveCol;
250 }
251
252 /* Add a digit to current numeric argument. */
253 static void Add_Digit(int c)
254 {
255     KASSERT(ISDIGIT(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');
260     }
261 }
262
263 /*
264  * Get a numeric argument.
265  * Returns zero if that argument was not actually specified.
266  */
267 static int Get_Arg(int argNum)
268 {
269     return argNum < s_cons.numArgs ? s_cons.argList[argNum] : 0;
270 }
271
272 /*
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.
277  */
278 static void Put_Char_Imp(int c)
279 {
280 again:
281     switch (s_cons.state) {
282     case S_NORMAL:
283         if (c == ESC)
284             Start_Escape();
285         else
286             Output_Literal_Character(c);
287         break;
288
289     case S_ESC:
290         if (c == '[')
291             s_cons.state = S_ESC2;
292         else
293             Reset();
294         break;
295
296     case S_ESC2:
297         if (ISDIGIT(c)) {
298             Start_Arg(0);
299             goto again;
300         } else if (c == ';') {
301             /* Special case: for "n;m" commands, "n" is implicitly 1 if omitted */
302             Start_Arg(0);
303             Add_Digit('1');
304             Start_Arg(1);
305         } else {
306             s_cons.state = S_CMD;
307             goto again;
308         }
309         break;
310
311     case S_ARG:
312         if (ISDIGIT(c))
313             Add_Digit(c);
314         else if (c == ';')
315             Start_Arg(s_cons.numArgs);
316         else {
317             s_cons.state = S_CMD;
318             goto again;
319         }
320         break;
321
322     case S_CMD:
323         switch (c) {
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;
332         case 'f': case 'H':
333             if (s_cons.numArgs == 2) Move_Cursor(Get_Arg(0)-1, Get_Arg(1)-1); break;
334         case 'J':
335             if (s_cons.numArgs == 1 && Get_Arg(0) == 2) {
336                 Clear_Screen();
337                 Put_Cursor(0, 0);
338             }
339             break;
340         default: break;
341         }
342         Reset();
343         break;
344
345     default:
346         KASSERT(false);
347     }
348 }
349
350 /*
351  * Update the location of the hardware cursor.
352  */
353 static void Update_Cursor(void)
354 {
355     /*
356      * The cursor location is a character offset from the beginning
357      * of page memory (I think).
358      */
359     uint_t characterPos = (s_cons.row * NUMCOLS) + s_cons.col;
360     uchar_t origAddr;
361
362     /*
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.
366      */
367     origAddr = In_Byte(CRT_ADDR_REG);
368     IO_Delay();
369
370     /* Set the high cursor location byte */
371     Out_Byte(CRT_ADDR_REG, CRT_CURSOR_LOC_HIGH_REG);
372     IO_Delay();
373     Out_Byte(CRT_DATA_REG, (characterPos>>8) & 0xff);
374     IO_Delay();
375
376     /* Set the low cursor location byte */
377     Out_Byte(CRT_ADDR_REG, CRT_CURSOR_LOC_LOW_REG);
378     IO_Delay();
379     Out_Byte(CRT_DATA_REG, characterPos & 0xff);
380     IO_Delay();
381
382     /* Restore contents of the CRT address register */
383     Out_Byte(CRT_ADDR_REG, origAddr);
384 }
385
386 /* ----------------------------------------------------------------------
387  * Public functions
388  * ---------------------------------------------------------------------- */
389
390 /*
391  * Initialize the screen module.
392  */
393 void Init_Screen(void)
394 {
395     bool iflag = Begin_Int_Atomic();
396
397     s_cons.row = s_cons.col = 0;
398     s_cons.currentAttr = DEFAULT_ATTRIBUTE;
399     Clear_Screen();
400
401     End_Int_Atomic(iflag);
402     Print("Screen Inited\n");
403 }
404
405 /*
406  * Clear the screen using the current attribute.
407  */
408 void Clear_Screen(void)
409 {
410     uint_t* v = (uint_t*)VIDMEM;
411     int i;
412     uint_t fill = FILL_DWORD;
413
414     bool iflag = Begin_Int_Atomic();
415
416     for (i = 0; i < NUM_SCREEN_DWORDS; ++i)
417         *v++ = fill;
418
419     End_Int_Atomic(iflag);
420 }
421
422 /*
423  * Get current cursor position.
424  */
425 void Get_Cursor(int* row, int* col)
426 {
427     bool iflag = Begin_Int_Atomic();
428     *row = s_cons.row;
429     *col = s_cons.col;
430     End_Int_Atomic(iflag);
431 }
432
433 /*
434  * Set the current cursor position.
435  * Return true if successful, or false if the specified
436  * cursor position is invalid.
437  */
438 bool Put_Cursor(int row, int col)
439 {
440     bool iflag;
441
442     if (row < 0 || row >= NUMROWS || col < 0 || col >= NUMCOLS)
443         return false;
444
445     iflag = Begin_Int_Atomic();
446     s_cons.row = row;
447     s_cons.col = col;
448     Update_Cursor();
449     End_Int_Atomic(iflag);
450
451     return true;
452 }
453
454 /*
455  * Get the current character attribute.
456  */
457 uchar_t Get_Current_Attr(void)
458 {
459     return s_cons.currentAttr;
460 }
461
462 /*
463  * Set the current character attribute.
464  */
465 void Set_Current_Attr(uchar_t attrib)
466 {
467     bool iflag = Begin_Int_Atomic();
468     s_cons.currentAttr = attrib;
469     End_Int_Atomic(iflag);
470 }
471
472 /*
473  * Write a single character to the screen at current position
474  * using current attribute, handling scrolling, special characters, etc.
475  */
476 void Put_Char(int c)
477 {
478     bool iflag = Begin_Int_Atomic();
479     Put_Char_Imp(c);
480     Update_Cursor();
481     End_Int_Atomic(iflag);
482 }
483
484 /*
485  * Write a string of characters to the screen at current cursor
486  * position using current attribute.
487  */
488 void Put_String(const char* s)
489 {
490     bool iflag = Begin_Int_Atomic();
491     while (*s != '\0')
492         Put_Char_Imp(*s++);
493     Update_Cursor();
494     End_Int_Atomic(iflag);
495 }
496
497 /*
498  * Write a buffer of characters at current cursor position
499  * using current attribute.
500  */
501 void Put_Buf(const char* buf, ulong_t length)
502 {
503     bool iflag = Begin_Int_Atomic();
504     while (length > 0) {
505         Put_Char_Imp(*buf++);
506         --length;
507     }
508     Update_Cursor();
509     End_Int_Atomic(iflag);
510 }
511
512 /* Support for Print(). */
513 static void Print_Emit(struct Output_Sink *o, int ch) { Put_Char_Imp(ch); }
514 static void Print_Finish(struct Output_Sink *o) { Update_Cursor(); }
515 static struct Output_Sink s_outputSink = { &Print_Emit, &Print_Finish };
516
517 /*
518  * Print to console using printf()-style formatting.
519  * Calls into Format_Output in common library.
520  */
521 void Print(const char *fmt, ...)
522 {
523     va_list args;
524
525     bool iflag = Begin_Int_Atomic();
526
527     va_start(args, fmt);
528     Format_Output(&s_outputSink, fmt, args);
529     va_end(args);
530
531     End_Int_Atomic(iflag);
532 }
533