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.


*** empty log message ***
[palacios.releases.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.3 $
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/screen.h>
16 #include <geekos/debug.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 #if 0
402     {
403     unsigned int z = (unsigned int)&Print_Emit;
404     int i;
405     Put_Char(' ');
406     Put_Char('0');
407     Put_Char('x');
408     
409     for (i = 0; i < 8; i++) {
410       int j = z & 0xf0000000;
411       
412       j = j >> 28;
413       j &= 0x0000000f;
414
415       if (j > 9) {
416         j += 55;
417       } else {
418         j += 48;
419       }
420
421       Put_Char(j);    
422       
423       z = z << 4;
424     }
425   }
426
427 #endif
428
429     End_Int_Atomic(iflag);
430     Print("Screen Inited\n");
431 }
432
433 /*
434  * Clear the screen using the current attribute.
435  */
436 void Clear_Screen(void)
437 {
438     uint_t* v = (uint_t*)VIDMEM;
439     int i;
440     uint_t fill = FILL_DWORD;
441
442     bool iflag = Begin_Int_Atomic();
443
444     for (i = 0; i < NUM_SCREEN_DWORDS; ++i)
445         *v++ = fill;
446
447     End_Int_Atomic(iflag);
448 }
449
450 /*
451  * Get current cursor position.
452  */
453 void Get_Cursor(int* row, int* col)
454 {
455     bool iflag = Begin_Int_Atomic();
456     *row = s_cons.row;
457     *col = s_cons.col;
458     End_Int_Atomic(iflag);
459 }
460
461 /*
462  * Set the current cursor position.
463  * Return true if successful, or false if the specified
464  * cursor position is invalid.
465  */
466 bool Put_Cursor(int row, int col)
467 {
468     bool iflag;
469
470     if (row < 0 || row >= NUMROWS || col < 0 || col >= NUMCOLS)
471         return false;
472
473     iflag = Begin_Int_Atomic();
474     s_cons.row = row;
475     s_cons.col = col;
476     Update_Cursor();
477     End_Int_Atomic(iflag);
478
479     return true;
480 }
481
482 /*
483  * Get the current character attribute.
484  */
485 uchar_t Get_Current_Attr(void)
486 {
487     return s_cons.currentAttr;
488 }
489
490 /*
491  * Set the current character attribute.
492  */
493 void Set_Current_Attr(uchar_t attrib)
494 {
495     bool iflag = Begin_Int_Atomic();
496     s_cons.currentAttr = attrib;
497     End_Int_Atomic(iflag);
498 }
499
500 /*
501  * Write a single character to the screen at current position
502  * using current attribute, handling scrolling, special characters, etc.
503  */
504 void Put_Char(int c)
505 {
506     bool iflag = Begin_Int_Atomic();
507     Put_Char_Imp(c);
508     Update_Cursor();
509     End_Int_Atomic(iflag);
510 }
511
512 /*
513  * Write a string of characters to the screen at current cursor
514  * position using current attribute.
515  */
516 void Put_String(const char* s)
517 {
518     bool iflag = Begin_Int_Atomic();
519     while (*s != '\0')
520         Put_Char_Imp(*s++);
521     Update_Cursor();
522     End_Int_Atomic(iflag);
523 }
524
525 /*
526  * Write a buffer of characters at current cursor position
527  * using current attribute.
528  */
529 void Put_Buf(const char* buf, ulong_t length)
530 {
531     bool iflag = Begin_Int_Atomic();
532     while (length > 0) {
533         Put_Char_Imp(*buf++);
534         --length;
535     }
536     Update_Cursor();
537     End_Int_Atomic(iflag);
538 }
539
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 };
544
545 /*
546  * Print to console using printf()-style formatting.
547  * Calls into Format_Output in common library.
548  */
549
550 static __inline__ void PrintInternal(const char * format, va_list ap) {
551     Format_Output(&s_outputSink, format, ap);
552 }
553
554 void Print(const char *fmt, ...)
555 {
556     va_list args;
557     bool iflag = Begin_Int_Atomic();
558
559     va_start(args, fmt);
560     PrintInternal(fmt, args);
561     va_end(args);
562
563     End_Int_Atomic(iflag);
564 }
565
566
567 void PrintList(const char * fmt, va_list ap) {
568     bool iflag = Begin_Int_Atomic();
569     PrintInternal(fmt, ap);
570     End_Int_Atomic(iflag);
571 }