Initial checkin of toy OS project.
[os.git] / kernel / driver / video.c
1 //+----------------------------------------------------------------------------
2 //
3 // File:     video.c
4 //
5 // Module:   Video driver
6 //
7 // Synopsis: low-level interface to video memory
8 //
9 // Created:  sgasch  5 Jul 2003
10 //
11 //+----------------------------------------------------------------------------
12
13 #include <stdarg.h>
14
15 #include "kernel.h"
16 #include "hal.h"
17 #include "rtl.h"
18 #include "video.h"
19
20 extern BIOS_HARDWARE_BLOCK g_BiosHardwareBlock;
21
22 //
23 // Local #defines
24 //
25 #define FG_BG_TO_ATTRIB_BYTE(fg,bg)            ((fg&0x0F)|((bg&0x0F)<<4))
26 #define LINES                                  (0)
27 #define COLS                                   (1)
28 #define CRT_ADDR_REG                           (0x3D4)
29 #define CRT_DATA_REG                           (0x3D5)
30 #define CRT_CURSOR_LOC_HIGH_REG                (0x0E)
31 #define CRT_CURSOR_LOC_LOW_REG                 (0x0F)
32 #define ZERO_BASED_LINE_COL_TO_BYTE_OFFSET(L,C)\
33     (((g_bScreenResolution[COLS] << 1) * (L)) + ((C) << 1))
34 #define ZERO_BASED_LINE_COL_TO_ULONG_OFFSET(L,C)\
35     (ZERO_BASED_LINE_COL_TO_BYTE_OFFSET((L),(C)) >> 2)
36
37 //
38 // Global identifiers
39 //
40 BYTE *g_pVideoMemory = NULL;
41 BYTE g_bScreenResolution[2] = { 0, 0 };
42 BYTE g_bSoftCursor[2] = { 0, 0 };
43 BYTE g_bColorAttribute = FG_BG_TO_ATTRIB_BYTE(BLACK, WHITE);
44
45 typedef struct _VIDEO_LOOKUP_ENTRY
46 {
47     CHAR *szBoardName;
48     BYTE *pStartOfVideoMemory;
49     BYTE uScreenLines[3];
50 }
51 VIDEO_LOOKUP_ENTRY;
52
53 static VIDEO_LOOKUP_ENTRY g_VideoLookupTable[] =
54 {
55     { "No video adapter",
56       (BYTE *)0x000B0000,
57       { 25, 25, 25 } },
58     { "Monochrome display adapter",
59       (BYTE *)0x000B0000,
60       { 25, 25, 25 } },
61     { "CGA",
62       (BYTE *)0x000B8000,
63       { 25, 25, 25 } },
64     { "Code 3: Undefined",
65       (BYTE *)0x000B0000,
66       { 25, 25, 25 } },
67     { "EGA with color display",
68       (BYTE *)0x000B8000,
69       { 43, 25, 25 } },
70     { "EGA with mono display",
71       (BYTE *)0x000B0000,
72       { 43, 25, 25 } },
73     { "Code 6: Undefined",
74       (BYTE *)0x000B0000,
75       { 25, 25, 25 } },
76     { "VGA with mono display",
77       (BYTE *)0x000B0000,
78       { 50, 27, 25 } },
79     { "VGA with color display",
80       (BYTE *)0x000B8000,
81       { 50, 27, 25 } },
82     { "Code 9: Undefined",
83       (BYTE *)0x000B0000,
84       { 25, 25, 25 } },
85     { "MCGA with digital color",
86       (BYTE *)0x000B8000,
87       { 25, 25, 25 } },
88     { "MCGA with monochrome",
89       (BYTE *)0x000B0000,
90       { 25, 25, 25 } },
91     { "MCGA with analog color",
92       (BYTE *)0x000B8000,
93       { 25, 25, 25 } } 
94 };
95
96
97 //+----------------------------------------------------------------------------
98 //
99 // Function:  VideopInitialize
100 //
101 // Synopsis:  
102 //
103 // Arguments: void
104 //            
105 // Returns:   STATUS
106 //
107 //+----------------------------------------------------------------------------
108 STATUS 
109 VideopInitialize(void)
110 {
111     BYTE uType = g_BiosHardwareBlock.bDisplayType;
112     BYTE uIndex = 2;
113
114     //
115     // Based on the font size (width in pixels), set the index into the
116     // screen lines lookup table
117     //
118     switch(g_BiosHardwareBlock.bFontSize)
119     {
120         case 8:
121             uIndex = 0;
122             break;
123         case 14:
124             uIndex = 1;
125             break;
126         case 16:
127             uIndex = 2;
128             break;
129     }
130
131     //
132     // If the display type returned by BIOS is known to us, use the
133     // lookup table to populate some globals here.
134     //
135     if (uType <= ARRAY_LENGTH(g_VideoLookupTable))
136     {
137         g_pVideoMemory = g_VideoLookupTable[uType].pStartOfVideoMemory;
138         g_bScreenResolution[LINES] = 
139             g_VideoLookupTable[uType].uScreenLines[uIndex];
140     }
141     
142     //
143     // Otherwise make some guesses
144     //
145     else
146     {
147         g_pVideoMemory = (BYTE *)0x000B8000;
148         g_bScreenResolution[LINES] = 25;
149     }
150     g_bScreenResolution[COLS] = 80;
151
152     //
153     // Set color to WHITE on BLACK
154     //
155     VideoSetCurrentColorAttribute(WHITE, BLACK);
156
157     //
158     // Clear the screen and thus synchronize the hardware and soft cursor
159     // position.
160     //
161     VideoClearScreen();
162     return(STATUS_SUCCESS);
163 }
164
165
166 //+----------------------------------------------------------------------------
167 //
168 // Function:  VideoDriverMain
169 //
170 // Synopsis:  
171 //
172 // Arguments: UINT uEvent
173 //            
174 // Returns:   STATUS
175 //
176 //+----------------------------------------------------------------------------
177 STATUS 
178 VideoDriverMain(UINT uEvent)
179 {
180     switch(uEvent)
181     {
182         case DEVICE_DRIVER_INITIALIZATION:
183             return(VideopInitialize());
184
185         case DEVICE_DETATCH:
186             ASSERT(FALSE);
187             return(STATUS_NOT_SUPPORTED);
188
189         case DEVICE_DRIVER_SHUTDOWN:
190             g_fVideoEnabled = FALSE;
191             return(STATUS_SUCCESS);
192             
193         default:
194     }
195     return(TRUE);
196 }
197
198 STATUS
199 VideoDriverRead(UINT uAddress,
200                 void *pBuffer,
201                 UINT uBufferLength);
202
203 STATUS 
204 VideoDriverWrite(UINT uAddress,
205                  void *pBuffer,
206                  UINT uBufferLength);
207
208 STATUS
209 VideoDriverIoctl(UINT uIoctlCode,
210                  void *pBuffer,
211                  UINT uBufferLength);
212
213
214 //
215 // Move the hardware cursor on the screen to sync up with the software
216 // cursor position in g_bSoftwareCursor
217 //
218 void
219 VideoSetHardwareCursor(void)
220 {
221     WORD wOffset;                             // byte offset into video mem
222     BYTE bOrig;                               // original port contents holder
223
224     //
225     // Cursor is zero-based (e.g. line 24 is the last one)
226     //
227     ASSERT(g_bSoftCursor[LINES] < g_bScreenResolution[LINES]);
228     ASSERT(g_bSoftCursor[COLS] < g_bScreenResolution[COLS]);
229
230     wOffset = ZERO_BASED_LINE_COL_TO_BYTE_OFFSET(g_bSoftCursor[LINES],
231                                                  g_bSoftCursor[COLS]);
232     ASSERT(wOffset <= (g_bScreenResolution[LINES] * 
233                        g_bScreenResolution[COLS]) * 2 - 2);
234
235     //
236     // Save original contents of CRT address register.
237     //
238     bOrig = in(CRT_ADDR_REG);
239
240     //
241     // Set the high cursor location byte
242     //
243     out(CRT_ADDR_REG, CRT_CURSOR_LOC_HIGH_REG);
244     out(CRT_DATA_REG, (wOffset >> 8) & 0xFF);
245     IoDelay();
246
247     //
248     // Set the low cursor location byte
249     //
250     out(CRT_ADDR_REG, CRT_CURSOR_LOC_LOW_REG);
251     IoDelay();
252     out(CRT_DATA_REG, (wOffset & 0xFF));
253     IoDelay();
254
255     //
256     // Restore contents of the CRT address register
257     //
258     out(CRT_ADDR_REG, bOrig);
259     IoDelay();
260 }
261
262 void
263 VideoSetCurrentColorAttribute(BYTE bFg, BYTE bBg)
264 {
265     bFg &= 0x0F;                              // restrict these guys to colors
266     bBg &= 0x0F;                              // and the high intense bit
267
268     g_bColorAttribute = FG_BG_TO_ATTRIB_BYTE(bFg, bBg);
269 }
270
271 void
272 VideoClearScreen(void)
273 {
274     ULONG *p = (ULONG *)g_pVideoMemory;
275     ULONG uFill;
276     ULONG uCount;
277
278     //
279     // Start uCount at the number of ULONGs in visible video memory
280     //
281     uCount = ZERO_BASED_LINE_COL_TO_BYTE_OFFSET(g_bScreenResolution[LINES] - 1,
282                                                 g_bScreenResolution[COLS] - 1);
283     uCount += 2;
284     uCount >>= 2;
285
286     //
287     // Construct a fill ULONG: blank attrib blank attrib
288     //
289     uFill = 0;
290     uFill |= (ULONG)g_bColorAttribute;
291     uFill |= ((ULONG)g_bColorAttribute) << 16;
292
293     //
294     // Blast uFills into video memory
295     //
296     while(uCount)
297     {
298         *p++ = uFill;
299         uCount--;
300     }
301     
302     //
303     // Set cursor position to 0,0
304     //
305     g_bSoftCursor[LINES] = g_bSoftCursor[COLS] = 0;
306     VideoSetHardwareCursor();
307 }
308
309
310 void 
311 VideoScroll(void)
312 {
313     ULONG *p = (ULONG *)g_pVideoMemory;
314     ULONG uUlongsPerLine = (g_bScreenResolution[COLS] >> 1); // 2X/4
315     ULONG i, j;
316     ULONG uFill;
317
318     ASSERT(sizeof(ULONG) == 4);
319     ASSERT(sizeof(BYTE) == 1);
320
321     for (i = 0;
322          i < (g_bScreenResolution[LINES] - 2);
323          i++)
324     {
325         //
326         // Start at i, 0.  Copy a ULONG up from i+1, 0.  Do the whole line i.
327         //
328         for (j = 0;
329              j < uUlongsPerLine;
330              j++)
331         {
332             *p = *(p + uUlongsPerLine);
333             p++;
334         }
335     }
336     
337     //
338     // Construct a fill ULONG: blank attrib blank attrib
339     //
340     uFill = 0;
341     uFill |= (ULONG)g_bColorAttribute;
342     uFill |= ((ULONG)g_bColorAttribute) << 16;
343
344     //
345     // Blank the last line
346     //
347     p = (ULONG *)(g_pVideoMemory + 
348         ZERO_BASED_LINE_COL_TO_BYTE_OFFSET(g_bScreenResolution[LINES] - 1, 0));
349     for (i = 0;
350          i < uUlongsPerLine;
351          i++)
352     {
353         *p = uFill;
354         p++;
355     }
356 }
357
358 void 
359 VideoPutChar(BYTE c)
360 {
361     BYTE *p = (g_pVideoMemory + 
362                ZERO_BASED_LINE_COL_TO_BYTE_OFFSET(g_bSoftCursor[LINES],
363                                                   g_bSoftCursor[COLS]));
364     switch(c)
365     {
366         case '\n':
367             g_bSoftCursor[LINES]++;
368             if (g_bSoftCursor[LINES] >= g_bScreenResolution[LINES])
369             {
370                 VideoScroll();
371                 g_bSoftCursor[LINES] = g_bScreenResolution[LINES] - 1;
372             }
373             g_bSoftCursor[COLS] = 0;
374             return;
375         default:
376             *p = c;
377             p++;
378             *p = g_bColorAttribute;
379             p++;
380             g_bSoftCursor[COLS]++;
381             if (g_bSoftCursor[COLS] >= g_bScreenResolution[COLS])
382             {
383                 g_bSoftCursor[COLS] = 0;
384                 g_bSoftCursor[LINES]++;
385                 if (g_bSoftCursor[LINES] >= g_bScreenResolution[LINES])
386                 {
387                     VideoScroll();
388                     g_bSoftCursor[LINES] = g_bScreenResolution[LINES] - 1;
389                 }
390             }
391     }
392 }
393
394 void
395 VideoPutNullTerminatedString(BYTE *s)
396 {
397     ULONG uSafety = 0;
398
399     while (*s != 0)
400     {
401         VideoPutChar(*s);
402         s++;
403         uSafety++;
404
405         if (uSafety > 256) 
406         {
407             VideoPutNullTerminatedString("RUNAWAYSTRING!?!\n\0");
408             break;
409         }
410     }
411     VideoSetHardwareCursor();
412 }
413
414 void
415 VideoSetCursorPosition(BYTE bLine, BYTE bCol)
416 {
417     if (bLine >= g_bScreenResolution[LINES])
418     {
419         bLine = g_bScreenResolution[LINES] - 1;
420     }
421     if (bCol >= g_bScreenResolution[COLS])
422     {
423         bCol = g_bScreenResolution[COLS] - 1;
424     }
425
426     g_bSoftCursor[LINES] = bLine;
427     g_bSoftCursor[COLS] = bCol;
428     VideoSetHardwareCursor();
429 }
430
431 void
432 VideoGetCursorPosition(BYTE *pbLine, BYTE *pbCol)
433 {
434     *pbLine = g_bSoftCursor[LINES];
435     *pbCol = g_bSoftCursor[COLS];
436 }
437
438 void 
439 VideoPrint(CHAR *szFormat, ...)
440 {
441     CHAR *p = szFormat;
442     CHAR *q;
443     CHAR buf[32];
444     va_list args;
445     INT iVal;
446     UINT uVal;
447     CHAR *szVal;
448
449     va_start(args, szFormat);
450     while (*p != '\0')
451     {
452         switch (*p)
453         {
454             case '%':
455                 p++;
456                 switch (*p) 
457                 {
458                     case '\0':
459                         goto done;
460                     case '%':
461                         VideoPutChar('%');
462                         break;
463                     case 'd':
464                         iVal = va_arg(args, int);
465                         q = RtlIntToAscii(iVal, buf, ARRAY_LENGTH(buf), 10);
466                         ASSERT(strlen(q) < 20);
467                         VideoPutNullTerminatedString(q);
468                         break;
469                     case 'u':
470                         uVal = va_arg(args, unsigned int);
471                         q = RtlIntToAscii(uVal, buf, ARRAY_LENGTH(buf), 10);
472                         ASSERT(strlen(q) < 20);
473                         VideoPutNullTerminatedString(q);
474                         break;
475                     case 'x':
476                         uVal = va_arg(args, unsigned int);
477                         q = RtlIntToAscii(uVal, buf, ARRAY_LENGTH(buf), 16);
478                         ASSERT(strlen(q) < 20);
479                         VideoPutNullTerminatedString(q);
480                         break;
481                     case 's':
482                         szVal = va_arg(args, char *);
483                         VideoPutNullTerminatedString(szVal);
484                         break;
485                     case 'c':
486                         iVal = va_arg(args, int);
487                         VideoPutChar(iVal & 0xFF);
488                         break;
489                     default:
490                         VideoPutChar(*p);
491                         break;
492                 }
493                 break;
494             default:
495                 VideoPutChar(*p);
496                 break;
497         }
498         p++;
499     }
500  done:
501     va_end(args);
502     VideoSetHardwareCursor();
503 }