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.


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