xref: /reactos/drivers/base/bootvid/i386/xbox/bootvid.c (revision d41dec2e)
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