https://git.reactos.org/?p=reactos.git;a=commitdiff;h=27cd9eaf1a3c494cb79b2…
commit 27cd9eaf1a3c494cb79b2a0826261466606723e1
Author: Dmitry Borisov <di.sean(a)protonmail.com>
AuthorDate: Tue Aug 11 18:39:42 2020 +0600
Commit: Stanislav Motylkov <x86corez(a)gmail.com>
CommitDate: Wed Aug 19 00:50:08 2020 +0300
[PC98VID] Add framebuffer video miniport driver for NEC PC-98 series (#3040)
This adds generic graphics support on PC-9821.
---
boot/bootdata/hiveinst_pc98.inf | 11 +
sdk/cmake/CMakeMacros.cmake | 3 +
sdk/include/reactos/drivers/pc98/video.h | 2 +
win32ss/drivers/miniport/CMakeLists.txt | 17 +-
win32ss/drivers/miniport/pc98vid/CMakeLists.txt | 16 +
win32ss/drivers/miniport/pc98vid/hardware.c | 378 +++++++++++++++++++++++
win32ss/drivers/miniport/pc98vid/ioctl.c | 351 +++++++++++++++++++++
win32ss/drivers/miniport/pc98vid/pc98disp.inf | 62 ++++
win32ss/drivers/miniport/pc98vid/pc98vid.c | 244 +++++++++++++++
win32ss/drivers/miniport/pc98vid/pc98vid.h | 167 ++++++++++
win32ss/drivers/miniport/pc98vid/pc98vid.rc | 5 +
win32ss/drivers/miniport/pc98vid/pc98vid_reg.inf | 10 +
12 files changed, 1260 insertions(+), 6 deletions(-)
diff --git a/boot/bootdata/hiveinst_pc98.inf b/boot/bootdata/hiveinst_pc98.inf
new file mode 100644
index 00000000000..1212f0365f2
--- /dev/null
+++ b/boot/bootdata/hiveinst_pc98.inf
@@ -0,0 +1,11 @@
+[Version]
+Signature = "$Windows NT$"
+
+[AddReg]
+; Enable _one_ driver per section by removing the leading semicolon.
+
+;
+; Display driver section
+
+; pc98vid video miniport driver
+HKLM,"SYSTEM\CurrentControlSet\Services\vga","Start",0x00010001,0x00000001
diff --git a/sdk/cmake/CMakeMacros.cmake b/sdk/cmake/CMakeMacros.cmake
index a9216ee5576..5ac5dec5555 100644
--- a/sdk/cmake/CMakeMacros.cmake
+++ b/sdk/cmake/CMakeMacros.cmake
@@ -863,6 +863,9 @@ function(create_registry_hives)
if(SARCH STREQUAL "xbox")
list(APPEND _livecd_inf_files
${CMAKE_SOURCE_DIR}/boot/bootdata/hiveinst_xbox.inf)
+ elseif(SARCH STREQUAL "pc98")
+ list(APPEND _livecd_inf_files
+ ${CMAKE_SOURCE_DIR}/boot/bootdata/hiveinst_pc98.inf)
else()
list(APPEND _livecd_inf_files
${CMAKE_SOURCE_DIR}/boot/bootdata/hiveinst.inf)
diff --git a/sdk/include/reactos/drivers/pc98/video.h
b/sdk/include/reactos/drivers/pc98/video.h
index 6c20b26cfcd..f6188d2abe7 100644
--- a/sdk/include/reactos/drivers/pc98/video.h
+++ b/sdk/include/reactos/drivers/pc98/video.h
@@ -353,6 +353,8 @@ WRITE_GDC2_COMMAND(UCHAR Command)
#define GRAPH_IO_i_DPMS 0x9A2
#define GRAPH_IO_o_DPMS 0x9A2
+ #define GRAPH_DPMS_HSYNC_MASK 0x40
+ #define GRAPH_DPMS_VSYNC_MASK 0x80
#define GRAPH_IO_i_HORIZONTAL_SCAN_RATE 0x9A8
#define GRAPH_IO_o_HORIZONTAL_SCAN_RATE 0x9A8
diff --git a/win32ss/drivers/miniport/CMakeLists.txt
b/win32ss/drivers/miniport/CMakeLists.txt
index 79b52e628bb..100182d8e66 100644
--- a/win32ss/drivers/miniport/CMakeLists.txt
+++ b/win32ss/drivers/miniport/CMakeLists.txt
@@ -1,9 +1,14 @@
-add_subdirectory(vbe)
-add_subdirectory(vga)
-add_subdirectory(vga_new)
-add_subdirectory(vmx_svga)
+if(SARCH STREQUAL "pc98")
+ # Actual binary filename is vga.sys
+ add_subdirectory(pc98vid)
+else()
+ add_subdirectory(vbe)
+ add_subdirectory(vga)
+ add_subdirectory(vga_new)
+ add_subdirectory(vmx_svga)
-if(ARCH STREQUAL "i386")
- add_subdirectory(xboxvmp)
+ if(ARCH STREQUAL "i386")
+ add_subdirectory(xboxvmp)
+ endif()
endif()
diff --git a/win32ss/drivers/miniport/pc98vid/CMakeLists.txt
b/win32ss/drivers/miniport/pc98vid/CMakeLists.txt
new file mode 100644
index 00000000000..f86abc2edf3
--- /dev/null
+++ b/win32ss/drivers/miniport/pc98vid/CMakeLists.txt
@@ -0,0 +1,16 @@
+
+list(APPEND SOURCE
+ hardware.c
+ ioctl.c
+ pc98vid.c
+ pc98vid.h)
+
+# Actual binary filename is vga.sys
+add_library(vga MODULE ${SOURCE} pc98vid.rc)
+
+set_module_type(vga kernelmodedriver)
+add_pch(vga pc98vid.h SOURCE)
+add_importlibs(vga ntoskrnl videoprt)
+add_cd_file(TARGET vga DESTINATION reactos/system32/drivers FOR all)
+add_registry_inf(pc98vid_reg.inf)
+add_driver_inf(vga pc98disp.inf)
diff --git a/win32ss/drivers/miniport/pc98vid/hardware.c
b/win32ss/drivers/miniport/pc98vid/hardware.c
new file mode 100644
index 00000000000..aab92d978d6
--- /dev/null
+++ b/win32ss/drivers/miniport/pc98vid/hardware.c
@@ -0,0 +1,378 @@
+/*
+ * PROJECT: ReactOS framebuffer driver for NEC PC-98 series
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Hardware support code
+ * COPYRIGHT: Copyright 2020 Dmitry Borisov (di.sean(a)protonmail.com)
+ */
+
+/* INCLUDES *******************************************************************/
+
+#include "pc98vid.h"
+
+/* GLOBALS ********************************************************************/
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, Pc98VidSetCurrentMode)
+#pragma alloc_text(PAGE, Pc98VidSetColorRegisters)
+#pragma alloc_text(PAGE, Pc98VidGetPowerState)
+#pragma alloc_text(PAGE, Pc98VidSetPowerState)
+#endif
+
+#define PEGC_MAX_COLORS 256
+
+/* FUNCTIONS ******************************************************************/
+
+static BOOLEAN
+GraphGetStatus(
+ _In_ UCHAR Status)
+{
+ UCHAR Result;
+
+ VideoPortWritePortUchar((PUCHAR)GRAPH_IO_o_STATUS_SELECT, Status);
+ Result = VideoPortReadPortUchar((PUCHAR)GRAPH_IO_i_STATUS);
+
+ return (Result & GRAPH_STATUS_SET) && (Result != 0xFF);
+}
+
+static BOOLEAN
+TestMmio(
+ _In_ PHW_DEVICE_EXTENSION DeviceExtension)
+{
+ USHORT OldValue, NewValue;
+
+ OldValue = VideoPortReadRegisterUshort((PUSHORT)(DeviceExtension->PegcControlVa +
+ PEGC_MMIO_MODE));
+
+ /* Bits [15:1] are not writable */
+ VideoPortWriteRegisterUshort((PUSHORT)(DeviceExtension->PegcControlVa +
+ PEGC_MMIO_MODE), 0x80);
+ NewValue = VideoPortReadRegisterUshort((PUSHORT)(DeviceExtension->PegcControlVa +
+ PEGC_MMIO_MODE));
+
+ VideoPortWriteRegisterUshort((PUSHORT)(DeviceExtension->PegcControlVa +
+ PEGC_MMIO_MODE), OldValue);
+
+ return !(NewValue & 0x80);
+}
+
+static VOID
+TextSync(VOID)
+{
+ while (VideoPortReadPortUchar((PUCHAR)GDC1_IO_i_STATUS) & GDC_STATUS_VSYNC)
+ NOTHING;
+
+ while (!(VideoPortReadPortUchar((PUCHAR)GDC1_IO_i_STATUS) & GDC_STATUS_VSYNC))
+ NOTHING;
+}
+
+BOOLEAN
+NTAPI
+HasPegcController(
+ _In_ PHW_DEVICE_EXTENSION DeviceExtension)
+{
+ BOOLEAN Success;
+
+ if (GraphGetStatus(GRAPH_STATUS_PEGC))
+ return TestMmio(DeviceExtension);
+
+ VideoPortWritePortUchar((PUCHAR)GDC2_IO_o_MODE_FLIPFLOP2, GDC2_EGC_FF_UNPROTECT);
+ VideoPortWritePortUchar((PUCHAR)GDC2_IO_o_MODE_FLIPFLOP2, GDC2_MODE_PEGC_ENABLE);
+ Success = GraphGetStatus(GRAPH_STATUS_PEGC) ? TestMmio(DeviceExtension) : FALSE;
+ VideoPortWritePortUchar((PUCHAR)GDC2_IO_o_MODE_FLIPFLOP2, GDC2_MODE_PEGC_DISABLE);
+ VideoPortWritePortUchar((PUCHAR)GDC2_IO_o_MODE_FLIPFLOP2, GDC2_EGC_FF_PROTECT);
+
+ return Success;
+}
+
+VP_STATUS
+FASTCALL
+Pc98VidSetCurrentMode(
+ _In_ PHW_DEVICE_EXTENSION DeviceExtension,
+ _In_ PVIDEO_MODE RequestedMode)
+{
+ SYNCPARAM SyncParameters;
+ CSRFORMPARAM CursorParameters;
+ CSRWPARAM CursorPosition;
+ PITCHPARAM PitchParameters;
+ PRAMPARAM RamParameters;
+ ZOOMPARAM ZoomParameters;
+ UCHAR RelayState;
+
+ PAGED_CODE();
+
+ VideoDebugPrint((Trace, "%s() Mode %d\n",
+ __FUNCTION__, RequestedMode->RequestedMode));
+
+ if (RequestedMode->RequestedMode > DeviceExtension->ModeCount)
+ return ERROR_INVALID_PARAMETER;
+
+ /* Blank screen */
+ VideoPortWritePortUchar((PUCHAR)GDC1_IO_o_MODE_FLIPFLOP1,
GRAPH_MODE_DISPLAY_DISABLE);
+
+ /* RESET, without FIFO check */
+ VideoPortWritePortUchar((PUCHAR)GDC1_IO_o_COMMAND, GDC_COMMAND_RESET1);
+ VideoPortWritePortUchar((PUCHAR)GDC2_IO_o_COMMAND, GDC_COMMAND_RESET1);
+
+ /* Configure chipset */
+ VideoPortWritePortUchar((PUCHAR)GDC1_IO_o_MODE_FLIPFLOP1, GRAPH_MODE_COLORED);
+ VideoPortWritePortUchar((PUCHAR)GDC1_IO_o_MODE_FLIPFLOP1, GDC2_MODE_ODD_RLINE_SHOW);
+ VideoPortWritePortUchar((PUCHAR)GDC2_IO_o_MODE_FLIPFLOP2, GDC2_MODE_COLORS_16);
+ VideoPortWritePortUchar((PUCHAR)GDC2_IO_o_MODE_FLIPFLOP2, GDC2_MODE_GRCG);
+ VideoPortWritePortUchar((PUCHAR)GDC2_IO_o_MODE_FLIPFLOP2, GDC2_MODE_LCD);
+ VideoPortWritePortUchar((PUCHAR)GDC2_IO_o_MODE_FLIPFLOP2, GDC2_MODE_LINES_400);
+ VideoPortWritePortUchar((PUCHAR)GDC2_IO_o_MODE_FLIPFLOP2,
+ VideoModes[RequestedMode->RequestedMode].Clock1);
+ VideoPortWritePortUchar((PUCHAR)GDC2_IO_o_MODE_FLIPFLOP2,
+ VideoModes[RequestedMode->RequestedMode].Clock2);
+ VideoPortWritePortUchar((PUCHAR)GRAPH_IO_o_HORIZONTAL_SCAN_RATE,
+
VideoModes[RequestedMode->RequestedMode].HorizontalScanRate);
+ VideoPortWritePortUchar((PUCHAR)GDC2_IO_o_VIDEO_PAGE, 0);
+ VideoPortWritePortUchar((PUCHAR)GDC2_IO_o_VIDEO_PAGE_ACCESS, 0);
+
+ /* =========================== MASTER ============================ */
+
+ /* MASTER */
+ WRITE_GDC1_COMMAND(GDC_COMMAND_MASTER);
+
+ /* SYNC */
+ SyncParameters = VideoModes[RequestedMode->RequestedMode].TextSyncParameters;
+ SyncParameters.Flags = SYNC_DISPLAY_MODE_GRAPHICS_AND_CHARACTERS |
SYNC_VIDEO_FRAMING_NONINTERLACED |
+ SYNC_DRAW_ONLY_DURING_RETRACE_BLANKING |
SYNC_STATIC_RAM_NO_REFRESH;
+ WRITE_GDC1_COMMAND(GDC_COMMAND_SYNC_ON);
+ WRITE_GDC_SYNC((PUCHAR)GDC1_IO_o_PARAM, &SyncParameters);
+
+ /* CSRFORM */
+ CursorParameters.Show = FALSE;
+ CursorParameters.Blink = FALSE;
+ CursorParameters.BlinkRate = 12;
+ CursorParameters.LinesPerRow = 16;
+ CursorParameters.StartScanLine = 0;
+ CursorParameters.EndScanLine = 15;
+ WRITE_GDC1_COMMAND(GDC_COMMAND_CSRFORM);
+ WRITE_GDC_CSRFORM((PUCHAR)GDC1_IO_o_PARAM, &CursorParameters);
+
+ /* PITCH */
+ PitchParameters.WordsPerScanline = 80;
+ WRITE_GDC1_COMMAND(GDC_COMMAND_PITCH);
+ WRITE_GDC_PITCH((PUCHAR)GDC1_IO_o_PARAM, &PitchParameters);
+
+ /* PRAM */
+ RamParameters.StartingAddress = 0;
+ RamParameters.Length = 1023;
+ RamParameters.ImageBit = FALSE;
+ RamParameters.WideDisplay = FALSE;
+ WRITE_GDC1_COMMAND(GDC_COMMAND_PRAM);
+ WRITE_GDC_PRAM((PUCHAR)GDC1_IO_o_PARAM, &RamParameters);
+
+ /* ZOOM */
+ ZoomParameters.DisplayZoomFactor = 0;
+ ZoomParameters.WritingZoomFactor = 0;
+ WRITE_GDC1_COMMAND(GDC_COMMAND_ZOOM);
+ WRITE_GDC_ZOOM((PUCHAR)GDC1_IO_o_PARAM, &ZoomParameters);
+
+ /* CSRW */
+ CursorPosition.CursorAddress = 0;
+ CursorPosition.DotAddress = 0;
+ WRITE_GDC1_COMMAND(GDC_COMMAND_CSRW);
+ WRITE_GDC_CSRW((PUCHAR)GDC1_IO_o_PARAM, &CursorPosition);
+
+ /* START */
+ WRITE_GDC1_COMMAND(GDC_COMMAND_BCTRL_START);
+
+ /* ============================ SLAVE ============================ */
+
+ /* SLAVE */
+ WRITE_GDC2_COMMAND(GDC_COMMAND_SLAVE);
+
+ /* SYNC */
+ SyncParameters = VideoModes[RequestedMode->RequestedMode].VideoSyncParameters;
+ SyncParameters.Flags = SYNC_DISPLAY_MODE_GRAPHICS | SYNC_VIDEO_FRAMING_NONINTERLACED
|
+ SYNC_DRAW_DURING_ACTIVE_DISPLAY_TIME_AND_RETRACE_BLANKING |
+ SYNC_STATIC_RAM_NO_REFRESH;
+ WRITE_GDC2_COMMAND(GDC_COMMAND_SYNC_ON);
+ WRITE_GDC_SYNC((PUCHAR)GDC2_IO_o_PARAM, &SyncParameters);
+
+ /* CSRFORM */
+ CursorParameters.Show = FALSE;
+ CursorParameters.Blink = FALSE;
+ CursorParameters.BlinkRate = 0;
+ CursorParameters.LinesPerRow = 1;
+ CursorParameters.StartScanLine = 0;
+ CursorParameters.EndScanLine = 0;
+ WRITE_GDC2_COMMAND(GDC_COMMAND_CSRFORM);
+ WRITE_GDC_CSRFORM((PUCHAR)GDC2_IO_o_PARAM, &CursorParameters);
+
+ /* PITCH */
+ PitchParameters.WordsPerScanline = 80;
+ WRITE_GDC2_COMMAND(GDC_COMMAND_PITCH);
+ WRITE_GDC_PITCH((PUCHAR)GDC2_IO_o_PARAM, &PitchParameters);
+
+ /* PRAM */
+ RamParameters.StartingAddress = 0;
+ RamParameters.Length = 1023;
+ RamParameters.ImageBit = TRUE;
+ RamParameters.WideDisplay = FALSE;
+ WRITE_GDC2_COMMAND(GDC_COMMAND_PRAM);
+ WRITE_GDC_PRAM((PUCHAR)GDC2_IO_o_PARAM, &RamParameters);
+
+ /* ZOOM */
+ ZoomParameters.DisplayZoomFactor = 0;
+ ZoomParameters.WritingZoomFactor = 0;
+ WRITE_GDC2_COMMAND(GDC_COMMAND_ZOOM);
+ WRITE_GDC_ZOOM((PUCHAR)GDC2_IO_o_PARAM, &ZoomParameters);
+
+ /* CSRW */
+ CursorPosition.CursorAddress = 0;
+ CursorPosition.DotAddress = 0;
+ WRITE_GDC2_COMMAND(GDC_COMMAND_CSRW);
+ WRITE_GDC_CSRW((PUCHAR)GDC2_IO_o_PARAM, &CursorPosition);
+
+ /* Synchronize the master sync source */
+ TextSync();
+ TextSync();
+ TextSync();
+ TextSync();
+
+ /* START */
+ WRITE_GDC2_COMMAND(GDC_COMMAND_BCTRL_START);
+
+ /* 256 colors, packed pixel */
+ VideoPortWritePortUchar((PUCHAR)GDC2_IO_o_MODE_FLIPFLOP2, GDC2_EGC_FF_UNPROTECT);
+ VideoPortWritePortUchar((PUCHAR)GDC2_IO_o_MODE_FLIPFLOP2, GDC2_MODE_PEGC_ENABLE);
+ VideoPortWritePortUchar((PUCHAR)GDC2_IO_o_MODE_FLIPFLOP2,
+ VideoModes[RequestedMode->RequestedMode].Mem);
+ VideoPortWritePortUchar((PUCHAR)GDC2_IO_o_MODE_FLIPFLOP2, GDC2_EGC_FF_PROTECT);
+ VideoPortWriteRegisterUshort((PUSHORT)(DeviceExtension->PegcControlVa +
+ PEGC_MMIO_MODE), PEGC_MODE_PACKED);
+ VideoPortWriteRegisterUshort((PUSHORT)(DeviceExtension->PegcControlVa +
+ PEGC_MMIO_FRAMEBUFFER), PEGC_FB_MAP);
+
+ /* Select the video source */
+ RelayState = VideoPortReadPortUchar((PUCHAR)GRAPH_IO_i_RELAY) &
+ ~(GRAPH_RELAY_0 | GRAPH_RELAY_1);
+ RelayState |= GRAPH_VID_SRC_INTERNAL | GRAPH_SRC_GDC;
+ VideoPortWritePortUchar((PUCHAR)GRAPH_IO_o_RELAY, RelayState);
+
+ /* Unblank screen */
+ VideoPortWritePortUchar((PUCHAR)GDC1_IO_o_MODE_FLIPFLOP1,
GRAPH_MODE_DISPLAY_ENABLE);
+
+ DeviceExtension->CurrentMode = RequestedMode->RequestedMode;
+
+ return NO_ERROR;
+}
+
+VP_STATUS
+FASTCALL
+Pc98VidSetColorRegisters(
+ _In_ PVIDEO_CLUT ColorLookUpTable)
+{
+ USHORT Entry;
+
+ PAGED_CODE();
+
+ VideoDebugPrint((Trace, "%s()\n", __FUNCTION__));
+
+ if (ColorLookUpTable->NumEntries > PEGC_MAX_COLORS)
+ return ERROR_INVALID_PARAMETER;
+
+ for (Entry = ColorLookUpTable->FirstEntry;
+ Entry < ColorLookUpTable->FirstEntry + ColorLookUpTable->NumEntries;
+ ++Entry)
+ {
+ VideoPortWritePortUchar((PUCHAR)GDC2_IO_o_PALETTE_INDEX, Entry);
+ VideoPortWritePortUchar((PUCHAR)GDC2_IO_o_RED,
+ ColorLookUpTable->LookupTable[Entry].RgbArray.Red);
+ VideoPortWritePortUchar((PUCHAR)GDC2_IO_o_GREEN,
+ ColorLookUpTable->LookupTable[Entry].RgbArray.Green);
+ VideoPortWritePortUchar((PUCHAR)GDC2_IO_o_BLUE,
+ ColorLookUpTable->LookupTable[Entry].RgbArray.Blue);
+ }
+
+ return NO_ERROR;
+}
+
+VP_STATUS
+NTAPI
+Pc98VidGetPowerState(
+ _In_ PVOID HwDeviceExtension,
+ _In_ ULONG HwId,
+ _In_ PVIDEO_POWER_MANAGEMENT VideoPowerControl)
+{
+ PAGED_CODE();
+
+ VideoDebugPrint((Trace, "%s() Id %lX, State %x\n",
+ __FUNCTION__, HwId, VideoPowerControl->PowerState));
+
+ if (HwId == MONITOR_HW_ID || HwId == DISPLAY_ADAPTER_HW_ID)
+ {
+ switch (VideoPowerControl->PowerState)
+ {
+ case VideoPowerOn:
+ case VideoPowerStandBy:
+ case VideoPowerSuspend:
+ case VideoPowerOff:
+ case VideoPowerShutdown:
+ return NO_ERROR;
+ }
+ }
+
+ return ERROR_DEVICE_REINITIALIZATION_NEEDED;
+}
+
+VP_STATUS
+NTAPI
+Pc98VidSetPowerState(
+ _In_ PVOID HwDeviceExtension,
+ _In_ ULONG HwId,
+ _In_ PVIDEO_POWER_MANAGEMENT VideoPowerControl)
+{
+ UCHAR Dpms;
+
+ PAGED_CODE();
+
+ VideoDebugPrint((Trace, "%s() Id %lX, State %x\n",
+ __FUNCTION__, HwId, VideoPowerControl->PowerState));
+
+ if (HwId == MONITOR_HW_ID)
+ {
+ Dpms = VideoPortReadPortUchar((PUCHAR)GRAPH_IO_i_DPMS);
+
+ switch (VideoPowerControl->PowerState)
+ {
+ case VideoPowerOn:
+ /* Turn on HS/VS signals */
+ Dpms &= ~(GRAPH_DPMS_HSYNC_MASK | GRAPH_DPMS_VSYNC_MASK);
+ VideoPortWritePortUchar((PUCHAR)GRAPH_IO_o_DPMS, Dpms);
+
+ /* Unblank screen */
+ VideoPortWritePortUchar((PUCHAR)GDC1_IO_o_MODE_FLIPFLOP1,
+ GRAPH_MODE_DISPLAY_ENABLE);
+ break;
+
+ case VideoPowerStandBy:
+ /* Disable HS signal */
+ Dpms = (Dpms | GRAPH_DPMS_HSYNC_MASK) & ~GRAPH_DPMS_VSYNC_MASK;
+ VideoPortWritePortUchar((PUCHAR)GRAPH_IO_o_DPMS, Dpms);
+ break;
+
+ case VideoPowerSuspend:
+ /* Disable VS signal */
+ Dpms = (Dpms | GRAPH_DPMS_VSYNC_MASK) & ~GRAPH_DPMS_HSYNC_MASK;
+ VideoPortWritePortUchar((PUCHAR)GRAPH_IO_o_DPMS, Dpms);
+ break;
+
+ case VideoPowerOff:
+ case VideoPowerShutdown:
+ /* Turn off HS/VS signals */
+ Dpms |= GRAPH_DPMS_HSYNC_MASK | GRAPH_DPMS_VSYNC_MASK;
+ VideoPortWritePortUchar((PUCHAR)GRAPH_IO_o_DPMS, Dpms);
+
+ /* Blank screen */
+ VideoPortWritePortUchar((PUCHAR)GDC1_IO_o_MODE_FLIPFLOP1,
+ GRAPH_MODE_DISPLAY_DISABLE);
+ break;
+ }
+ }
+
+ return NO_ERROR;
+}
diff --git a/win32ss/drivers/miniport/pc98vid/ioctl.c
b/win32ss/drivers/miniport/pc98vid/ioctl.c
new file mode 100644
index 00000000000..2ab12e5c680
--- /dev/null
+++ b/win32ss/drivers/miniport/pc98vid/ioctl.c
@@ -0,0 +1,351 @@
+/*
+ * PROJECT: ReactOS framebuffer driver for NEC PC-98 series
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: I/O control handling
+ * COPYRIGHT: Copyright 2020 Dmitry Borisov (di.sean(a)protonmail.com)
+ */
+
+/* INCLUDES *******************************************************************/
+
+#include "pc98vid.h"
+
+/* GLOBALS ********************************************************************/
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, Pc98VidStartIO)
+#pragma alloc_text(PAGE, Pc98VidQueryMode)
+#pragma alloc_text(PAGE, Pc98VidQueryAvailModes)
+#pragma alloc_text(PAGE, Pc98VidQueryNumAvailModes)
+#pragma alloc_text(PAGE, Pc98VidQueryCurrentMode)
+#pragma alloc_text(PAGE, Pc98VidMapVideoMemory)
+#pragma alloc_text(PAGE, Pc98VidUnmapVideoMemory)
+#pragma alloc_text(PAGE, Pc98VidResetDevice)
+#pragma alloc_text(PAGE, Pc98VidGetChildState)
+#endif
+
+/* FUNCTIONS ******************************************************************/
+
+VOID
+FASTCALL
+Pc98VidQueryMode(
+ _In_ PHW_DEVICE_EXTENSION DeviceExtension,
+ _In_ ULONG ModeNumber,
+ _Out_ PVIDEO_MODE_INFORMATION VideoMode)
+{
+ PAGED_CODE();
+
+ VideoDebugPrint((Trace, "%s() Mode %d\n", __FUNCTION__, ModeNumber));
+
+ VideoMode->Length = sizeof(VIDEO_MODE_INFORMATION);
+ VideoMode->ModeIndex = ModeNumber;
+ VideoMode->VisScreenWidth = VideoModes[ModeNumber].HResolution;
+ VideoMode->VisScreenHeight = VideoModes[ModeNumber].VResolution;
+ VideoMode->ScreenStride = VideoModes[ModeNumber].HResolution;
+ VideoMode->NumberOfPlanes = 1;
+ VideoMode->BitsPerPlane = 8;
+ VideoMode->Frequency = VideoModes[ModeNumber].RefreshRate;
+ VideoMode->XMillimeter = 320;
+ VideoMode->YMillimeter = 240;
+ VideoMode->NumberRedBits =
+ VideoMode->NumberGreenBits =
+ VideoMode->NumberBlueBits = 8;
+ VideoMode->RedMask =
+ VideoMode->GreenMask =
+ VideoMode->BlueMask = 0;
+ VideoMode->AttributeFlags = VIDEO_MODE_COLOR | VIDEO_MODE_GRAPHICS |
+ VIDEO_MODE_PALETTE_DRIVEN | VIDEO_MODE_MANAGED_PALETTE;
+}
+
+VP_STATUS
+FASTCALL
+Pc98VidQueryAvailModes(
+ _In_ PHW_DEVICE_EXTENSION DeviceExtension,
+ _Out_ PVIDEO_MODE_INFORMATION ModeInformation,
+ _Out_ PSTATUS_BLOCK StatusBlock)
+{
+ UCHAR ModeNumber;
+ PVIDEO_MODE_INFORMATION VideoMode;
+
+ PAGED_CODE();
+
+ VideoDebugPrint((Trace, "%s()\n", __FUNCTION__));
+
+ for (ModeNumber = 0, VideoMode = ModeInformation;
+ ModeNumber < DeviceExtension->ModeCount;
+ ++ModeNumber, ++VideoMode)
+ {
+ Pc98VidQueryMode(DeviceExtension, ModeNumber, VideoMode);
+ }
+
+ StatusBlock->Information = sizeof(VIDEO_MODE_INFORMATION) *
DeviceExtension->ModeCount;
+
+ return NO_ERROR;
+}
+
+VP_STATUS
+FASTCALL
+Pc98VidQueryNumAvailModes(
+ _In_ PHW_DEVICE_EXTENSION DeviceExtension,
+ _Out_ PVIDEO_NUM_MODES Modes,
+ _Out_ PSTATUS_BLOCK StatusBlock)
+{
+ PAGED_CODE();
+
+ VideoDebugPrint((Trace, "%s()\n", __FUNCTION__));
+
+ Modes->NumModes = DeviceExtension->ModeCount;
+ Modes->ModeInformationLength = sizeof(VIDEO_MODE_INFORMATION);
+
+ StatusBlock->Information = sizeof(VIDEO_NUM_MODES);
+
+ return NO_ERROR;
+}
+
+VP_STATUS
+FASTCALL
+Pc98VidQueryCurrentMode(
+ _In_ PHW_DEVICE_EXTENSION DeviceExtension,
+ _Out_ PVIDEO_MODE_INFORMATION VideoMode,
+ _Out_ PSTATUS_BLOCK StatusBlock)
+{
+ PAGED_CODE();
+
+ VideoDebugPrint((Trace, "%s() Mode %d\n",
+ __FUNCTION__, DeviceExtension->CurrentMode));
+
+ Pc98VidQueryMode(DeviceExtension, DeviceExtension->CurrentMode, VideoMode);
+
+ StatusBlock->Information = sizeof(VIDEO_MODE_INFORMATION);
+
+ return NO_ERROR;
+}
+
+VP_STATUS
+FASTCALL
+Pc98VidMapVideoMemory(
+ _In_ PHW_DEVICE_EXTENSION DeviceExtension,
+ _In_ PVIDEO_MEMORY RequestedAddress,
+ _Out_ PVIDEO_MEMORY_INFORMATION MapInformation,
+ _Out_ PSTATUS_BLOCK StatusBlock)
+{
+ VP_STATUS Status;
+ ULONG inIoSpace = VIDEO_MEMORY_SPACE_MEMORY;
+
+ PAGED_CODE();
+
+ VideoDebugPrint((Trace, "%s()\n", __FUNCTION__));
+
+ MapInformation->VideoRamBase = RequestedAddress->RequestedVirtualAddress;
+ MapInformation->VideoRamLength = DeviceExtension->FrameBufferLength;
+
+ Status = VideoPortMapMemory(DeviceExtension,
+ DeviceExtension->FrameBuffer,
+ &MapInformation->VideoRamLength,
+ &inIoSpace,
+ &MapInformation->VideoRamBase);
+ if (Status != NO_ERROR)
+ {
+ VideoDebugPrint((Error, "%s() Failed to map framebuffer memory\n",
+ __FUNCTION__));
+ }
+ else
+ {
+ MapInformation->FrameBufferBase = MapInformation->VideoRamBase;
+ MapInformation->FrameBufferLength = MapInformation->VideoRamLength;
+
+ StatusBlock->Information = sizeof(VIDEO_MEMORY_INFORMATION);
+ }
+
+ return Status;
+}
+
+VP_STATUS
+FASTCALL
+Pc98VidUnmapVideoMemory(
+ _In_ PHW_DEVICE_EXTENSION DeviceExtension,
+ _In_ PVIDEO_MEMORY VideoMemory)
+{
+ PAGED_CODE();
+
+ VideoDebugPrint((Trace, "%s()\n", __FUNCTION__));
+
+ return VideoPortUnmapMemory(DeviceExtension,
+ VideoMemory->RequestedVirtualAddress,
+ NULL);
+}
+
+VP_STATUS
+FASTCALL
+Pc98VidResetDevice(VOID)
+{
+ PAGED_CODE();
+
+ VideoDebugPrint((Trace, "%s()\n", __FUNCTION__));
+
+ return NO_ERROR;
+}
+
+VP_STATUS
+FASTCALL
+Pc98VidGetChildState(
+ _In_ PHW_DEVICE_EXTENSION DeviceExtension,
+ _In_ PULONG ChildIndex,
+ _Out_ PULONG ChildState,
+ _Out_ PSTATUS_BLOCK StatusBlock)
+{
+ PAGED_CODE();
+
+ VideoDebugPrint((Trace, "%s() Child %d\n", __FUNCTION__, *ChildIndex));
+
+ *ChildState = VIDEO_CHILD_ACTIVE;
+
+ StatusBlock->Information = sizeof(ULONG);
+
+ return NO_ERROR;
+}
+
+BOOLEAN
+NTAPI
+Pc98VidStartIO(
+ _In_ PVOID HwDeviceExtension,
+ _Inout_ PVIDEO_REQUEST_PACKET RequestPacket)
+{
+ VP_STATUS Status;
+
+ PAGED_CODE();
+
+ VideoDebugPrint((Trace, "%s() IOCTL 0x%lX\n",
+ __FUNCTION__, RequestPacket->IoControlCode));
+
+ switch (RequestPacket->IoControlCode)
+ {
+ case IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES:
+ {
+ if (RequestPacket->OutputBufferLength < sizeof(VIDEO_NUM_MODES))
+ {
+ Status = ERROR_INSUFFICIENT_BUFFER;
+ break;
+ }
+
+ Status = Pc98VidQueryNumAvailModes((PHW_DEVICE_EXTENSION)HwDeviceExtension,
+
(PVIDEO_NUM_MODES)RequestPacket->OutputBuffer,
+ RequestPacket->StatusBlock);
+ break;
+ }
+
+ case IOCTL_VIDEO_QUERY_AVAIL_MODES:
+ {
+ if (RequestPacket->OutputBufferLength < sizeof(VIDEO_MODE_INFORMATION)
*
+ ((PHW_DEVICE_EXTENSION)HwDeviceExtension)->ModeCount)
+ {
+ Status = ERROR_INSUFFICIENT_BUFFER;
+ break;
+ }
+
+ Status = Pc98VidQueryAvailModes((PHW_DEVICE_EXTENSION)HwDeviceExtension,
+
(PVIDEO_MODE_INFORMATION)RequestPacket->OutputBuffer,
+ RequestPacket->StatusBlock);
+ break;
+ }
+
+ case IOCTL_VIDEO_SET_CURRENT_MODE:
+ {
+ if (RequestPacket->InputBufferLength < sizeof(VIDEO_MODE))
+ {
+ Status = ERROR_INSUFFICIENT_BUFFER;
+ break;
+ }
+
+ Status = Pc98VidSetCurrentMode((PHW_DEVICE_EXTENSION)HwDeviceExtension,
+ (PVIDEO_MODE)RequestPacket->InputBuffer);
+ break;
+ }
+
+ case IOCTL_VIDEO_QUERY_CURRENT_MODE:
+ {
+ if (RequestPacket->OutputBufferLength <
sizeof(VIDEO_MODE_INFORMATION))
+ {
+ Status = ERROR_INSUFFICIENT_BUFFER;
+ break;
+ }
+
+ Status = Pc98VidQueryCurrentMode((PHW_DEVICE_EXTENSION)HwDeviceExtension,
+
(PVIDEO_MODE_INFORMATION)RequestPacket->OutputBuffer,
+ RequestPacket->StatusBlock);
+ break;
+ }
+
+ case IOCTL_VIDEO_MAP_VIDEO_MEMORY:
+ {
+ if (RequestPacket->InputBufferLength < sizeof(VIDEO_MEMORY) ||
+ RequestPacket->OutputBufferLength <
sizeof(VIDEO_MEMORY_INFORMATION))
+ {
+ Status = ERROR_INSUFFICIENT_BUFFER;
+ break;
+ }
+
+ Status = Pc98VidMapVideoMemory((PHW_DEVICE_EXTENSION)HwDeviceExtension,
+ (PVIDEO_MEMORY)RequestPacket->InputBuffer,
+
(PVIDEO_MEMORY_INFORMATION)RequestPacket->OutputBuffer,
+ RequestPacket->StatusBlock);
+ break;
+ }
+
+ case IOCTL_VIDEO_UNMAP_VIDEO_MEMORY:
+ {
+ if (RequestPacket->InputBufferLength < sizeof(VIDEO_MEMORY))
+ {
+ Status = ERROR_INSUFFICIENT_BUFFER;
+ break;
+ }
+
+ Status = Pc98VidUnmapVideoMemory((PHW_DEVICE_EXTENSION)HwDeviceExtension,
+
(PVIDEO_MEMORY)RequestPacket->InputBuffer);
+ break;
+ }
+
+ case IOCTL_VIDEO_RESET_DEVICE:
+ {
+ Status = Pc98VidResetDevice();
+ break;
+ }
+
+ case IOCTL_VIDEO_SET_COLOR_REGISTERS:
+ {
+ if (RequestPacket->InputBufferLength < sizeof(VIDEO_CLUT))
+ {
+ Status = ERROR_INSUFFICIENT_BUFFER;
+ break;
+ }
+
+ Status =
Pc98VidSetColorRegisters((PVIDEO_CLUT)RequestPacket->InputBuffer);
+ break;
+ }
+
+ case IOCTL_VIDEO_GET_CHILD_STATE:
+ {
+ if (RequestPacket->InputBufferLength < sizeof(ULONG) ||
+ RequestPacket->OutputBufferLength < sizeof(ULONG))
+ {
+ Status = ERROR_INSUFFICIENT_BUFFER;
+ break;
+ }
+
+ Status = Pc98VidGetChildState((PHW_DEVICE_EXTENSION)HwDeviceExtension,
+ (PULONG)RequestPacket->InputBuffer,
+ (PULONG)RequestPacket->OutputBuffer,
+ RequestPacket->StatusBlock);
+ break;
+ }
+
+ default:
+ Status = ERROR_INVALID_FUNCTION;
+ }
+
+ if (Status != NO_ERROR)
+ VideoDebugPrint((Trace, "%s() Failed 0x%lX\n", __FUNCTION__, Status));
+
+ RequestPacket->StatusBlock->Status = Status;
+
+ return TRUE;
+}
diff --git a/win32ss/drivers/miniport/pc98vid/pc98disp.inf
b/win32ss/drivers/miniport/pc98vid/pc98disp.inf
new file mode 100644
index 00000000000..81a8608625a
--- /dev/null
+++ b/win32ss/drivers/miniport/pc98vid/pc98disp.inf
@@ -0,0 +1,62 @@
+; pc98disp.inf
+;
+; Installation file for the display adapter on PC-9821
+;
+[Version]
+Signature = "$Windows NT$"
+;Signature = "$ReactOS$"
+LayoutFile = layout.inf
+Class = Display
+ClassGUID = {4D36E968-E325-11CE-BFC1-08002BE10318}
+Provider = %ReactOS%
+DriverVer = 08/11/2020,5.2
+
+[DestinationDirs]
+DefaultDestDir = 12
+
+[Manufacturer]
+%NecMfg% = NecMfg
+
+[NecMfg]
+%pc98vid.DeviceDesc% = Pc98Vid_Inst
+%PCI\VEN_1033&DEV_0009.DeviceDesc% = Pc98Vid_Inst,PCI\VEN_1033&DEV_0009
+
+;---------------------------- PC98VID DRIVER ----------------------------
+
+[Pc98Vid_Inst.NT]
+CopyFiles = pc98vid_CopyFiles.NT
+
+[pc98vid_CopyFiles.NT]
+vga.sys
+
+[Pc98Vid_Inst.NT.Services]
+AddService = vga, 0x00000002, vga_Service_Inst
+
+[vga_Service_Inst]
+ServiceType = 1
+StartType = 1
+ErrorControl = 0
+ServiceBinary = %12%\vga.sys
+LoadOrderGroup = Video
+
+[vga.SoftwareSettings]
+AddReg = vga_SoftwareDeviceSettings
+
+[vga_SoftwareDeviceSettings]
+HKR,, InstalledDisplayDrivers, 0x00010000, framebuf
+HKR,, VgaCompatible, 0x00010001, 1
+HKR,, VideoDebugLevel, 0x00010001, 0
+
+;-------------------------------- STRINGS -------------------------------
+
+[Strings]
+ReactOS = "ReactOS Team"
+
+NecMfg = "NEC"
+PCI\VEN_1033&DEV_0009.DeviceDesc = "NEC PCI to Core-Graph Bridge"
+pc98vid.DeviceDesc = "Graphic controller for PC-9821"
+
+[Strings.0419]
+ReactOS = "Команда ReactOS"
+
+pc98vid.DeviceDesc = "Графический контроллер для PC-9821"
diff --git a/win32ss/drivers/miniport/pc98vid/pc98vid.c
b/win32ss/drivers/miniport/pc98vid/pc98vid.c
new file mode 100644
index 00000000000..fd7fa07523c
--- /dev/null
+++ b/win32ss/drivers/miniport/pc98vid/pc98vid.c
@@ -0,0 +1,244 @@
+/*
+ * PROJECT: ReactOS framebuffer driver for NEC PC-98 series
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Miniport driver entrypoint
+ * COPYRIGHT: Copyright 2020 Dmitry Borisov (di.sean(a)protonmail.com)
+ */
+
+/* INCLUDES *******************************************************************/
+
+#include "pc98vid.h"
+
+/* GLOBALS ********************************************************************/
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(INIT, DriverEntry)
+#pragma alloc_text(PAGE, Pc98VidFindAdapter)
+#pragma alloc_text(PAGE, Pc98VidInitialize)
+#pragma alloc_text(PAGE, Pc98VidGetVideoChildDescriptor)
+#endif
+
+const VIDEOMODE VideoModes[] =
+{
+ {640, 480, GRAPH_HF_31KHZ, GDC2_CLOCK1_5MHZ, GDC2_CLOCK2_5MHZ,
+ GDC2_MODE_LINES_800, 60,
+ {0, 80, 12, 2, 4, 4, 6, 480, 37}, {0, 80, 12, 2, 4, 132, 6, 480, 37}}
+};
+
+static VIDEO_ACCESS_RANGE LegacyRangeList[] =
+{
+ { {{0x60, 0}}, 0x00000001, 1, 1, 1, 0 },
+ { {{0x62, 0}}, 0x00000001, 1, 1, 1, 0 },
+ { {{0x68, 0}}, 0x00000001, 1, 1, 1, 0 },
+ { {{0x6A, 0}}, 0x00000001, 1, 1, 1, 0 },
+ { {{0x7C, 0}}, 0x00000001, 1, 1, 1, 0 },
+ { {{0xA0, 0}}, 0x00000001, 1, 1, 1, 0 },
+ { {{0xA2, 0}}, 0x00000001, 1, 1, 1, 0 },
+ { {{0xA4, 0}}, 0x00000001, 1, 1, 1, 0 },
+ { {{0xA6, 0}}, 0x00000001, 1, 1, 1, 0 },
+ { {{0xA8, 0}}, 0x00000001, 1, 1, 1, 0 },
+ { {{0xAA, 0}}, 0x00000001, 1, 1, 1, 0 },
+ { {{0xAC, 0}}, 0x00000001, 1, 1, 1, 0 },
+ { {{0xAE, 0}}, 0x00000001, 1, 1, 1, 0 },
+ { {{0x9A0, 0}}, 0x00000001, 1, 1, 1, 0 },
+ { {{0x9A2, 0}}, 0x00000001, 1, 1, 1, 0 },
+ { {{0x9A8, 0}}, 0x00000001, 1, 1, 1, 0 },
+ { {{0xFAC, 0}}, 0x00000001, 1, 1, 1, 0 },
+ { {{VRAM_NORMAL_PLANE_I, 0}}, PEGC_CONTROL_SIZE, 0, 0, 1, 0 },
+ { {{PEGC_FRAMEBUFFER_PACKED, 0}}, PEGC_FRAMEBUFFER_SIZE, 0, 0, 1, 0 }
+};
+#define CONTROL_RANGE_INDEX 17
+#define FRAMEBUFFER_RANGE_INDEX 18
+
+/* FUNCTIONS ******************************************************************/
+
+VP_STATUS
+NTAPI
+Pc98VidFindAdapter(
+ _In_ PVOID HwDeviceExtension,
+ _In_opt_ PVOID HwContext,
+ _In_opt_ PWSTR ArgumentString,
+ _Inout_ PVIDEO_PORT_CONFIG_INFO ConfigInfo,
+ _Out_ PUCHAR Again)
+{
+ VP_STATUS Status;
+ PHW_DEVICE_EXTENSION DeviceExtension = HwDeviceExtension;
+ ULONG inIoSpace = VIDEO_MEMORY_SPACE_MEMORY;
+ static WCHAR AdapterChipType[] = L"Onboard";
+ static WCHAR AdapterDacType[] = L"8 bit";
+ static WCHAR AdapterString[] = L"PEGC";
+
+ PAGED_CODE();
+
+ VideoDebugPrint((Trace, "%s()\n", __FUNCTION__));
+
+ if (ConfigInfo->Length < sizeof(VIDEO_PORT_CONFIG_INFO))
+ return ERROR_INVALID_PARAMETER;
+
+ Status = VideoPortVerifyAccessRanges(DeviceExtension,
+ RTL_NUMBER_OF(LegacyRangeList),
+ LegacyRangeList);
+ if (Status != NO_ERROR)
+ {
+ VideoDebugPrint((Error, "%s() Resource conflict was found\n",
__FUNCTION__));
+
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ DeviceExtension->PegcControl = LegacyRangeList[CONTROL_RANGE_INDEX].RangeStart;
+ DeviceExtension->PegcControlLength =
LegacyRangeList[CONTROL_RANGE_INDEX].RangeLength;
+ DeviceExtension->FrameBuffer =
LegacyRangeList[FRAMEBUFFER_RANGE_INDEX].RangeStart;
+ DeviceExtension->FrameBufferLength =
LegacyRangeList[FRAMEBUFFER_RANGE_INDEX].RangeLength;
+
+ Status = VideoPortMapMemory(DeviceExtension,
+ DeviceExtension->PegcControl,
+ &DeviceExtension->PegcControlLength,
+ &inIoSpace,
+ (PVOID)&DeviceExtension->PegcControlVa);
+ if (Status != NO_ERROR)
+ {
+ VideoDebugPrint((Error, "%s() Failed to map control memory\n",
__FUNCTION__));
+
+ VideoPortVerifyAccessRanges(DeviceExtension, 0, NULL);
+
+ return ERROR_DEV_NOT_EXIST;
+ }
+
+ if (!HasPegcController(DeviceExtension))
+ {
+ VideoDebugPrint((Error, "%s() Unsupported hardware\n", __FUNCTION__));
+
+ VideoPortVerifyAccessRanges(DeviceExtension, 0, NULL);
+ VideoPortUnmapMemory(DeviceExtension,
+ (PVOID)DeviceExtension->PegcControlVa,
+ NULL);
+
+ return ERROR_DEV_NOT_EXIST;
+ }
+
+ /* Not VGA-compatible */
+ ConfigInfo->NumEmulatorAccessEntries = 0;
+ ConfigInfo->EmulatorAccessEntries = NULL;
+ ConfigInfo->EmulatorAccessEntriesContext = 0;
+ ConfigInfo->HardwareStateSize = 0;
+ ConfigInfo->VdmPhysicalVideoMemoryAddress.QuadPart = 0;
+ ConfigInfo->VdmPhysicalVideoMemoryLength = 0;
+
+ VideoPortSetRegistryParameters(DeviceExtension,
+ L"HardwareInformation.ChipType",
+ AdapterChipType,
+ sizeof(AdapterChipType));
+ VideoPortSetRegistryParameters(DeviceExtension,
+ L"HardwareInformation.DacType",
+ AdapterDacType,
+ sizeof(AdapterDacType));
+ VideoPortSetRegistryParameters(DeviceExtension,
+ L"HardwareInformation.MemorySize",
+ &DeviceExtension->FrameBufferLength,
+ sizeof(ULONG));
+ VideoPortSetRegistryParameters(DeviceExtension,
+ L"HardwareInformation.AdapterString",
+ AdapterString,
+ sizeof(AdapterString));
+
+ return NO_ERROR;
+}
+
+BOOLEAN
+NTAPI
+Pc98VidInitialize(
+ _In_ PVOID HwDeviceExtension)
+{
+ PHW_DEVICE_EXTENSION DeviceExtension = HwDeviceExtension;
+
+ PAGED_CODE();
+
+ VideoDebugPrint((Trace, "%s()\n", __FUNCTION__));
+
+ DeviceExtension->ModeCount = RTL_NUMBER_OF(VideoModes);
+ DeviceExtension->MonitorCount = 1;
+
+ return TRUE;
+}
+
+VP_STATUS
+NTAPI
+Pc98VidGetVideoChildDescriptor(
+ _In_ PVOID HwDeviceExtension,
+ _In_ PVIDEO_CHILD_ENUM_INFO ChildEnumInfo,
+ _Out_ PVIDEO_CHILD_TYPE VideoChildType,
+ _Out_ PUCHAR pChildDescriptor,
+ _Out_ PULONG UId,
+ _Out_ PULONG pUnused)
+{
+ PHW_DEVICE_EXTENSION DeviceExtension = HwDeviceExtension;
+
+ UNREFERENCED_PARAMETER(pChildDescriptor);
+
+ PAGED_CODE();
+
+ VideoDebugPrint((Trace, "%s() Index %d\n",
+ __FUNCTION__, ChildEnumInfo->ChildIndex));
+
+ *pUnused = 0;
+
+ if (ChildEnumInfo->ChildIndex > 0 &&
+ ChildEnumInfo->ChildIndex <= DeviceExtension->MonitorCount)
+ {
+ *VideoChildType = Monitor;
+ *UId = MONITOR_HW_ID;
+
+ return VIDEO_ENUM_MORE_DEVICES;
+ }
+
+ return ERROR_NO_MORE_DEVICES;
+}
+
+ULONG
+NTAPI
+DriverEntry(
+ _In_ PVOID Context1,
+ _In_ PVOID Context2)
+{
+ VIDEO_HW_INITIALIZATION_DATA InitData;
+ ULONG Status;
+ BOOLEAN IsLiveCd;
+
+ VideoDebugPrint((Trace, "(%s:%d) %s()\n",
+ __FILE__, __LINE__, __FUNCTION__));
+
+ // FIXME: Detect IsLiveCd
+ IsLiveCd = TRUE;
+
+ VideoPortZeroMemory(&InitData, sizeof(VIDEO_HW_INITIALIZATION_DATA));
+ InitData.HwInitDataSize = sizeof(VIDEO_HW_INITIALIZATION_DATA);
+ InitData.HwDeviceExtensionSize = sizeof(HW_DEVICE_EXTENSION);
+ InitData.HwFindAdapter = Pc98VidFindAdapter;
+ InitData.HwInitialize = Pc98VidInitialize;
+ InitData.HwStartIO = Pc98VidStartIO;
+ /*
+ * On LiveCD, we expect to see the initialized video
+ * before starting the device enumeration,
+ * so we should mark the driver as non-PnP miniport.
+ */
+ if (!IsLiveCd)
+ {
+ InitData.HwGetPowerState = Pc98VidGetPowerState;
+ InitData.HwSetPowerState = Pc98VidSetPowerState;
+ InitData.HwGetVideoChildDescriptor = Pc98VidGetVideoChildDescriptor;
+ }
+
+ InitData.HwLegacyResourceList = LegacyRangeList;
+ InitData.HwLegacyResourceCount = RTL_NUMBER_OF(LegacyRangeList);
+
+ InitData.AdapterInterfaceType = Isa;
+
+ Status = VideoPortInitialize(Context1, Context2, &InitData, NULL);
+ if (!NT_SUCCESS(Status))
+ {
+ VideoDebugPrint((Error, "(%s:%d) %s() Initialization failed 0x%lX\n",
+ __FILE__, __LINE__, __FUNCTION__, Status));
+ }
+
+ return Status;
+}
diff --git a/win32ss/drivers/miniport/pc98vid/pc98vid.h
b/win32ss/drivers/miniport/pc98vid/pc98vid.h
new file mode 100644
index 00000000000..fd2d8e29b05
--- /dev/null
+++ b/win32ss/drivers/miniport/pc98vid/pc98vid.h
@@ -0,0 +1,167 @@
+/*
+ * PROJECT: ReactOS framebuffer driver for NEC PC-98 series
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Miniport driver header file
+ * COPYRIGHT: Copyright 2020 Dmitry Borisov (di.sean(a)protonmail.com)
+ */
+
+#ifndef _PC98VID_PCH_
+#define _PC98VID_PCH_
+
+#include <ntdef.h>
+#include <dderror.h>
+#include <devioctl.h>
+#include <miniport.h>
+#include <video.h>
+#include <debug.h>
+
+#undef WRITE_PORT_UCHAR
+#undef READ_PORT_UCHAR
+#define WRITE_PORT_UCHAR(p, d) VideoPortWritePortUchar(p, d)
+#define READ_PORT_UCHAR(p) VideoPortReadPortUchar(p)
+#include <drivers/pc98/video.h>
+
+#define MONITOR_HW_ID 0x1033FACE /* Dummy */
+
+typedef struct _VIDEOMODE
+{
+ USHORT HResolution;
+ USHORT VResolution;
+ UCHAR HorizontalScanRate;
+ UCHAR Clock1;
+ UCHAR Clock2;
+ UCHAR Mem;
+ UCHAR RefreshRate;
+ SYNCPARAM TextSyncParameters;
+ SYNCPARAM VideoSyncParameters;
+} VIDEOMODE, *PVIDEOMODE;
+
+typedef struct _HW_DEVICE_EXTENSION
+{
+ UCHAR MonitorCount;
+ UCHAR ModeCount;
+ UCHAR CurrentMode;
+ PHYSICAL_ADDRESS PegcControl;
+ ULONG PegcControlLength;
+ ULONG_PTR PegcControlVa;
+ PHYSICAL_ADDRESS FrameBuffer;
+ ULONG FrameBufferLength;
+} HW_DEVICE_EXTENSION, *PHW_DEVICE_EXTENSION;
+
+VP_STATUS
+NTAPI
+Pc98VidFindAdapter(
+ _In_ PVOID HwDeviceExtension,
+ _In_opt_ PVOID HwContext,
+ _In_opt_ PWSTR ArgumentString,
+ _Inout_ PVIDEO_PORT_CONFIG_INFO ConfigInfo,
+ _Out_ PUCHAR Again);
+
+BOOLEAN
+NTAPI
+HasPegcController(
+ _In_ PHW_DEVICE_EXTENSION DeviceExtension);
+
+BOOLEAN
+NTAPI
+Pc98VidInitialize(
+ _In_ PVOID HwDeviceExtension);
+
+VP_STATUS
+NTAPI
+Pc98VidGetVideoChildDescriptor(
+ _In_ PVOID HwDeviceExtension,
+ _In_ PVIDEO_CHILD_ENUM_INFO ChildEnumInfo,
+ _Out_ PVIDEO_CHILD_TYPE VideoChildType,
+ _Out_ PUCHAR pChildDescriptor,
+ _Out_ PULONG UId,
+ _Out_ PULONG pUnused);
+
+BOOLEAN
+NTAPI
+Pc98VidStartIO(
+ _In_ PVOID HwDeviceExtension,
+ _Inout_ PVIDEO_REQUEST_PACKET RequestPacket);
+
+VOID
+FASTCALL
+Pc98VidQueryMode(
+ _In_ PHW_DEVICE_EXTENSION DeviceExtension,
+ _In_ ULONG ModeNumber,
+ _Out_ PVIDEO_MODE_INFORMATION VideoMode);
+
+VP_STATUS
+FASTCALL
+Pc98VidQueryAvailModes(
+ _In_ PHW_DEVICE_EXTENSION DeviceExtension,
+ _Out_ PVIDEO_MODE_INFORMATION ModeInformation,
+ _Out_ PSTATUS_BLOCK StatusBlock);
+
+VP_STATUS
+FASTCALL
+Pc98VidQueryNumAvailModes(
+ _In_ PHW_DEVICE_EXTENSION DeviceExtension,
+ _Out_ PVIDEO_NUM_MODES Modes,
+ _Out_ PSTATUS_BLOCK StatusBlock);
+
+VP_STATUS
+FASTCALL
+Pc98VidSetCurrentMode(
+ _In_ PHW_DEVICE_EXTENSION DeviceExtension,
+ _In_ PVIDEO_MODE RequestedMode);
+
+VP_STATUS
+FASTCALL
+Pc98VidQueryCurrentMode(
+ _In_ PHW_DEVICE_EXTENSION DeviceExtension,
+ _Out_ PVIDEO_MODE_INFORMATION VideoMode,
+ _Out_ PSTATUS_BLOCK StatusBlock);
+
+VP_STATUS
+FASTCALL
+Pc98VidMapVideoMemory(
+ _In_ PHW_DEVICE_EXTENSION DeviceExtension,
+ _In_ PVIDEO_MEMORY RequestedAddress,
+ _Out_ PVIDEO_MEMORY_INFORMATION MapInformation,
+ _Out_ PSTATUS_BLOCK StatusBlock);
+
+VP_STATUS
+FASTCALL
+Pc98VidUnmapVideoMemory(
+ _In_ PHW_DEVICE_EXTENSION DeviceExtension,
+ _In_ PVIDEO_MEMORY VideoMemory);
+
+VP_STATUS
+FASTCALL
+Pc98VidResetDevice(VOID);
+
+VP_STATUS
+FASTCALL
+Pc98VidSetColorRegisters(
+ _In_ PVIDEO_CLUT ColorLookUpTable);
+
+VP_STATUS
+FASTCALL
+Pc98VidGetChildState(
+ _In_ PHW_DEVICE_EXTENSION DeviceExtension,
+ _In_ PULONG ChildIndex,
+ _Out_ PULONG ChildState,
+ _Out_ PSTATUS_BLOCK StatusBlock);
+
+VP_STATUS
+NTAPI
+Pc98VidGetPowerState(
+ _In_ PVOID HwDeviceExtension,
+ _In_ ULONG HwId,
+ _In_ PVIDEO_POWER_MANAGEMENT VideoPowerControl);
+
+VP_STATUS
+NTAPI
+Pc98VidSetPowerState(
+ _In_ PVOID HwDeviceExtension,
+ _In_ ULONG HwId,
+ _In_ PVIDEO_POWER_MANAGEMENT VideoPowerControl);
+
+extern const VIDEOMODE VideoModes[];
+
+#endif /* _PC98VID_PCH_ */
diff --git a/win32ss/drivers/miniport/pc98vid/pc98vid.rc
b/win32ss/drivers/miniport/pc98vid/pc98vid.rc
new file mode 100644
index 00000000000..17cc7729468
--- /dev/null
+++ b/win32ss/drivers/miniport/pc98vid/pc98vid.rc
@@ -0,0 +1,5 @@
+#define REACTOS_VERSION_DLL
+#define REACTOS_STR_FILE_DESCRIPTION "NEC PC-9821 Video Miniport Driver"
+#define REACTOS_STR_INTERNAL_NAME "vga"
+#define REACTOS_STR_ORIGINAL_FILENAME "vga.sys"
+#include <reactos/version.rc>
diff --git a/win32ss/drivers/miniport/pc98vid/pc98vid_reg.inf
b/win32ss/drivers/miniport/pc98vid/pc98vid_reg.inf
new file mode 100644
index 00000000000..becce43fdff
--- /dev/null
+++ b/win32ss/drivers/miniport/pc98vid/pc98vid_reg.inf
@@ -0,0 +1,10 @@
+; pc98vid video miniport driver
+[AddReg]
+HKLM,"SYSTEM\CurrentControlSet\Services\vga","ErrorControl",0x00010001,0x00000000
+HKLM,"SYSTEM\CurrentControlSet\Services\vga","Group",0x00000000,"Video
Save"
+HKLM,"SYSTEM\CurrentControlSet\Services\vga","ImagePath",0x00020000,"system32\drivers\vga.sys"
+HKLM,"SYSTEM\CurrentControlSet\Services\vga","Start",0x00010001,0x00000004
+HKLM,"SYSTEM\CurrentControlSet\Services\vga","Type",0x00010001,0x00000001
+
+HKLM,"SYSTEM\CurrentControlSet\Hardware
Profiles\Current\System\CurrentControlSet\Services\vga\Device0","InstalledDisplayDrivers",0x00010000,"framebuf"
+HKLM,"SYSTEM\CurrentControlSet\Hardware
Profiles\Current\System\CurrentControlSet\Services\vga\Device0","VgaCompatible",0x00010001,1