1 /* 2 * PROJECT: ReactOS Boot Video Driver for Original Xbox 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: Main file 5 * COPYRIGHT: Copyright 2004 Gé van Geldorp <gvg@reactos.org> 6 * Copyright 2005 Filip Navara <navaraf@reactos.org> 7 * Copyright 2020 Stanislav Motylkov <x86corez@gmail.com> 8 */ 9 10 #include "precomp.h" 11 #include <drivers/xbox/xgpu.h> 12 13 #include <debug.h> 14 15 /* GLOBALS ********************************************************************/ 16 17 static ULONG_PTR FrameBufferStart = 0; 18 static ULONG FrameBufferWidth, FrameBufferHeight, PanH, PanV; 19 static UCHAR BytesPerPixel; 20 static RGBQUAD CachedPalette[BV_MAX_COLORS]; 21 static PUCHAR BackBuffer = NULL; 22 23 /* PRIVATE FUNCTIONS *********************************************************/ 24 25 static UCHAR 26 NvGetCrtc( 27 ULONG Base, 28 UCHAR Index) 29 { 30 WRITE_REGISTER_UCHAR((PUCHAR)(Base + NV2A_CRTC_REGISTER_INDEX), Index); 31 return READ_REGISTER_UCHAR((PUCHAR)(Base + NV2A_CRTC_REGISTER_VALUE)); 32 } 33 34 static UCHAR 35 NvGetBytesPerPixel( 36 ULONG Base, 37 ULONG ScreenWidth) 38 { 39 /* Get BPP directly from NV2A CRTC (magic constants are from Cromwell) */ 40 UCHAR BytesPerPixel = 8 * (((NvGetCrtc(Base, 0x19) & 0xE0) << 3) | (NvGetCrtc(Base, 0x13) & 0xFF)) / ScreenWidth; 41 42 if (BytesPerPixel == 4) 43 { 44 ASSERT((NvGetCrtc(Base, 0x28) & 0xF) == BytesPerPixel - 1); 45 } 46 else 47 { 48 ASSERT((NvGetCrtc(Base, 0x28) & 0xF) == BytesPerPixel); 49 } 50 51 return BytesPerPixel; 52 } 53 54 static VOID 55 ApplyPalette(VOID) 56 { 57 PULONG Frame = (PULONG)FrameBufferStart; 58 ULONG x, y; 59 60 /* Top panning */ 61 for (x = 0; x < PanV * FrameBufferWidth; x++) 62 { 63 *Frame++ = CachedPalette[0]; 64 } 65 66 /* Left panning */ 67 for (y = 0; y < SCREEN_HEIGHT; y++) 68 { 69 Frame = (PULONG)(FrameBufferStart + FB_OFFSET(-PanH, y)); 70 71 for (x = 0; x < PanH; x++) 72 { 73 *Frame++ = CachedPalette[0]; 74 } 75 } 76 77 /* Screen redraw */ 78 PUCHAR Back = BackBuffer; 79 for (y = 0; y < SCREEN_HEIGHT; y++) 80 { 81 Frame = (PULONG)(FrameBufferStart + FB_OFFSET(0, y)); 82 83 for (x = 0; x < SCREEN_WIDTH; x++) 84 { 85 *Frame++ = CachedPalette[*Back++]; 86 } 87 } 88 89 /* Right panning */ 90 for (y = 0; y < SCREEN_HEIGHT; y++) 91 { 92 Frame = (PULONG)(FrameBufferStart + FB_OFFSET(SCREEN_WIDTH, y)); 93 94 for (x = 0; x < PanH; x++) 95 { 96 *Frame++ = CachedPalette[0]; 97 } 98 } 99 100 /* Bottom panning */ 101 Frame = (PULONG)(FrameBufferStart + FB_OFFSET(-PanH, SCREEN_HEIGHT)); 102 for (x = 0; x < PanV * FrameBufferWidth; x++) 103 { 104 *Frame++ = CachedPalette[0]; 105 } 106 } 107 108 /* PUBLIC FUNCTIONS **********************************************************/ 109 110 BOOLEAN 111 NTAPI 112 VidInitialize( 113 _In_ BOOLEAN SetMode) 114 { 115 BOOLEAN Result = FALSE; 116 117 /* FIXME: Add platform check */ 118 /* 1. Access PCI device 1:0:0 */ 119 /* 2. Check if device ID is 10DE:02A0 */ 120 121 /* FIXME: Get device MMIO ranges from PCI */ 122 PHYSICAL_ADDRESS PhysControlStart = {.QuadPart = 0xFD000000}; 123 PHYSICAL_ADDRESS PhysFrameBufferStart = {.QuadPart = 0xF0000000}; 124 ULONG ControlLength = 16 * 1024 * 1024; 125 126 ULONG_PTR ControlStart = (ULONG_PTR)MmMapIoSpace(PhysControlStart, ControlLength, MmNonCached); 127 if (!ControlStart) 128 { 129 DPRINT1("Out of memory!\n"); 130 return FALSE; 131 } 132 133 ULONG_PTR FrameBuffer = READ_REGISTER_ULONG((PULONG)(ControlStart + NV2A_CRTC_FRAMEBUFFER_START)); 134 FrameBufferWidth = READ_REGISTER_ULONG((PULONG)(ControlStart + NV2A_RAMDAC_FP_HVALID_END)) + 1; 135 FrameBufferHeight = READ_REGISTER_ULONG((PULONG)(ControlStart + NV2A_RAMDAC_FP_VVALID_END)) + 1; 136 137 FrameBuffer &= 0x0FFFFFFF; 138 if (FrameBuffer != 0x3C00000 && FrameBuffer != 0x7C00000) 139 { 140 /* Check framebuffer address (high 4 MB of either 64 or 128 MB RAM) */ 141 DPRINT1("Non-standard framebuffer address 0x%p\n", FrameBuffer); 142 } 143 /* Verify that framebuffer address is page-aligned */ 144 ASSERT(FrameBuffer % PAGE_SIZE == 0); 145 146 if (FrameBufferWidth < SCREEN_WIDTH || FrameBufferHeight < SCREEN_HEIGHT) 147 { 148 DPRINT1("Unsupported screen resolution!\n"); 149 goto cleanup; 150 } 151 152 BytesPerPixel = NvGetBytesPerPixel(ControlStart, FrameBufferWidth); 153 ASSERT(BytesPerPixel >= 1 && BytesPerPixel <= 4); 154 155 if (BytesPerPixel != 4) 156 { 157 DPRINT1("Unsupported BytesPerPixel = %d\n", BytesPerPixel); 158 goto cleanup; 159 } 160 161 /* Calculate panning values */ 162 PanH = (FrameBufferWidth - SCREEN_WIDTH) / 2; 163 PanV = (FrameBufferHeight - SCREEN_HEIGHT) / 2; 164 165 /* Verify that screen fits framebuffer size */ 166 ULONG FrameBufferSize = FrameBufferWidth * FrameBufferHeight * BytesPerPixel; 167 168 /* FIXME: obtain fb size from firmware somehow (Cromwell reserves high 4 MB of RAM) */ 169 if (FrameBufferSize > NV2A_VIDEO_MEMORY_SIZE) 170 { 171 DPRINT1("Current screen resolution exceeds video memory bounds!\n"); 172 goto cleanup; 173 } 174 175 /* 176 * Reserve off-screen area for the backbuffer that contains 8-bit indexed 177 * color screen image, plus preserved row data. 178 */ 179 ULONG BackBufferSize = SCREEN_WIDTH * (SCREEN_HEIGHT + BOOTCHAR_HEIGHT + 1); 180 181 /* Make sure there is enough video memory for backbuffer */ 182 if (NV2A_VIDEO_MEMORY_SIZE - FrameBufferSize < BackBufferSize) 183 { 184 DPRINT1("Out of memory!\n"); 185 goto cleanup; 186 } 187 188 /* Return the address back to GPU memory mapped I/O */ 189 PhysFrameBufferStart.QuadPart += FrameBuffer; 190 FrameBufferStart = (ULONG_PTR)MmMapIoSpace(PhysFrameBufferStart, NV2A_VIDEO_MEMORY_SIZE, MmNonCached); 191 if (!FrameBufferStart) 192 { 193 DPRINT1("Out of memory!\n"); 194 goto cleanup; 195 } 196 197 Result = TRUE; 198 199 /* Place backbuffer in the hidden part of framebuffer */ 200 BackBuffer = (PUCHAR)(FrameBufferStart + NV2A_VIDEO_MEMORY_SIZE - BackBufferSize); 201 202 /* Now check if we have to set the mode */ 203 if (SetMode) 204 VidResetDisplay(TRUE); 205 206 cleanup: 207 if (ControlStart) 208 MmUnmapIoSpace((PVOID)ControlStart, ControlLength); 209 210 /* Video is ready */ 211 return Result; 212 } 213 214 VOID 215 NTAPI 216 VidCleanUp(VOID) 217 { 218 /* Just fill the screen black */ 219 VidSolidColorFill(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1, BV_COLOR_BLACK); 220 } 221 222 VOID 223 NTAPI 224 VidResetDisplay( 225 _In_ BOOLEAN HalReset) 226 { 227 /* Clear the current position */ 228 VidpCurrentX = 0; 229 VidpCurrentY = 0; 230 231 /* Clear the screen with HAL if we were asked to */ 232 if (HalReset) 233 HalResetDisplay(); 234 235 /* Re-initialize the palette and fill the screen black */ 236 RtlZeroMemory((PULONG)FrameBufferStart, NV2A_VIDEO_MEMORY_SIZE); 237 InitializePalette(); 238 VidSolidColorFill(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1, BV_COLOR_BLACK); 239 } 240 241 VOID 242 InitPaletteWithTable( 243 _In_ PULONG Table, 244 _In_ ULONG Count) 245 { 246 PULONG Entry = Table; 247 248 for (ULONG i = 0; i < Count; i++, Entry++) 249 { 250 CachedPalette[i] = *Entry | 0xFF000000; 251 } 252 ApplyPalette(); 253 } 254 255 VOID 256 PrepareForSetPixel(VOID) 257 { 258 /* Nothing to prepare */ 259 NOTHING; 260 } 261 262 VOID 263 SetPixel( 264 _In_ ULONG Left, 265 _In_ ULONG Top, 266 _In_ UCHAR Color) 267 { 268 PUCHAR Back = BackBuffer + BB_OFFSET(Left, Top); 269 PULONG Frame = (PULONG)(FrameBufferStart + FB_OFFSET(Left, Top)); 270 271 *Back = Color; 272 *Frame = CachedPalette[Color]; 273 } 274 275 VOID 276 PreserveRow( 277 _In_ ULONG CurrentTop, 278 _In_ ULONG TopDelta, 279 _In_ BOOLEAN Restore) 280 { 281 PUCHAR NewPosition, OldPosition; 282 283 /* Calculate the position in memory for the row */ 284 if (Restore) 285 { 286 /* Restore the row by copying back the contents saved off-screen */ 287 NewPosition = BackBuffer + BB_OFFSET(0, CurrentTop); 288 OldPosition = BackBuffer + BB_OFFSET(0, SCREEN_HEIGHT); 289 } 290 else 291 { 292 /* Preserve the row by saving its contents off-screen */ 293 NewPosition = BackBuffer + BB_OFFSET(0, SCREEN_HEIGHT); 294 OldPosition = BackBuffer + BB_OFFSET(0, CurrentTop); 295 } 296 297 /* Set the count and loop every pixel of backbuffer */ 298 ULONG Count = TopDelta * SCREEN_WIDTH; 299 300 RtlCopyMemory(NewPosition, OldPosition, Count); 301 302 if (Restore) 303 { 304 NewPosition = BackBuffer + BB_OFFSET(0, CurrentTop); 305 306 /* Set the count and loop every pixel of framebuffer */ 307 for (ULONG y = 0; y < TopDelta; y++) 308 { 309 PULONG Frame = (PULONG)(FrameBufferStart + FB_OFFSET(0, CurrentTop + y)); 310 311 Count = SCREEN_WIDTH; 312 while (Count--) 313 { 314 *Frame++ = CachedPalette[*NewPosition++]; 315 } 316 } 317 } 318 } 319 320 VOID 321 DoScroll( 322 _In_ ULONG Scroll) 323 { 324 ULONG RowSize = VidpScrollRegion[2] - VidpScrollRegion[0] + 1; 325 326 /* Calculate the position in memory for the row */ 327 PUCHAR OldPosition = BackBuffer + BB_OFFSET(VidpScrollRegion[0], VidpScrollRegion[1] + Scroll); 328 PUCHAR NewPosition = BackBuffer + BB_OFFSET(VidpScrollRegion[0], VidpScrollRegion[1]); 329 330 /* Start loop */ 331 for (ULONG Top = VidpScrollRegion[1]; Top <= VidpScrollRegion[3]; ++Top) 332 { 333 ULONG i; 334 335 /* Scroll the row */ 336 RtlCopyMemory(NewPosition, OldPosition, RowSize); 337 338 PULONG Frame = (PULONG)(FrameBufferStart + FB_OFFSET(VidpScrollRegion[0], Top)); 339 340 for (i = 0; i < RowSize; ++i) 341 Frame[i] = CachedPalette[NewPosition[i]]; 342 343 OldPosition += SCREEN_WIDTH; 344 NewPosition += SCREEN_WIDTH; 345 } 346 } 347 348 VOID 349 DisplayCharacter( 350 _In_ CHAR Character, 351 _In_ ULONG Left, 352 _In_ ULONG Top, 353 _In_ ULONG TextColor, 354 _In_ ULONG BackColor) 355 { 356 /* Get the font and pixel pointer */ 357 PUCHAR FontChar = GetFontPtr(Character); 358 359 /* Loop each pixel height */ 360 for (ULONG y = Top; y < Top + BOOTCHAR_HEIGHT; y++, FontChar += FONT_PTR_DELTA) 361 { 362 /* Loop each pixel width */ 363 ULONG x = Left; 364 365 for (UCHAR bit = 1 << (BOOTCHAR_WIDTH - 1); bit > 0; bit >>= 1, x++) 366 { 367 /* Check if we should draw this pixel */ 368 if (*FontChar & bit) 369 { 370 /* We do, use the given Text Color */ 371 SetPixel(x, y, (UCHAR)TextColor); 372 } 373 else if (BackColor < BV_COLOR_NONE) 374 { 375 /* 376 * This is a background pixel. We're drawing it 377 * unless it's transparent. 378 */ 379 SetPixel(x, y, (UCHAR)BackColor); 380 } 381 } 382 } 383 } 384 385 VOID 386 NTAPI 387 VidSolidColorFill( 388 _In_ ULONG Left, 389 _In_ ULONG Top, 390 _In_ ULONG Right, 391 _In_ ULONG Bottom, 392 _In_ UCHAR Color) 393 { 394 while (Top <= Bottom) 395 { 396 PUCHAR Back = BackBuffer + BB_OFFSET(Left, Top); 397 PULONG Frame = (PULONG)(FrameBufferStart + FB_OFFSET(Left, Top)); 398 ULONG L = Left; 399 400 while (L++ <= Right) 401 { 402 *Back++ = Color; 403 *Frame++ = CachedPalette[Color]; 404 } 405 Top++; 406 } 407 } 408 409 VOID 410 NTAPI 411 VidScreenToBufferBlt( 412 _Out_writes_bytes_(Delta * Height) PUCHAR Buffer, 413 _In_ ULONG Left, 414 _In_ ULONG Top, 415 _In_ ULONG Width, 416 _In_ ULONG Height, 417 _In_ ULONG Delta) 418 { 419 /* Clear the destination buffer */ 420 RtlZeroMemory(Buffer, Delta * Height); 421 422 /* Start the outer Y height loop */ 423 for (ULONG y = 0; y < Height; y++) 424 { 425 /* Set current scanline */ 426 PUCHAR Back = BackBuffer + BB_OFFSET(Left, Top + y); 427 PUCHAR Buf = Buffer + y * Delta; 428 429 /* Start the X inner loop */ 430 for (ULONG x = 0; x < Width; x += sizeof(USHORT)) 431 { 432 /* Read the current value */ 433 *Buf = (*Back++ & 0xF) << 4; 434 *Buf |= *Back++ & 0xF; 435 Buf++; 436 } 437 } 438 } 439