xref: /reactos/drivers/base/bootvid/i386/pc98/bootvid.c (revision 7b1049c8)
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
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
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
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
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
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
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
251 NTAPI
252 InitPaletteWithTable(
253     _In_ PULONG Table,
254     _In_ ULONG Count)
255 {
256     ULONG i;
257     PULONG Entry = Table;
258 
259     for (i = 0; i < Count; i++)
260         SetPaletteEntryRGB(i, *Entry++);
261 
262     for (i = Count; i < PEGC_MAX_COLORS; i++)
263         SetPaletteEntryRGB(i, VidpDefaultPalette[BV_COLOR_BLACK]);
264 }
265 
266 VOID
267 NTAPI
268 DisplayCharacter(
269     _In_ CHAR Character,
270     _In_ ULONG Left,
271     _In_ ULONG Top,
272     _In_ ULONG TextColor,
273     _In_ ULONG BackColor)
274 {
275     ULONG X, Y, PixelMask;
276     PUCHAR FontChar = GetFontPtr(Character);
277 
278     for (Y = Top;
279          Y < Top + BOOTCHAR_HEIGHT;
280          ++Y, FontChar += FONT_PTR_DELTA)
281     {
282         for (X = Left, PixelMask = 1 << (BOOTCHAR_WIDTH - 1);
283              X < Left + BOOTCHAR_WIDTH;
284              ++X, PixelMask >>= 1)
285         {
286             if (*FontChar & PixelMask)
287                 SetPixel(X, Y, (UCHAR)TextColor);
288             else if (BackColor < BV_COLOR_NONE)
289                 SetPixel(X, Y, (UCHAR)BackColor);
290         }
291     }
292 }
293 
294 VOID
295 NTAPI
296 PreserveRow(
297     _In_ ULONG CurrentTop,
298     _In_ ULONG TopDelta,
299     _In_ BOOLEAN Restore)
300 {
301     PULONG OldPosition, NewPosition;
302     ULONG PixelCount = TopDelta * (SCREEN_WIDTH / sizeof(ULONG));
303 
304     if (Restore)
305     {
306         /* Restore the row by copying back the contents saved off-screen */
307         OldPosition = (PULONG)(FrameBuffer + FB_OFFSET(0, SCREEN_HEIGHT));
308         NewPosition = (PULONG)(FrameBuffer + FB_OFFSET(0, CurrentTop));
309     }
310     else
311     {
312         /* Preserve the row by saving its contents off-screen */
313         OldPosition = (PULONG)(FrameBuffer + FB_OFFSET(0, CurrentTop));
314         NewPosition = (PULONG)(FrameBuffer + FB_OFFSET(0, SCREEN_HEIGHT));
315     }
316 
317     while (PixelCount--)
318         WRITE_REGISTER_ULONG(NewPosition++, READ_REGISTER_ULONG(OldPosition++));
319 }
320 
321 VOID
322 PrepareForSetPixel(VOID)
323 {
324     NOTHING;
325 }
326 
327 VOID
328 NTAPI
329 DoScroll(
330     _In_ ULONG Scroll)
331 {
332     USHORT i, Line;
333     PUCHAR Src, Dst;
334     PULONG SrcWide, DstWide;
335     USHORT PixelCount = (VidpScrollRegion[2] - VidpScrollRegion[0]) + 1;
336     ULONG_PTR SourceOffset = FrameBuffer + FB_OFFSET(VidpScrollRegion[0], VidpScrollRegion[1] + Scroll);
337     ULONG_PTR DestinationOffset = FrameBuffer + FB_OFFSET(VidpScrollRegion[0], VidpScrollRegion[1]);
338 
339     for (Line = VidpScrollRegion[1]; Line <= VidpScrollRegion[3]; Line++)
340     {
341         SrcWide = (PULONG)SourceOffset;
342         DstWide = (PULONG)DestinationOffset;
343         for (i = 0; i < PixelCount / sizeof(ULONG); i++)
344             WRITE_REGISTER_ULONG(DstWide++, READ_REGISTER_ULONG(SrcWide++));
345 
346         Src = (PUCHAR)SrcWide;
347         Dst = (PUCHAR)DstWide;
348         for (i = 0; i < PixelCount % sizeof(ULONG); i++)
349             WRITE_REGISTER_UCHAR(Dst++, READ_REGISTER_UCHAR(Src++));
350 
351         SourceOffset += SCREEN_WIDTH;
352         DestinationOffset += SCREEN_WIDTH;
353     }
354 }
355 
356 /* PUBLIC FUNCTIONS ***********************************************************/
357 
358 BOOLEAN
359 NTAPI
360 VidInitialize(
361     _In_ BOOLEAN SetMode)
362 {
363     PHYSICAL_ADDRESS BaseAddress;
364 
365     BaseAddress.QuadPart = VRAM_NORMAL_PLANE_I;
366     PegcControl = (ULONG_PTR)MmMapIoSpace(BaseAddress, PEGC_CONTROL_SIZE, MmNonCached);
367     if (!PegcControl)
368         goto Failure;
369 
370     if (!HasPegcController())
371         goto Failure;
372 
373     BaseAddress.QuadPart = PEGC_FRAMEBUFFER_PACKED;
374     FrameBuffer = (ULONG_PTR)MmMapIoSpace(BaseAddress, PEGC_FRAMEBUFFER_SIZE, MmNonCached);
375     if (!FrameBuffer)
376         goto Failure;
377 
378     if (SetMode)
379         VidResetDisplay(TRUE);
380 
381     return TRUE;
382 
383 Failure:
384     if (PegcControl)
385         MmUnmapIoSpace((PVOID)PegcControl, PEGC_CONTROL_SIZE);
386 
387     return FALSE;
388 }
389 
390 VOID
391 NTAPI
392 VidCleanUp(VOID)
393 {
394     WRITE_PORT_UCHAR((PUCHAR)GDC1_IO_o_MODE_FLIPFLOP1, GRAPH_MODE_DISPLAY_DISABLE);
395 }
396 
397 VOID
398 NTAPI
399 VidResetDisplay(
400     _In_ BOOLEAN HalReset)
401 {
402     PULONG PixelsPosition = (PULONG)(FrameBuffer + FB_OFFSET(0, 0));
403     ULONG PixelCount = ((SCREEN_WIDTH * SCREEN_HEIGHT) / sizeof(ULONG)) + 1;
404 
405     /* Clear the current position */
406     VidpCurrentX = 0;
407     VidpCurrentY = 0;
408 
409     /* Clear the screen with HAL if we were asked to */
410     if (HalReset)
411         HalResetDisplay();
412 
413     WRITE_PORT_UCHAR((PUCHAR)GDC1_IO_o_MODE_FLIPFLOP1, GRAPH_MODE_DISPLAY_DISABLE);
414 
415     /* 640x480 256-color 31 kHz mode */
416     InitializeDisplay();
417 
418     /* Re-initialize the palette and fill the screen black */
419     InitializePalette();
420     while (PixelCount--)
421         WRITE_REGISTER_ULONG(PixelsPosition++, 0);
422 
423     WRITE_PORT_UCHAR((PUCHAR)GDC1_IO_o_MODE_FLIPFLOP1, GRAPH_MODE_DISPLAY_ENABLE);
424 }
425 
426 VOID
427 NTAPI
428 VidScreenToBufferBlt(
429     _Out_writes_bytes_(Delta * Height) PUCHAR Buffer,
430     _In_ ULONG Left,
431     _In_ ULONG Top,
432     _In_ ULONG Width,
433     _In_ ULONG Height,
434     _In_ ULONG Delta)
435 {
436     ULONG X, Y;
437     PUCHAR OutputBuffer;
438     USHORT Px;
439     PUSHORT PixelsPosition;
440 
441     /* Clear the destination buffer */
442     RtlZeroMemory(Buffer, Delta * Height);
443 
444     for (Y = 0; Y < Height; Y++)
445     {
446         OutputBuffer = Buffer + Y * Delta;
447         PixelsPosition = (PUSHORT)(FrameBuffer + FB_OFFSET(Left, Top + Y));
448 
449         for (X = 0; X < Width; X += sizeof(USHORT))
450         {
451             Px = READ_REGISTER_USHORT(PixelsPosition++);
452             *OutputBuffer++ = (FIRSTBYTE(Px) << 4) | (SECONDBYTE(Px) & 0x0F);
453         }
454     }
455 }
456 
457 VOID
458 NTAPI
459 VidSolidColorFill(
460     _In_ ULONG Left,
461     _In_ ULONG Top,
462     _In_ ULONG Right,
463     _In_ ULONG Bottom,
464     _In_ UCHAR Color)
465 {
466     USHORT i, Line;
467     PUCHAR PixelPtr;
468     PULONG PixelsPtr;
469     ULONG WideColor = (Color << 24) | (Color << 16) | (Color << 8) | Color;
470     USHORT PixelCount = (Right - Left) + 1;
471     ULONG_PTR StartOffset = FrameBuffer + FB_OFFSET(Left, Top);
472 
473     for (Line = Top; Line <= Bottom; Line++)
474     {
475         PixelsPtr = (PULONG)StartOffset;
476         for (i = 0; i < PixelCount / sizeof(ULONG); i++)
477             WRITE_REGISTER_ULONG(PixelsPtr++, WideColor);
478 
479         PixelPtr = (PUCHAR)PixelsPtr;
480         for (i = 0; i < PixelCount % sizeof(ULONG); i++)
481             WRITE_REGISTER_UCHAR(PixelPtr++, Color);
482 
483         StartOffset += SCREEN_WIDTH;
484     }
485 }
486