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