xref: /reactos/drivers/base/bootvid/i386/pc/vga.c (revision 92dfec21)
1 #include "precomp.h"
2 
3 /* GLOBALS *******************************************************************/
4 
5 static UCHAR lMaskTable[8] =
6 {
7     (1 << 8) - (1 << 0),
8     (1 << 7) - (1 << 0),
9     (1 << 6) - (1 << 0),
10     (1 << 5) - (1 << 0),
11     (1 << 4) - (1 << 0),
12     (1 << 3) - (1 << 0),
13     (1 << 2) - (1 << 0),
14     (1 << 1) - (1 << 0)
15 };
16 static UCHAR rMaskTable[8] =
17 {
18     (1 << 7),
19     (1 << 7) + (1 << 6),
20     (1 << 7) + (1 << 6) + (1 << 5),
21     (1 << 7) + (1 << 6) + (1 << 5) + (1 << 4),
22     (1 << 7) + (1 << 6) + (1 << 5) + (1 << 4) + (1 << 3),
23     (1 << 7) + (1 << 6) + (1 << 5) + (1 << 4) + (1 << 3) + (1 << 2),
24     (1 << 7) + (1 << 6) + (1 << 5) + (1 << 4) + (1 << 3) + (1 << 2) + (1 << 1),
25     (1 << 7) + (1 << 6) + (1 << 5) + (1 << 4) + (1 << 3) + (1 << 2) + (1 << 1) + (1 << 0),
26 };
27 UCHAR PixelMask[8] =
28 {
29     (1 << 7),
30     (1 << 6),
31     (1 << 5),
32     (1 << 4),
33     (1 << 3),
34     (1 << 2),
35     (1 << 1),
36     (1 << 0),
37 };
38 static ULONG lookup[16] =
39 {
40     0x0000,
41     0x0100,
42     0x1000,
43     0x1100,
44     0x0001,
45     0x0101,
46     0x1001,
47     0x1101,
48     0x0010,
49     0x0110,
50     0x1010,
51     0x1110,
52     0x0011,
53     0x0111,
54     0x1011,
55     0x1111,
56 };
57 
58 ULONG_PTR VgaRegisterBase = 0;
59 ULONG_PTR VgaBase = 0;
60 static BOOLEAN ClearRow = FALSE;
61 
62 /* PRIVATE FUNCTIONS *********************************************************/
63 
64 static VOID
65 NTAPI
66 ReadWriteMode(IN UCHAR Mode)
67 {
68     UCHAR Value;
69 
70     /* Switch to graphics mode register */
71     __outpb(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, IND_GRAPH_MODE);
72 
73     /* Get the current register value, minus the current mode */
74     Value = __inpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT) & 0xF4;
75 
76     /* Set the new mode */
77     __outpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT, Mode | Value);
78 }
79 
80 VOID
81 PrepareForSetPixel(VOID)
82 {
83     /* Switch to mode 10 */
84     ReadWriteMode(10);
85 
86     /* Clear the 4 planes (we're already in unchained mode here) */
87     __outpw(VGA_BASE_IO_PORT + SEQ_ADDRESS_PORT, 0x0F02);
88 
89     /* Select the color don't care register */
90     __outpw(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, 7);
91 }
92 
93 #define SET_PIXELS(_PixelPtr, _PixelMask, _TextColor)       \
94 do {                                                        \
95     /* Select the bitmask register and write the mask */    \
96     __outpw(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, ((_PixelMask) << 8) | IND_BIT_MASK); \
97     /* Set the new color */                                 \
98     WRITE_REGISTER_UCHAR((_PixelPtr), (UCHAR)(_TextColor)); \
99 } while (0);
100 
101 #ifdef CHAR_GEN_UPSIDE_DOWN
102 # define GetFontPtr(_Char) &FontData[_Char * BOOTCHAR_HEIGHT] + BOOTCHAR_HEIGHT - 1;
103 # define FONT_PTR_DELTA (-1)
104 #else
105 # define GetFontPtr(_Char) &FontData[_Char * BOOTCHAR_HEIGHT];
106 # define FONT_PTR_DELTA (1)
107 #endif
108 
109 VOID
110 NTAPI
111 DisplayCharacter(
112     _In_ CHAR Character,
113     _In_ ULONG Left,
114     _In_ ULONG Top,
115     _In_ ULONG TextColor,
116     _In_ ULONG BackColor)
117 {
118     PUCHAR FontChar, PixelPtr;
119     ULONG Height;
120     UCHAR Shift;
121 
122     PrepareForSetPixel();
123 
124     /* Calculate shift */
125     Shift = Left & 7;
126 
127     /* Get the font and pixel pointer */
128     FontChar = GetFontPtr(Character);
129     PixelPtr = (PUCHAR)(VgaBase + (Left >> 3) + (Top * (SCREEN_WIDTH / 8)));
130 
131     /* Loop all pixel rows */
132     for (Height = BOOTCHAR_HEIGHT; Height > 0; --Height)
133     {
134         SET_PIXELS(PixelPtr, *FontChar >> Shift, TextColor);
135         PixelPtr += (SCREEN_WIDTH / 8);
136         FontChar += FONT_PTR_DELTA;
137     }
138 
139     /* Check if we need to update neighbor bytes */
140     if (Shift)
141     {
142         /* Calculate shift for 2nd byte */
143         Shift = 8 - Shift;
144 
145         /* Get the font and pixel pointer (2nd byte) */
146         FontChar = GetFontPtr(Character);
147         PixelPtr = (PUCHAR)(VgaBase + (Left >> 3) + (Top * (SCREEN_WIDTH / 8)) + 1);
148 
149         /* Loop all pixel rows */
150         for (Height = BOOTCHAR_HEIGHT; Height > 0; --Height)
151         {
152             SET_PIXELS(PixelPtr, *FontChar << Shift, TextColor);
153             PixelPtr += (SCREEN_WIDTH / 8);
154             FontChar += FONT_PTR_DELTA;
155         }
156     }
157 
158     /* Check if the background color is transparent */
159     if (BackColor >= 16)
160     {
161         /* We are done */
162         return;
163     }
164 
165     /* Calculate shift */
166     Shift = Left & 7;
167 
168     /* Get the font and pixel pointer */
169     FontChar = GetFontPtr(Character);
170     PixelPtr = (PUCHAR)(VgaBase + (Left >> 3) + (Top * (SCREEN_WIDTH / 8)));
171 
172     /* Loop all pixel rows */
173     for (Height = BOOTCHAR_HEIGHT; Height > 0; --Height)
174     {
175         SET_PIXELS(PixelPtr, ~*FontChar >> Shift, BackColor);
176         PixelPtr += (SCREEN_WIDTH / 8);
177         FontChar += FONT_PTR_DELTA;
178     }
179 
180     /* Check if we need to update neighbor bytes */
181     if (Shift)
182     {
183         /* Calculate shift for 2nd byte */
184         Shift = 8 - Shift;
185 
186         /* Get the font and pixel pointer (2nd byte) */
187         FontChar = GetFontPtr(Character);
188         PixelPtr = (PUCHAR)(VgaBase + (Left >> 3) + (Top * (SCREEN_WIDTH / 8)) + 1);
189 
190         /* Loop all pixel rows */
191         for (Height = BOOTCHAR_HEIGHT; Height > 0; --Height)
192         {
193             SET_PIXELS(PixelPtr, ~*FontChar << Shift, BackColor);
194             PixelPtr += (SCREEN_WIDTH / 8);
195             FontChar += FONT_PTR_DELTA;
196         }
197     }
198 }
199 
200 static VOID
201 NTAPI
202 SetPaletteEntryRGB(IN ULONG Id,
203                    IN ULONG Rgb)
204 {
205     PCHAR Colors = (PCHAR)&Rgb;
206 
207     /* Set the palette index */
208     __outpb(VGA_BASE_IO_PORT + DAC_ADDRESS_WRITE_PORT, (UCHAR)Id);
209 
210     /* Set RGB colors */
211     __outpb(VGA_BASE_IO_PORT + DAC_DATA_REG_PORT, Colors[2] >> 2);
212     __outpb(VGA_BASE_IO_PORT + DAC_DATA_REG_PORT, Colors[1] >> 2);
213     __outpb(VGA_BASE_IO_PORT + DAC_DATA_REG_PORT, Colors[0] >> 2);
214 }
215 
216 VOID
217 NTAPI
218 InitPaletteWithTable(
219     _In_ PULONG Table,
220     _In_ ULONG Count)
221 {
222     ULONG i;
223     PULONG Entry = Table;
224 
225     /* Loop every entry */
226     for (i = 0; i < Count; i++, Entry++)
227     {
228         /* Set the entry */
229         SetPaletteEntryRGB(i, *Entry);
230     }
231 }
232 
233 static VOID
234 NTAPI
235 SetPaletteEntry(IN ULONG Id,
236                 IN ULONG PaletteEntry)
237 {
238     /* Set the palette index */
239     __outpb(VGA_BASE_IO_PORT + DAC_ADDRESS_WRITE_PORT, (UCHAR)Id);
240 
241     /* Set RGB colors */
242     __outpb(VGA_BASE_IO_PORT + DAC_DATA_REG_PORT, PaletteEntry & 0xFF);
243     __outpb(VGA_BASE_IO_PORT + DAC_DATA_REG_PORT, (PaletteEntry >>= 8) & 0xFF);
244     __outpb(VGA_BASE_IO_PORT + DAC_DATA_REG_PORT, (PaletteEntry >> 8) & 0xFF);
245 }
246 
247 VOID
248 NTAPI
249 InitializePalette(VOID)
250 {
251     ULONG PaletteEntry[16] = {0x000000,
252                               0x000020,
253                               0x002000,
254                               0x002020,
255                               0x200000,
256                               0x200020,
257                               0x202000,
258                               0x202020,
259                               0x303030,
260                               0x00003F,
261                               0x003F00,
262                               0x003F3F,
263                               0x3F0000,
264                               0x3F003F,
265                               0x3F3F00,
266                               0x3F3F3F};
267     ULONG i;
268 
269     /* Loop all the entries and set their palettes */
270     for (i = 0; i < 16; i++) SetPaletteEntry(i, PaletteEntry[i]);
271 }
272 
273 static VOID
274 NTAPI
275 VgaScroll(IN ULONG Scroll)
276 {
277     ULONG Top, RowSize;
278     PUCHAR OldPosition, NewPosition;
279 
280     /* Clear the 4 planes */
281     __outpw(VGA_BASE_IO_PORT + SEQ_ADDRESS_PORT, 0x0F02);
282 
283     /* Set the bitmask to 0xFF for all 4 planes */
284     __outpw(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, 0xFF08);
285 
286     /* Set Mode 1 */
287     ReadWriteMode(1);
288 
289     RowSize = (VidpScrollRegion[2] - VidpScrollRegion[0] + 1) / 8;
290 
291     /* Calculate the position in memory for the row */
292     OldPosition = (PUCHAR)(VgaBase + (VidpScrollRegion[1] + Scroll) * (SCREEN_WIDTH / 8) + VidpScrollRegion[0] / 8);
293     NewPosition = (PUCHAR)(VgaBase + VidpScrollRegion[1] * (SCREEN_WIDTH / 8) + VidpScrollRegion[0] / 8);
294 
295     /* Start loop */
296     for (Top = VidpScrollRegion[1]; Top <= VidpScrollRegion[3]; ++Top)
297     {
298 #if defined(_M_IX86) || defined(_M_AMD64)
299         __movsb(NewPosition, OldPosition, RowSize);
300 #else
301         ULONG i;
302 
303         /* Scroll the row */
304         for (i = 0; i < RowSize; ++i)
305             WRITE_REGISTER_UCHAR(NewPosition + i, READ_REGISTER_UCHAR(OldPosition + i));
306 #endif
307         OldPosition += (SCREEN_WIDTH / 8);
308         NewPosition += (SCREEN_WIDTH / 8);
309     }
310 }
311 
312 static VOID
313 NTAPI
314 PreserveRow(IN ULONG CurrentTop,
315             IN ULONG TopDelta,
316             IN BOOLEAN Restore)
317 {
318     PUCHAR Position1, Position2;
319     ULONG Count;
320 
321     /* Clear the 4 planes */
322     __outpw(VGA_BASE_IO_PORT + SEQ_ADDRESS_PORT, 0x0F02);
323 
324     /* Set the bitmask to 0xFF for all 4 planes */
325     __outpw(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, 0xFF08);
326 
327     /* Set Mode 1 */
328     ReadWriteMode(1);
329 
330     /* Calculate the position in memory for the row */
331     if (Restore)
332     {
333         /* Restore the row by copying back the contents saved off-screen */
334         Position1 = (PUCHAR)(VgaBase + CurrentTop * (SCREEN_WIDTH / 8));
335         Position2 = (PUCHAR)(VgaBase + SCREEN_HEIGHT * (SCREEN_WIDTH / 8));
336     }
337     else
338     {
339         /* Preserve the row by saving its contents off-screen */
340         Position1 = (PUCHAR)(VgaBase + SCREEN_HEIGHT * (SCREEN_WIDTH / 8));
341         Position2 = (PUCHAR)(VgaBase + CurrentTop * (SCREEN_WIDTH / 8));
342     }
343 
344     /* Set the count and loop every pixel */
345     Count = TopDelta * (SCREEN_WIDTH / 8);
346 #if defined(_M_IX86) || defined(_M_AMD64)
347     __movsb(Position1, Position2, Count);
348 #else
349     while (Count--)
350     {
351         /* Write the data back on the other position */
352         WRITE_REGISTER_UCHAR(Position1, READ_REGISTER_UCHAR(Position2));
353 
354         /* Increase both positions */
355         Position1++;
356         Position2++;
357     }
358 #endif
359 }
360 
361 /* PUBLIC FUNCTIONS **********************************************************/
362 
363 /*
364  * @implemented
365  */
366 ULONG
367 NTAPI
368 VidSetTextColor(IN ULONG Color)
369 {
370     ULONG OldColor;
371 
372     /* Save the old color and set the new one */
373     OldColor = VidpTextColor;
374     VidpTextColor = Color;
375     return OldColor;
376 }
377 
378 /*
379  * @implemented
380  */
381 VOID
382 NTAPI
383 VidCleanUp(VOID)
384 {
385     /* Select bit mask register and clear it */
386     __outpb(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, IND_BIT_MASK);
387     __outpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT, BIT_MASK_DEFAULT);
388 }
389 
390 /*
391  * @implemented
392  */
393 VOID
394 NTAPI
395 VidDisplayString(IN PUCHAR String)
396 {
397     ULONG TopDelta = BOOTCHAR_HEIGHT + 1;
398 
399     /* Start looping the string */
400     for (; *String; ++String)
401     {
402         /* Treat new-line separately */
403         if (*String == '\n')
404         {
405             /* Modify Y position */
406             VidpCurrentY += TopDelta;
407             if (VidpCurrentY + TopDelta - 1 > VidpScrollRegion[3])
408             {
409                 /* Scroll the view and clear the current row */
410                 VgaScroll(TopDelta);
411                 VidpCurrentY -= TopDelta;
412                 PreserveRow(VidpCurrentY, TopDelta, TRUE);
413             }
414             else
415             {
416                 /* Preserve the current row */
417                 PreserveRow(VidpCurrentY, TopDelta, FALSE);
418             }
419 
420             /* Update current X */
421             VidpCurrentX = VidpScrollRegion[0];
422 
423             /* No need to clear this row */
424             ClearRow = FALSE;
425         }
426         else if (*String == '\r')
427         {
428             /* Update current X */
429             VidpCurrentX = VidpScrollRegion[0];
430 
431             /* If a new-line does not follow we will clear the current row */
432             if (String[1] != '\n') ClearRow = TRUE;
433         }
434         else
435         {
436             /* Clear the current row if we had a return-carriage without a new-line */
437             if (ClearRow)
438             {
439                 PreserveRow(VidpCurrentY, TopDelta, TRUE);
440                 ClearRow = FALSE;
441             }
442 
443             /* Display this character */
444             DisplayCharacter(*String, VidpCurrentX, VidpCurrentY, VidpTextColor, 16);
445             VidpCurrentX += 8;
446 
447             /* Check if we should scroll */
448             if (VidpCurrentX + 7 > VidpScrollRegion[2])
449             {
450                 /* Update Y position and check if we should scroll it */
451                 VidpCurrentY += TopDelta;
452                 if (VidpCurrentY + TopDelta - 1 > VidpScrollRegion[3])
453                 {
454                     /* Scroll the view and clear the current row */
455                     VgaScroll(TopDelta);
456                     VidpCurrentY -= TopDelta;
457                     PreserveRow(VidpCurrentY, TopDelta, TRUE);
458                 }
459                 else
460                 {
461                     /* Preserve the current row */
462                     PreserveRow(VidpCurrentY, TopDelta, FALSE);
463                 }
464 
465                 /* Update current X */
466                 VidpCurrentX = VidpScrollRegion[0];
467             }
468         }
469     }
470 }
471 
472 /*
473  * @implemented
474  */
475 VOID
476 NTAPI
477 VidScreenToBufferBlt(OUT PUCHAR Buffer,
478                      IN ULONG Left,
479                      IN ULONG Top,
480                      IN ULONG Width,
481                      IN ULONG Height,
482                      IN ULONG Delta)
483 {
484     ULONG Plane;
485     ULONG XDistance;
486     ULONG LeftDelta, RightDelta;
487     ULONG PixelOffset;
488     PUCHAR PixelPosition;
489     PUCHAR k, i;
490     PULONG m;
491     UCHAR Value, Value2;
492     UCHAR a;
493     ULONG b;
494     ULONG x, y;
495 
496     /* Calculate total distance to copy on X */
497     XDistance = Left + Width - 1;
498 
499     /* Calculate the 8-byte left and right deltas */
500     LeftDelta = Left & 7;
501     RightDelta = 8 - LeftDelta;
502 
503     /* Clear the destination buffer */
504     RtlZeroMemory(Buffer, Delta * Height);
505 
506     /* Calculate the pixel offset and convert the X distance into byte form */
507     PixelOffset = Top * (SCREEN_WIDTH / 8) + (Left >> 3);
508     XDistance >>= 3;
509 
510     /* Loop the 4 planes */
511     for (Plane = 0; Plane < 4; ++Plane)
512     {
513         /* Set the current pixel position and reset buffer loop variable */
514         PixelPosition = (PUCHAR)(VgaBase + PixelOffset);
515         i = Buffer;
516 
517         /* Set Mode 0 */
518         ReadWriteMode(0);
519 
520         /* Set the current plane */
521         __outpw(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, (Plane << 8) | IND_READ_MAP);
522 
523         /* Start the outer Y height loop */
524         for (y = Height; y > 0; --y)
525         {
526             /* Read the current value */
527             m = (PULONG)i;
528             Value = READ_REGISTER_UCHAR(PixelPosition);
529 
530             /* Set Pixel Position loop variable */
531             k = PixelPosition + 1;
532 
533             /* Check if we're still within bounds */
534             if (Left <= XDistance)
535             {
536                 /* Start the X inner loop */
537                 for (x = (XDistance - Left) + 1; x > 0; --x)
538                 {
539                     /* Read the current value */
540                     Value2 = READ_REGISTER_UCHAR(k);
541 
542                     /* Increase pixel position */
543                     k++;
544 
545                     /* Do the blt */
546                     a = Value2 >> (UCHAR)RightDelta;
547                     a |= Value << (UCHAR)LeftDelta;
548                     b = lookup[a & 0xF];
549                     a >>= 4;
550                     b <<= 16;
551                     b |= lookup[a];
552 
553                     /* Save new value to buffer */
554                     *m |= (b << Plane);
555 
556                     /* Move to next destination location */
557                     m++;
558 
559                     /* Write new value */
560                     Value = Value2;
561                 }
562             }
563 
564             /* Update pixel position */
565             PixelPosition += (SCREEN_WIDTH / 8);
566             i += Delta;
567         }
568     }
569 }
570 
571 /*
572  * @implemented
573  */
574 VOID
575 NTAPI
576 VidSolidColorFill(IN ULONG Left,
577                   IN ULONG Top,
578                   IN ULONG Right,
579                   IN ULONG Bottom,
580                   IN UCHAR Color)
581 {
582     ULONG rMask, lMask;
583     ULONG LeftOffset, RightOffset, Distance;
584     PUCHAR Offset;
585     ULONG i, j;
586 
587     /* Get the left and right masks, shifts, and delta */
588     LeftOffset = Left >> 3;
589     lMask = (lMaskTable[Left & 0x7] << 8) | IND_BIT_MASK;
590     RightOffset = Right >> 3;
591     rMask = (rMaskTable[Right & 0x7] << 8) | IND_BIT_MASK;
592     Distance = RightOffset - LeftOffset;
593 
594     /* If there is no distance, then combine the right and left masks */
595     if (!Distance) lMask &= rMask;
596 
597     PrepareForSetPixel();
598 
599     /* Calculate pixel position for the read */
600     Offset = (PUCHAR)(VgaBase + (Top * (SCREEN_WIDTH / 8)) + LeftOffset);
601 
602     /* Select the bitmask register and write the mask */
603     __outpw(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, (USHORT)lMask);
604 
605     /* Check if the top coord is below the bottom one */
606     if (Top <= Bottom)
607     {
608         /* Start looping each line */
609         for (i = (Bottom - Top) + 1; i > 0; --i)
610         {
611             /* Read the previous value and add our color */
612             WRITE_REGISTER_UCHAR(Offset, READ_REGISTER_UCHAR(Offset) & Color);
613 
614             /* Move to the next line */
615             Offset += (SCREEN_WIDTH / 8);
616         }
617     }
618 
619     /* Check if we have a delta */
620     if (Distance > 0)
621     {
622         /* Calculate new pixel position */
623         Offset = (PUCHAR)(VgaBase + (Top * (SCREEN_WIDTH / 8)) + RightOffset);
624         Distance--;
625 
626         /* Select the bitmask register and write the mask */
627         __outpw(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, (USHORT)rMask);
628 
629         /* Check if the top coord is below the bottom one */
630         if (Top <= Bottom)
631         {
632             /* Start looping each line */
633             for (i = (Bottom - Top) + 1; i > 0; --i)
634             {
635                 /* Read the previous value and add our color */
636                 WRITE_REGISTER_UCHAR(Offset, READ_REGISTER_UCHAR(Offset) & Color);
637 
638                 /* Move to the next line */
639                 Offset += (SCREEN_WIDTH / 8);
640             }
641         }
642 
643         /* Check if we still have a delta */
644         if (Distance > 0)
645         {
646             /* Calculate new pixel position */
647             Offset = (PUCHAR)(VgaBase + (Top * (SCREEN_WIDTH / 8)) + LeftOffset + 1);
648 
649             /* Set the bitmask to 0xFF for all 4 planes */
650             __outpw(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, 0xFF08);
651 
652             /* Check if the top coord is below the bottom one */
653             if (Top <= Bottom)
654             {
655                 /* Start looping each line */
656                 for (i = (Bottom - Top) + 1; i > 0; --i)
657                 {
658                     /* Loop the shift delta */
659                     for (j = Distance; j > 0; Offset++, --j)
660                     {
661                         /* Write the color */
662                         WRITE_REGISTER_UCHAR(Offset, Color);
663                     }
664 
665                     /* Update position in memory */
666                     Offset += ((SCREEN_WIDTH / 8) - Distance);
667                 }
668             }
669         }
670     }
671 }
672