xref: /reactos/drivers/base/bootvid/i386/pc98/bootvid.c (revision 3b62a89d)
1 /*
2  * PROJECT:     ReactOS Boot Video Driver for NEC PC-98 series
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Main file
5  * COPYRIGHT:   Copyright 2020 Dmitry Borisov <di.sean@protonmail.com>
6  */
7 
8 /* INCLUDES *******************************************************************/
9 
10 #include "precomp.h"
11 
12 /* GLOBALS ********************************************************************/
13 
14 static ULONG_PTR PegcControl = 0;
15 ULONG_PTR FrameBuffer = 0;
16 
17 #define PEGC_MAX_COLORS    256
18 
19 /* PRIVATE FUNCTIONS **********************************************************/
20 
21 static BOOLEAN
GraphGetStatus(_In_ UCHAR Status)22 GraphGetStatus(
23     _In_ UCHAR Status)
24 {
25     UCHAR Result;
26 
27     WRITE_PORT_UCHAR((PUCHAR)GRAPH_IO_o_STATUS_SELECT, Status);
28     Result = READ_PORT_UCHAR((PUCHAR)GRAPH_IO_i_STATUS);
29 
30     return (Result & GRAPH_STATUS_SET) && (Result != 0xFF);
31 }
32 
33 static BOOLEAN
TestMmio(VOID)34 TestMmio(VOID)
35 {
36     USHORT OldValue, NewValue;
37 
38     OldValue = READ_REGISTER_USHORT((PUSHORT)(PegcControl + PEGC_MMIO_MODE));
39 
40     /* Bits [15:1] are not writable */
41     WRITE_REGISTER_USHORT((PUSHORT)(PegcControl + PEGC_MMIO_MODE), 0x80);
42     NewValue = READ_REGISTER_USHORT((PUSHORT)(PegcControl + PEGC_MMIO_MODE));
43 
44     WRITE_REGISTER_USHORT((PUSHORT)(PegcControl + PEGC_MMIO_MODE), OldValue);
45 
46     return !(NewValue & 0x80);
47 }
48 
49 static BOOLEAN
HasPegcController(VOID)50 HasPegcController(VOID)
51 {
52     BOOLEAN Success;
53 
54     if (GraphGetStatus(GRAPH_STATUS_PEGC))
55         return TestMmio();
56 
57     WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_MODE_FLIPFLOP2, GDC2_EGC_FF_UNPROTECT);
58     WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_MODE_FLIPFLOP2, GDC2_MODE_PEGC_ENABLE);
59     Success = GraphGetStatus(GRAPH_STATUS_PEGC) ? TestMmio() : FALSE;
60     WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_MODE_FLIPFLOP2, GDC2_MODE_PEGC_DISABLE);
61     WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_MODE_FLIPFLOP2, GDC2_EGC_FF_PROTECT);
62 
63     return Success;
64 }
65 
66 static VOID
TextSync(VOID)67 TextSync(VOID)
68 {
69     while (READ_PORT_UCHAR((PUCHAR)GDC1_IO_i_STATUS) & GDC_STATUS_VSYNC)
70         NOTHING;
71 
72     while (!(READ_PORT_UCHAR((PUCHAR)GDC1_IO_i_STATUS) & GDC_STATUS_VSYNC))
73         NOTHING;
74 }
75 
76 static VOID
InitializeDisplay(VOID)77 InitializeDisplay(VOID)
78 {
79     SYNCPARAM SyncParameters;
80     CSRFORMPARAM CursorParameters;
81     CSRWPARAM CursorPosition;
82     PITCHPARAM PitchParameters;
83     PRAMPARAM RamParameters;
84     ZOOMPARAM ZoomParameters;
85     UCHAR RelayState;
86 
87     /* RESET, without FIFO check */
88     WRITE_PORT_UCHAR((PUCHAR)GDC1_IO_o_COMMAND, GDC_COMMAND_RESET1);
89     WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_COMMAND, GDC_COMMAND_RESET1);
90 
91     /* Configure chipset */
92     WRITE_PORT_UCHAR((PUCHAR)GDC1_IO_o_MODE_FLIPFLOP1, GRAPH_MODE_COLORED);
93     WRITE_PORT_UCHAR((PUCHAR)GDC1_IO_o_MODE_FLIPFLOP1, GDC2_MODE_ODD_RLINE_SHOW);
94     WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_MODE_FLIPFLOP2, GDC2_MODE_COLORS_16);
95     WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_MODE_FLIPFLOP2, GDC2_MODE_GRCG);
96     WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_MODE_FLIPFLOP2, GDC2_MODE_LCD);
97     WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_MODE_FLIPFLOP2, GDC2_MODE_LINES_400);
98     WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_MODE_FLIPFLOP2, GDC2_CLOCK1_5MHZ);
99     WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_MODE_FLIPFLOP2, GDC2_CLOCK2_5MHZ);
100     WRITE_PORT_UCHAR((PUCHAR)GRAPH_IO_o_HORIZONTAL_SCAN_RATE, GRAPH_HF_31KHZ);
101     WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_VIDEO_PAGE, 0);
102     WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_VIDEO_PAGE_ACCESS, 0);
103 
104     /* =========================== MASTER ============================ */
105 
106     /* MASTER */
107     WRITE_GDC1_COMMAND(GDC_COMMAND_MASTER);
108 
109     /* SYNC */
110     SyncParameters.Flags = SYNC_DISPLAY_MODE_GRAPHICS_AND_CHARACTERS | SYNC_VIDEO_FRAMING_NONINTERLACED |
111                            SYNC_DRAW_ONLY_DURING_RETRACE_BLANKING | SYNC_STATIC_RAM_NO_REFRESH;
112     SyncParameters.ScreenWidthChars = 80;
113     SyncParameters.HorizontalSyncWidth = 12;
114     SyncParameters.VerticalSyncWidth = 2;
115     SyncParameters.HorizontalFrontPorchWidth = 4;
116     SyncParameters.HorizontalBackPorchWidth = 4;
117     SyncParameters.VerticalFrontPorchWidth = 6;
118     SyncParameters.ScreenWidthLines = 480;
119     SyncParameters.VerticalBackPorchWidth = 37;
120     WRITE_GDC1_COMMAND(GDC_COMMAND_SYNC_ON);
121     WRITE_GDC_SYNC((PUCHAR)GDC1_IO_o_PARAM, &SyncParameters);
122 
123     /* CSRFORM */
124     CursorParameters.Show = FALSE;
125     CursorParameters.Blink = FALSE;
126     CursorParameters.BlinkRate = 12;
127     CursorParameters.LinesPerRow = 16;
128     CursorParameters.StartScanLine = 0;
129     CursorParameters.EndScanLine = 15;
130     WRITE_GDC1_COMMAND(GDC_COMMAND_CSRFORM);
131     WRITE_GDC_CSRFORM((PUCHAR)GDC1_IO_o_PARAM, &CursorParameters);
132 
133     /* PITCH */
134     PitchParameters.WordsPerScanline = BYTES_PER_SCANLINE;
135     WRITE_GDC1_COMMAND(GDC_COMMAND_PITCH);
136     WRITE_GDC_PITCH((PUCHAR)GDC1_IO_o_PARAM, &PitchParameters);
137 
138     /* PRAM */
139     RamParameters.StartingAddress = 0;
140     RamParameters.Length = 1023;
141     RamParameters.ImageBit = FALSE;
142     RamParameters.WideDisplay = FALSE;
143     WRITE_GDC1_COMMAND(GDC_COMMAND_PRAM);
144     WRITE_GDC_PRAM((PUCHAR)GDC1_IO_o_PARAM, &RamParameters);
145 
146     /* ZOOM */
147     ZoomParameters.DisplayZoomFactor = 0;
148     ZoomParameters.WritingZoomFactor = 0;
149     WRITE_GDC1_COMMAND(GDC_COMMAND_ZOOM);
150     WRITE_GDC_ZOOM((PUCHAR)GDC1_IO_o_PARAM, &ZoomParameters);
151 
152     /* CSRW */
153     CursorPosition.CursorAddress = 0;
154     CursorPosition.DotAddress = 0;
155     WRITE_GDC1_COMMAND(GDC_COMMAND_CSRW);
156     WRITE_GDC_CSRW((PUCHAR)GDC1_IO_o_PARAM, &CursorPosition);
157 
158     /* START */
159     WRITE_GDC1_COMMAND(GDC_COMMAND_BCTRL_START);
160 
161     /* ============================ SLAVE ============================ */
162 
163     /* SLAVE */
164     WRITE_GDC2_COMMAND(GDC_COMMAND_SLAVE);
165 
166     /* SYNC */
167     SyncParameters.Flags = SYNC_DISPLAY_MODE_GRAPHICS | SYNC_VIDEO_FRAMING_NONINTERLACED |
168                            SYNC_DRAW_DURING_ACTIVE_DISPLAY_TIME_AND_RETRACE_BLANKING |
169                            SYNC_STATIC_RAM_NO_REFRESH;
170     SyncParameters.ScreenWidthChars = 80;
171     SyncParameters.HorizontalSyncWidth = 12;
172     SyncParameters.VerticalSyncWidth = 2;
173     SyncParameters.HorizontalFrontPorchWidth = 4;
174     SyncParameters.HorizontalBackPorchWidth = 132;
175     SyncParameters.VerticalFrontPorchWidth = 6;
176     SyncParameters.ScreenWidthLines = 480;
177     SyncParameters.VerticalBackPorchWidth = 37;
178     WRITE_GDC2_COMMAND(GDC_COMMAND_SYNC_ON);
179     WRITE_GDC_SYNC((PUCHAR)GDC2_IO_o_PARAM, &SyncParameters);
180 
181     /* CSRFORM */
182     CursorParameters.Show = FALSE;
183     CursorParameters.Blink = FALSE;
184     CursorParameters.BlinkRate = 0;
185     CursorParameters.LinesPerRow = 1;
186     CursorParameters.StartScanLine = 0;
187     CursorParameters.EndScanLine = 0;
188     WRITE_GDC2_COMMAND(GDC_COMMAND_CSRFORM);
189     WRITE_GDC_CSRFORM((PUCHAR)GDC2_IO_o_PARAM, &CursorParameters);
190 
191     /* PITCH */
192     PitchParameters.WordsPerScanline = BYTES_PER_SCANLINE;
193     WRITE_GDC2_COMMAND(GDC_COMMAND_PITCH);
194     WRITE_GDC_PITCH((PUCHAR)GDC2_IO_o_PARAM, &PitchParameters);
195 
196     /* PRAM */
197     RamParameters.StartingAddress = 0;
198     RamParameters.Length = 1023;
199     RamParameters.ImageBit = TRUE;
200     RamParameters.WideDisplay = FALSE;
201     WRITE_GDC2_COMMAND(GDC_COMMAND_PRAM);
202     WRITE_GDC_PRAM((PUCHAR)GDC2_IO_o_PARAM, &RamParameters);
203 
204     /* ZOOM */
205     ZoomParameters.DisplayZoomFactor = 0;
206     ZoomParameters.WritingZoomFactor = 0;
207     WRITE_GDC2_COMMAND(GDC_COMMAND_ZOOM);
208     WRITE_GDC_ZOOM((PUCHAR)GDC2_IO_o_PARAM, &ZoomParameters);
209 
210     /* CSRW */
211     CursorPosition.CursorAddress = 0;
212     CursorPosition.DotAddress = 0;
213     WRITE_GDC2_COMMAND(GDC_COMMAND_CSRW);
214     WRITE_GDC_CSRW((PUCHAR)GDC2_IO_o_PARAM, &CursorPosition);
215 
216     /* Synchronize the master sync source */
217     TextSync();
218     TextSync();
219     TextSync();
220     TextSync();
221 
222     /* START */
223     WRITE_GDC2_COMMAND(GDC_COMMAND_BCTRL_START);
224 
225     /* 256 colors */
226     WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_MODE_FLIPFLOP2, GDC2_EGC_FF_UNPROTECT);
227     WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_MODE_FLIPFLOP2, GDC2_MODE_PEGC_ENABLE);
228     WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_MODE_FLIPFLOP2, GDC2_MODE_LINES_800);
229     WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_MODE_FLIPFLOP2, GDC2_EGC_FF_PROTECT);
230     WRITE_REGISTER_USHORT((PUSHORT)(PegcControl + PEGC_MMIO_MODE), PEGC_MODE_PACKED);
231     WRITE_REGISTER_USHORT((PUSHORT)(PegcControl + PEGC_MMIO_FRAMEBUFFER), PEGC_FB_MAP);
232 
233     /* Select the video source */
234     RelayState = READ_PORT_UCHAR((PUCHAR)GRAPH_IO_i_RELAY) & ~(GRAPH_RELAY_0 | GRAPH_RELAY_1);
235     RelayState |= GRAPH_VID_SRC_INTERNAL | GRAPH_SRC_GDC;
236     WRITE_PORT_UCHAR((PUCHAR)GRAPH_IO_o_RELAY, RelayState);
237 }
238 
239 static VOID
SetPaletteEntryRGB(_In_ ULONG Id,_In_ RGBQUAD Rgb)240 SetPaletteEntryRGB(
241     _In_ ULONG Id,
242     _In_ RGBQUAD Rgb)
243 {
244     WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_PALETTE_INDEX, Id);
245     WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_RED, GetRValue(Rgb));
246     WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_GREEN, GetGValue(Rgb));
247     WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_BLUE, GetBValue(Rgb));
248 }
249 
250 VOID
InitPaletteWithTable(_In_ PULONG Table,_In_ ULONG Count)251 InitPaletteWithTable(
252     _In_ PULONG Table,
253     _In_ ULONG Count)
254 {
255     ULONG i;
256     PULONG Entry = Table;
257 
258     for (i = 0; i < Count; i++)
259         SetPaletteEntryRGB(i, *Entry++);
260 
261     for (i = Count; i < PEGC_MAX_COLORS; i++)
262         SetPaletteEntryRGB(i, VidpDefaultPalette[BV_COLOR_BLACK]);
263 }
264 
265 VOID
DisplayCharacter(_In_ CHAR Character,_In_ ULONG Left,_In_ ULONG Top,_In_ ULONG TextColor,_In_ ULONG BackColor)266 DisplayCharacter(
267     _In_ CHAR Character,
268     _In_ ULONG Left,
269     _In_ ULONG Top,
270     _In_ ULONG TextColor,
271     _In_ ULONG BackColor)
272 {
273     ULONG X, Y, PixelMask;
274     PUCHAR FontChar = GetFontPtr(Character);
275 
276     for (Y = Top;
277          Y < Top + BOOTCHAR_HEIGHT;
278          ++Y, FontChar += FONT_PTR_DELTA)
279     {
280         for (X = Left, PixelMask = 1 << (BOOTCHAR_WIDTH - 1);
281              X < Left + BOOTCHAR_WIDTH;
282              ++X, PixelMask >>= 1)
283         {
284             if (*FontChar & PixelMask)
285                 SetPixel(X, Y, (UCHAR)TextColor);
286             else if (BackColor < BV_COLOR_NONE)
287                 SetPixel(X, Y, (UCHAR)BackColor);
288         }
289     }
290 }
291 
292 VOID
PreserveRow(_In_ ULONG CurrentTop,_In_ ULONG TopDelta,_In_ BOOLEAN Restore)293 PreserveRow(
294     _In_ ULONG CurrentTop,
295     _In_ ULONG TopDelta,
296     _In_ BOOLEAN Restore)
297 {
298     PULONG OldPosition, NewPosition;
299     ULONG PixelCount = TopDelta * (SCREEN_WIDTH / sizeof(ULONG));
300 
301     if (Restore)
302     {
303         /* Restore the row by copying back the contents saved off-screen */
304         OldPosition = (PULONG)(FrameBuffer + FB_OFFSET(0, SCREEN_HEIGHT));
305         NewPosition = (PULONG)(FrameBuffer + FB_OFFSET(0, CurrentTop));
306     }
307     else
308     {
309         /* Preserve the row by saving its contents off-screen */
310         OldPosition = (PULONG)(FrameBuffer + FB_OFFSET(0, CurrentTop));
311         NewPosition = (PULONG)(FrameBuffer + FB_OFFSET(0, SCREEN_HEIGHT));
312     }
313 
314     while (PixelCount--)
315         WRITE_REGISTER_ULONG(NewPosition++, READ_REGISTER_ULONG(OldPosition++));
316 }
317 
318 VOID
PrepareForSetPixel(VOID)319 PrepareForSetPixel(VOID)
320 {
321     NOTHING;
322 }
323 
324 VOID
DoScroll(_In_ ULONG Scroll)325 DoScroll(
326     _In_ ULONG Scroll)
327 {
328     USHORT i, Line;
329     PUCHAR Src, Dst;
330     PULONG SrcWide, DstWide;
331     USHORT PixelCount = (VidpScrollRegion[2] - VidpScrollRegion[0]) + 1;
332     ULONG_PTR SourceOffset = FrameBuffer + FB_OFFSET(VidpScrollRegion[0], VidpScrollRegion[1] + Scroll);
333     ULONG_PTR DestinationOffset = FrameBuffer + FB_OFFSET(VidpScrollRegion[0], VidpScrollRegion[1]);
334 
335     for (Line = VidpScrollRegion[1]; Line <= VidpScrollRegion[3]; Line++)
336     {
337         SrcWide = (PULONG)SourceOffset;
338         DstWide = (PULONG)DestinationOffset;
339         for (i = 0; i < PixelCount / sizeof(ULONG); i++)
340             WRITE_REGISTER_ULONG(DstWide++, READ_REGISTER_ULONG(SrcWide++));
341 
342         Src = (PUCHAR)SrcWide;
343         Dst = (PUCHAR)DstWide;
344         for (i = 0; i < PixelCount % sizeof(ULONG); i++)
345             WRITE_REGISTER_UCHAR(Dst++, READ_REGISTER_UCHAR(Src++));
346 
347         SourceOffset += SCREEN_WIDTH;
348         DestinationOffset += SCREEN_WIDTH;
349     }
350 }
351 
352 /* PUBLIC FUNCTIONS ***********************************************************/
353 
354 BOOLEAN
355 NTAPI
VidInitialize(_In_ BOOLEAN SetMode)356 VidInitialize(
357     _In_ BOOLEAN SetMode)
358 {
359     PHYSICAL_ADDRESS BaseAddress;
360 
361     BaseAddress.QuadPart = VRAM_NORMAL_PLANE_I;
362     PegcControl = (ULONG_PTR)MmMapIoSpace(BaseAddress, PEGC_CONTROL_SIZE, MmNonCached);
363     if (!PegcControl)
364         goto Failure;
365 
366     if (!HasPegcController())
367         goto Failure;
368 
369     BaseAddress.QuadPart = PEGC_FRAMEBUFFER_PACKED;
370     FrameBuffer = (ULONG_PTR)MmMapIoSpace(BaseAddress, PEGC_FRAMEBUFFER_SIZE, MmNonCached);
371     if (!FrameBuffer)
372         goto Failure;
373 
374     if (SetMode)
375         VidResetDisplay(TRUE);
376 
377     return TRUE;
378 
379 Failure:
380     if (PegcControl)
381         MmUnmapIoSpace((PVOID)PegcControl, PEGC_CONTROL_SIZE);
382 
383     return FALSE;
384 }
385 
386 VOID
387 NTAPI
VidCleanUp(VOID)388 VidCleanUp(VOID)
389 {
390     WRITE_PORT_UCHAR((PUCHAR)GDC1_IO_o_MODE_FLIPFLOP1, GRAPH_MODE_DISPLAY_DISABLE);
391 }
392 
393 VOID
394 NTAPI
VidResetDisplay(_In_ BOOLEAN HalReset)395 VidResetDisplay(
396     _In_ BOOLEAN HalReset)
397 {
398     PULONG PixelsPosition = (PULONG)(FrameBuffer + FB_OFFSET(0, 0));
399     ULONG PixelCount = ((SCREEN_WIDTH * SCREEN_HEIGHT) / sizeof(ULONG)) + 1;
400 
401     /* Clear the current position */
402     VidpCurrentX = 0;
403     VidpCurrentY = 0;
404 
405     /* Clear the screen with HAL if we were asked to */
406     if (HalReset)
407         HalResetDisplay();
408 
409     WRITE_PORT_UCHAR((PUCHAR)GDC1_IO_o_MODE_FLIPFLOP1, GRAPH_MODE_DISPLAY_DISABLE);
410 
411     /* 640x480 256-color 31 kHz mode */
412     InitializeDisplay();
413 
414     /* Re-initialize the palette and fill the screen black */
415     InitializePalette();
416     while (PixelCount--)
417         WRITE_REGISTER_ULONG(PixelsPosition++, 0);
418 
419     WRITE_PORT_UCHAR((PUCHAR)GDC1_IO_o_MODE_FLIPFLOP1, GRAPH_MODE_DISPLAY_ENABLE);
420 }
421 
422 VOID
423 NTAPI
VidScreenToBufferBlt(_Out_writes_bytes_ (Delta * Height)PUCHAR Buffer,_In_ ULONG Left,_In_ ULONG Top,_In_ ULONG Width,_In_ ULONG Height,_In_ ULONG Delta)424 VidScreenToBufferBlt(
425     _Out_writes_bytes_(Delta * Height) PUCHAR Buffer,
426     _In_ ULONG Left,
427     _In_ ULONG Top,
428     _In_ ULONG Width,
429     _In_ ULONG Height,
430     _In_ ULONG Delta)
431 {
432     ULONG X, Y;
433     PUCHAR OutputBuffer;
434     USHORT Px;
435     PUSHORT PixelsPosition;
436 
437     /* Clear the destination buffer */
438     RtlZeroMemory(Buffer, Delta * Height);
439 
440     for (Y = 0; Y < Height; Y++)
441     {
442         OutputBuffer = Buffer + Y * Delta;
443         PixelsPosition = (PUSHORT)(FrameBuffer + FB_OFFSET(Left, Top + Y));
444 
445         for (X = 0; X < Width; X += sizeof(USHORT))
446         {
447             Px = READ_REGISTER_USHORT(PixelsPosition++);
448             *OutputBuffer++ = (FIRSTBYTE(Px) << 4) | (SECONDBYTE(Px) & 0x0F);
449         }
450     }
451 }
452 
453 VOID
454 NTAPI
VidSolidColorFill(_In_ ULONG Left,_In_ ULONG Top,_In_ ULONG Right,_In_ ULONG Bottom,_In_ UCHAR Color)455 VidSolidColorFill(
456     _In_ ULONG Left,
457     _In_ ULONG Top,
458     _In_ ULONG Right,
459     _In_ ULONG Bottom,
460     _In_ UCHAR Color)
461 {
462     USHORT i, Line;
463     PUCHAR PixelPtr;
464     PULONG PixelsPtr;
465     ULONG WideColor = (Color << 24) | (Color << 16) | (Color << 8) | Color;
466     USHORT PixelCount = (Right - Left) + 1;
467     ULONG_PTR StartOffset = FrameBuffer + FB_OFFSET(Left, Top);
468 
469     for (Line = Top; Line <= Bottom; Line++)
470     {
471         PixelsPtr = (PULONG)StartOffset;
472         for (i = 0; i < PixelCount / sizeof(ULONG); i++)
473             WRITE_REGISTER_ULONG(PixelsPtr++, WideColor);
474 
475         PixelPtr = (PUCHAR)PixelsPtr;
476         for (i = 0; i < PixelCount % sizeof(ULONG); i++)
477             WRITE_REGISTER_UCHAR(PixelPtr++, Color);
478 
479         StartOffset += SCREEN_WIDTH;
480     }
481 }
482