xref: /reactos/drivers/base/bootvid/i386/pc/vga.c (revision 3b62a89d)
1 /*
2  * PROJECT:     ReactOS Boot Video Driver for VGA-compatible cards
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     VGA helper functions
5  * COPYRIGHT:   Copyright 2007 Alex Ionescu <alex.ionescu@reactos.org>
6  *              Copyright 2013 Timo Kreuzer <timo.kreuzer@reactos.org>
7  *              Copyright 2019 Hermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
8  *              Copyright 2020 Stanislav Motylkov <x86corez@gmail.com>
9  */
10 
11 #include "precomp.h"
12 
13 /* GLOBALS *******************************************************************/
14 
15 static UCHAR lMaskTable[8] =
16 {
17     (1 << 8) - (1 << 0),
18     (1 << 7) - (1 << 0),
19     (1 << 6) - (1 << 0),
20     (1 << 5) - (1 << 0),
21     (1 << 4) - (1 << 0),
22     (1 << 3) - (1 << 0),
23     (1 << 2) - (1 << 0),
24     (1 << 1) - (1 << 0)
25 };
26 static UCHAR rMaskTable[8] =
27 {
28     (1 << 7),
29     (1 << 7) + (1 << 6),
30     (1 << 7) + (1 << 6) + (1 << 5),
31     (1 << 7) + (1 << 6) + (1 << 5) + (1 << 4),
32     (1 << 7) + (1 << 6) + (1 << 5) + (1 << 4) + (1 << 3),
33     (1 << 7) + (1 << 6) + (1 << 5) + (1 << 4) + (1 << 3) + (1 << 2),
34     (1 << 7) + (1 << 6) + (1 << 5) + (1 << 4) + (1 << 3) + (1 << 2) + (1 << 1),
35     (1 << 7) + (1 << 6) + (1 << 5) + (1 << 4) + (1 << 3) + (1 << 2) + (1 << 1) + (1 << 0),
36 };
37 UCHAR PixelMask[8] =
38 {
39     (1 << 7),
40     (1 << 6),
41     (1 << 5),
42     (1 << 4),
43     (1 << 3),
44     (1 << 2),
45     (1 << 1),
46     (1 << 0),
47 };
48 static ULONG lookup[16] =
49 {
50     0x0000,
51     0x0100,
52     0x1000,
53     0x1100,
54     0x0001,
55     0x0101,
56     0x1001,
57     0x1101,
58     0x0010,
59     0x0110,
60     0x1010,
61     0x1110,
62     0x0011,
63     0x0111,
64     0x1011,
65     0x1111,
66 };
67 
68 ULONG_PTR VgaRegisterBase = 0;
69 ULONG_PTR VgaBase = 0;
70 
71 /* PRIVATE FUNCTIONS *********************************************************/
72 
73 static VOID
ReadWriteMode(_In_ UCHAR Mode)74 ReadWriteMode(
75     _In_ UCHAR Mode)
76 {
77     UCHAR Value;
78 
79     /* Switch to graphics mode register */
80     __outpb(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, IND_GRAPH_MODE);
81 
82     /* Get the current register value, minus the current mode */
83     Value = __inpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT) & 0xF4;
84 
85     /* Set the new mode */
86     __outpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT, Mode | Value);
87 }
88 
89 VOID
PrepareForSetPixel(VOID)90 PrepareForSetPixel(VOID)
91 {
92     /* Switch to mode 10 */
93     ReadWriteMode(10);
94 
95     /* Clear the 4 planes (we're already in unchained mode here) */
96     __outpw(VGA_BASE_IO_PORT + SEQ_ADDRESS_PORT, 0x0F02);
97 
98     /* Select the color don't care register */
99     __outpw(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, 7);
100 }
101 
102 #define SET_PIXELS(_PixelPtr, _PixelMask, _TextColor)       \
103 do {                                                        \
104     /* Select the bitmask register and write the mask */    \
105     __outpw(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, ((_PixelMask) << 8) | IND_BIT_MASK); \
106     /* Dummy read to load latch registers */                \
107     (VOID)READ_REGISTER_UCHAR((_PixelPtr));                 \
108     /* Set the new color */                                 \
109     WRITE_REGISTER_UCHAR((_PixelPtr), (UCHAR)(_TextColor)); \
110 } while (0);
111 
112 VOID
DisplayCharacter(_In_ CHAR Character,_In_ ULONG Left,_In_ ULONG Top,_In_ ULONG TextColor,_In_ ULONG BackColor)113 DisplayCharacter(
114     _In_ CHAR Character,
115     _In_ ULONG Left,
116     _In_ ULONG Top,
117     _In_ ULONG TextColor,
118     _In_ ULONG BackColor)
119 {
120     PUCHAR FontChar, PixelPtr;
121     ULONG Height;
122     UCHAR Shift;
123 
124     PrepareForSetPixel();
125 
126     /* Calculate shift */
127     Shift = Left & 7;
128 
129     /* Get the font and pixel pointer */
130     FontChar = GetFontPtr(Character);
131     PixelPtr = (PUCHAR)(VgaBase + (Left >> 3) + (Top * (SCREEN_WIDTH / 8)));
132 
133     /* Loop all pixel rows */
134     for (Height = BOOTCHAR_HEIGHT; Height > 0; --Height)
135     {
136         SET_PIXELS(PixelPtr, *FontChar >> Shift, TextColor);
137         PixelPtr += (SCREEN_WIDTH / 8);
138         FontChar += FONT_PTR_DELTA;
139     }
140 
141     /* Check if we need to update neighbor bytes */
142     if (Shift)
143     {
144         /* Calculate shift for 2nd byte */
145         Shift = 8 - Shift;
146 
147         /* Get the font and pixel pointer (2nd byte) */
148         FontChar = GetFontPtr(Character);
149         PixelPtr = (PUCHAR)(VgaBase + (Left >> 3) + (Top * (SCREEN_WIDTH / 8)) + 1);
150 
151         /* Loop all pixel rows */
152         for (Height = BOOTCHAR_HEIGHT; Height > 0; --Height)
153         {
154             SET_PIXELS(PixelPtr, *FontChar << Shift, TextColor);
155             PixelPtr += (SCREEN_WIDTH / 8);
156             FontChar += FONT_PTR_DELTA;
157         }
158     }
159 
160     /* Check if the background color is transparent */
161     if (BackColor >= BV_COLOR_NONE)
162     {
163         /* We are done */
164         return;
165     }
166 
167     /* Calculate shift */
168     Shift = Left & 7;
169 
170     /* Get the font and pixel pointer */
171     FontChar = GetFontPtr(Character);
172     PixelPtr = (PUCHAR)(VgaBase + (Left >> 3) + (Top * (SCREEN_WIDTH / 8)));
173 
174     /* Loop all pixel rows */
175     for (Height = BOOTCHAR_HEIGHT; Height > 0; --Height)
176     {
177         SET_PIXELS(PixelPtr, ~*FontChar >> Shift, BackColor);
178         PixelPtr += (SCREEN_WIDTH / 8);
179         FontChar += FONT_PTR_DELTA;
180     }
181 
182     /* Check if we need to update neighbor bytes */
183     if (Shift)
184     {
185         /* Calculate shift for 2nd byte */
186         Shift = 8 - Shift;
187 
188         /* Get the font and pixel pointer (2nd byte) */
189         FontChar = GetFontPtr(Character);
190         PixelPtr = (PUCHAR)(VgaBase + (Left >> 3) + (Top * (SCREEN_WIDTH / 8)) + 1);
191 
192         /* Loop all pixel rows */
193         for (Height = BOOTCHAR_HEIGHT; Height > 0; --Height)
194         {
195             SET_PIXELS(PixelPtr, ~*FontChar << Shift, BackColor);
196             PixelPtr += (SCREEN_WIDTH / 8);
197             FontChar += FONT_PTR_DELTA;
198         }
199     }
200 }
201 
202 static VOID
SetPaletteEntryRGB(_In_ ULONG Id,_In_ RGBQUAD Rgb)203 SetPaletteEntryRGB(
204     _In_ ULONG Id,
205     _In_ RGBQUAD 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, GetRValue(Rgb) >> 2);
212     __outpb(VGA_BASE_IO_PORT + DAC_DATA_REG_PORT, GetGValue(Rgb) >> 2);
213     __outpb(VGA_BASE_IO_PORT + DAC_DATA_REG_PORT, GetBValue(Rgb) >> 2);
214 }
215 
216 VOID
InitPaletteWithTable(_In_ PULONG Table,_In_ ULONG Count)217 InitPaletteWithTable(
218     _In_ PULONG Table,
219     _In_ ULONG Count)
220 {
221     ULONG i;
222     PULONG Entry = Table;
223 
224     for (i = 0; i < Count; i++, Entry++)
225     {
226         SetPaletteEntryRGB(i, *Entry);
227     }
228 }
229 
230 VOID
DoScroll(_In_ ULONG Scroll)231 DoScroll(
232     _In_ ULONG Scroll)
233 {
234     ULONG Top, RowSize;
235     PUCHAR OldPosition, NewPosition;
236 
237     /* Clear the 4 planes */
238     __outpw(VGA_BASE_IO_PORT + SEQ_ADDRESS_PORT, 0x0F02);
239 
240     /* Set the bitmask to 0xFF for all 4 planes */
241     __outpw(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, 0xFF08);
242 
243     /* Set Mode 1 */
244     ReadWriteMode(1);
245 
246     RowSize = (VidpScrollRegion[2] - VidpScrollRegion[0] + 1) / 8;
247 
248     /* Calculate the position in memory for the row */
249     OldPosition = (PUCHAR)(VgaBase + (VidpScrollRegion[1] + Scroll) * (SCREEN_WIDTH / 8) + VidpScrollRegion[0] / 8);
250     NewPosition = (PUCHAR)(VgaBase + VidpScrollRegion[1] * (SCREEN_WIDTH / 8) + VidpScrollRegion[0] / 8);
251 
252     /* Start loop */
253     for (Top = VidpScrollRegion[1]; Top <= VidpScrollRegion[3]; ++Top)
254     {
255 #if defined(_M_IX86) || defined(_M_AMD64)
256         __movsb(NewPosition, OldPosition, RowSize);
257 #else
258         ULONG i;
259 
260         /* Scroll the row */
261         for (i = 0; i < RowSize; ++i)
262             WRITE_REGISTER_UCHAR(NewPosition + i, READ_REGISTER_UCHAR(OldPosition + i));
263 #endif
264         OldPosition += (SCREEN_WIDTH / 8);
265         NewPosition += (SCREEN_WIDTH / 8);
266     }
267 }
268 
269 VOID
PreserveRow(_In_ ULONG CurrentTop,_In_ ULONG TopDelta,_In_ BOOLEAN Restore)270 PreserveRow(
271     _In_ ULONG CurrentTop,
272     _In_ ULONG TopDelta,
273     _In_ BOOLEAN Restore)
274 {
275     PUCHAR Position1, Position2;
276     ULONG Count;
277 
278     /* Clear the 4 planes */
279     __outpw(VGA_BASE_IO_PORT + SEQ_ADDRESS_PORT, 0x0F02);
280 
281     /* Set the bitmask to 0xFF for all 4 planes */
282     __outpw(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, 0xFF08);
283 
284     /* Set Mode 1 */
285     ReadWriteMode(1);
286 
287     /* Calculate the position in memory for the row */
288     if (Restore)
289     {
290         /* Restore the row by copying back the contents saved off-screen */
291         Position1 = (PUCHAR)(VgaBase + CurrentTop * (SCREEN_WIDTH / 8));
292         Position2 = (PUCHAR)(VgaBase + SCREEN_HEIGHT * (SCREEN_WIDTH / 8));
293     }
294     else
295     {
296         /* Preserve the row by saving its contents off-screen */
297         Position1 = (PUCHAR)(VgaBase + SCREEN_HEIGHT * (SCREEN_WIDTH / 8));
298         Position2 = (PUCHAR)(VgaBase + CurrentTop * (SCREEN_WIDTH / 8));
299     }
300 
301     /* Set the count and loop every pixel */
302     Count = TopDelta * (SCREEN_WIDTH / 8);
303 #if defined(_M_IX86) || defined(_M_AMD64)
304     __movsb(Position1, Position2, Count);
305 #else
306     while (Count--)
307     {
308         /* Write the data back on the other position */
309         WRITE_REGISTER_UCHAR(Position1, READ_REGISTER_UCHAR(Position2));
310 
311         /* Increase both positions */
312         Position1++;
313         Position2++;
314     }
315 #endif
316 }
317 
318 /* PUBLIC FUNCTIONS **********************************************************/
319 
320 VOID
321 NTAPI
VidCleanUp(VOID)322 VidCleanUp(VOID)
323 {
324     /* Select bit mask register and clear it */
325     __outpb(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, IND_BIT_MASK);
326     __outpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT, BIT_MASK_DEFAULT);
327 }
328 
329 VOID
330 NTAPI
VidScreenToBufferBlt(_Out_writes_bytes_ (Delta * Height)PUCHAR Buffer,_In_ ULONG Left,_In_ ULONG Top,_In_ ULONG Width,_In_ ULONG Height,_In_ ULONG Delta)331 VidScreenToBufferBlt(
332     _Out_writes_bytes_(Delta * Height) PUCHAR Buffer,
333     _In_ ULONG Left,
334     _In_ ULONG Top,
335     _In_ ULONG Width,
336     _In_ ULONG Height,
337     _In_ ULONG Delta)
338 {
339     ULONG Plane;
340     ULONG XDistance;
341     ULONG LeftDelta, RightDelta;
342     ULONG PixelOffset;
343     PUCHAR PixelPosition;
344     PUCHAR k, i;
345     PULONG m;
346     UCHAR Value, Value2;
347     UCHAR a;
348     ULONG b;
349     ULONG x, y;
350 
351     /* Calculate total distance to copy on X */
352     XDistance = Left + Width - 1;
353 
354     /* Calculate the 8-byte left and right deltas */
355     LeftDelta = Left & 7;
356     RightDelta = 8 - LeftDelta;
357 
358     /* Clear the destination buffer */
359     RtlZeroMemory(Buffer, Delta * Height);
360 
361     /* Calculate the pixel offset and convert the X distance into byte form */
362     PixelOffset = Top * (SCREEN_WIDTH / 8) + (Left >> 3);
363     XDistance >>= 3;
364 
365     /* Loop the 4 planes */
366     for (Plane = 0; Plane < 4; ++Plane)
367     {
368         /* Set the current pixel position and reset buffer loop variable */
369         PixelPosition = (PUCHAR)(VgaBase + PixelOffset);
370         i = Buffer;
371 
372         /* Set Mode 0 */
373         ReadWriteMode(0);
374 
375         /* Set the current plane */
376         __outpw(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, (Plane << 8) | IND_READ_MAP);
377 
378         /* Start the outer Y height loop */
379         for (y = Height; y > 0; --y)
380         {
381             /* Read the current value */
382             m = (PULONG)i;
383             Value = READ_REGISTER_UCHAR(PixelPosition);
384 
385             /* Set Pixel Position loop variable */
386             k = PixelPosition + 1;
387 
388             /* Check if we're still within bounds */
389             if (Left <= XDistance)
390             {
391                 /* Start the X inner loop */
392                 for (x = (XDistance - Left) + 1; x > 0; --x)
393                 {
394                     /* Read the current value */
395                     Value2 = READ_REGISTER_UCHAR(k);
396 
397                     /* Increase pixel position */
398                     k++;
399 
400                     /* Do the blt */
401                     a = Value2 >> (UCHAR)RightDelta;
402                     a |= Value << (UCHAR)LeftDelta;
403                     b = lookup[a & 0xF];
404                     a >>= 4;
405                     b <<= 16;
406                     b |= lookup[a];
407 
408                     /* Save new value to buffer */
409                     *m |= (b << Plane);
410 
411                     /* Move to next destination location */
412                     m++;
413 
414                     /* Write new value */
415                     Value = Value2;
416                 }
417             }
418 
419             /* Update pixel position */
420             PixelPosition += (SCREEN_WIDTH / 8);
421             i += Delta;
422         }
423     }
424 }
425 
426 VOID
427 NTAPI
VidSolidColorFill(_In_ ULONG Left,_In_ ULONG Top,_In_ ULONG Right,_In_ ULONG Bottom,_In_ UCHAR Color)428 VidSolidColorFill(
429     _In_ ULONG Left,
430     _In_ ULONG Top,
431     _In_ ULONG Right,
432     _In_ ULONG Bottom,
433     _In_ UCHAR Color)
434 {
435     ULONG rMask, lMask;
436     ULONG LeftOffset, RightOffset, Distance;
437     PUCHAR Offset;
438     ULONG i, j;
439 
440     /* Get the left and right masks, shifts, and delta */
441     LeftOffset = Left >> 3;
442     lMask = (lMaskTable[Left & 0x7] << 8) | IND_BIT_MASK;
443     RightOffset = Right >> 3;
444     rMask = (rMaskTable[Right & 0x7] << 8) | IND_BIT_MASK;
445     Distance = RightOffset - LeftOffset;
446 
447     /* If there is no distance, then combine the right and left masks */
448     if (!Distance) lMask &= rMask;
449 
450     PrepareForSetPixel();
451 
452     /* Calculate pixel position for the read */
453     Offset = (PUCHAR)(VgaBase + (Top * (SCREEN_WIDTH / 8)) + LeftOffset);
454 
455     /* Select the bitmask register and write the mask */
456     __outpw(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, (USHORT)lMask);
457 
458     /* Check if the top coord is below the bottom one */
459     if (Top <= Bottom)
460     {
461         /* Start looping each line */
462         for (i = (Bottom - Top) + 1; i > 0; --i)
463         {
464             /* Read the previous value and add our color */
465             WRITE_REGISTER_UCHAR(Offset, READ_REGISTER_UCHAR(Offset) & Color);
466 
467             /* Move to the next line */
468             Offset += (SCREEN_WIDTH / 8);
469         }
470     }
471 
472     /* Check if we have a delta */
473     if (Distance > 0)
474     {
475         /* Calculate new pixel position */
476         Offset = (PUCHAR)(VgaBase + (Top * (SCREEN_WIDTH / 8)) + RightOffset);
477         Distance--;
478 
479         /* Select the bitmask register and write the mask */
480         __outpw(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, (USHORT)rMask);
481 
482         /* Check if the top coord is below the bottom one */
483         if (Top <= Bottom)
484         {
485             /* Start looping each line */
486             for (i = (Bottom - Top) + 1; i > 0; --i)
487             {
488                 /* Read the previous value and add our color */
489                 WRITE_REGISTER_UCHAR(Offset, READ_REGISTER_UCHAR(Offset) & Color);
490 
491                 /* Move to the next line */
492                 Offset += (SCREEN_WIDTH / 8);
493             }
494         }
495 
496         /* Check if we still have a delta */
497         if (Distance > 0)
498         {
499             /* Calculate new pixel position */
500             Offset = (PUCHAR)(VgaBase + (Top * (SCREEN_WIDTH / 8)) + LeftOffset + 1);
501 
502             /* Set the bitmask to 0xFF for all 4 planes */
503             __outpw(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, 0xFF08);
504 
505             /* Check if the top coord is below the bottom one */
506             if (Top <= Bottom)
507             {
508                 /* Start looping each line */
509                 for (i = (Bottom - Top) + 1; i > 0; --i)
510                 {
511                     /* Loop the shift delta */
512                     for (j = Distance; j > 0; Offset++, --j)
513                     {
514                         /* Write the color */
515                         WRITE_REGISTER_UCHAR(Offset, Color);
516                     }
517 
518                     /* Update position in memory */
519                     Offset += ((SCREEN_WIDTH / 8) - Distance);
520                 }
521             }
522         }
523     }
524 }
525