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@reactos.org) + * Copyright 2005 Filip Navara (navaraf@reactos.org) + * Copyright 2020 Stanislav Motylkov (x86corez@gmail.com) + */ + +#include "precomp.h" + +#include + +/* 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@reactos.org) + * Copyright 2005 Filip Navara (navaraf@reactos.org) + * Copyright 2020 Stanislav Motylkov (x86corez@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