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