1 /* $Id: bootvid.c,v 1.1 2003/08/11 18:50:12 chorns Exp $
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/inbv/bootvid.c
6 * PURPOSE: Boot video support
7 * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
9 * 12-07-2003 CSH Created
12 /* INCLUDES ******************************************************************/
15 #include <reactos/resource.h>
16 #include <internal/i386/mm.h>
17 #include <internal/v86m.h>
20 #include <internal/debug.h>
24 typedef struct tagRGBQUAD
{
25 unsigned char rgbBlue
;
26 unsigned char rgbGreen
;
28 unsigned char rgbReserved
;
31 typedef long FXPT2DOT30
;
33 typedef struct tagCIEXYZ
{
38 typedef CIEXYZ
* LPCIEXYZ
;
40 typedef struct tagCIEXYZTRIPLE
{
45 typedef CIEXYZTRIPLE
*LPCIEXYZTRIPLE
;
55 LONG bV5XPelsPerMeter
;
56 LONG bV5YPelsPerMeter
;
58 DWORD bV5ClrImportant
;
64 CIEXYZTRIPLE bV5Endpoints
;
72 } BITMAPV5HEADER
, *PBITMAPV5HEADER
;
78 #define GRAPHICS 0x3ce
83 typedef struct _VideoMode
{
84 unsigned short VidSeg
;
86 unsigned char Feature
;
87 unsigned short Seq
[6];
88 unsigned short Crtc
[25];
89 unsigned short Gfx
[9];
90 unsigned char Attrib
[21];
97 } FADER_PALETTE_ENTRY
;
101 InbvPutPixels(int x
, int y
, unsigned long c
);
103 /* GLOBALS *******************************************************************/
107 /* Must be 4 bytes per entry */
111 static HANDLE BitmapThreadHandle
;
112 static CLIENT_ID BitmapThreadId
;
113 static BOOLEAN BitmapIsDrawn
;
114 static BOOLEAN BitmapThreadShouldTerminate
;
115 static PUCHAR BootimageBitmap
;
116 static BOOLEAN InGraphicsMode
= FALSE
;
118 /* DATA **********************************************************************/
120 static VideoMode Mode12
= {
123 {0x03, 0x01, 0x0f, 0x00, 0x06 },
125 {0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0x0b, 0x3e, 0x00, 0x40, 0x00, 0x00,
126 0x00, 0x00, 0x00, 0x59, 0xea, 0x8c, 0xdf, 0x28, 0x00, 0xe7, 0x04, 0xe3,
129 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff},
131 {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
132 0x0c, 0x0d, 0x0e, 0x0f, 0x81, 0x00, 0x0f, 0x00, 0x00}
135 static BOOLEAN VideoAddressSpaceInitialized
= FALSE
;
136 static PVOID NonBiosBaseAddress
;
138 /* FUNCTIONS *****************************************************************/
143 PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry
;
144 LDR_RESOURCE_INFO ResourceInfo
;
146 PVOID BaseAddress
= (PVOID
)KERNEL_BASE
;
149 ResourceInfo
.Type
= RT_BITMAP
;
150 ResourceInfo
.Name
= IDB_BOOTIMAGE
;
151 ResourceInfo
.Language
= 0x09;
153 Status
= LdrFindResource_U(BaseAddress
,
157 if (!NT_SUCCESS(Status
))
159 DPRINT("LdrFindResource_U() failed with status 0x%.08x\n", Status
);
163 Status
= LdrAccessResource(BaseAddress
,
165 (PVOID
*)&BootimageBitmap
,
167 if (!NT_SUCCESS(Status
))
169 DPRINT("LdrAccessResource() failed with status 0x%.08x\n", Status
);
178 InbvInitializeVideoAddressSpace(VOID
)
180 OBJECT_ATTRIBUTES ObjectAttributes
;
181 UNICODE_STRING PhysMemName
;
183 HANDLE PhysMemHandle
;
185 LARGE_INTEGER Offset
;
189 PVOID start
= (PVOID
)0x0;
192 * Open the physical memory section
194 RtlInitUnicodeStringFromLiteral(&PhysMemName
, L
"\\Device\\PhysicalMemory");
195 InitializeObjectAttributes(&ObjectAttributes
,
200 Status
= NtOpenSection(&PhysMemHandle
, SECTION_ALL_ACCESS
,
202 if (!NT_SUCCESS(Status
))
204 DPRINT("Couldn't open \\Device\\PhysicalMemory\n");
209 * Map the BIOS and device registers into the address space
211 Offset
.QuadPart
= 0xa0000;
212 ViewSize
= 0x100000 - 0xa0000;
213 BaseAddress
= (PVOID
)0xa0000;
214 Status
= NtMapViewOfSection(PhysMemHandle
,
223 PAGE_EXECUTE_READWRITE
);
224 if (!NT_SUCCESS(Status
))
226 DPRINT("Couldn't map physical memory (%x)\n", Status
);
227 NtClose(PhysMemHandle
);
230 NtClose(PhysMemHandle
);
231 if (BaseAddress
!= (PVOID
)0xa0000)
233 DPRINT("Couldn't map physical memory at the right address "
234 "(was %x)\n", BaseAddress
);
239 * Map some memory to use for the non-BIOS parts of the v86 mode address
242 NonBiosBaseAddress
= (PVOID
)0x1;
243 ViewSize
= 0xa0000 - 0x1000;
244 Status
= NtAllocateVirtualMemory(NtCurrentProcess(),
249 PAGE_EXECUTE_READWRITE
);
250 if (!NT_SUCCESS(Status
))
252 DPRINT("Failed to allocate virtual memory (Status %x)\n", Status
);
255 if (NonBiosBaseAddress
!= (PVOID
)0x0)
257 DPRINT("Failed to allocate virtual memory at right address "
258 "(was %x)\n", NonBiosBaseAddress
);
263 * Get the real mode IVT from the kernel
265 Status
= NtVdmControl(0, IVT
);
266 if (!NT_SUCCESS(Status
))
268 DPRINT("NtVdmControl failed (status %x)\n", Status
);
273 * Copy the real mode IVT into the right place
275 memcpy(start
, IVT
, 1024);
278 * Get the BDA from the kernel
280 Status
= NtVdmControl(1, BDA
);
281 if (!NT_SUCCESS(Status
))
283 DPRINT("NtVdmControl failed (status %x)\n", Status
);
288 * Copy the BDA into the right place
290 memcpy((PVOID
)0x400, BDA
, 256);
297 InbvDeinitializeVideoAddressSpace(VOID
)
302 RegionSize
= 0xa0000 - 0x1000;
303 NtFreeVirtualMemory(NtCurrentProcess(),
308 ViewBase
= (PUCHAR
) 0xa0000;
309 NtUnmapViewOfSection(NtCurrentProcess(), ViewBase
);
320 for(j
= 0; j
< 80; j
++)
322 maskbit
[j
* 8 + 0] = 128;
323 maskbit
[j
* 8 + 1] = 64;
324 maskbit
[j
* 8 + 2] = 32;
325 maskbit
[j
* 8 + 3] = 16;
326 maskbit
[j
* 8 + 4] = 8;
327 maskbit
[j
* 8 + 5] = 4;
328 maskbit
[j
* 8 + 6] = 2;
329 maskbit
[j
* 8 + 7] = 1;
331 for(j
= 0; j
< 480; j
++)
333 y80
[j
] = j
* 80; /* 80 = 640 / 8 = Number of bytes per scanline */
337 static __inline__ VOID
338 InbvOutxay(PUSHORT ad
, UCHAR x
, UCHAR y
)
340 USHORT xy
= (x
<< 8) + y
;
341 WRITE_PORT_USHORT(ad
, xy
);
346 InbvSetMode(VideoMode mode
)
350 WRITE_PORT_UCHAR((PUCHAR
)MISC
, mode
.Misc
);
351 WRITE_PORT_UCHAR((PUCHAR
)STATUS
, 0);
352 WRITE_PORT_UCHAR((PUCHAR
)FEATURE
, mode
.Feature
);
356 InbvOutxay((PUSHORT
)SEQ
, mode
.Seq
[x
], x
);
359 WRITE_PORT_USHORT((PUSHORT
)CRTC
, 0x11);
360 WRITE_PORT_USHORT((PUSHORT
)CRTC
, (mode
.Crtc
[0x11] & 0x7f));
364 InbvOutxay((PUSHORT
)CRTC
, mode
.Crtc
[x
], x
);
369 InbvOutxay((PUSHORT
)GRAPHICS
, mode
.Gfx
[x
], x
);
372 x
=READ_PORT_UCHAR((PUCHAR
)FEATURE
);
376 WRITE_PORT_UCHAR((PUCHAR
)ATTRIB
, x
);
377 WRITE_PORT_UCHAR((PUCHAR
)ATTRIB
, mode
.Attrib
[x
]);
380 x
=READ_PORT_UCHAR((PUCHAR
)STATUS
);
382 WRITE_PORT_UCHAR((PUCHAR
)ATTRIB
, 0x20);
387 InbvInitVGAMode(VOID
)
389 KV86M_REGISTERS Regs
;
392 vidmem
= (char *)(0xd0000000 + 0xa0000);
393 memset(&Regs
, 0, sizeof(Regs
));
396 Status
= Ke386CallBios(0x10, &Regs
);
397 assert(NT_SUCCESS(Status
));
399 /* Get VGA registers into the correct state (mainly for setting up the palette registers correctly) */
402 /* Get the VGA into the mode we want to work with */
403 WRITE_PORT_UCHAR((PUCHAR
)0x3ce,0x08); /* Set */
404 WRITE_PORT_UCHAR((PUCHAR
)0x3cf,0); /* the MASK */
405 WRITE_PORT_USHORT((PUSHORT
)0x3ce,0x0205); /* write mode = 2 (bits 0,1) read mode = 0 (bit 3) */
406 (UCHAR
) READ_REGISTER_UCHAR(vidmem
); /* Update bit buffer */
407 WRITE_REGISTER_UCHAR(vidmem
, 0); /* Write the pixel */
408 WRITE_PORT_UCHAR((PUCHAR
)0x3ce,0x08);
409 WRITE_PORT_UCHAR((PUCHAR
)0x3cf,0xff);
411 /* Zero out video memory (clear a possibly trashed screen) */
412 RtlZeroMemory(vidmem
, 64000);
420 VidResetDisplay(VOID
)
422 KV86M_REGISTERS Regs
;
426 /* FIXME: What if the system has crashed, eg. this function is called from KeBugCheck() ? */
428 /* Maybe wait until boot screen bitmap is drawn */
429 while (!BitmapIsDrawn
)
435 /* Zero out video memory (clear the screen) */
436 RtlZeroMemory(vidmem
, 64000);
438 memset(&Regs
, 0, sizeof(Regs
));
440 Status
= Ke386CallBios(0x10, &Regs
);
441 assert(NT_SUCCESS(Status
));
443 memset(&Regs
, 0, sizeof(Regs
));
445 Status
= Ke386CallBios(0x10, &Regs
);
446 assert(NT_SUCCESS(Status
));
448 memset(&Regs
, 0, sizeof(Regs
));
449 Regs
.Eax
= 0x0632; // AH = 0x06 - Scroll active page up, AL = 0x32 - Clear 25 lines
450 Regs
.Ecx
= 0x0000; // CX = 0x0000 - Upper left of scroll
451 Regs
.Edx
= 0x314F; // DX = 0x314F - Lower right of scroll
452 Regs
.Ebx
= 0x1100; // Use normal attribute on blanked line
453 Status
= Ke386CallBios(0x10, &Regs
);
454 assert(NT_SUCCESS(Status
));
456 InGraphicsMode
= FALSE
;
471 if (VideoAddressSpaceInitialized
)
473 InbvDeinitializeVideoAddressSpace();
474 VideoAddressSpaceInitialized
= FALSE
;
477 BitmapThreadShouldTerminate
= TRUE
;
481 static __inline__ VOID
482 InbvSetColor(int cindex
, unsigned char red
, unsigned char green
, unsigned char blue
)
484 red
= red
/ (256 / 64);
485 green
= green
/ (256 / 64);
486 blue
= blue
/ (256 / 64);
488 WRITE_PORT_UCHAR((PUCHAR
)0x03c8, cindex
);
489 WRITE_PORT_UCHAR((PUCHAR
)0x03c9, red
);
490 WRITE_PORT_UCHAR((PUCHAR
)0x03c9, green
);
491 WRITE_PORT_UCHAR((PUCHAR
)0x03c9, blue
);
495 static __inline__ VOID
496 InbvSetBlackPalette()
498 register ULONG r
= 0;
500 for (r
= 0; r
< 16; r
++)
502 InbvSetColor(r
, 0, 0, 0);
508 InbvDisplayBitmap(ULONG Width
, ULONG Height
, PCHAR ImageData
)
516 for (y
= 0; y
< Height
; y
++)
518 for (j
= 0; j
< 8; j
++)
523 * Loop through the line and process every 8th pixel.
524 * This way we can get a way with using the same bit mask
525 * for several pixels and thus not need to do as much I/O
534 c
= ImageData
[k
+ x
];
535 for (i
= 1; i
< 4; i
++)
539 c
|= (ImageData
[k
+ x
+ i
* 8] << i
* 8);
544 InbvPutPixels(x
, 479 - y
, c
);
554 InbvDisplayCompressedBitmap()
556 PBITMAPV5HEADER bminfo
;
564 bminfo
= (PBITMAPV5HEADER
) &BootimageBitmap
[0];
565 DPRINT("bV5Size = %d\n", bminfo
->bV5Size
);
566 DPRINT("bV5Width = %d\n", bminfo
->bV5Width
);
567 DPRINT("bV5Height = %d\n", bminfo
->bV5Height
);
568 DPRINT("bV5Planes = %d\n", bminfo
->bV5Planes
);
569 DPRINT("bV5BitCount = %d\n", bminfo
->bV5BitCount
);
570 DPRINT("bV5Compression = %d\n", bminfo
->bV5Compression
);
571 DPRINT("bV5SizeImage = %d\n", bminfo
->bV5SizeImage
);
572 DPRINT("bV5XPelsPerMeter = %d\n", bminfo
->bV5XPelsPerMeter
);
573 DPRINT("bV5YPelsPerMeter = %d\n", bminfo
->bV5YPelsPerMeter
);
574 DPRINT("bV5ClrUsed = %d\n", bminfo
->bV5ClrUsed
);
575 DPRINT("bV5ClrImportant = %d\n", bminfo
->bV5ClrImportant
);
577 bfOffBits
= bminfo
->bV5Size
+ bminfo
->bV5ClrUsed
* sizeof(RGBQUAD
);
578 DPRINT("bfOffBits = %d\n", bfOffBits
);
579 DPRINT("size of color indices = %d\n", bminfo
->bV5ClrUsed
* sizeof(RGBQUAD
));
580 DPRINT("first byte of data = %d\n", BootimageBitmap
[bfOffBits
]);
582 InbvSetBlackPalette();
584 ImageData
= ExAllocatePool(NonPagedPool
, bminfo
->bV5Width
* bminfo
->bV5Height
);
585 RtlZeroMemory(ImageData
, bminfo
->bV5Width
* bminfo
->bV5Height
);
588 * ImageData has 1 pixel per byte.
589 * bootimage has 2 pixels per byte.
592 if (bminfo
->bV5Compression
== 2)
596 while ((j
< bminfo
->bV5SizeImage
) && (k
< (ULONG
) (bminfo
->bV5Width
* bminfo
->bV5Height
)))
600 clen
= BootimageBitmap
[bfOffBits
+ j
];
607 b
= BootimageBitmap
[bfOffBits
+ j
];
610 for (i
= 0; i
< (clen
/ 2); i
++)
612 ImageData
[k
] = (b
& 0xf0) >> 4;
614 ImageData
[k
] = b
& 0xf;
619 ImageData
[k
] = (b
& 0xf0) >> 4;
626 b
= BootimageBitmap
[bfOffBits
+ j
];
640 x
= BootimageBitmap
[bfOffBits
+ j
];
642 y
= BootimageBitmap
[bfOffBits
+ j
];
644 curx
= k
% bminfo
->bV5Width
;
645 cury
= k
/ bminfo
->bV5Width
;
646 k
= (cury
+ y
) * bminfo
->bV5Width
+ (curx
+ x
);
652 DPRINT("Unaligned copy!\n");
656 for (i
= 0; i
< (clen
/ 2); i
++)
658 b
= BootimageBitmap
[bfOffBits
+ j
];
661 ImageData
[k
] = (b
& 0xf0) >> 4;
663 ImageData
[k
] = b
& 0xf;
668 b
= BootimageBitmap
[bfOffBits
+ j
];
670 ImageData
[k
] = (b
& 0xf0) >> 4;
679 InbvDisplayBitmap(bminfo
->bV5Width
, bminfo
->bV5Height
, ImageData
);
683 DbgPrint("Warning boot image need to be compressed using RLE4\n");
686 ExFreePool(ImageData
);
690 #define PALETTE_FADE_STEPS 20
691 #define PALETTE_FADE_TIME 20 * 10000 /* 20ms */
696 PBITMAPV5HEADER bminfo
;
701 LARGE_INTEGER Interval
;
702 FADER_PALETTE_ENTRY FaderPalette
[16];
703 FADER_PALETTE_ENTRY FaderPaletteDelta
[16];
705 RtlZeroMemory(&FaderPalette
, sizeof(FaderPalette
));
706 RtlZeroMemory(&FaderPaletteDelta
, sizeof(FaderPaletteDelta
));
708 bminfo
= (PBITMAPV5HEADER
) &BootimageBitmap
[0]; //sizeof(BITMAPFILEHEADER)];
709 Palette
= (PRGBQUAD
) &BootimageBitmap
[/* sizeof(BITMAPFILEHEADER) + */ bminfo
->bV5Size
];
711 for (i
= 0; i
< 16; i
++)
713 if (i
< bminfo
->bV5ClrUsed
)
715 FaderPaletteDelta
[i
].r
= ((Palette
[i
].rgbRed
<< 8) / PALETTE_FADE_STEPS
);
716 FaderPaletteDelta
[i
].g
= ((Palette
[i
].rgbGreen
<< 8) / PALETTE_FADE_STEPS
);
717 FaderPaletteDelta
[i
].b
= ((Palette
[i
].rgbBlue
<< 8) / PALETTE_FADE_STEPS
);
721 for (i
= 0; i
< PALETTE_FADE_STEPS
; i
++)
723 for (c
= 0; c
< bminfo
->bV5ClrUsed
; c
++)
726 FaderPalette
[c
].r
+= FaderPaletteDelta
[c
].r
;
727 FaderPalette
[c
].g
+= FaderPaletteDelta
[c
].g
;
728 FaderPalette
[c
].b
+= FaderPaletteDelta
[c
].b
;
730 /* Get the integer values */
731 r
= FaderPalette
[c
].r
>> 8;
732 g
= FaderPalette
[c
].g
>> 8;
733 b
= FaderPalette
[c
].b
>> 8;
735 /* Don't go too far */
736 if (r
> Palette
[c
].rgbRed
)
737 r
= Palette
[c
].rgbRed
;
738 if (g
> Palette
[c
].rgbGreen
)
739 g
= Palette
[c
].rgbGreen
;
740 if (b
> Palette
[c
].rgbBlue
)
741 b
= Palette
[c
].rgbBlue
;
743 /* Update the hardware */
744 InbvSetColor(c
, r
, g
, b
);
746 Interval
.QuadPart
= -PALETTE_FADE_TIME
;
747 KeDelayExecutionThread(KernelMode
, FALSE
, &Interval
);
752 InbvBitmapThreadMain(PVOID Ignored
)
754 if (InbvFindBootimage())
756 InbvDisplayCompressedBitmap();
761 DbgPrint("Warning: Cannot find boot image\n");
764 BitmapIsDrawn
= TRUE
;
767 if (BitmapThreadShouldTerminate
)
769 DPRINT("Terminating\n");
779 VidIsBootDriverInstalled(VOID
)
781 return InGraphicsMode
;
791 if (!VideoAddressSpaceInitialized
)
793 InbvInitializeVideoAddressSpace();
798 InGraphicsMode
= TRUE
;
800 BitmapIsDrawn
= FALSE
;
801 BitmapThreadShouldTerminate
= FALSE
;
803 Status
= PsCreateSystemThread(&BitmapThreadHandle
,
808 InbvBitmapThreadMain
,
810 if (!NT_SUCCESS(Status
))