[BOOTVID] More improvements for PC-98 (#2936)
[reactos.git] / drivers / base / bootvid / i386 / pc98 / bootvid.c
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 }