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