xref: /reactos/drivers/base/bootvid/i386/pc/vga.c (revision 11345aed)
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     /* Set the new color */                                 \
98     WRITE_REGISTER_UCHAR((_PixelPtr), (UCHAR)(_TextColor)); \
99 } while (0);
100 
101 VOID
102 NTAPI
103 DisplayCharacter(
104     _In_ CHAR Character,
105     _In_ ULONG Left,
106     _In_ ULONG Top,
107     _In_ ULONG TextColor,
108     _In_ ULONG BackColor)
109 {
110     PUCHAR FontChar, PixelPtr;
111     ULONG Height;
112     UCHAR Shift;
113 
114     PrepareForSetPixel();
115 
116     /* Calculate shift */
117     Shift = Left & 7;
118 
119     /* Get the font and pixel pointer */
120     FontChar = GetFontPtr(Character);
121     PixelPtr = (PUCHAR)(VgaBase + (Left >> 3) + (Top * (SCREEN_WIDTH / 8)));
122 
123     /* Loop all pixel rows */
124     for (Height = BOOTCHAR_HEIGHT; Height > 0; --Height)
125     {
126         SET_PIXELS(PixelPtr, *FontChar >> Shift, TextColor);
127         PixelPtr += (SCREEN_WIDTH / 8);
128         FontChar += FONT_PTR_DELTA;
129     }
130 
131     /* Check if we need to update neighbor bytes */
132     if (Shift)
133     {
134         /* Calculate shift for 2nd byte */
135         Shift = 8 - Shift;
136 
137         /* Get the font and pixel pointer (2nd byte) */
138         FontChar = GetFontPtr(Character);
139         PixelPtr = (PUCHAR)(VgaBase + (Left >> 3) + (Top * (SCREEN_WIDTH / 8)) + 1);
140 
141         /* Loop all pixel rows */
142         for (Height = BOOTCHAR_HEIGHT; Height > 0; --Height)
143         {
144             SET_PIXELS(PixelPtr, *FontChar << Shift, TextColor);
145             PixelPtr += (SCREEN_WIDTH / 8);
146             FontChar += FONT_PTR_DELTA;
147         }
148     }
149 
150     /* Check if the background color is transparent */
151     if (BackColor >= BV_COLOR_NONE)
152     {
153         /* We are done */
154         return;
155     }
156 
157     /* Calculate shift */
158     Shift = Left & 7;
159 
160     /* Get the font and pixel pointer */
161     FontChar = GetFontPtr(Character);
162     PixelPtr = (PUCHAR)(VgaBase + (Left >> 3) + (Top * (SCREEN_WIDTH / 8)));
163 
164     /* Loop all pixel rows */
165     for (Height = BOOTCHAR_HEIGHT; Height > 0; --Height)
166     {
167         SET_PIXELS(PixelPtr, ~*FontChar >> Shift, BackColor);
168         PixelPtr += (SCREEN_WIDTH / 8);
169         FontChar += FONT_PTR_DELTA;
170     }
171 
172     /* Check if we need to update neighbor bytes */
173     if (Shift)
174     {
175         /* Calculate shift for 2nd byte */
176         Shift = 8 - Shift;
177 
178         /* Get the font and pixel pointer (2nd byte) */
179         FontChar = GetFontPtr(Character);
180         PixelPtr = (PUCHAR)(VgaBase + (Left >> 3) + (Top * (SCREEN_WIDTH / 8)) + 1);
181 
182         /* Loop all pixel rows */
183         for (Height = BOOTCHAR_HEIGHT; Height > 0; --Height)
184         {
185             SET_PIXELS(PixelPtr, ~*FontChar << Shift, BackColor);
186             PixelPtr += (SCREEN_WIDTH / 8);
187             FontChar += FONT_PTR_DELTA;
188         }
189     }
190 }
191 
192 static VOID
193 NTAPI
194 SetPaletteEntryRGB(
195     _In_ ULONG Id,
196     _In_ RGBQUAD Rgb)
197 {
198     /* Set the palette index */
199     __outpb(VGA_BASE_IO_PORT + DAC_ADDRESS_WRITE_PORT, (UCHAR)Id);
200 
201     /* Set RGB colors */
202     __outpb(VGA_BASE_IO_PORT + DAC_DATA_REG_PORT, GetRValue(Rgb) >> 2);
203     __outpb(VGA_BASE_IO_PORT + DAC_DATA_REG_PORT, GetGValue(Rgb) >> 2);
204     __outpb(VGA_BASE_IO_PORT + DAC_DATA_REG_PORT, GetBValue(Rgb) >> 2);
205 }
206 
207 VOID
208 NTAPI
209 InitPaletteWithTable(
210     _In_ PULONG Table,
211     _In_ ULONG Count)
212 {
213     ULONG i;
214     PULONG Entry = Table;
215 
216     for (i = 0; i < Count; i++, Entry++)
217     {
218         SetPaletteEntryRGB(i, *Entry);
219     }
220 }
221 
222 VOID
223 NTAPI
224 DoScroll(
225     _In_ ULONG Scroll)
226 {
227     ULONG Top, RowSize;
228     PUCHAR OldPosition, NewPosition;
229 
230     /* Clear the 4 planes */
231     __outpw(VGA_BASE_IO_PORT + SEQ_ADDRESS_PORT, 0x0F02);
232 
233     /* Set the bitmask to 0xFF for all 4 planes */
234     __outpw(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, 0xFF08);
235 
236     /* Set Mode 1 */
237     ReadWriteMode(1);
238 
239     RowSize = (VidpScrollRegion[2] - VidpScrollRegion[0] + 1) / 8;
240 
241     /* Calculate the position in memory for the row */
242     OldPosition = (PUCHAR)(VgaBase + (VidpScrollRegion[1] + Scroll) * (SCREEN_WIDTH / 8) + VidpScrollRegion[0] / 8);
243     NewPosition = (PUCHAR)(VgaBase + VidpScrollRegion[1] * (SCREEN_WIDTH / 8) + VidpScrollRegion[0] / 8);
244 
245     /* Start loop */
246     for (Top = VidpScrollRegion[1]; Top <= VidpScrollRegion[3]; ++Top)
247     {
248 #if defined(_M_IX86) || defined(_M_AMD64)
249         __movsb(NewPosition, OldPosition, RowSize);
250 #else
251         ULONG i;
252 
253         /* Scroll the row */
254         for (i = 0; i < RowSize; ++i)
255             WRITE_REGISTER_UCHAR(NewPosition + i, READ_REGISTER_UCHAR(OldPosition + i));
256 #endif
257         OldPosition += (SCREEN_WIDTH / 8);
258         NewPosition += (SCREEN_WIDTH / 8);
259     }
260 }
261 
262 VOID
263 NTAPI
264 PreserveRow(
265     _In_ ULONG CurrentTop,
266     _In_ ULONG TopDelta,
267     _In_ BOOLEAN Restore)
268 {
269     PUCHAR Position1, Position2;
270     ULONG Count;
271 
272     /* Clear the 4 planes */
273     __outpw(VGA_BASE_IO_PORT + SEQ_ADDRESS_PORT, 0x0F02);
274 
275     /* Set the bitmask to 0xFF for all 4 planes */
276     __outpw(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, 0xFF08);
277 
278     /* Set Mode 1 */
279     ReadWriteMode(1);
280 
281     /* Calculate the position in memory for the row */
282     if (Restore)
283     {
284         /* Restore the row by copying back the contents saved off-screen */
285         Position1 = (PUCHAR)(VgaBase + CurrentTop * (SCREEN_WIDTH / 8));
286         Position2 = (PUCHAR)(VgaBase + SCREEN_HEIGHT * (SCREEN_WIDTH / 8));
287     }
288     else
289     {
290         /* Preserve the row by saving its contents off-screen */
291         Position1 = (PUCHAR)(VgaBase + SCREEN_HEIGHT * (SCREEN_WIDTH / 8));
292         Position2 = (PUCHAR)(VgaBase + CurrentTop * (SCREEN_WIDTH / 8));
293     }
294 
295     /* Set the count and loop every pixel */
296     Count = TopDelta * (SCREEN_WIDTH / 8);
297 #if defined(_M_IX86) || defined(_M_AMD64)
298     __movsb(Position1, Position2, Count);
299 #else
300     while (Count--)
301     {
302         /* Write the data back on the other position */
303         WRITE_REGISTER_UCHAR(Position1, READ_REGISTER_UCHAR(Position2));
304 
305         /* Increase both positions */
306         Position1++;
307         Position2++;
308     }
309 #endif
310 }
311 
312 /* PUBLIC FUNCTIONS **********************************************************/
313 
314 /*
315  * @implemented
316  */
317 VOID
318 NTAPI
319 VidCleanUp(VOID)
320 {
321     /* Select bit mask register and clear it */
322     __outpb(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, IND_BIT_MASK);
323     __outpb(VGA_BASE_IO_PORT + GRAPH_DATA_PORT, BIT_MASK_DEFAULT);
324 }
325 
326 /*
327  * @implemented
328  */
329 VOID
330 NTAPI
331 VidScreenToBufferBlt(
332     _Out_ 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 /*
427  * @implemented
428  */
429 VOID
430 NTAPI
431 VidSolidColorFill(
432     _In_ ULONG Left,
433     _In_ ULONG Top,
434     _In_ ULONG Right,
435     _In_ ULONG Bottom,
436     _In_ UCHAR Color)
437 {
438     ULONG rMask, lMask;
439     ULONG LeftOffset, RightOffset, Distance;
440     PUCHAR Offset;
441     ULONG i, j;
442 
443     /* Get the left and right masks, shifts, and delta */
444     LeftOffset = Left >> 3;
445     lMask = (lMaskTable[Left & 0x7] << 8) | IND_BIT_MASK;
446     RightOffset = Right >> 3;
447     rMask = (rMaskTable[Right & 0x7] << 8) | IND_BIT_MASK;
448     Distance = RightOffset - LeftOffset;
449 
450     /* If there is no distance, then combine the right and left masks */
451     if (!Distance) lMask &= rMask;
452 
453     PrepareForSetPixel();
454 
455     /* Calculate pixel position for the read */
456     Offset = (PUCHAR)(VgaBase + (Top * (SCREEN_WIDTH / 8)) + LeftOffset);
457 
458     /* Select the bitmask register and write the mask */
459     __outpw(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, (USHORT)lMask);
460 
461     /* Check if the top coord is below the bottom one */
462     if (Top <= Bottom)
463     {
464         /* Start looping each line */
465         for (i = (Bottom - Top) + 1; i > 0; --i)
466         {
467             /* Read the previous value and add our color */
468             WRITE_REGISTER_UCHAR(Offset, READ_REGISTER_UCHAR(Offset) & Color);
469 
470             /* Move to the next line */
471             Offset += (SCREEN_WIDTH / 8);
472         }
473     }
474 
475     /* Check if we have a delta */
476     if (Distance > 0)
477     {
478         /* Calculate new pixel position */
479         Offset = (PUCHAR)(VgaBase + (Top * (SCREEN_WIDTH / 8)) + RightOffset);
480         Distance--;
481 
482         /* Select the bitmask register and write the mask */
483         __outpw(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, (USHORT)rMask);
484 
485         /* Check if the top coord is below the bottom one */
486         if (Top <= Bottom)
487         {
488             /* Start looping each line */
489             for (i = (Bottom - Top) + 1; i > 0; --i)
490             {
491                 /* Read the previous value and add our color */
492                 WRITE_REGISTER_UCHAR(Offset, READ_REGISTER_UCHAR(Offset) & Color);
493 
494                 /* Move to the next line */
495                 Offset += (SCREEN_WIDTH / 8);
496             }
497         }
498 
499         /* Check if we still have a delta */
500         if (Distance > 0)
501         {
502             /* Calculate new pixel position */
503             Offset = (PUCHAR)(VgaBase + (Top * (SCREEN_WIDTH / 8)) + LeftOffset + 1);
504 
505             /* Set the bitmask to 0xFF for all 4 planes */
506             __outpw(VGA_BASE_IO_PORT + GRAPH_ADDRESS_PORT, 0xFF08);
507 
508             /* Check if the top coord is below the bottom one */
509             if (Top <= Bottom)
510             {
511                 /* Start looping each line */
512                 for (i = (Bottom - Top) + 1; i > 0; --i)
513                 {
514                     /* Loop the shift delta */
515                     for (j = Distance; j > 0; Offset++, --j)
516                     {
517                         /* Write the color */
518                         WRITE_REGISTER_UCHAR(Offset, Color);
519                     }
520 
521                     /* Update position in memory */
522                     Offset += ((SCREEN_WIDTH / 8) - Distance);
523                 }
524             }
525         }
526     }
527 }
528