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
NvGetCrtc(ULONG Base,UCHAR Index)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
NvGetBytesPerPixel(ULONG Base,ULONG ScreenWidth)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
ApplyPalette(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
VidInitialize(_In_ BOOLEAN SetMode)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
VidCleanUp(VOID)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
VidResetDisplay(_In_ BOOLEAN HalReset)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
InitPaletteWithTable(_In_ PULONG Table,_In_ ULONG Count)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
PrepareForSetPixel(VOID)256 PrepareForSetPixel(VOID)
257 {
258 /* Nothing to prepare */
259 NOTHING;
260 }
261
262 VOID
SetPixel(_In_ ULONG Left,_In_ ULONG Top,_In_ UCHAR Color)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
PreserveRow(_In_ ULONG CurrentTop,_In_ ULONG TopDelta,_In_ BOOLEAN Restore)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
DoScroll(_In_ ULONG Scroll)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
DisplayCharacter(_In_ CHAR Character,_In_ ULONG Left,_In_ ULONG Top,_In_ ULONG TextColor,_In_ ULONG BackColor)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
VidSolidColorFill(_In_ ULONG Left,_In_ ULONG Top,_In_ ULONG Right,_In_ ULONG Bottom,_In_ UCHAR Color)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
VidScreenToBufferBlt(_Out_writes_bytes_ (Delta * Height)PUCHAR Buffer,_In_ ULONG Left,_In_ ULONG Top,_In_ ULONG Width,_In_ ULONG Height,_In_ ULONG Delta)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