https://git.reactos.org/?p=reactos.git;a=commitdiff;h=fa1ec0a2ed44fd4e5e05f…
commit fa1ec0a2ed44fd4e5e05fec85450940281481ce9
Author: Stanislav Motylkov <x86corez(a)gmail.com>
AuthorDate: Tue May 12 17:32:37 2020 +0300
Commit: Stanislav Motylkov <x86corez(a)gmail.com>
CommitDate: Sun May 17 19:18:32 2020 +0300
[BOOTVID][HALXBOX] Implement boot video driver for Xbox (#2774)
Also make HalpBiosDisplayReset() function always return FALSE,
because we don't have a BIOS on Xbox.
CORE-16216 CORE-16219
---
drivers/base/bootvid/CMakeLists.txt | 9 +
drivers/base/bootvid/bootvid.rc | 2 +
drivers/base/bootvid/i386/xbox/bootvid.c | 441 +++++++++++++++++++++++++++++++
drivers/base/bootvid/i386/xbox/nv2a.h | 63 +++++
drivers/base/bootvid/precomp.h | 2 +
hal/halx86/generic/bios.c | 4 +
6 files changed, 521 insertions(+)
diff --git a/drivers/base/bootvid/CMakeLists.txt b/drivers/base/bootvid/CMakeLists.txt
index 033a37c53ef..d9f1819478b 100644
--- a/drivers/base/bootvid/CMakeLists.txt
+++ b/drivers/base/bootvid/CMakeLists.txt
@@ -4,15 +4,24 @@ spec2def(bootvid.dll bootvid.spec ADD_IMPORTLIB)
if((ARCH STREQUAL "i386") OR (ARCH STREQUAL "amd64"))
if(SARCH STREQUAL "pc98")
list(APPEND SOURCE
+ i386/pc98/pc98.h
i386/pc98/bootvid.c)
+ elseif(SARCH STREQUAL "xbox")
+ list(APPEND SOURCE
+ i386/xbox/nv2a.h
+ i386/xbox/bootvid.c)
else()
list(APPEND SOURCE
+ i386/pc/pc.h
+ i386/pc/cmdcnst.h
i386/pc/bootvid.c
i386/pc/bootdata.c
+ i386/pc/vga.h
i386/pc/vga.c)
endif()
elseif(ARCH STREQUAL "arm")
list(APPEND SOURCE
+ arm/arm.h
arm/bootvid.c)
endif()
diff --git a/drivers/base/bootvid/bootvid.rc b/drivers/base/bootvid/bootvid.rc
index 18d91d14650..8706fe26d69 100644
--- a/drivers/base/bootvid/bootvid.rc
+++ b/drivers/base/bootvid/bootvid.rc
@@ -2,6 +2,8 @@
#if defined(SARCH_PC98)
#define REACTOS_STR_FILE_DESCRIPTION "NEC PC-98 Boot Video Driver"
+#elif defined(SARCH_XBOX)
+#define REACTOS_STR_FILE_DESCRIPTION "Original Xbox Boot Video Driver"
#else
#define REACTOS_STR_FILE_DESCRIPTION "VGA Boot Driver"
#endif
diff --git a/drivers/base/bootvid/i386/xbox/bootvid.c
b/drivers/base/bootvid/i386/xbox/bootvid.c
new file mode 100644
index 00000000000..5fddab54769
--- /dev/null
+++ b/drivers/base/bootvid/i386/xbox/bootvid.c
@@ -0,0 +1,441 @@
+/*
+ * PROJECT: ReactOS Boot Video Driver for Original Xbox
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Main file
+ * COPYRIGHT: Copyright 2004 Gé van Geldorp (gvg(a)reactos.org)
+ * Copyright 2005 Filip Navara (navaraf(a)reactos.org)
+ * Copyright 2020 Stanislav Motylkov (x86corez(a)gmail.com)
+ */
+
+#include "precomp.h"
+
+#include <debug.h>
+
+/* GLOBALS ********************************************************************/
+
+static ULONG_PTR FrameBufferStart = 0;
+static ULONG FrameBufferWidth, FrameBufferHeight, PanH, PanV;
+static UCHAR BytesPerPixel;
+static RGBQUAD CachedPalette[BV_MAX_COLORS];
+static PUCHAR BackBuffer = NULL;
+
+/* PRIVATE FUNCTIONS *********************************************************/
+
+static UCHAR
+NvGetCrtc(
+ ULONG Base,
+ UCHAR Index)
+{
+ WRITE_REGISTER_UCHAR((PUCHAR)(Base + NV2A_CRTC_REGISTER_INDEX), Index);
+ return READ_REGISTER_UCHAR((PUCHAR)(Base + NV2A_CRTC_REGISTER_VALUE));
+}
+
+static UCHAR
+NvGetBytesPerPixel(
+ ULONG Base,
+ ULONG ScreenWidth)
+{
+ /* Get BPP directly from NV2A CRTC (magic constants are from Cromwell) */
+ UCHAR BytesPerPixel = 8 * (((NvGetCrtc(Base, 0x19) & 0xE0) << 3) |
(NvGetCrtc(Base, 0x13) & 0xFF)) / ScreenWidth;
+
+ if (BytesPerPixel == 4)
+ {
+ ASSERT((NvGetCrtc(Base, 0x28) & 0xF) == BytesPerPixel - 1);
+ }
+ else
+ {
+ ASSERT((NvGetCrtc(Base, 0x28) & 0xF) == BytesPerPixel);
+ }
+
+ return BytesPerPixel;
+}
+
+static VOID
+ApplyPalette(VOID)
+{
+ PULONG Frame = (PULONG)FrameBufferStart;
+ ULONG x, y;
+
+ /* Top panning */
+ for (x = 0; x < PanV * FrameBufferWidth; x++)
+ {
+ *Frame++ = CachedPalette[0];
+ }
+
+ /* Left panning */
+ for (y = 0; y < SCREEN_HEIGHT; y++)
+ {
+ Frame = (PULONG)(FrameBufferStart + FB_OFFSET(-PanH, y));
+
+ for (x = 0; x < PanH; x++)
+ {
+ *Frame++ = CachedPalette[0];
+ }
+ }
+
+ /* Screen redraw */
+ PUCHAR Back = BackBuffer;
+ for (y = 0; y < SCREEN_HEIGHT; y++)
+ {
+ Frame = (PULONG)(FrameBufferStart + FB_OFFSET(0, y));
+
+ for (x = 0; x < SCREEN_WIDTH; x++)
+ {
+ *Frame++ = CachedPalette[*Back++];
+ }
+ }
+
+ /* Right panning */
+ for (y = 0; y < SCREEN_HEIGHT; y++)
+ {
+ Frame = (PULONG)(FrameBufferStart + FB_OFFSET(SCREEN_WIDTH, y));
+
+ for (x = 0; x < PanH; x++)
+ {
+ *Frame++ = CachedPalette[0];
+ }
+ }
+
+ /* Bottom panning */
+ Frame = (PULONG)(FrameBufferStart + FB_OFFSET(-PanH, SCREEN_HEIGHT));
+ for (x = 0; x < PanV * FrameBufferWidth; x++)
+ {
+ *Frame++ = CachedPalette[0];
+ }
+}
+
+/* PUBLIC FUNCTIONS **********************************************************/
+
+BOOLEAN
+NTAPI
+VidInitialize(
+ _In_ BOOLEAN SetMode)
+{
+ BOOLEAN Result = FALSE;
+
+ /* FIXME: Add platform check */
+ /* 1. Access PCI device 1:0:0 */
+ /* 2. Check if device ID is 10DE:02A0 */
+
+ /* FIXME: Get device MMIO ranges from PCI */
+ PHYSICAL_ADDRESS PhysControlStart = {.QuadPart = 0xFD000000};
+ PHYSICAL_ADDRESS PhysFrameBufferStart = {.QuadPart = 0xF0000000};
+ ULONG ControlLength = 16 * 1024 * 1024;
+
+ ULONG_PTR ControlStart = (ULONG_PTR)MmMapIoSpace(PhysControlStart, ControlLength,
MmNonCached);
+ if (!ControlStart)
+ {
+ DPRINT1("Out of memory!\n");
+ return FALSE;
+ }
+
+ ULONG_PTR FrameBuffer = READ_REGISTER_ULONG((PULONG)(ControlStart +
NV2A_CONTROL_FRAMEBUFFER_ADDRESS_OFFSET));
+ FrameBufferWidth = READ_REGISTER_ULONG((PULONG)(ControlStart +
NV2A_RAMDAC_FP_HVALID_END)) + 1;
+ FrameBufferHeight = READ_REGISTER_ULONG((PULONG)(ControlStart +
NV2A_RAMDAC_FP_VVALID_END)) + 1;
+
+ FrameBuffer &= 0x0FFFFFFF;
+ if (FrameBuffer != 0x3C00000 && FrameBuffer != 0x7C00000)
+ {
+ /* Check framebuffer address (high 4 MB of either 64 or 128 MB RAM) */
+ DPRINT1("Non-standard framebuffer address 0x%p\n", FrameBuffer);
+ }
+ /* Verify that framebuffer address is page-aligned */
+ ASSERT(FrameBuffer % PAGE_SIZE == 0);
+
+ if (FrameBufferWidth < SCREEN_WIDTH || FrameBufferHeight < SCREEN_HEIGHT)
+ {
+ DPRINT1("Unsupported screen resolution!\n");
+ goto cleanup;
+ }
+
+ BytesPerPixel = NvGetBytesPerPixel(ControlStart, FrameBufferWidth);
+ ASSERT(BytesPerPixel >= 1 && BytesPerPixel <= 4);
+
+ if (BytesPerPixel != 4)
+ {
+ DPRINT1("Unsupported BytesPerPixel = %d\n", BytesPerPixel);
+ goto cleanup;
+ }
+
+ /* Calculate panning values */
+ PanH = (FrameBufferWidth - SCREEN_WIDTH) / 2;
+ PanV = (FrameBufferHeight - SCREEN_HEIGHT) / 2;
+
+ /* Verify that screen fits framebuffer size */
+ ULONG FrameBufferSize = FrameBufferWidth * FrameBufferHeight * BytesPerPixel;
+
+ /* FIXME: obtain fb size from firmware somehow (Cromwell reserves high 4 MB of RAM)
*/
+ if (FrameBufferSize > NV2A_VIDEO_MEMORY_SIZE)
+ {
+ DPRINT1("Current screen resolution exceeds video memory bounds!\n");
+ goto cleanup;
+ }
+
+ /*
+ * Reserve off-screen area for the backbuffer that contains 8-bit indexed
+ * color screen image, plus preserved row data.
+ */
+ ULONG BackBufferSize = SCREEN_WIDTH * (SCREEN_HEIGHT + BOOTCHAR_HEIGHT + 1);
+
+ /* Make sure there is enough video memory for backbuffer */
+ if (NV2A_VIDEO_MEMORY_SIZE - FrameBufferSize < BackBufferSize)
+ {
+ DPRINT1("Out of memory!\n");
+ goto cleanup;
+ }
+
+ /* Return the address back to GPU memory mapped I/O */
+ PhysFrameBufferStart.QuadPart += FrameBuffer;
+ FrameBufferStart = (ULONG_PTR)MmMapIoSpace(PhysFrameBufferStart,
NV2A_VIDEO_MEMORY_SIZE, MmNonCached);
+ if (!FrameBufferStart)
+ {
+ DPRINT1("Out of memory!\n");
+ goto cleanup;
+ }
+
+ Result = TRUE;
+
+ /* Place backbuffer in the hidden part of framebuffer */
+ BackBuffer = (PUCHAR)(FrameBufferStart + NV2A_VIDEO_MEMORY_SIZE - BackBufferSize);
+
+ /* Now check if we have to set the mode */
+ if (SetMode)
+ VidResetDisplay(TRUE);
+
+cleanup:
+ if (ControlStart)
+ MmUnmapIoSpace((PVOID)ControlStart, ControlLength);
+
+ /* Video is ready */
+ return Result;
+}
+
+VOID
+NTAPI
+VidCleanUp(VOID)
+{
+ /* Just fill the screen black */
+ VidSolidColorFill(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1, BV_COLOR_BLACK);
+}
+
+VOID
+NTAPI
+VidResetDisplay(
+ _In_ BOOLEAN HalReset)
+{
+ /* Clear the current position */
+ VidpCurrentX = 0;
+ VidpCurrentY = 0;
+
+ /* Clear the screen with HAL if we were asked to */
+ if (HalReset)
+ HalResetDisplay();
+
+ /* Re-initialize the palette and fill the screen black */
+ RtlZeroMemory((PULONG)FrameBufferStart, NV2A_VIDEO_MEMORY_SIZE);
+ InitializePalette();
+ VidSolidColorFill(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1, BV_COLOR_BLACK);
+}
+
+VOID
+NTAPI
+InitPaletteWithTable(
+ _In_ PULONG Table,
+ _In_ ULONG Count)
+{
+ PULONG Entry = Table;
+
+ for (ULONG i = 0; i < Count; i++, Entry++)
+ {
+ CachedPalette[i] = *Entry | 0xFF000000;
+ }
+ ApplyPalette();
+}
+
+VOID
+PrepareForSetPixel(VOID)
+{
+ /* Nothing to prepare */
+ NOTHING;
+}
+
+VOID
+SetPixel(
+ _In_ ULONG Left,
+ _In_ ULONG Top,
+ _In_ UCHAR Color)
+{
+ PUCHAR Back = BackBuffer + BB_OFFSET(Left, Top);
+ PULONG Frame = (PULONG)(FrameBufferStart + FB_OFFSET(Left, Top));
+
+ *Back = Color;
+ *Frame = CachedPalette[Color];
+}
+
+VOID
+NTAPI
+PreserveRow(
+ _In_ ULONG CurrentTop,
+ _In_ ULONG TopDelta,
+ _In_ BOOLEAN Restore)
+{
+ PUCHAR NewPosition, OldPosition;
+
+ /* Calculate the position in memory for the row */
+ if (Restore)
+ {
+ /* Restore the row by copying back the contents saved off-screen */
+ NewPosition = BackBuffer + BB_OFFSET(0, CurrentTop);
+ OldPosition = BackBuffer + BB_OFFSET(0, SCREEN_HEIGHT);
+ }
+ else
+ {
+ /* Preserve the row by saving its contents off-screen */
+ NewPosition = BackBuffer + BB_OFFSET(0, SCREEN_HEIGHT);
+ OldPosition = BackBuffer + BB_OFFSET(0, CurrentTop);
+ }
+
+ /* Set the count and loop every pixel of backbuffer */
+ ULONG Count = TopDelta * SCREEN_WIDTH;
+
+ RtlCopyMemory(NewPosition, OldPosition, Count);
+
+ if (Restore)
+ {
+ NewPosition = BackBuffer + BB_OFFSET(0, CurrentTop);
+
+ /* Set the count and loop every pixel of framebuffer */
+ for (ULONG y = 0; y < TopDelta; y++)
+ {
+ PULONG Frame = (PULONG)(FrameBufferStart + FB_OFFSET(0, CurrentTop + y));
+
+ Count = SCREEN_WIDTH;
+ while (Count--)
+ {
+ *Frame++ = CachedPalette[*NewPosition++];
+ }
+ }
+ }
+}
+
+VOID
+NTAPI
+DoScroll(
+ _In_ ULONG Scroll)
+{
+ ULONG RowSize = VidpScrollRegion[2] - VidpScrollRegion[0] + 1;
+
+ /* Calculate the position in memory for the row */
+ PUCHAR OldPosition = BackBuffer + BB_OFFSET(VidpScrollRegion[0], VidpScrollRegion[1]
+ Scroll);
+ PUCHAR NewPosition = BackBuffer + BB_OFFSET(VidpScrollRegion[0],
VidpScrollRegion[1]);
+
+ /* Start loop */
+ for (ULONG Top = VidpScrollRegion[1]; Top <= VidpScrollRegion[3]; ++Top)
+ {
+ ULONG i;
+
+ /* Scroll the row */
+ RtlCopyMemory(NewPosition, OldPosition, RowSize);
+
+ PULONG Frame = (PULONG)(FrameBufferStart + FB_OFFSET(VidpScrollRegion[0], Top));
+
+ for (i = 0; i < RowSize; ++i)
+ Frame[i] = CachedPalette[NewPosition[i]];
+
+ OldPosition += SCREEN_WIDTH;
+ NewPosition += SCREEN_WIDTH;
+ }
+}
+
+VOID
+NTAPI
+DisplayCharacter(
+ _In_ CHAR Character,
+ _In_ ULONG Left,
+ _In_ ULONG Top,
+ _In_ ULONG TextColor,
+ _In_ ULONG BackColor)
+{
+ /* Get the font and pixel pointer */
+ PUCHAR FontChar = GetFontPtr(Character);
+
+ /* Loop each pixel height */
+ for (ULONG y = Top; y < Top + BOOTCHAR_HEIGHT; y++, FontChar += FONT_PTR_DELTA)
+ {
+ /* Loop each pixel width */
+ ULONG x = Left;
+
+ for (UCHAR bit = 1 << (BOOTCHAR_WIDTH - 1); bit > 0; bit >>= 1,
x++)
+ {
+ /* Check if we should draw this pixel */
+ if (*FontChar & bit)
+ {
+ /* We do, use the given Text Color */
+ SetPixel(x, y, (UCHAR)TextColor);
+ }
+ else if (BackColor < BV_COLOR_NONE)
+ {
+ /*
+ * This is a background pixel. We're drawing it
+ * unless it's transparent.
+ */
+ SetPixel(x, y, (UCHAR)BackColor);
+ }
+ }
+ }
+}
+
+VOID
+NTAPI
+VidSolidColorFill(
+ _In_ ULONG Left,
+ _In_ ULONG Top,
+ _In_ ULONG Right,
+ _In_ ULONG Bottom,
+ _In_ UCHAR Color)
+{
+ while (Top <= Bottom)
+ {
+ PUCHAR Back = BackBuffer + BB_OFFSET(Left, Top);
+ PULONG Frame = (PULONG)(FrameBufferStart + FB_OFFSET(Left, Top));
+ ULONG L = Left;
+
+ while (L++ <= Right)
+ {
+ *Back++ = Color;
+ *Frame++ = CachedPalette[Color];
+ }
+ Top++;
+ }
+}
+
+VOID
+NTAPI
+VidScreenToBufferBlt(
+ _Out_ PUCHAR Buffer,
+ _In_ ULONG Left,
+ _In_ ULONG Top,
+ _In_ ULONG Width,
+ _In_ ULONG Height,
+ _In_ ULONG Delta)
+{
+ /* Clear the destination buffer */
+ RtlZeroMemory(Buffer, Delta * Height);
+
+ /* Start the outer Y height loop */
+ for (ULONG y = 0; y < Height; y++)
+ {
+ /* Set current scanline */
+ PUCHAR Back = BackBuffer + BB_OFFSET(Left, Top + y);
+ PUCHAR Buf = Buffer + y * Delta;
+
+ /* Start the X inner loop */
+ for (ULONG x = 0; x < Width; x += 2)
+ {
+ /* Read the current value */
+ *Buf = (*Back++ & 0xF) << 4;
+ *Buf |= *Back++ & 0xF;
+ Buf++;
+ }
+ }
+}
diff --git a/drivers/base/bootvid/i386/xbox/nv2a.h
b/drivers/base/bootvid/i386/xbox/nv2a.h
new file mode 100644
index 00000000000..e7581fee646
--- /dev/null
+++ b/drivers/base/bootvid/i386/xbox/nv2a.h
@@ -0,0 +1,63 @@
+/*
+ * PROJECT: ReactOS Boot Video Driver for Original Xbox
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Arch-specific header file
+ * COPYRIGHT: Copyright 2004 Gé van Geldorp (gvg(a)reactos.org)
+ * Copyright 2005 Filip Navara (navaraf(a)reactos.org)
+ * Copyright 2020 Stanislav Motylkov (x86corez(a)gmail.com)
+ */
+
+#ifndef _BOOTVID_NV2A_H_
+#define _BOOTVID_NV2A_H_
+
+#pragma once
+
+/* FIXME: obtain fb size from firmware somehow (Cromwell reserves high 4 MB of RAM) */
+#define NV2A_VIDEO_MEMORY_SIZE (4 * 1024 * 1024)
+
+#define NV2A_CONTROL_FRAMEBUFFER_ADDRESS_OFFSET 0x600800
+#define NV2A_CRTC_REGISTER_INDEX 0x6013D4
+#define NV2A_CRTC_REGISTER_VALUE 0x6013D5
+#define NV2A_RAMDAC_FP_HVALID_END 0x680838
+#define NV2A_RAMDAC_FP_VVALID_END 0x680818
+
+#define BB_OFFSET(x, y) ((y) * SCREEN_WIDTH + (x))
+#define FB_OFFSET(x, y) (((PanV + (y)) * FrameBufferWidth + PanH + (x)) *
BytesPerPixel)
+
+VOID
+NTAPI
+InitPaletteWithTable(
+ _In_ PULONG Table,
+ _In_ ULONG Count);
+
+VOID
+PrepareForSetPixel(VOID);
+
+VOID
+SetPixel(
+ _In_ ULONG Left,
+ _In_ ULONG Top,
+ _In_ UCHAR Color);
+
+VOID
+NTAPI
+PreserveRow(
+ _In_ ULONG CurrentTop,
+ _In_ ULONG TopDelta,
+ _In_ BOOLEAN Restore);
+
+VOID
+NTAPI
+DoScroll(
+ _In_ ULONG Scroll);
+
+VOID
+NTAPI
+DisplayCharacter(
+ _In_ CHAR Character,
+ _In_ ULONG Left,
+ _In_ ULONG Top,
+ _In_ ULONG TextColor,
+ _In_ ULONG BackColor);
+
+#endif /* _BOOTVID_NV2A_H_ */
diff --git a/drivers/base/bootvid/precomp.h b/drivers/base/bootvid/precomp.h
index a5a27f3ead2..bf11f82473b 100644
--- a/drivers/base/bootvid/precomp.h
+++ b/drivers/base/bootvid/precomp.h
@@ -9,6 +9,8 @@
#if defined(_M_IX86) || defined(_M_AMD64)
#if defined(SARCH_PC98)
#include "i386/pc98/pc98.h"
+#elif defined(SARCH_XBOX)
+#include "i386/xbox/nv2a.h"
#else
#include "i386/pc/vga.h"
#include "i386/pc/pc.h"
diff --git a/hal/halx86/generic/bios.c b/hal/halx86/generic/bios.c
index 85a9d5b37d5..2fe73aa15e5 100644
--- a/hal/halx86/generic/bios.c
+++ b/hal/halx86/generic/bios.c
@@ -645,6 +645,9 @@ BOOLEAN
NTAPI
HalpBiosDisplayReset(VOID)
{
+#ifdef SARCH_XBOX
+ return FALSE;
+#else
ULONG Flags;
PHARDWARE_PTE IdtPte;
BOOLEAN RestoreWriteProtection = FALSE;
@@ -709,6 +712,7 @@ HalpBiosDisplayReset(VOID)
//
__writeeflags(Flags);
return TRUE;
+#endif
}
#endif