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