Skip to content

Commit b050a45

Browse files
committed
[FREELDR:PC] Add a mechanism to retrieve the initial video mode on startup (reactos#8506)
This allows FreeLoader to know which initial video mode it has been started with (e.g. if chainloaded from another bootloader that has set the display to something different from 80x25 text mode), and, either update its internal state and use the video mode if supported, or, fall back to a known supported video mode (80x25 text).
1 parent e048380 commit b050a45

File tree

1 file changed

+229
-31
lines changed

1 file changed

+229
-31
lines changed

boot/freeldr/freeldr/arch/i386/pc/pcvideo.c

Lines changed: 229 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,13 @@ typedef struct
106106
ULONG MaximumPixelClock; /* Maximum pixel clock for graphics video mode, in Hz */
107107
UCHAR Reserved2[190]; /* 190 BYTEs reserved (0) */
108108
} SVGA_MODE_INFORMATION, *PSVGA_MODE_INFORMATION;
109+
C_ASSERT(sizeof(SVGA_MODE_INFORMATION) == 256);
109110
#include <poppack.h>
110111

111112
UCHAR MachDefaultTextColor = COLOR_GRAY;
112113

113114
static ULONG VideoCard = VIDEOCARD_CGA_OR_OTHER; /* Type of video card installed on the system */
114-
static USHORT BiosVideoMode; /* Current video mode as known by BIOS */
115+
static USHORT BiosVideoMode = VIDEOMODE_NORMAL_TEXT; /* Current video mode as known by BIOS */
115116
static ULONG ScreenWidth = 80; /* Screen Width in characters */
116117
static ULONG ScreenHeight = 25; /* Screen Height in characters */
117118
static ULONG BytesPerScanLine = 160; /* Number of bytes per scanline (delta) */
@@ -202,11 +203,13 @@ PcVideoDetectVideoCard(VOID)
202203
}
203204

204205
static BOOLEAN
205-
PcVideoVesaGetSVGAModeInformation(USHORT Mode, PSVGA_MODE_INFORMATION ModeInformation)
206+
PcVideoVesaGetSVGAModeInformation(
207+
_In_ USHORT Mode,
208+
_Out_ PSVGA_MODE_INFORMATION ModeInformation)
206209
{
207210
REGS Regs;
208211

209-
RtlZeroMemory((PVOID)BIOSCALLBUFFER, 256);
212+
RtlZeroMemory((PVOID)BIOSCALLBUFFER, sizeof(*ModeInformation));
210213

211214
/* VESA SuperVGA BIOS - GET SuperVGA MODE INFORMATION
212215
* AX = 4F01h
@@ -234,43 +237,230 @@ PcVideoVesaGetSVGAModeInformation(USHORT Mode, PSVGA_MODE_INFORMATION ModeInform
234237
if (Regs.w.ax != 0x004F)
235238
return FALSE;
236239

237-
RtlCopyMemory(ModeInformation, (PVOID)BIOSCALLBUFFER, sizeof(SVGA_MODE_INFORMATION));
240+
RtlCopyMemory(ModeInformation, (PVOID)BIOSCALLBUFFER, sizeof(*ModeInformation));
238241

239242
TRACE("\n");
240-
TRACE("BiosVesaGetSVGAModeInformation() mode 0x%x\n", Mode);
241-
TRACE("ModeAttributes = 0x%x\n", ModeInformation->ModeAttributes);
242-
TRACE("WindowAttributesA = 0x%x\n", ModeInformation->WindowAttributesA);
243-
TRACE("WindowAttributesB = 0x%x\n", ModeInformation->WindowsAttributesB);
244-
TRACE("WindowGranularity = %dKB\n", ModeInformation->WindowGranularity);
245-
TRACE("WindowSize = %dKB\n", ModeInformation->WindowSize);
246-
TRACE("WindowAStartSegment = 0x%x\n", ModeInformation->WindowAStartSegment);
247-
TRACE("WindowBStartSegment = 0x%x\n", ModeInformation->WindowBStartSegment);
243+
TRACE("BiosVesaGetSVGAModeInformation(Mode 0x%x)\n", Mode);
244+
TRACE("ModeAttributes = 0x%04x\n", ModeInformation->ModeAttributes);
245+
TRACE("WindowAttributesA = 0x%02x\n", ModeInformation->WindowAttributesA);
246+
TRACE("WindowAttributesB = 0x%02x\n", ModeInformation->WindowsAttributesB);
247+
TRACE("WindowGranularity = %huKB\n", ModeInformation->WindowGranularity);
248+
TRACE("WindowSize = %huKB\n", ModeInformation->WindowSize);
249+
TRACE("WindowAStartSegment = 0x%04x\n", ModeInformation->WindowAStartSegment);
250+
TRACE("WindowBStartSegment = 0x%04x\n", ModeInformation->WindowBStartSegment);
248251
TRACE("WindowPositioningFunction = 0x%x\n", ModeInformation->WindowPositioningFunction);
249-
TRACE("BytesPerScanLine = %d\n", ModeInformation->BytesPerScanLine);
250-
TRACE("WidthInPixels = %d\n", ModeInformation->WidthInPixels);
251-
TRACE("HeightInPixels = %d\n", ModeInformation->HeightInPixels);
252-
TRACE("CharacterWidthInPixels = %d\n", ModeInformation->CharacterWidthInPixels);
253-
TRACE("CharacterHeightInPixels = %d\n", ModeInformation->CharacterHeightInPixels);
254-
TRACE("NumberOfMemoryPlanes = %d\n", ModeInformation->NumberOfMemoryPlanes);
255-
TRACE("BitsPerPixel = %d\n", ModeInformation->BitsPerPixel);
256-
TRACE("NumberOfBanks = %d\n", ModeInformation->NumberOfBanks);
257-
TRACE("MemoryModel = %d\n", ModeInformation->MemoryModel);
258-
TRACE("BankSize = %d\n", ModeInformation->BankSize);
259-
TRACE("NumberOfImagePlanes = %d\n", ModeInformation->NumberOfImagePanes);
252+
TRACE("BytesPerScanLine = %hu\n", ModeInformation->BytesPerScanLine);
253+
TRACE("WidthInPixels = %hu\n", ModeInformation->WidthInPixels);
254+
TRACE("HeightInPixels = %hu\n", ModeInformation->HeightInPixels);
255+
TRACE("CharacterWidthInPixels = %u\n", ModeInformation->CharacterWidthInPixels);
256+
TRACE("CharacterHeightInPixels = %u\n", ModeInformation->CharacterHeightInPixels);
257+
TRACE("NumberOfMemoryPlanes = %u\n", ModeInformation->NumberOfMemoryPlanes);
258+
TRACE("BitsPerPixel = %u\n", ModeInformation->BitsPerPixel);
259+
TRACE("NumberOfBanks = %u\n", ModeInformation->NumberOfBanks);
260+
TRACE("MemoryModel = %u\n", ModeInformation->MemoryModel);
261+
TRACE("BankSize = %u\n", ModeInformation->BankSize);
262+
TRACE("NumberOfImagePanes = %u\n", ModeInformation->NumberOfImagePanes);
263+
TRACE("Reserved1 = 0x%02x\n", ModeInformation->Reserved1);
260264
TRACE("---VBE v1.2+ ---\n");
261-
TRACE("RedMaskSize = %d\n", ModeInformation->RedMaskSize);
262-
TRACE("RedMaskPosition = %d\n", ModeInformation->RedMaskPosition);
263-
TRACE("GreenMaskSize = %d\n", ModeInformation->GreenMaskSize);
264-
TRACE("GreenMaskPosition = %d\n", ModeInformation->GreenMaskPosition);
265-
TRACE("BlueMaskSize = %d\n", ModeInformation->BlueMaskSize);
266-
TRACE("BlueMaskPosition = %d\n", ModeInformation->BlueMaskPosition);
267-
TRACE("ReservedMaskSize = %d\n", ModeInformation->ReservedMaskSize);
268-
TRACE("ReservedMaskPosition = %d\n", ModeInformation->ReservedMaskPosition);
265+
TRACE("RedMaskSize = %u\n", ModeInformation->RedMaskSize);
266+
TRACE("RedMaskPosition = %u\n", ModeInformation->RedMaskPosition);
267+
TRACE("GreenMaskSize = %u\n", ModeInformation->GreenMaskSize);
268+
TRACE("GreenMaskPosition = %u\n", ModeInformation->GreenMaskPosition);
269+
TRACE("BlueMaskSize = %u\n", ModeInformation->BlueMaskSize);
270+
TRACE("BlueMaskPosition = %u\n", ModeInformation->BlueMaskPosition);
271+
TRACE("ReservedMaskSize = %u\n", ModeInformation->ReservedMaskSize);
272+
TRACE("ReservedMaskPosition = %u\n", ModeInformation->ReservedMaskPosition);
273+
TRACE("DirectColorModeInfo = 0x%02x\n", ModeInformation->DirectColorModeInfo);
274+
TRACE("---VBE v2.0+ ---\n");
275+
TRACE("LinearVideoBufferAddress = 0x%x\n", ModeInformation->LinearVideoBufferAddress);
276+
TRACE("OffscreenMemoryPointer = 0x%x\n", ModeInformation->OffscreenMemoryPointer);
277+
TRACE("OffscreenMemorySize = %huKB\n", ModeInformation->OffscreenMemorySize);
278+
TRACE("---VBE v3.0 ---\n");
279+
TRACE("LinearBytesPerScanLine = %hu\n", ModeInformation->LinearBytesPerScanLine);
280+
TRACE("BankedNumberOfImages = %u\n", ModeInformation->BankedNumberOfImages);
281+
TRACE("LinearNumberOfImages = %u\n", ModeInformation->LinearNumberOfImages);
282+
TRACE("LinearRedMaskSize = %u\n", ModeInformation->LinearRedMaskSize);
283+
TRACE("LinearRedMaskPosition = %u\n", ModeInformation->LinearRedMaskPosition);
284+
TRACE("LinearGreenMaskSize = %u\n", ModeInformation->LinearGreenMaskSize);
285+
TRACE("LinearGreenMaskPosition = %u\n", ModeInformation->LinearGreenMaskPosition);
286+
TRACE("LinearBlueMaskSize = %u\n", ModeInformation->LinearBlueMaskSize);
287+
TRACE("LinearBlueMaskPosition = %u\n", ModeInformation->LinearBlueMaskPosition);
288+
TRACE("LinearReservedMaskSize = %u\n", ModeInformation->LinearReservedMaskSize);
289+
TRACE("LinearReservedMaskPosition = %u\n", ModeInformation->LinearReservedMaskPosition);
290+
TRACE("MaximumPixelClock = %luHz\n", ModeInformation->MaximumPixelClock);
269291
TRACE("\n");
270292

271293
return TRUE;
272294
}
273295

296+
static BOOLEAN
297+
PcVideoVesaGetCurrentSVGAMode(
298+
_Out_ PUSHORT Mode)
299+
{
300+
REGS Regs;
301+
302+
/*
303+
* VESA SuperVGA BIOS - GET CURRENT VIDEO MODE
304+
*
305+
* AX = 4F03h
306+
* Return:
307+
* AL = 4Fh if function supported
308+
* AH = status
309+
* 00h successful
310+
* BX = video mode (see #00083,#00084)
311+
*
312+
* bit 13:
313+
* VBE/AF v1.0P accelerated video mode
314+
*
315+
* bit 14:
316+
* Linear frame buffer enabled (VBE v2.0+)
317+
*
318+
* bit 15:
319+
* Don't clear video memory
320+
* 01h failed
321+
*/
322+
Regs.w.ax = 0x4F03;
323+
Int386(0x10, &Regs, &Regs);
324+
325+
if (Regs.w.ax != 0x004F)
326+
return FALSE;
327+
328+
*Mode = Regs.w.bx;
329+
return TRUE;
330+
}
331+
332+
static BOOLEAN
333+
PcVideoGetBiosMode(
334+
_Out_ PUSHORT Mode)
335+
{
336+
REGS Regs;
337+
338+
/* Int 10h AH=0Fh
339+
* VIDEO - GET CURRENT VIDEO MODE
340+
*
341+
* AH = 0Fh
342+
* Return:
343+
* AH = number of character columns
344+
* AL = display mode (see #00010 at AH=00h)
345+
* BH = active page (see AH=05h)
346+
*
347+
* Notes: If mode was set with bit 7 set ("no blanking"), the returned
348+
* mode will also have bit 7 set. EGA, VGA, and UltraVision return either
349+
* AL=03h (color) or AL=07h (monochrome) in all extended-row text modes.
350+
* HP 200LX returns AL=07h (monochrome) if mode was set to AL=21h and
351+
* always 80 resp. 40 columns in all text modes regardless of current
352+
* zoom setting (see AH=D0h). When using a Hercules Graphics Card,
353+
* additional checks are necessary:
354+
*
355+
* mode 05h:
356+
* If WORD 0040h:0063h is 03B4h, may be in graphics page 1
357+
* (as set by DOSSHELL and other Microsoft software)
358+
*
359+
* mode 06h:
360+
* If WORD 0040h:0063h is 03B4h, may be in graphics page 0
361+
* (as set by DOSSHELL and other Microsoft software)
362+
*
363+
* mode 07h:
364+
* If BYTE 0040h:0065h bit 1 is set, Hercules card is in
365+
* graphics mode, with bit 7 indicating the page (mode set by
366+
* Hercules driver for Borland Turbo C).
367+
* The Tandy 2000 BIOS is only documented as returning AL, not AH or BH
368+
*/
369+
Regs.b.ah = 0x0F;
370+
Int386(0x10, &Regs, &Regs);
371+
372+
if (Regs.b.ah == 0)
373+
return FALSE;
374+
375+
*Mode = Regs.b.al;
376+
return TRUE;
377+
}
378+
379+
static VOID
380+
PcVideoGetDisplayMode(VOID)
381+
{
382+
USHORT Mode = 0;
383+
384+
/* Is the current mode VESA? */
385+
if (PcVideoVesaGetCurrentSVGAMode(&Mode) &&
386+
PcVideoVesaGetSVGAModeInformation(Mode, &VesaVideoModeInformation))
387+
{
388+
TRACE("VESA mode detected\n");
389+
ScreenWidth = VesaVideoModeInformation.WidthInPixels;
390+
ScreenHeight = VesaVideoModeInformation.HeightInPixels;
391+
BytesPerScanLine = VesaVideoModeInformation.BytesPerScanLine;
392+
BiosVideoMode = Mode;
393+
DisplayMode = (0x0108 <= Mode && Mode <= 0x010C) ? VideoTextMode : VideoGraphicsMode;
394+
VesaVideoMode = TRUE;
395+
}
396+
/* If not, it should be a regular BIOS mode */
397+
else if (PcVideoGetBiosMode(&Mode))
398+
{
399+
/*
400+
* The BIOS data area 0x400 holds information about the current video mode.
401+
* Infos at: https://web.archive.org/web/20240119203029/http://www.bioscentral.com/misc/bda.htm
402+
* https://stanislavs.org/helppc/bios_data_area.html
403+
*/
404+
405+
UCHAR BiosMode = (*(PUCHAR)0x449) & 0x7F; /* Current video mode */
406+
ASSERT(BiosMode == Mode);
407+
BiosVideoMode = Mode;
408+
TRACE("BIOS mode detected\n");
409+
410+
ScreenWidth = *(PUSHORT)0x44A; /* Number of screen columns */
411+
ScreenHeight = 1 + *(PUCHAR)0x484; /* 1 + "Rows on the screen (less 1, EGA+)" */
412+
413+
/*
414+
* Select bits 7 and 4 of "Video display data area (MCGA and VGA)"
415+
* |7|6|5|4|3|2|1|0|
416+
* | `------ see table below
417+
* `--------- alphanumeric scan lines (see table below)
418+
*
419+
* then convert to a number and map it to the scanline number:
420+
* Bit7 Bit4 Scan Lines
421+
* 0 0 350 line mode
422+
* 0 1 400 line mode
423+
* 1 0 200 line mode
424+
* 1 1 reserved
425+
*/
426+
BytesPerScanLine = (*(PUCHAR)0x489) & 0x90;
427+
BytesPerScanLine = ((BytesPerScanLine & 0x80) >> 6) | ((BytesPerScanLine & 0x10) >> 4);
428+
switch (BytesPerScanLine)
429+
{
430+
case VERTRES_200_SCANLINES:
431+
BytesPerScanLine = 200; break;
432+
case VERTRES_350_SCANLINES:
433+
BytesPerScanLine = 350; break;
434+
case VERTRES_400_SCANLINES:
435+
BytesPerScanLine = 400; break;
436+
default:
437+
BytesPerScanLine = 160; break;
438+
}
439+
440+
DisplayMode = VideoTextMode;
441+
VesaVideoMode = FALSE;
442+
}
443+
else
444+
{
445+
/* Set the values for the default text mode.
446+
* If a graphics mode is set, these values will be changed. */
447+
TRACE("Fallback mode detected\n");
448+
BiosVideoMode = VIDEOMODE_NORMAL_TEXT;
449+
ScreenWidth = 80;
450+
ScreenHeight = 25;
451+
BytesPerScanLine = 160;
452+
DisplayMode = VideoTextMode;
453+
VesaVideoMode = FALSE;
454+
}
455+
456+
TRACE("This is a %s video mode\n", VesaVideoMode ? "VESA" : "BIOS");
457+
TRACE("BiosVideoMode = 0x%x\n", BiosVideoMode);
458+
TRACE("DisplayMode = %sMode\n", DisplayMode == VideoTextMode ? "Text" : "Graphics");
459+
TRACE("ScreenWidth = %lu\n", ScreenWidth);
460+
TRACE("ScreenHeight = %lu\n", ScreenHeight);
461+
TRACE("BytesPerScanLine = %lu\n", BytesPerScanLine);
462+
}
463+
274464
static BOOLEAN
275465
PcVideoSetBiosVesaMode(USHORT Mode)
276466
{
@@ -849,6 +1039,14 @@ PcVideoInit(VOID)
8491039
{
8501040
/* Detect the installed video card */
8511041
VideoCard = PcVideoDetectVideoCard();
1042+
1043+
/* Retrieve the initial display mode parameters */
1044+
PcVideoGetDisplayMode();
1045+
1046+
// FIXME: We don't support graphics modes yet!
1047+
// Revert to 80x25 text mode.
1048+
if (DisplayMode != VideoTextMode)
1049+
PcVideoSetMode(VIDEOMODE_NORMAL_TEXT);
8521050
}
8531051

8541052
VIDEODISPLAYMODE

0 commit comments

Comments
 (0)