https://git.reactos.org/?p=reactos.git;a=commitdiff;h=27cd9eaf1a3c494cb79b2a0826261466606723e1

commit 27cd9eaf1a3c494cb79b2a0826261466606723e1
Author:     Dmitry Borisov <[email protected]>
AuthorDate: Tue Aug 11 18:39:42 2020 +0600
Commit:     Stanislav Motylkov <[email protected]>
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 ([email protected])
+ */
+
+/* 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 ([email protected])
+ */
+
+/* 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 ([email protected])
+ */
+
+/* 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 ([email protected])
+ */
+
+#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

Reply via email to