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 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 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 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 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 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 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 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 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 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 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