xref: /reactos/drivers/base/bootvid/i386/pc/vga.c (revision 1734f297)
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 
61 /* PRIVATE FUNCTIONS *********************************************************/
62 
63 static VOID
64 NTAPI
65 ReadWriteMode(
66     _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     /* Dummy read to load latch registers */                \
98     (VOID)READ_REGISTER_UCHAR((_PixelPtr));                 \
99     /* Set the new color */                                 \
100     WRITE_REGISTER_UCHAR((_PixelPtr), (UCHAR)(_TextColor)); \
101 } while (0);
102 
103 VOID
104 NTAPI
105 DisplayCharacter(
106     _In_ CHAR Character,
107     _In_ ULONG Left,
108     _In_ ULONG Top,
109     _In_ ULONG TextColor,
110     _In_ ULONG BackColor)
111 {
112     PUCHAR FontChar, PixelPtr;
113     ULONG Height;
114     UCHAR Shift;
115 
116     PrepareForSetPixel();
117 
118     /* Calculate shift */
119     Shift = Left & 7;
120 
121     /* Get the font and pixel pointer */
122     FontChar = GetFontPtr(Character);
123     PixelPtr = (PUCHAR)(VgaBase + (Left >> 3) + (Top * (SCREEN_WIDTH / 8)));
124 
125     /* Loop all pixel rows */
126     for (Height = BOOTCHAR_HEIGHT; Height > 0; --Height)
127     {
128         SET_PIXELS(PixelPtr, *FontChar >> Shift, TextColor);
129         PixelPtr += (SCREEN_WIDTH / 8);
130         FontChar += FONT_PTR_DELTA;
131     }
132 
133     /* Check if we need to update neighbor bytes */
134     if (Shift)
135     {
136         /* Calculate shift for 2nd byte */
137         Shift = 8 - Shift;
138 
139         /* Get the font and pixel pointer (2nd byte) */
140         FontChar = GetFontPtr(Character);
141         PixelPtr = (PUCHAR)(VgaBase + (Left >> 3) + (Top * (SCREEN_WIDTH / 8)) + 1);
142 
143         /* Loop all pixel rows */
144         for (Height = BOOTCHAR_HEIGHT; Height > 0; --Height)
145         {
146             SET_PIXELS(PixelPtr, *FontChar << Shift, TextColor);
147             PixelPtr += (SCREEN_WIDTH / 8);
148             FontChar += FONT_PTR_DELTA;
149         }
150     }
151 
152     /* Check if the background color is transparent */
153     if (BackColor >= BV_COLOR_NONE)
154     {
155         /* We are done */
156         return;
157     }
158 
159     /* Calculate shift */
160     Shift = Left & 7;
161 
162     /* Get the font and pixel pointer */
163     FontChar = GetFontPtr(Character);
164     PixelPtr = (PUCHAR)(VgaBase + (Left >> 3) + (Top * (SCREEN_WIDTH / 8)));
165 
166     /* Loop all pixel rows */
167     for (Height = BOOTCHAR_HEIGHT; Height > 0; --Height)
168     {
169         SET_PIXELS(PixelPtr, ~*FontChar >> Shift, BackColor);
170         PixelPtr += (SCREEN_WIDTH / 8);
171         FontChar += FONT_PTR_DELTA;
172     }
173 
174     /* Check if we need to update neighbor bytes */
175     if (Shift)
176     {
177         /* Calculate shift for 2nd byte */
178         Shift = 8 - Shift;
179 
180         /* Get the font and pixel pointer (2nd byte) */
181         FontChar = GetFontPtr(Character);
182         PixelPtr = (PUCHAR)(VgaBase + (Left >> 3) + (Top * (SCREEN_WIDTH / 8)) + 1);
183 
184         /* Loop all pixel rows */
185         for (Height = BOOTCHAR_HEIGHT; Height > 0; --Height)
186         {
187             SET_PIXELS(PixelPtr, ~*FontChar << Shift, BackColor);
188             PixelPtr += (SCREEN_WIDTH / 8);
189             FontChar += FONT_PTR_DELTA;
190         }
191     }
192 }
193 
194 static VOID
195 NTAPI
196 SetPaletteEntryRGB(
197     _In_ ULONG Id,
198     _In_ RGBQUAD Rgb)
199 {
200     /* Set the palette index */
201     __outpb(VGA_BASE_IO_PORT + DAC_ADDRESS_WRITE_PORT, (UCHAR)Id);
202 
203     /* Set RGB colors */
204     __outpb(VGA_BASE_IO_PORT + DAC_DATA_REG_PORT, GetRValue(Rgb) >> 2);
205     __outpb(VGA_BASE_IO_PORT + DAC_DATA_REG_PORT, GetGValue(Rgb) >> 2);
206     __outpb(VGA_BASE_IO_PORT + DAC_DATA_REG_PORT, GetBValue(Rgb) >> 2);
207 }
208 
209 VOID
210 NTAPI
211 InitPaletteWithTable(
212     _In_ PULONG Table,
213     _In_ ULONG Count)
214 {
215     ULONG i;
216     PULONG Entry = Table;
217 
218     for (i = 0; i < Count; i++, Entry++)
219     {
220         SetPaletteEntryRGB(i, *Entry);
221     }
222 }
223 
224 VOID
225 NTAPI
226 DoScroll(
227     _In_ ULONG Scroll)
228 {
229     ULONG Top, RowSize;
230     PUCHAR OldPosition, NewPosition;
231 
232     /* Clear the 4 planes */
233     __outpw(VGA_BASE_IO_PORT + SEQ_ADDRESS_PORT, 0x0F02);
234 
235     /* Set the bitmask to 0xFF for all 4 planes */
236     __outpw(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, 0xFF08);
237 
238     /* Set Mode 1 */
239     ReadWriteMode(1);
240 
241     RowSize = (VidpScrollRegion[2] - VidpScrollRegion[0] + 1) / 8;
242 
243     /* Calculate the position in memory for the row */
244     OldPosition = (PUCHAR)(VgaBase + (VidpScrollRegion[1] + Scroll) * (SCREEN_WIDTH / 8) + VidpScrollRegion[0] / 8);
245     NewPosition = (PUCHAR)(VgaBase + VidpScrollRegion[1] * (SCREEN_WIDTH / 8) + VidpScrollRegion[0] / 8);
246 
247     /* Start loop */
248     for (Top = VidpScrollRegion[1]; Top <= VidpScrollRegion[3]; ++Top)
249     {
250 #if defined(_M_IX86) || defined(_M_AMD64)
251         __movsb(NewPosition, OldPosition, RowSize);
252 #else
253         ULONG i;
254 
255         /* Scroll the row */
256         for (i = 0; i < RowSize; ++i)
257             WRITE_REGISTER_UCHAR(NewPosition + i, READ_REGISTER_UCHAR(OldPosition + i));
258 #endif
259         OldPosition += (SCREEN_WIDTH / 8);
260         NewPosition += (SCREEN_WIDTH / 8);
261     }
262 }
263 
264 VOID
265 NTAPI
266 PreserveRow(
267     _In_ ULONG CurrentTop,
268     _In_ ULONG TopDelta,
269     _In_ BOOLEAN Restore)
270 {
271     PUCHAR Position1, Position2;
272     ULONG Count;
273 
274     /* Clear the 4 planes */
275     __outpw(VGA_BASE_IO_PORT + SEQ_ADDRESS_PORT, 0x0F02);
276 
277     /* Set the bitmask to 0xFF for all 4 planes */
278     __outpw(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, 0xFF08);
279 
280     /* Set Mode 1 */
281     ReadWriteMode(1);
282 
283     /* Calculate the position in memory for the row */
284     if (Restore)
285     {
286         /* Restore the row by copying back the contents saved off-screen */
287         Position1 = (PUCHAR)(VgaBase + CurrentTop * (SCREEN_WIDTH / 8));
288         Position2 = (PUCHAR)(VgaBase + SCREEN_HEIGHT * (SCREEN_WIDTH / 8));
289     }
290     else
291     {
292         /* Preserve the row by saving its contents off-screen */
293         Position1 = (PUCHAR)(VgaBase + SCREEN_HEIGHT * (SCREEN_WIDTH / 8));
294         Position2 = (PUCHAR)(VgaBase + CurrentTop * (SCREEN_WIDTH / 8));
295     }
296 
297     /* Set the count and loop every pixel */
298     Count = TopDelta * (SCREEN_WIDTH / 8);
299 #if defined(_M_IX86) || defined(_M_AMD64)
300     __movsb(Position1, Position2, Count);
301 #else
302     while (Count--)
303     {
304         /* Write the data back on the other position */
305         WRITE_REGISTER_UCHAR(Position1, READ_REGISTER_UCHAR(Position2));
306 
307         /* Increase both positions */
308         Position1++;
309         Position2++;
310     }
311 #endif
312 }
313 
314 /* PUBLIC FUNCTIONS **********************************************************/
315 
316 /*
317  * @implemented
318  */
319 VOID
320 NTAPI
321 VidCleanUp(VOID)
322 {
323     /* Select bit mask register and clear it */
324     __outpb(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, IND_BIT_MASK);
325     __outpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT, BIT_MASK_DEFAULT);
326 }
327 
328 /*
329  * @implemented
330  */
331 VOID
332 NTAPI
333 VidScreenToBufferBlt(
334     _Out_writes_bytes_(Delta * Height) PUCHAR Buffer,
335     _In_ ULONG Left,
336     _In_ ULONG Top,
337     _In_ ULONG Width,
338     _In_ ULONG Height,
339     _In_ ULONG Delta)
340 {
341     ULONG Plane;
342     ULONG XDistance;
343     ULONG LeftDelta, RightDelta;
344     ULONG PixelOffset;
345     PUCHAR PixelPosition;
346     PUCHAR k, i;
347     PULONG m;
348     UCHAR Value, Value2;
349     UCHAR a;
350     ULONG b;
351     ULONG x, y;
352 
353     /* Calculate total distance to copy on X */
354     XDistance = Left + Width - 1;
355 
356     /* Calculate the 8-byte left and right deltas */
357     LeftDelta = Left & 7;
358     RightDelta = 8 - LeftDelta;
359 
360     /* Clear the destination buffer */
361     RtlZeroMemory(Buffer, Delta * Height);
362 
363     /* Calculate the pixel offset and convert the X distance into byte form */
364     PixelOffset = Top * (SCREEN_WIDTH / 8) + (Left >> 3);
365     XDistance >>= 3;
366 
367     /* Loop the 4 planes */
368     for (Plane = 0; Plane < 4; ++Plane)
369     {
370         /* Set the current pixel position and reset buffer loop variable */
371         PixelPosition = (PUCHAR)(VgaBase + PixelOffset);
372         i = Buffer;
373 
374         /* Set Mode 0 */
375         ReadWriteMode(0);
376 
377         /* Set the current plane */
378         __outpw(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, (Plane << 8) | IND_READ_MAP);
379 
380         /* Start the outer Y height loop */
381         for (y = Height; y > 0; --y)
382         {
383             /* Read the current value */
384             m = (PULONG)i;
385             Value = READ_REGISTER_UCHAR(PixelPosition);
386 
387             /* Set Pixel Position loop variable */
388             k = PixelPosition + 1;
389 
390             /* Check if we're still within bounds */
391             if (Left <= XDistance)
392             {
393                 /* Start the X inner loop */
394                 for (x = (XDistance - Left) + 1; x > 0; --x)
395                 {
396                     /* Read the current value */
397                     Value2 = READ_REGISTER_UCHAR(k);
398 
399                     /* Increase pixel position */
400                     k++;
401 
402                     /* Do the blt */
403                     a = Value2 >> (UCHAR)RightDelta;
404                     a |= Value << (UCHAR)LeftDelta;
405                     b = lookup[a & 0xF];
406                     a >>= 4;
407                     b <<= 16;
408                     b |= lookup[a];
409 
410                     /* Save new value to buffer */
411                     *m |= (b << Plane);
412 
413                     /* Move to next destination location */
414                     m++;
415 
416                     /* Write new value */
417                     Value = Value2;
418                 }
419             }
420 
421             /* Update pixel position */
422             PixelPosition += (SCREEN_WIDTH / 8);
423             i += Delta;
424         }
425     }
426 }
427 
428 /*
429  * @implemented
430  */
431 VOID
432 NTAPI
433 VidSolidColorFill(
434     _In_ ULONG Left,
435     _In_ ULONG Top,
436     _In_ ULONG Right,
437     _In_ ULONG Bottom,
438     _In_ UCHAR Color)
439 {
440     ULONG rMask, lMask;
441     ULONG LeftOffset, RightOffset, Distance;
442     PUCHAR Offset;
443     ULONG i, j;
444 
445     /* Get the left and right masks, shifts, and delta */
446     LeftOffset = Left >> 3;
447     lMask = (lMaskTable[Left & 0x7] << 8) | IND_BIT_MASK;
448     RightOffset = Right >> 3;
449     rMask = (rMaskTable[Right & 0x7] << 8) | IND_BIT_MASK;
450     Distance = RightOffset - LeftOffset;
451 
452     /* If there is no distance, then combine the right and left masks */
453     if (!Distance) lMask &= rMask;
454 
455     PrepareForSetPixel();
456 
457     /* Calculate pixel position for the read */
458     Offset = (PUCHAR)(VgaBase + (Top * (SCREEN_WIDTH / 8)) + LeftOffset);
459 
460     /* Select the bitmask register and write the mask */
461     __outpw(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, (USHORT)lMask);
462 
463     /* Check if the top coord is below the bottom one */
464     if (Top <= Bottom)
465     {
466         /* Start looping each line */
467         for (i = (Bottom - Top) + 1; i > 0; --i)
468         {
469             /* Read the previous value and add our color */
470             WRITE_REGISTER_UCHAR(Offset, READ_REGISTER_UCHAR(Offset) & Color);
471 
472             /* Move to the next line */
473             Offset += (SCREEN_WIDTH / 8);
474         }
475     }
476 
477     /* Check if we have a delta */
478     if (Distance > 0)
479     {
480         /* Calculate new pixel position */
481         Offset = (PUCHAR)(VgaBase + (Top * (SCREEN_WIDTH / 8)) + RightOffset);
482         Distance--;
483 
484         /* Select the bitmask register and write the mask */
485         __outpw(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, (USHORT)rMask);
486 
487         /* Check if the top coord is below the bottom one */
488         if (Top <= Bottom)
489         {
490             /* Start looping each line */
491             for (i = (Bottom - Top) + 1; i > 0; --i)
492             {
493                 /* Read the previous value and add our color */
494                 WRITE_REGISTER_UCHAR(Offset, READ_REGISTER_UCHAR(Offset) & Color);
495 
496                 /* Move to the next line */
497                 Offset += (SCREEN_WIDTH / 8);
498             }
499         }
500 
501         /* Check if we still have a delta */
502         if (Distance > 0)
503         {
504             /* Calculate new pixel position */
505             Offset = (PUCHAR)(VgaBase + (Top * (SCREEN_WIDTH / 8)) + LeftOffset + 1);
506 
507             /* Set the bitmask to 0xFF for all 4 planes */
508             __outpw(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, 0xFF08);
509 
510             /* Check if the top coord is below the bottom one */
511             if (Top <= Bottom)
512             {
513                 /* Start looping each line */
514                 for (i = (Bottom - Top) + 1; i > 0; --i)
515                 {
516                     /* Loop the shift delta */
517                     for (j = Distance; j > 0; Offset++, --j)
518                     {
519                         /* Write the color */
520                         WRITE_REGISTER_UCHAR(Offset, Color);
521                     }
522 
523                     /* Update position in memory */
524                     Offset += ((SCREEN_WIDTH / 8) - Distance);
525                 }
526             }
527         }
528     }
529 }
530