From 26c230dd5a0a7b7fdd0888fdd78e937767b6156f Mon Sep 17 00:00:00 2001 From: David Welch Date: Sun, 24 Aug 2003 12:11:13 +0000 Subject: [PATCH] - Moved boot video support to a seperate driver. svn path=/trunk/; revision=5819 --- reactos/Makefile | 2 +- reactos/drivers/dd/bootvid/.cvsignore | 8 + reactos/drivers/dd/bootvid/Makefile | 13 + reactos/drivers/dd/bootvid/bootimage.bmp | Bin 0 -> 97116 bytes reactos/drivers/dd/bootvid/bootvid.c | 863 +++++++++++++++++++++ reactos/drivers/dd/bootvid/bootvid.rc | 40 + reactos/drivers/dd/bootvid/pixelsup_i386.S | 101 +++ reactos/include/ddk/ntbootvid.h | 11 + 8 files changed, 1037 insertions(+), 1 deletion(-) create mode 100644 reactos/drivers/dd/bootvid/.cvsignore create mode 100644 reactos/drivers/dd/bootvid/Makefile create mode 100755 reactos/drivers/dd/bootvid/bootimage.bmp create mode 100644 reactos/drivers/dd/bootvid/bootvid.c create mode 100644 reactos/drivers/dd/bootvid/bootvid.rc create mode 100644 reactos/drivers/dd/bootvid/pixelsup_i386.S create mode 100644 reactos/include/ddk/ntbootvid.h diff --git a/reactos/Makefile b/reactos/Makefile index a5cbd8d84aa..6043e158e1d 100644 --- a/reactos/Makefile +++ b/reactos/Makefile @@ -59,7 +59,7 @@ DRIVERS_LIB = bzip2 # Kernel mode device drivers # Obsolete: ide # beep blue floppy null parallel ramdrv serenum serial vga videoprt -DEVICE_DRIVERS = beep blue debugout floppy null serial vga videoprt +DEVICE_DRIVERS = beep blue debugout floppy null serial vga videoprt bootvid # Kernel mode input drivers INPUT_DRIVERS = keyboard mouclass psaux sermouse diff --git a/reactos/drivers/dd/bootvid/.cvsignore b/reactos/drivers/dd/bootvid/.cvsignore new file mode 100644 index 00000000000..0d002707473 --- /dev/null +++ b/reactos/drivers/dd/bootvid/.cvsignore @@ -0,0 +1,8 @@ +base.tmp +junk.tmp +temp.exp +bootvid.coff +*.sym +*.o +*.sys +*.map diff --git a/reactos/drivers/dd/bootvid/Makefile b/reactos/drivers/dd/bootvid/Makefile new file mode 100644 index 00000000000..cbe61da0f6d --- /dev/null +++ b/reactos/drivers/dd/bootvid/Makefile @@ -0,0 +1,13 @@ +# $Id: Makefile,v 1.1 2003/08/24 12:11:13 dwelch Exp $ + +PATH_TO_TOP = ../../.. + +TARGET_TYPE = driver + +TARGET_NAME = bootvid + +TARGET_OBJECTS = bootvid.o pixelsup_i386.o + +include $(PATH_TO_TOP)/rules.mak + +include $(TOOLS_PATH)/helper.mk diff --git a/reactos/drivers/dd/bootvid/bootimage.bmp b/reactos/drivers/dd/bootvid/bootimage.bmp new file mode 100755 index 0000000000000000000000000000000000000000..39561bdc27e78aa04bed1ba82b21df2d9faa8281 GIT binary patch literal 97116 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 +#include +#include + +#include "../../../ntoskrnl/include/internal/v86m.h" + +#define NDEBUG +#include + +#define RT_BITMAP 2 + +typedef struct tagRGBQUAD { + unsigned char rgbBlue; + unsigned char rgbGreen; + unsigned char rgbRed; + unsigned char rgbReserved; +} RGBQUAD, *PRGBQUAD; + +typedef long FXPT2DOT30; + +typedef struct tagCIEXYZ { + FXPT2DOT30 ciexyzX; + FXPT2DOT30 ciexyzY; + FXPT2DOT30 ciexyzZ; +} CIEXYZ; +typedef CIEXYZ * LPCIEXYZ; + +typedef struct tagCIEXYZTRIPLE { + CIEXYZ ciexyzRed; + CIEXYZ ciexyzGreen; + CIEXYZ ciexyzBlue; +} CIEXYZTRIPLE; +typedef CIEXYZTRIPLE *LPCIEXYZTRIPLE; + +typedef struct { + DWORD bV5Size; + LONG bV5Width; + LONG bV5Height; + WORD bV5Planes; + WORD bV5BitCount; + DWORD bV5Compression; + DWORD bV5SizeImage; + LONG bV5XPelsPerMeter; + LONG bV5YPelsPerMeter; + DWORD bV5ClrUsed; + DWORD bV5ClrImportant; + DWORD bV5RedMask; + DWORD bV5GreenMask; + DWORD bV5BlueMask; + DWORD bV5AlphaMask; + DWORD bV5CSType; + CIEXYZTRIPLE bV5Endpoints; + DWORD bV5GammaRed; + DWORD bV5GammaGreen; + DWORD bV5GammaBlue; + DWORD bV5Intent; + DWORD bV5ProfileData; + DWORD bV5ProfileSize; + DWORD bV5Reserved; +} BITMAPV5HEADER, *PBITMAPV5HEADER; + + +#define MISC 0x3c2 +#define SEQ 0x3c4 +#define CRTC 0x3d4 +#define GRAPHICS 0x3ce +#define FEATURE 0x3da +#define ATTRIB 0x3c0 +#define STATUS 0x3da + +typedef struct _VideoMode { + unsigned short VidSeg; + unsigned char Misc; + unsigned char Feature; + unsigned short Seq[6]; + unsigned short Crtc[25]; + unsigned short Gfx[9]; + unsigned char Attrib[21]; +} VideoMode; + +typedef struct { + ULONG r; + ULONG g; + ULONG b; +} FADER_PALETTE_ENTRY; + +/* In pixelsups.S */ +extern VOID +InbvPutPixels(int x, int y, unsigned long c); + +/* GLOBALS *******************************************************************/ + +char *vidmem; + +/* Must be 4 bytes per entry */ +long maskbit[640]; +long y80[480]; + +static HANDLE BitmapThreadHandle; +static CLIENT_ID BitmapThreadId; +static BOOLEAN BitmapIsDrawn; +static BOOLEAN BitmapThreadShouldTerminate; +static PUCHAR BootimageBitmap; +static BOOLEAN InGraphicsMode = FALSE; + +/* DATA **********************************************************************/ + +static VideoMode Mode12 = { + 0xa000, 0xe3, 0x00, + + {0x03, 0x01, 0x0f, 0x00, 0x06 }, + + {0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0x0b, 0x3e, 0x00, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x59, 0xea, 0x8c, 0xdf, 0x28, 0x00, 0xe7, 0x04, 0xe3, + 0xff}, + + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff}, + + {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, 0x81, 0x00, 0x0f, 0x00, 0x00} +}; + +static BOOLEAN VideoAddressSpaceInitialized = FALSE; +static PVOID NonBiosBaseAddress; +static PDRIVER_OBJECT BootVidDriverObject = NULL; + +/* FUNCTIONS *****************************************************************/ + +static BOOLEAN +InbvFindBootimage() +{ + PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry; + LDR_RESOURCE_INFO ResourceInfo; + NTSTATUS Status; + PVOID BaseAddress = BootVidDriverObject->DriverStart; + ULONG Size; + + ResourceInfo.Type = RT_BITMAP; + ResourceInfo.Name = IDB_BOOTIMAGE; + ResourceInfo.Language = 0x09; + + Status = LdrFindResource_U(BaseAddress, + &ResourceInfo, + RESOURCE_DATA_LEVEL, + &ResourceDataEntry); + if (!NT_SUCCESS(Status)) + { + DPRINT("LdrFindResource_U() failed with status 0x%.08x\n", Status); + return FALSE; + } + + Status = LdrAccessResource(BaseAddress, + ResourceDataEntry, + (PVOID*)&BootimageBitmap, + &Size); + if (!NT_SUCCESS(Status)) + { + DPRINT("LdrAccessResource() failed with status 0x%.08x\n", Status); + return FALSE; + } + + return TRUE; +} + + +static BOOLEAN +InbvInitializeVideoAddressSpace(VOID) +{ + OBJECT_ATTRIBUTES ObjectAttributes; + UNICODE_STRING PhysMemName; + NTSTATUS Status; + HANDLE PhysMemHandle; + PVOID BaseAddress; + LARGE_INTEGER Offset; + ULONG ViewSize; + CHAR IVT[1024]; + CHAR BDA[256]; + PVOID start = (PVOID)0x0; + + /* + * Open the physical memory section + */ + RtlInitUnicodeStringFromLiteral(&PhysMemName, L"\\Device\\PhysicalMemory"); + InitializeObjectAttributes(&ObjectAttributes, + &PhysMemName, + 0, + NULL, + NULL); + Status = ZwOpenSection(&PhysMemHandle, SECTION_ALL_ACCESS, + &ObjectAttributes); + if (!NT_SUCCESS(Status)) + { + DPRINT("Couldn't open \\Device\\PhysicalMemory\n"); + return FALSE; + } + + /* + * Map the BIOS and device registers into the address space + */ + Offset.QuadPart = 0xa0000; + ViewSize = 0x100000 - 0xa0000; + BaseAddress = (PVOID)0xa0000; + Status = NtMapViewOfSection(PhysMemHandle, + NtCurrentProcess(), + &BaseAddress, + 0, + 8192, + &Offset, + &ViewSize, + ViewUnmap, + 0, + PAGE_EXECUTE_READWRITE); + if (!NT_SUCCESS(Status)) + { + DPRINT("Couldn't map physical memory (%x)\n", Status); + NtClose(PhysMemHandle); + return FALSE; + } + NtClose(PhysMemHandle); + if (BaseAddress != (PVOID)0xa0000) + { + DPRINT("Couldn't map physical memory at the right address " + "(was %x)\n", BaseAddress); + return FALSE; + } + + /* + * Map some memory to use for the non-BIOS parts of the v86 mode address + * space + */ + NonBiosBaseAddress = (PVOID)0x1; + ViewSize = 0xa0000 - 0x1000; + Status = NtAllocateVirtualMemory(NtCurrentProcess(), + &NonBiosBaseAddress, + 0, + &ViewSize, + MEM_COMMIT, + PAGE_EXECUTE_READWRITE); + if (!NT_SUCCESS(Status)) + { + DPRINT("Failed to allocate virtual memory (Status %x)\n", Status); + return FALSE; + } + if (NonBiosBaseAddress != (PVOID)0x0) + { + DPRINT("Failed to allocate virtual memory at right address " + "(was %x)\n", NonBiosBaseAddress); + return FALSE; + } + + /* + * Get the real mode IVT from the kernel + */ + Status = NtVdmControl(0, IVT); + if (!NT_SUCCESS(Status)) + { + DPRINT("NtVdmControl failed (status %x)\n", Status); + return FALSE; + } + + /* + * Copy the real mode IVT into the right place + */ + memcpy(start, IVT, 1024); + + /* + * Get the BDA from the kernel + */ + Status = NtVdmControl(1, BDA); + if (!NT_SUCCESS(Status)) + { + DPRINT("NtVdmControl failed (status %x)\n", Status); + return FALSE; + } + + /* + * Copy the BDA into the right place + */ + memcpy((PVOID)0x400, BDA, 256); + + return TRUE; +} + + +static BOOLEAN +InbvDeinitializeVideoAddressSpace(VOID) +{ + ULONG RegionSize; + PUCHAR ViewBase; + + RegionSize = 0xa0000 - 0x1000; + NtFreeVirtualMemory(NtCurrentProcess(), + &NonBiosBaseAddress, + &RegionSize, + MEM_RELEASE); + + ViewBase = (PUCHAR) 0xa0000; + ZwUnmapViewOfSection(NtCurrentProcess(), ViewBase); + + return TRUE; +} + + +static VOID +vgaPreCalc() +{ + ULONG j; + + for(j = 0; j < 80; j++) + { + maskbit[j * 8 + 0] = 128; + maskbit[j * 8 + 1] = 64; + maskbit[j * 8 + 2] = 32; + maskbit[j * 8 + 3] = 16; + maskbit[j * 8 + 4] = 8; + maskbit[j * 8 + 5] = 4; + maskbit[j * 8 + 6] = 2; + maskbit[j * 8 + 7] = 1; + } + for(j = 0; j < 480; j++) + { + y80[j] = j * 80; /* 80 = 640 / 8 = Number of bytes per scanline */ + } +} + +static __inline__ VOID +InbvOutxay(PUSHORT ad, UCHAR x, UCHAR y) +{ + USHORT xy = (x << 8) + y; + WRITE_PORT_USHORT(ad, xy); +} + + +static VOID +InbvSetMode(VideoMode mode) +{ + unsigned char x; + + WRITE_PORT_UCHAR((PUCHAR)MISC, mode.Misc); + WRITE_PORT_UCHAR((PUCHAR)STATUS, 0); + WRITE_PORT_UCHAR((PUCHAR)FEATURE, mode.Feature); + + for(x=0; x<5; x++) + { + InbvOutxay((PUSHORT)SEQ, mode.Seq[x], x); + } + + WRITE_PORT_USHORT((PUSHORT)CRTC, 0x11); + WRITE_PORT_USHORT((PUSHORT)CRTC, (mode.Crtc[0x11] & 0x7f)); + + for(x=0; x<25; x++) + { + InbvOutxay((PUSHORT)CRTC, mode.Crtc[x], x); + } + + for(x=0; x<9; x++) + { + InbvOutxay((PUSHORT)GRAPHICS, mode.Gfx[x], x); + } + + x=READ_PORT_UCHAR((PUCHAR)FEATURE); + + for(x=0; x<21; x++) + { + WRITE_PORT_UCHAR((PUCHAR)ATTRIB, x); + WRITE_PORT_UCHAR((PUCHAR)ATTRIB, mode.Attrib[x]); + } + + x=READ_PORT_UCHAR((PUCHAR)STATUS); + + WRITE_PORT_UCHAR((PUCHAR)ATTRIB, 0x20); +} + + +static VOID +InbvInitVGAMode(VOID) +{ + KV86M_REGISTERS Regs; + NTSTATUS Status; + + vidmem = (char *)(0xd0000000 + 0xa0000); + memset(&Regs, 0, sizeof(Regs)); + Regs.Eax = 0x0012; + Status = Ke386CallBios(0x10, &Regs); + assert(NT_SUCCESS(Status)); + + /* Get VGA registers into the correct state (mainly for setting up the palette registers correctly) */ + InbvSetMode(Mode12); + + /* Get the VGA into the mode we want to work with */ + WRITE_PORT_UCHAR((PUCHAR)0x3ce,0x08); /* Set */ + WRITE_PORT_UCHAR((PUCHAR)0x3cf,0); /* the MASK */ + WRITE_PORT_USHORT((PUSHORT)0x3ce,0x0205); /* write mode = 2 (bits 0,1) read mode = 0 (bit 3) */ + (UCHAR) READ_REGISTER_UCHAR(vidmem); /* Update bit buffer */ + WRITE_REGISTER_UCHAR(vidmem, 0); /* Write the pixel */ + WRITE_PORT_UCHAR((PUCHAR)0x3ce,0x08); + WRITE_PORT_UCHAR((PUCHAR)0x3cf,0xff); + + /* Zero out video memory (clear a possibly trashed screen) */ + RtlZeroMemory(vidmem, 64000); + + vgaPreCalc(); +} + + +BOOLEAN +STDCALL +VidResetDisplay(VOID) +{ + /* + We are only using standard VGA facilities so we can rely on the HAL 'int10mode3' + reset to cleanup the hardware state. + */ + InGraphicsMode = FALSE; + + return FALSE; +} + + +VOID +STDCALL +VidCleanUp(VOID) +{ + /* + We are only using standard VGA facilities so we can rely on the HAL 'int10mode3' + reset to cleanup the hardware state. + */ + InGraphicsMode = FALSE; + + BitmapThreadShouldTerminate = TRUE; +} + + +static __inline__ VOID +InbvSetColor(int cindex, unsigned char red, unsigned char green, unsigned char blue) +{ + red = red / (256 / 64); + green = green / (256 / 64); + blue = blue / (256 / 64); + + WRITE_PORT_UCHAR((PUCHAR)0x03c8, cindex); + WRITE_PORT_UCHAR((PUCHAR)0x03c9, red); + WRITE_PORT_UCHAR((PUCHAR)0x03c9, green); + WRITE_PORT_UCHAR((PUCHAR)0x03c9, blue); +} + + +static __inline__ VOID +InbvSetBlackPalette() +{ + register ULONG r = 0; + + for (r = 0; r < 16; r++) + { + InbvSetColor(r, 0, 0, 0); + } +} + + +static VOID +InbvDisplayBitmap(ULONG Width, ULONG Height, PCHAR ImageData) +{ + ULONG j,k,y; + register ULONG i; + register ULONG x; + register ULONG c; + + k = 0; + for (y = 0; y < Height; y++) + { + for (j = 0; j < 8; j++) + { + x = j; + + /* + * Loop through the line and process every 8th pixel. + * This way we can get a way with using the same bit mask + * for several pixels and thus not need to do as much I/O + * communication. + */ + while (x < 640) + { + c = 0; + + if (x < Width) + { + c = ImageData[k + x]; + for (i = 1; i < 4; i++) + { + if (x + i*8 < Width) + { + c |= (ImageData[k + x + i * 8] << i * 8); + } + } + } + + InbvPutPixels(x, 479 - y, c); + x += 8*4; + } + } + k += Width; + } +} + + +static VOID +InbvDisplayCompressedBitmap() +{ + PBITMAPV5HEADER bminfo; + ULONG i,j,k; + ULONG x,y; + ULONG curx,cury; + ULONG bfOffBits; + ULONG clen; + PCHAR ImageData; + + bminfo = (PBITMAPV5HEADER) &BootimageBitmap[0]; + DPRINT("bV5Size = %d\n", bminfo->bV5Size); + DPRINT("bV5Width = %d\n", bminfo->bV5Width); + DPRINT("bV5Height = %d\n", bminfo->bV5Height); + DPRINT("bV5Planes = %d\n", bminfo->bV5Planes); + DPRINT("bV5BitCount = %d\n", bminfo->bV5BitCount); + DPRINT("bV5Compression = %d\n", bminfo->bV5Compression); + DPRINT("bV5SizeImage = %d\n", bminfo->bV5SizeImage); + DPRINT("bV5XPelsPerMeter = %d\n", bminfo->bV5XPelsPerMeter); + DPRINT("bV5YPelsPerMeter = %d\n", bminfo->bV5YPelsPerMeter); + DPRINT("bV5ClrUsed = %d\n", bminfo->bV5ClrUsed); + DPRINT("bV5ClrImportant = %d\n", bminfo->bV5ClrImportant); + + bfOffBits = bminfo->bV5Size + bminfo->bV5ClrUsed * sizeof(RGBQUAD); + DPRINT("bfOffBits = %d\n", bfOffBits); + DPRINT("size of color indices = %d\n", bminfo->bV5ClrUsed * sizeof(RGBQUAD)); + DPRINT("first byte of data = %d\n", BootimageBitmap[bfOffBits]); + + InbvSetBlackPalette(); + + ImageData = ExAllocatePool(NonPagedPool, bminfo->bV5Width * bminfo->bV5Height); + RtlZeroMemory(ImageData, bminfo->bV5Width * bminfo->bV5Height); + + /* + * ImageData has 1 pixel per byte. + * bootimage has 2 pixels per byte. + */ + + if (bminfo->bV5Compression == 2) + { + k = 0; + j = 0; + while ((j < bminfo->bV5SizeImage) && (k < (ULONG) (bminfo->bV5Width * bminfo->bV5Height))) + { + unsigned char b; + + clen = BootimageBitmap[bfOffBits + j]; + j++; + + if (clen > 0) + { + /* Encoded mode */ + + b = BootimageBitmap[bfOffBits + j]; + j++; + + for (i = 0; i < (clen / 2); i++) + { + ImageData[k] = (b & 0xf0) >> 4; + k++; + ImageData[k] = b & 0xf; + k++; + } + if ((clen & 1) > 0) + { + ImageData[k] = (b & 0xf0) >> 4; + k++; + } + } + else + { + /* Absolute mode */ + b = BootimageBitmap[bfOffBits + j]; + j++; + + if (b == 0) + { + /* End of line */ + } + else if (b == 1) + { + /* End of image */ + break; + } + else if (b == 2) + { + x = BootimageBitmap[bfOffBits + j]; + j++; + y = BootimageBitmap[bfOffBits + j]; + j++; + curx = k % bminfo->bV5Width; + cury = k / bminfo->bV5Width; + k = (cury + y) * bminfo->bV5Width + (curx + x); + } + else + { + if ((j & 1) > 0) + { + DPRINT("Unaligned copy!\n"); + } + + clen = b; + for (i = 0; i < (clen / 2); i++) + { + b = BootimageBitmap[bfOffBits + j]; + j++; + + ImageData[k] = (b & 0xf0) >> 4; + k++; + ImageData[k] = b & 0xf; + k++; + } + if ((clen & 1) > 0) + { + b = BootimageBitmap[bfOffBits + j]; + j++; + ImageData[k] = (b & 0xf0) >> 4; + k++; + } + /* Word align */ + j += (j & 1); + } + } + } + + InbvDisplayBitmap(bminfo->bV5Width, bminfo->bV5Height, ImageData); + } + else + { + DbgPrint("Warning boot image need to be compressed using RLE4\n"); + } + + ExFreePool(ImageData); +} + + +#define PALETTE_FADE_STEPS 20 +#define PALETTE_FADE_TIME 20 * 10000 /* 20ms */ + +static VOID +InbvFadeUpPalette() +{ + PBITMAPV5HEADER bminfo; + PRGBQUAD Palette; + ULONG i; + unsigned char r,g,b; + register ULONG c; + LARGE_INTEGER Interval; + FADER_PALETTE_ENTRY FaderPalette[16]; + FADER_PALETTE_ENTRY FaderPaletteDelta[16]; + + RtlZeroMemory(&FaderPalette, sizeof(FaderPalette)); + RtlZeroMemory(&FaderPaletteDelta, sizeof(FaderPaletteDelta)); + + bminfo = (PBITMAPV5HEADER) &BootimageBitmap[0]; //sizeof(BITMAPFILEHEADER)]; + Palette = (PRGBQUAD) &BootimageBitmap[/* sizeof(BITMAPFILEHEADER) + */ bminfo->bV5Size]; + + for (i = 0; i < 16; i++) + { + if (i < bminfo->bV5ClrUsed) + { + FaderPaletteDelta[i].r = ((Palette[i].rgbRed << 8) / PALETTE_FADE_STEPS); + FaderPaletteDelta[i].g = ((Palette[i].rgbGreen << 8) / PALETTE_FADE_STEPS); + FaderPaletteDelta[i].b = ((Palette[i].rgbBlue << 8) / PALETTE_FADE_STEPS); + } + } + + for (i = 0; i < PALETTE_FADE_STEPS; i++) + { + for (c = 0; c < bminfo->bV5ClrUsed; c++) + { + /* Add the delta */ + FaderPalette[c].r += FaderPaletteDelta[c].r; + FaderPalette[c].g += FaderPaletteDelta[c].g; + FaderPalette[c].b += FaderPaletteDelta[c].b; + + /* Get the integer values */ + r = FaderPalette[c].r >> 8; + g = FaderPalette[c].g >> 8; + b = FaderPalette[c].b >> 8; + + /* Don't go too far */ + if (r > Palette[c].rgbRed) + r = Palette[c].rgbRed; + if (g > Palette[c].rgbGreen) + g = Palette[c].rgbGreen; + if (b > Palette[c].rgbBlue) + b = Palette[c].rgbBlue; + + /* Update the hardware */ + InbvSetColor(c, r, g, b); + } + Interval.QuadPart = -PALETTE_FADE_TIME; + KeDelayExecutionThread(KernelMode, FALSE, &Interval); + } +} + +static VOID STDCALL +InbvBitmapThreadMain(PVOID Ignored) +{ + if (InbvFindBootimage()) + { + InbvDisplayCompressedBitmap(); + InbvFadeUpPalette(); + } + else + { + DbgPrint("Warning: Cannot find boot image\n"); + } + + BitmapIsDrawn = TRUE; + for(;;) + { + if (BitmapThreadShouldTerminate) + { + DPRINT("Terminating\n"); + return; + } + ZwYieldExecution(); + } +} + + +BOOLEAN +STDCALL +VidIsBootDriverInstalled(VOID) +{ + return InGraphicsMode; +} + + +BOOLEAN +STDCALL +VidInitialize(VOID) +{ + NTSTATUS Status; + + if (!VideoAddressSpaceInitialized) + { + InbvInitializeVideoAddressSpace(); + } + + InbvInitVGAMode(); + + InGraphicsMode = TRUE; + + BitmapIsDrawn = FALSE; + BitmapThreadShouldTerminate = FALSE; + + Status = PsCreateSystemThread(&BitmapThreadHandle, + THREAD_ALL_ACCESS, + NULL, + NULL, + &BitmapThreadId, + InbvBitmapThreadMain, + NULL); + if (!NT_SUCCESS(Status)) + { + return FALSE; + } + + InbvDeinitializeVideoAddressSpace(); + VideoAddressSpaceInitialized = FALSE; + + return TRUE; +} + +NTSTATUS STDCALL_FUNC +VidDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) +{ + PIO_STACK_LOCATION piosStack = IoGetCurrentIrpStackLocation(Irp); + NTSTATUS nErrCode; + NTBOOTVID_FUNCTION_TABLE* FunctionTable; + + nErrCode = STATUS_SUCCESS; + + switch(piosStack->MajorFunction) + { + /* opening and closing handles to the device */ + case IRP_MJ_CREATE: + case IRP_MJ_CLOSE: + break; + + case IRP_MJ_DEVICE_CONTROL: + switch (piosStack->Parameters.DeviceIoControl.IoControlCode) + { + case IOCTL_BOOTVID_INITIALIZE: + VidInitialize(); + FunctionTable = (NTBOOTVID_FUNCTION_TABLE*) + Irp->AssociatedIrp.SystemBuffer; + FunctionTable->ResetDisplay = VidResetDisplay; + break; + case IOCTL_BOOTVID_CLEANUP: + VidCleanUp(); + break; + default: + nErrCode = STATUS_NOT_IMPLEMENTED; + break; + } + break; + + /* unsupported operations */ + default: + nErrCode = STATUS_NOT_IMPLEMENTED; + } + + Irp->IoStatus.Status = nErrCode; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return nErrCode; +} + + +NTSTATUS STDCALL +DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) +{ + PDEVICE_OBJECT BootVidDevice; + UNICODE_STRING DeviceName; + UNICODE_STRING DosName; + NTSTATUS Status; + + BootVidDriverObject = DriverObject; + + /* register driver routines */ + DriverObject->MajorFunction[IRP_MJ_CLOSE] = (PDRIVER_DISPATCH)VidDispatch; + DriverObject->MajorFunction[IRP_MJ_CREATE] = (PDRIVER_DISPATCH)VidDispatch; + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = + (PDRIVER_DISPATCH)VidDispatch; + DriverObject->DriverUnload = NULL; + + /* create device */ + RtlInitUnicodeStringFromLiteral(&DeviceName, L"\\Device\\BootVid"); + + Status = IoCreateDevice(DriverObject, 0, &DeviceName, FILE_DEVICE_BOOTVID, + 0, FALSE, &BootVidDevice); + if (! NT_SUCCESS(Status)) + { + return Status; + } + + BootVidDevice->Flags |= DO_BUFFERED_IO; + + return Status; +} diff --git a/reactos/drivers/dd/bootvid/bootvid.rc b/reactos/drivers/dd/bootvid/bootvid.rc new file mode 100644 index 00000000000..5fc2f58cf00 --- /dev/null +++ b/reactos/drivers/dd/bootvid/bootvid.rc @@ -0,0 +1,40 @@ +#include +#include + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +VS_VERSION_INFO VERSIONINFO + FILEVERSION RES_UINT_FV_MAJOR,RES_UINT_FV_MINOR,RES_UINT_FV_REVISION,RES_UINT_FV_BUILD + PRODUCTVERSION RES_UINT_PV_MAJOR,RES_UINT_PV_MINOR,RES_UINT_PV_REVISION,RES_UINT_PV_BUILD + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", RES_STR_COMPANY_NAME + VALUE "FileDescription", "ReactOS Boot Video\0" + VALUE "FileVersion", RES_STR_FILE_VERSION + VALUE "InternalName", "bootvid\0" + VALUE "LegalCopyright", RES_STR_LEGAL_COPYRIGHT + VALUE "OriginalFilename", "bootvid.sys\0" + VALUE "ProductName", RES_STR_PRODUCT_NAME + VALUE "ProductVersion", RES_STR_PRODUCT_VERSION + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +IDB_BOOTIMAGE BITMAP DISCARDABLE "bootimage.bmp" + diff --git a/reactos/drivers/dd/bootvid/pixelsup_i386.S b/reactos/drivers/dd/bootvid/pixelsup_i386.S new file mode 100644 index 00000000000..72efbb008ce --- /dev/null +++ b/reactos/drivers/dd/bootvid/pixelsup_i386.S @@ -0,0 +1,101 @@ +/* $Id: pixelsup_i386.S,v 1.1 2003/08/24 12:11:13 dwelch Exp $ + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: ntoskrnl/inbv/i386/pixelsup.S + * PURPOSE: Boot video support + * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net) + */ + +/* + * VOID + * InbvPutPixels(int x, int y, unsigned long c); + * + * Will put 4 pixels on the screen at + * (x+0*8,y), (x+1*8,y), (x+2*8,y), and (x+3*8,y) + * c will contain: + * bits 0- 3: Palette index for pixel at (x+0*8,y) + * bits 8-11: Palette index for pixel at (x+1*8,y) + * bits 16-19: Palette index for pixel at (x+2*8,y) + * bits 24-27: Palette index for pixel at (x+3*8,y) + * + * Parameters: + * [EBP+08h] - x X-coordinate of first pixel + * [ESP+0Ch] - y Y-coordinate of first pixel + * [ESP+10h] - c 4*4-bit color indices + */ +.globl _InbvPutPixels +_InbvPutPixels: + pushl %ebp + movl %esp, %ebp + + /* Compute mask and put it in EBX + mask = maskbit[x] */ + movl 0x8(%ebp), %esi + movl _maskbit(,%esi, 4), %ebx + + /* Don't set bit mask if it is already set */ + cmpl (inbv_last_mask),%ebx + je .nomask + + /* Set Mask Bit Register + WRITE_PORT_UCHAR((PUCHAR)0x3ce,0x08); + WRITE_PORT_UCHAR((PUCHAR)0x3cf,mask); */ + movl %ebx,(inbv_last_mask) + movw $0x3ce,%dx + movb $0x08,%al + outb %al,%dx + movw $0x3cf,%dx + movb %bl,%al + outb %al,%dx + +.nomask: + + /* Compute offset in video memory and put it in EBX + offset = (x >> 3) + y80[y]; */ + movl 0xC(%ebp), %esi /* y */ + movl _y80(,%esi, 4), %ebx + movl 0x8(%ebp), %eax /* x */ + shrl $0x3, %eax + addl %eax, %ebx + + /* Latch first byte + (UCHAR) READ_REGISTER_UCHAR(vidmem + offset+0); */ + movl (_vidmem), %esi + addl %ebx, %esi + movb 0x0(%esi), %bl + /* Write color index for first pixel + *((PUCHAR)(vidmem + offset+0)) = (c >> 0*8) & 0xff; */ + movl 0x10(%ebp), %eax + movb %al, 0x0(%esi) + + /* Latch second byte + (UCHAR) READ_REGISTER_UCHAR(vidmem + offset+1); */ + movb 0x1(%esi), %bl + /* Write color index for second pixel + *((PUCHAR)(vidmem + offset+1)) = (c >> 1*8) & 0xff; */ + shrl $0x8, %eax + movb %al, 0x1(%esi) + + /* Latch third byte + (UCHAR) READ_REGISTER_UCHAR(vidmem + offset+2); */ + movb 0x2(%esi), %bl + /* Write color index for third pixel + *((PUCHAR)(vidmem + offset+2)) = (c >> 2*8) & 0xff; */ + shrl $0x8, %eax + movb %al, 0x2(%esi) + + /* Latch fourth byte + (UCHAR) READ_REGISTER_UCHAR(vidmem + offset+3); */ + movb 0x3(%esi), %bl + /* Write color index for fourth pixel + *((PUCHAR)(vidmem + offset+3)) = (c >> 3*8) & 0xff; */ + shrl $0x8, %eax + movb %al, 0x3(%esi) + + popl %ebp + ret + +.bss +inbv_last_mask: + .short 0 diff --git a/reactos/include/ddk/ntbootvid.h b/reactos/include/ddk/ntbootvid.h new file mode 100644 index 00000000000..2ea41cf62d5 --- /dev/null +++ b/reactos/include/ddk/ntbootvid.h @@ -0,0 +1,11 @@ +#define FILE_DEVICE_BOOTVID 53335 + +#define IOCTL_BOOTVID_INITIALIZE \ + CTL_CODE(FILE_DEVICE_BOOTVID, 1, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_BOOTVID_CLEANUP \ + CTL_CODE(FILE_DEVICE_BOOTVID, 2, METHOD_BUFFERED, FILE_ANY_ACCESS) + +typedef struct +{ + BOOL STDCALL (*ResetDisplay)(VOID); +} NTBOOTVID_FUNCTION_TABLE;