windows-nt/Source/XPSP1/NT/base/ntsetup/textmode/kernel/spvidfb.c
2020-09-26 16:20:57 +08:00

874 lines
21 KiB
C

/*++
Copyright (c) 1993 Microsoft Corporation
Module Name:
spvidfb.c
Abstract:
Text setup display support for frame buffer displays.
Author:
Ted Miller (tedm) 29-July-1993
Revision History:
--*/
#include "spprecmp.h"
#pragma hdrstop
#define MINXRES 80
#define MINYRES 32
//
// Vector for frame buffer functions.
//
VIDEO_FUNCTION_VECTOR FrameBufferVideoVector =
{
FrameBufferDisplayString,
FrameBufferClearRegion,
FrameBufferSpecificInit,
FrameBufferSpecificReInit,
FrameBufferSpecificTerminate,
FrameBufferSpecificInitPalette,
FrameBufferSpecificScrollUp
};
BOOLEAN FrameBufferInitialized = FALSE;
//
// Variables that indicate whether we should double the width
// and/or height of a font glyph when it is drawn. This is useful
// on a 1280*1024 screen for example, to make things readable
// with an 8*12 font like vgaoem.fon.
//
BOOLEAN DoubleCharWidth,DoubleCharHeight;
//
// Number of bytes that make up a row of characters.
// Equal to the screen stride (number of bytes on a scan line)
// multiplied by the height of a char in bytes; double that
// if DoubleCharHeight is TRUE.
//
ULONG CharRowDelta;
ULONG ScaledCharWidth,HeightIterations;
ULONG BytesPerPixel;
PULONG GlyphMap;
//
// Pointer to a dynamically allocated buffer that is the size of one scanline.
//
VOID
pFrameBufferInitGlyphs(
VOID
);
VOID
FrameBufferSpecificInit(
IN PVIDEO_MODE_INFORMATION VideoModes,
IN ULONG NumberOfModes,
IN ULONG ModeSize
)
/*++
Routine Description:
Perform frame buffer specific initialization. This includes
- setting the desired video mode.
Arguments:
None.
Return Value:
--*/
{
NTSTATUS Status;
IO_STATUS_BLOCK IoStatusBlock;
VIDEO_MODE VideoMode;
PVIDEO_MODE_INFORMATION mode;
//
// headless isn't enabled on frame buffer because no frame buffer systems
// currently exist. if this changes, then this code must be enabled for
// headless operation.
//
ASSERT( HeadlessTerminalConnected == FALSE );
if(FrameBufferInitialized) {
return;
}
mode = pFrameBufferDetermineModeToUse(VideoModes,NumberOfModes, ModeSize);
if(mode == 0) {
SpDisplayRawMessage(SP_SCRN_VIDEO_ERROR_RAW, 2, VIDEOBUG_BADMODE, 0);
while(TRUE); // loop forever
}
//
// Save away the mode info in a global.
//
VideoVars.VideoModeInfo = *mode;
//
// Set the desired mode.
//
VideoMode.RequestedMode = VideoVars.VideoModeInfo.ModeIndex;
Status = ZwDeviceIoControlFile(
VideoVars.hDisplay,
NULL,
NULL,
NULL,
&IoStatusBlock,
IOCTL_VIDEO_SET_CURRENT_MODE,
&VideoMode,
sizeof(VideoMode),
NULL,
0
);
if(!NT_SUCCESS(Status)) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to set mode %u (status = %lx)\n",VideoMode.RequestedMode,Status));
SpDisplayRawMessage(SP_SCRN_VIDEO_ERROR_RAW, 2, VIDEOBUG_SETMODE, Status);
while(TRUE); // loop forever
}
//
// Map the frame buffer.
//
pSpvidMapVideoMemory(TRUE);
FrameBufferInitialized = TRUE;
//
// Determine the width of the screen. If it's double the size
// of the minimum number of characters per row (or larger)
// then we'll double the width of each character as we draw it.
//
VideoVars.ScreenWidth = VideoVars.VideoModeInfo.VisScreenWidth / FontCharacterWidth;
if(VideoVars.ScreenWidth >= 2*MINXRES) {
VideoVars.ScreenWidth /= 2;
DoubleCharWidth = TRUE;
} else {
DoubleCharWidth = FALSE;
}
//
// Determine the height of the screen. If it's double the size
// of the minimum number of characters per column (or larger)
// then we'll double the height of each character as we draw it.
//
VideoVars.ScreenHeight = VideoVars.VideoModeInfo.VisScreenHeight / FontCharacterHeight;
CharRowDelta = VideoVars.VideoModeInfo.ScreenStride * FontCharacterHeight;
if(VideoVars.ScreenHeight >= 2*MINYRES) {
VideoVars.ScreenHeight /= 2;
DoubleCharHeight = TRUE;
CharRowDelta *= 2;
} else {
DoubleCharHeight = FALSE;
}
BytesPerPixel = VideoVars.VideoModeInfo.BitsPerPlane / 8;
if(BytesPerPixel == 3) {
BytesPerPixel = 4;
}
ScaledCharWidth = (DoubleCharWidth ? 2 : 1) * FontCharacterWidth * BytesPerPixel;
HeightIterations = DoubleCharHeight ? 2 : 1;
//
// initialize glyphs.
//
pFrameBufferInitGlyphs();
//
// get hold of the space require for background textmode video buffer
// while upgrade graphics mode is running in the foreground
//
if (SP_IS_UPGRADE_GRAPHICS_MODE()) {
VideoVars.VideoBufferSize = VideoVars.VideoModeInfo.VisScreenHeight *
VideoVars.VideoModeInfo.VisScreenWidth * BytesPerPixel;
VideoVars.VideoBuffer = SpMemAlloc(VideoVars.VideoBufferSize);
if (!VideoVars.VideoBuffer) {
//
// Out of memory, run only in textmode
//
VideoVars.VideoBufferSize = 0;
SP_SET_UPGRADE_GRAPHICS_MODE(FALSE);
VideoVars.ActiveVideoBuffer = VideoVars.VideoMemoryInfo.FrameBufferBase;
} else {
VideoVars.ActiveVideoBuffer = VideoVars.VideoBuffer;
}
} else {
VideoVars.VideoBufferSize = 0;
VideoVars.VideoBuffer = NULL;
VideoVars.ActiveVideoBuffer = VideoVars.VideoMemoryInfo.FrameBufferBase;
}
}
VOID
FrameBufferSpecificReInit(
VOID
)
{
NTSTATUS Status;
IO_STATUS_BLOCK IoStatusBlock;
VIDEO_MODE VideoMode;
if (!FrameBufferInitialized) {
return;
}
//
// Set the desired mode.
//
VideoMode.RequestedMode = VideoVars.VideoModeInfo.ModeIndex;
Status = ZwDeviceIoControlFile(
VideoVars.hDisplay,
NULL,
NULL,
NULL,
&IoStatusBlock,
IOCTL_VIDEO_SET_CURRENT_MODE,
&VideoMode,
sizeof(VideoMode),
NULL,
0
);
if(!NT_SUCCESS(Status)) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to set mode %u (status = %lx)\n",VideoMode.RequestedMode,Status));
SpDisplayRawMessage(SP_SCRN_VIDEO_ERROR_RAW, 2, VIDEOBUG_SETMODE, Status);
while(TRUE); // loop forever
}
//
// Determine the width of the screen. If it's double the size
// of the minimum number of characters per row (or larger)
// then we'll double the width of each character as we draw it.
//
VideoVars.ScreenWidth = VideoVars.VideoModeInfo.VisScreenWidth / FontCharacterWidth;
if(VideoVars.ScreenWidth >= 2*MINXRES) {
VideoVars.ScreenWidth /= 2;
DoubleCharWidth = TRUE;
} else {
DoubleCharWidth = FALSE;
}
//
// Determine the height of the screen. If it's double the size
// of the minimum number of characters per column (or larger)
// then we'll double the height of each character as we draw it.
//
VideoVars.ScreenHeight = VideoVars.VideoModeInfo.VisScreenHeight / FontCharacterHeight;
CharRowDelta = VideoVars.VideoModeInfo.ScreenStride * FontCharacterHeight;
if(VideoVars.ScreenHeight >= 2*MINYRES) {
VideoVars.ScreenHeight /= 2;
DoubleCharHeight = TRUE;
CharRowDelta *= 2;
} else {
DoubleCharHeight = FALSE;
}
BytesPerPixel = VideoVars.VideoModeInfo.BitsPerPlane / 8;
if(BytesPerPixel == 3) {
BytesPerPixel = 4;
}
ScaledCharWidth = (DoubleCharWidth ? 2 : 1) * FontCharacterWidth * BytesPerPixel;
HeightIterations = DoubleCharHeight ? 2 : 1;
//
// initialize glyphs.
//
pFrameBufferInitGlyphs();
FrameBufferSpecificInitPalette();
//
// Blast the cached video memory to the real framebuffer now
//
if (SP_IS_UPGRADE_GRAPHICS_MODE() && VideoVars.VideoBuffer &&
VideoVars.VideoBufferSize) {
PUCHAR Source = VideoVars.VideoBuffer;
PUCHAR Destination = VideoVars.VideoMemoryInfo.FrameBufferBase;
ULONG Index;
for (Index=0; Index < VideoVars.VideoBufferSize; Index++) {
WRITE_REGISTER_UCHAR(Destination + Index, *(Source + Index));
}
SP_SET_UPGRADE_GRAPHICS_MODE(FALSE);
VideoVars.ActiveVideoBuffer = VideoVars.VideoMemoryInfo.FrameBufferBase;
}
}
BOOLEAN
FrameBufferSpecificInitPalette(
VOID
)
{
BOOLEAN rc;
ULONG NumEntries;
ULONG BufferSize;
PVIDEO_CLUT clut;
// NTSTATUS Status;
// IO_STATUS_BLOCK IoStatusBlock;
UCHAR i;
rc = TRUE;
//
// For non-palette-driven displays, we construct a simple palette
// for use w/ gamma correcting adapters.
//
if(!(VideoVars.VideoModeInfo.AttributeFlags & VIDEO_MODE_PALETTE_DRIVEN)) {
switch(BytesPerPixel) {
case 1:
NumEntries = 3;
break;
case 2:
NumEntries = 32;
break;
default:
NumEntries = 255;
break;
}
BufferSize = sizeof(VIDEO_CLUT)+(sizeof(VIDEO_CLUTDATA)*NumEntries); // size is close enough
clut = SpMemAlloc(BufferSize);
clut->NumEntries = (USHORT)NumEntries;
clut->FirstEntry = 0;
for(i=0; i<NumEntries; i++) {
clut->LookupTable[i].RgbArray.Red = i;
clut->LookupTable[i].RgbArray.Green = i;
clut->LookupTable[i].RgbArray.Blue = i;
clut->LookupTable[i].RgbArray.Unused = 0;
}
// Status = ZwDeviceIoControlFile(
// hDisplay,
// NULL,
// NULL,
// NULL,
// &IoStatusBlock,
// IOCTL_VIDEO_SET_COLOR_REGISTERS,
// clut,
// BufferSize,
// NULL,
// 0
// );
SpMemFree(clut);
// if(!NT_SUCCESS(Status)) {
// KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to set palette (status = %lx)\n",Status));
// rc = FALSE;
// }
}
return(rc);
}
VOID
FrameBufferSpecificTerminate(
VOID
)
/*++
Routine Description:
Perform frame buffer specific termination. This includes
- unmapping the frame buffer from memory
Arguments:
None.
Return Value:
--*/
{
if(FrameBufferInitialized) {
//
// Be a good citizen and clear the screen. Important in Far East where
// we switch screen modes on the fly as we go in and out of localized mode.
//
FrameBufferClearRegion(0,0,VideoVars.ScreenWidth,VideoVars.ScreenHeight,ATT_FG_BLACK|ATT_BG_BLACK);
pSpvidMapVideoMemory(FALSE);
FrameBufferInitialized = FALSE;
SpMemFree(GlyphMap);
if (VideoVars.VideoBuffer && VideoVars.VideoBufferSize) {
SpMemFree(VideoVars.VideoBuffer);
VideoVars.VideoBuffer = NULL;
VideoVars.VideoBufferSize = 0;
}
}
}
VOID
FrameBufferDisplayString(
IN PSTR String,
IN UCHAR Attribute,
IN ULONG X, // 0-based coordinates (character units)
IN ULONG Y
)
/*++
Routine Description:
Write a string of characters to the display.
Arguments:
Character - supplies a string in the OEM character set, to be displayed
at the given position.
Attribute - supplies the attributes for the character.
X,Y - specify the character-based (0-based) position of the output.
Return Value:
None.
--*/
{
ULONG BgColorValue;
ULONG FgColorValue;
PUCHAR Destination;
ULONG I;
ULONG J;
ULONG K;
ULONG Length;
PUCHAR Origin;
ULONG Pixel;
ULONG PixelMap;
ULONG RealHeight;
ASSERT(X < VideoVars.ScreenWidth);
ASSERT(Y < VideoVars.ScreenHeight);
//
// Calculate the bit patterns that yield the foreground and background
// attributes when poked into the frame buffer.
//
FgColorValue = VideoVars.AttributeToColorValue[Attribute & 0x0f];
BgColorValue = VideoVars.AttributeToColorValue[(Attribute >> 4) & 0x0f];
//
// Calculate the address of the upper left pixel of the first character
// to be displayed.
//
Origin = (PUCHAR)VideoVars.ActiveVideoBuffer
+ (Y * CharRowDelta)
+ (X * ScaledCharWidth);
RealHeight = FontCharacterHeight * HeightIterations;
//
// Output the character string by generating a complete scanline into
// a temporary buffer using glyph segments from each character, then
// copy the scanline to the frame buffer.
//
Length = strlen(String);
for (I = 0; I < RealHeight; I += 1) {
Destination = Origin;
for (J = 0; J < Length; J += 1) {
PixelMap = *(GlyphMap + (((UCHAR)String[J] * RealHeight) + I));
for (K = 0; K < FontCharacterWidth; K += 1) {
Pixel = (PixelMap >> 31) ? FgColorValue : BgColorValue;
switch(BytesPerPixel) {
case 1:
*Destination++ = (UCHAR)Pixel;
if(DoubleCharWidth) {
*Destination++ = (UCHAR)Pixel;
}
break;
case 2:
*(PUSHORT)Destination = (USHORT)Pixel;
Destination += 2;
if(DoubleCharWidth) {
*(PUSHORT)Destination = (USHORT)Pixel;
Destination += 2;
}
break;
case 4:
*(PULONG)Destination = Pixel;
Destination += 4;
if(DoubleCharWidth) {
*(PULONG)Destination = Pixel;
Destination += 4;
}
break;
}
PixelMap <<= 1;
}
}
Origin += VideoVars.VideoModeInfo.ScreenStride;
}
}
VOID
FrameBufferClearRegion(
IN ULONG X,
IN ULONG Y,
IN ULONG W,
IN ULONG H,
IN UCHAR Attribute
)
/*++
Routine Description:
Clear out a screen region to a specific attribute.
Arguments:
X,Y,W,H - specify rectangle in 0-based character coordinates.
Attribute - Low nibble specifies attribute to be filled in the rectangle
(ie, the background color to be cleared to).
Return Value:
None.
--*/
{
PUCHAR Destination;
ULONG Fill;
ULONG i;
ULONG FillLength;
ULONG x;
ULONG Iterations;
ASSERT(X+W <= VideoVars.ScreenWidth);
ASSERT(Y+H <= VideoVars.ScreenHeight);
if(X+W > VideoVars.ScreenWidth) {
W = VideoVars.ScreenWidth-X;
}
if(Y+H > VideoVars.ScreenHeight) {
H = VideoVars.ScreenHeight-Y;
}
Fill = VideoVars.AttributeToColorValue[Attribute & 0x0f];
Destination = (PUCHAR)VideoVars.ActiveVideoBuffer
+ (Y * CharRowDelta)
+ (X * ScaledCharWidth);
FillLength = W * ScaledCharWidth;
Iterations = H * FontCharacterHeight * HeightIterations;
switch(BytesPerPixel) {
case 1:
for(i=0; i<Iterations; i++) {
for(x=0; x<FillLength; x++) {
((PUCHAR)Destination)[x] = (UCHAR)Fill;
}
Destination += VideoVars.VideoModeInfo.ScreenStride;
}
break;
case 2:
for(i=0; i<Iterations; i++) {
for(x=0; x<FillLength/2; x++) {
((PUSHORT)Destination)[x] = (USHORT)Fill;
}
Destination += VideoVars.VideoModeInfo.ScreenStride;
}
break;
case 4:
for(i=0; i<Iterations; i++) {
for(x=0; x<FillLength/4; x++) {
((PULONG)Destination)[x] = (ULONG)Fill;
}
Destination += VideoVars.VideoModeInfo.ScreenStride;
}
break;
}
}
BOOLEAN
FrameBufferSpecificScrollUp(
IN ULONG TopLine,
IN ULONG BottomLine,
IN ULONG LineCount,
IN UCHAR FillAttribute
)
{
PUCHAR Source,Target;
ULONG Count;
Target = (PUCHAR)VideoVars.ActiveVideoBuffer
+ (TopLine * CharRowDelta);
Source = Target + (LineCount * CharRowDelta);
Count = (((BottomLine - TopLine) + 1) - LineCount) * CharRowDelta;
RtlMoveMemory(Target,Source,Count);
FrameBufferClearRegion(
0,
(BottomLine - LineCount) + 1,
VideoVars.ScreenWidth,
LineCount,
FillAttribute
);
return(TRUE);
}
VOID
pFrameBufferInitGlyphs(
VOID
)
{
ULONG I,J,z,FontValue;
UCHAR Character;
USHORT chr;
PUCHAR Glyph;
PULONG dest;
if (!GlyphMap) {
GlyphMap = SpMemAlloc(sizeof(ULONG)*256*FontCharacterHeight*HeightIterations);
}
dest = GlyphMap;
for(chr=0; chr<256; chr++) {
Character = (UCHAR)chr;
if((Character < FontHeader->FirstCharacter)
|| (Character > FontHeader->LastCharacter))
{
Character = FontHeader->DefaultCharacter;
}
Character -= FontHeader->FirstCharacter;
Glyph = (PUCHAR)FontHeader + FontHeader->Map[Character].Offset;
for (I = 0; I < FontCharacterHeight; I++) {
//
// Build up a bitmap of pixels that comprise the row of the glyph
// we are drawing.
//
FontValue = 0;
for (J = 0; J < FontBytesPerRow; J++) {
FontValue |= *(Glyph + (J * FontCharacterHeight)) << (24 - (J * 8));
}
Glyph++;
for(z=0; z<HeightIterations; z++) {
*dest++ = FontValue;
}
}
}
}
PVIDEO_MODE_INFORMATION
pFrameBufferLocateMode(
IN PVIDEO_MODE_INFORMATION VideoModes,
IN ULONG NumberOfModes,
IN ULONG ModeSize,
IN ULONG X,
IN ULONG Y,
IN ULONG Bpp,
IN ULONG VRefresh
)
{
ULONG modenum;
PVIDEO_MODE_INFORMATION pVideoMode = &VideoModes[0];
for(modenum=0; modenum<NumberOfModes; modenum++) {
if((pVideoMode->AttributeFlags & VIDEO_MODE_GRAPHICS)
&& (pVideoMode->VisScreenWidth == X)
&& (pVideoMode->VisScreenHeight == Y)
&& (((Bpp == (ULONG)(-1)) && (pVideoMode->BitsPerPlane >= 8)) || (pVideoMode->BitsPerPlane == Bpp))
&& ((VRefresh == (ULONG)(-1)) || (pVideoMode->Frequency == VRefresh)))
{
return(pVideoMode);
}
pVideoMode = (PVIDEO_MODE_INFORMATION) (((PUCHAR) pVideoMode) + ModeSize);
}
return(0);
}
PVIDEO_MODE_INFORMATION
pFrameBufferDetermineModeToUse(
IN PVIDEO_MODE_INFORMATION VideoModes,
IN ULONG NumberOfModes,
IN ULONG ModeSize
)
{
PCHAR p,q,end;
ULONG X,Y;
PVIDEO_MODE_INFORMATION mode;
ULONG i; //NEC98
//return(2); //TEDM
if(!NumberOfModes) {
return(0);
}
X = Y = 0;
//
// Get x and y resolution. If we have a monitor id string
// in the form XxY, then it is the resolution to use.
//
if((p=MonitorFirmwareIdString) && (q=strchr(p+3,'x')) && (strlen(q+1) >= 3)) {
*q++ = 0;
//
// Now p points to the x resolution and q to the y resolution.
//
X = SpMultiByteStringToUnsigned(p,&end);
if(X && (end == (q-1))) {
Y = SpMultiByteStringToUnsigned(q,&end);
if(end != (q+strlen(q))) {
Y = 0;
}
} else {
X = 0;
}
}
//
// If we don't have x or y resolution yet, look in the
// monitor config data.
//
if((!X || !Y) && MonitorConfigData) {
X = (ULONG)MonitorConfigData->HorizontalResolution;
Y = (ULONG)MonitorConfigData->VerticalResolution;
}
if(X && Y) {
//
// We found what seems like a reasonable resolution.
// Now try to locate a mode that uses it.
//
//
// Find a mode of 8bpp with the x and y resolution at 60 Hz.
//
mode = pFrameBufferLocateMode(VideoModes,NumberOfModes,ModeSize,X,Y,8,60);
if (mode) {
return(mode);
}
//
// Couldn't find an 8bpp mode @ 60Hz; find any mode with that resolution at 8bpp.
//
mode = pFrameBufferLocateMode(VideoModes,NumberOfModes,ModeSize,X,Y,8,(ULONG)(-1));
if(mode) {
return(mode);
}
}
//
// Can't find a mode so far. See if mode 0 is acceptable.
//
// First video mode in list is not for VGA on NEC98,
// so make a loop to check VGA mode.
// (First video mode in list is for VGA on PC/AT.)
//
for(i=0;
i<((!IsNEC_98) ? 1 : NumberOfModes);
i++, VideoModes=(PVIDEO_MODE_INFORMATION)(((PUCHAR)VideoModes)+ModeSize))
{
if((VideoModes->AttributeFlags & VIDEO_MODE_GRAPHICS)
&& (VideoModes->BitsPerPlane >= 8)
&& (VideoModes->VisScreenWidth >= 640)
&& (VideoModes->VisScreenHeight >= 480))
{
return(VideoModes);
}
} //NEC98
//
// Give up.
//
return(0);
}