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