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

commit c14cc22bfde4d775ea78dd9d43193b20fdb1afa6
Author:     Dmitry Borisov <[email protected]>
AuthorDate: Sun Nov 10 22:47:39 2019 +0600
Commit:     Hermès BÉLUSCA - MAÏTO <[email protected]>
CommitDate: Sat Mar 7 00:52:40 2020 +0100

    [FREELDR] Add ARC-emulation support for NEC PC-98 series
    
    - Add ARC-emulation support for NEC PC-98 series
    - Add global definition for PC-98 port into CMakeLists.txt
    - Add floppy verison of freeldr.ini for PC-98 CD boot
---
 CMakeLists.txt                                     |    2 +
 boot/bootdata/floppy_pc98.ini                      |   56 +
 boot/freeldr/freeldr/CMakeLists.txt                |   14 +-
 boot/freeldr/freeldr/arch/i386/drivemap.c          |   10 +
 boot/freeldr/freeldr/arch/i386/hwapm.c             |   11 +-
 boot/freeldr/freeldr/arch/i386/pc98/machpc98.c     |  162 +++
 boot/freeldr/freeldr/arch/i386/pc98/pc98beep.c     |   27 +
 boot/freeldr/freeldr/arch/i386/pc98/pc98cons.c     |  122 ++
 boot/freeldr/freeldr/arch/i386/pc98/pc98disk.c     |  902 ++++++++++++++
 boot/freeldr/freeldr/arch/i386/pc98/pc98hw.c       | 1292 ++++++++++++++++++++
 boot/freeldr/freeldr/arch/i386/pc98/pc98mem.c      |  105 ++
 boot/freeldr/freeldr/arch/i386/pc98/pc98rtc.c      |   42 +
 boot/freeldr/freeldr/arch/i386/pc98/pc98video.c    |  339 +++++
 .../freeldr/freeldr/arch/realmode/helpers_pc98.inc |  245 ++++
 boot/freeldr/freeldr/arch/realmode/i386.S          |    4 +
 boot/freeldr/freeldr/arch/realmode/int386.inc      |    6 +
 boot/freeldr/freeldr/include/arch/i386/machpc98.h  |  153 +++
 boot/freeldr/freeldr/include/freeldr.h             |   11 +-
 boot/freeldr/freeldr/include/keycodes.h            |   50 +-
 boot/freeldr/freeldr/miscboot.c                    |   24 +-
 sdk/include/reactos/drivers/pc98/fdc.h             |   26 +
 sdk/include/reactos/drivers/pc98/video.h           |  279 +++++
 22 files changed, 3853 insertions(+), 29 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7aa199fdaf9..ea234693986 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -161,6 +161,8 @@ else()
         add_definitions(-D_X86_ -D__i386__ -Di386)
         if(SARCH STREQUAL "xbox")
             add_definitions(-DSARCH_XBOX)
+        elseif(SARCH STREQUAL "pc98")
+            add_definitions(-DSARCH_PC98)
         endif()
     elseif(ARCH STREQUAL "amd64")
         add_definitions(-D_M_AMD64 -D_AMD64_ -D__x86_64__ -D_WIN64)
diff --git a/boot/bootdata/floppy_pc98.ini b/boot/bootdata/floppy_pc98.ini
new file mode 100644
index 00000000000..0466754d765
--- /dev/null
+++ b/boot/bootdata/floppy_pc98.ini
@@ -0,0 +1,56 @@
+[FREELOADER]
+DefaultOS=LiveCD_Debug
+TimeOut=5
+
+[Display]
+TitleText=ReactOS CD boot
+StatusBarColor=Cyan
+StatusBarTextColor=Black
+BackdropTextColor=White
+BackdropColor=Blue
+BackdropFillStyle=Medium
+TitleBoxTextColor=White
+TitleBoxColor=Red
+MessageBoxTextColor=White
+MessageBoxColor=Blue
+MenuTextColor=Gray
+MenuColor=Black
+TextColor=Gray
+SelectedTextColor=Black
+SelectedColor=Gray
+ShowTime=No
+MenuBox=No
+CenterMenu=No
+MinimalUI=Yes
+TimeText=Seconds until highlighted choice will be started automatically:   
+
+[Operating Systems]
+LiveCD="LiveCD"
+LiveCD_Debug="LiveCD (Debug)"
+LiveCD_Screen="LiveCD (Screen)"
+LiveCD_LogFile="LiveCD (Log file)"
+Setup="Setup"
+
+[LiveCD]
+BootType=Windows2003
+SystemPath=multi(0)disk(0)cdrom(0)\reactos
+Options=/MININT
+
+[LiveCD_Debug]
+BootType=Windows2003
+SystemPath=multi(0)disk(0)cdrom(0)\reactos
+Options=/DEBUG /DEBUGPORT=COM1 /BAUDRATE=115200 /SOS /MININT
+
+[LiveCD_Screen]
+BootType=Windows2003
+SystemPath=multi(0)disk(0)cdrom(0)\reactos
+Options=/DEBUG /DEBUGPORT=SCREEN /SOS /MININT
+
+[LiveCD_LogFile]
+BootType=Windows2003
+SystemPath=multi(0)disk(0)cdrom(0)\reactos
+Options=/DEBUG /DEBUGPORT=FILE:\Device\HarddiskX\PartitionY\debug.log /SOS 
/MININT
+
+[Setup]
+BootType=ReactOSSetup
+SystemPath=multi(0)disk(0)cdrom(0)\reactos
diff --git a/boot/freeldr/freeldr/CMakeLists.txt 
b/boot/freeldr/freeldr/CMakeLists.txt
index 725419c37fe..75a8a98fec1 100644
--- a/boot/freeldr/freeldr/CMakeLists.txt
+++ b/boot/freeldr/freeldr/CMakeLists.txt
@@ -139,6 +139,18 @@ if(ARCH STREQUAL "i386")
             arch/i386/xbox/xboxmem.c
             arch/i386/xbox/xboxrtc.c
             arch/i386/xbox/xboxvideo.c)
+    elseif(SARCH STREQUAL "pc98")
+        list(APPEND FREELDR_ARC_SOURCE
+            arch/i386/pc/pcmem.c
+            arch/i386/pc98/machpc98.c
+            arch/i386/pc98/pc98beep.c
+            arch/i386/pc98/pc98cons.c
+            arch/i386/pc98/pc98disk.c
+            arch/i386/pc98/pc98hw.c
+            arch/i386/pc98/pc98mem.c
+            arch/i386/pc98/pc98rtc.c
+            arch/i386/pc98/pc98video.c
+            arch/i386/xbox/xboxfont.c)
     else()
         list(APPEND FREELDR_ARC_SOURCE
             arch/i386/pc/machpc.c
@@ -307,7 +319,7 @@ add_dependencies(freeldr_pe_dbg asm)
 if(SARCH STREQUAL "pc98")
     file(MAKE_DIRECTORY ${REACTOS_BINARY_DIR}/PC98)
     add_custom_target(pc98bootfdd
-        COMMAND native-fatten ${REACTOS_BINARY_DIR}/PC98/ReactOS-98.IMG 
-format 2880 ROS98BOOT -boot 
${CMAKE_BINARY_DIR}/boot/freeldr/bootsect/pc98/fat12fdd.bin -add 
${CMAKE_CURRENT_BINARY_DIR}/freeldr.sys FREELDR.SYS -add 
${CMAKE_SOURCE_DIR}/boot/bootdata/livecd.ini FREELDR.INI
+        COMMAND native-fatten ${REACTOS_BINARY_DIR}/PC98/ReactOS-98.IMG 
-format 2880 ROS98BOOT -boot 
${CMAKE_BINARY_DIR}/boot/freeldr/bootsect/pc98/fat12fdd.bin -add 
${CMAKE_CURRENT_BINARY_DIR}/freeldr.sys FREELDR.SYS -add 
${CMAKE_SOURCE_DIR}/boot/bootdata/floppy_pc98.ini FREELDR.INI
         DEPENDS native-fatten fat12pc98 freeldr
         VERBATIM)
 endif()
diff --git a/boot/freeldr/freeldr/arch/i386/drivemap.c 
b/boot/freeldr/freeldr/arch/i386/drivemap.c
index 1fc96c0f93f..e9fa105b889 100644
--- a/boot/freeldr/freeldr/arch/i386/drivemap.c
+++ b/boot/freeldr/freeldr/arch/i386/drivemap.c
@@ -180,6 +180,11 @@ VOID DriveMapInstallInt13Handler(PDRIVE_MAP_LIST DriveMap)
     ULONG*  RealModeIVT = (ULONG*)UlongToPtr(0x00000000);
     USHORT* BiosLowMemorySize = (USHORT*)ULongToPtr(0x00000413);
 
+#if defined(SARCH_PC98)
+    /* FIXME */
+    return;
+#endif
+
     if (!DriveMapInstalled)
     {
         // Get the old INT 13h handler address from the vector table
@@ -218,6 +223,11 @@ VOID DriveMapRemoveInt13Handler(VOID)
     ULONG*  RealModeIVT = (ULONG*)0x00000000;
     USHORT* BiosLowMemorySize = (USHORT*)0x00000413;
 
+#if defined(SARCH_PC98)
+    /* FIXME */
+    return;
+#endif
+
     if (DriveMapInstalled)
     {
         // Get the old INT 13h handler address from the vector table
diff --git a/boot/freeldr/freeldr/arch/i386/hwapm.c 
b/boot/freeldr/freeldr/arch/i386/hwapm.c
index f3e64e27bd0..7339af9cdd1 100644
--- a/boot/freeldr/freeldr/arch/i386/hwapm.c
+++ b/boot/freeldr/freeldr/arch/i386/hwapm.c
@@ -29,12 +29,15 @@ FindApmBios(VOID)
     REGS  RegsIn;
     REGS  RegsOut;
 
-    RegsIn.b.ah = 0x53;
-    RegsIn.b.al = 0x00;
+#if defined(SARCH_PC98)
+    RegsIn.w.ax = 0x9A00;
+    RegsIn.w.bx = 0x0000;
+    Int386(0x1F, &RegsIn, &RegsOut);
+#else
+    RegsIn.w.ax = 0x5300;
     RegsIn.w.bx = 0x0000;
-
     Int386(0x15, &RegsIn, &RegsOut);
-
+#endif
     if (INT386_SUCCESS(RegsOut))
     {
         TRACE("Found APM BIOS\n");
diff --git a/boot/freeldr/freeldr/arch/i386/pc98/machpc98.c 
b/boot/freeldr/freeldr/arch/i386/pc98/machpc98.c
new file mode 100644
index 00000000000..ca94ea54976
--- /dev/null
+++ b/boot/freeldr/freeldr/arch/i386/pc98/machpc98.c
@@ -0,0 +1,162 @@
+/*
+ * PROJECT:     FreeLoader
+ * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE:     Hardware-specific routines for NEC PC-98 series
+ * COPYRIGHT:   Copyright 2020 Dmitry Borisov ([email protected])
+ */
+
+/* INCLUDES 
*******************************************************************/
+
+#include <freeldr.h>
+
+#include <debug.h>
+DBG_DEFAULT_CHANNEL(HWDETECT);
+
+/* GLOBALS 
********************************************************************/
+
+BOOLEAN HiResoMachine;
+
+/* FUNCTIONS 
******************************************************************/
+
+VOID
+Pc98GetExtendedBIOSData(PULONG ExtendedBIOSDataArea, PULONG 
ExtendedBIOSDataSize)
+{
+    *ExtendedBIOSDataArea = HiResoMachine ? MEM_EXTENDED_HIGH_RESO : 
MEM_EXTENDED_NORMAL;
+    *ExtendedBIOSDataSize = 64;
+}
+
+VOID
+Pc98HwIdle(VOID)
+{
+    /* Unimplemented */
+}
+
+VOID
+Pc98PrepareForReactOS(VOID)
+{
+    Pc98DiskPrepareForReactOS();
+    Pc98VideoPrepareForReactOS();
+    DiskStopFloppyMotor();
+}
+
+ULONG
+Pc98GetBootSectorLoadAddress(IN UCHAR DriveNumber)
+{
+    PPC98_DISK_DRIVE DiskDrive;
+
+    DiskDrive = Pc98DiskDriveNumberToDrive(DriveNumber);
+    if (!DiskDrive)
+    {
+        ERR("Failed to get drive 0x%x\n", DriveNumber);
+        return 0x1FC00;
+    }
+
+    if (((DiskDrive->DaUa & 0xF0) == 0x30) ||
+        ((DiskDrive->DaUa & 0xF0) == 0xB0))
+    {
+        /* 1.44 MB floppy */
+        return 0x1FE00;
+    }
+    else if (DiskDrive->Type & DRIVE_FDD)
+    {
+        return 0x1FC00;
+    }
+
+    return 0x1F800;
+}
+
+VOID __cdecl ChainLoadBiosBootSectorCode(
+    IN UCHAR BootDrive OPTIONAL,
+    IN ULONG BootPartition OPTIONAL)
+{
+    REGS Regs;
+    PPC98_DISK_DRIVE DiskDrive;
+    USHORT LoadAddress;
+    UCHAR DriveNumber = BootDrive ? BootDrive : FrldrBootDrive;
+
+    DiskDrive = Pc98DiskDriveNumberToDrive(DriveNumber);
+    if (!DiskDrive)
+    {
+        ERR("Failed to get drive 0x%x\n", DriveNumber);
+        return;
+    }
+
+    LoadAddress = (USHORT)(Pc98GetBootSectorLoadAddress(DriveNumber) >> 4);
+
+    RtlZeroMemory(&Regs, sizeof(Regs));
+    Regs.w.ax = DiskDrive->DaUa;
+    Regs.w.si = LoadAddress;
+    Regs.w.es = LoadAddress;
+    *(PUCHAR)MEM_DISK_BOOT = DiskDrive->DaUa;
+
+    Pc98VideoClearScreen(ATTR(COLOR_WHITE, COLOR_BLACK));
+
+    Relocator16Boot(&Regs,
+                    /* Stack segment:pointer */
+                    0x0020, 0x00FF,
+                    /* Code segment:pointer */
+                    LoadAddress, 0x0000);
+}
+
+static BOOLEAN
+Pc98ArchTest(VOID)
+{
+    REGS RegsIn, RegsOut;
+
+    /* Int 1Ah AX=1000h
+     * NEC PC-9800 series - Installation check
+     */
+    RegsIn.w.ax = 0x1000;
+    Int386(0x1A, &RegsIn, &RegsOut);
+
+    return RegsOut.w.ax != 0x1000;
+}
+
+VOID
+MachInit(const char *CmdLine)
+{
+    if (!Pc98ArchTest())
+    {
+        _disable();
+        __halt();
+
+        while (TRUE)
+            NOTHING;
+    }
+
+    /* Setup vtbl */
+    RtlZeroMemory(&MachVtbl, sizeof(MACHVTBL));
+    MachVtbl.ConsPutChar = Pc98ConsPutChar;
+    MachVtbl.ConsKbHit = Pc98ConsKbHit;
+    MachVtbl.ConsGetCh = Pc98ConsGetCh;
+    MachVtbl.VideoClearScreen = Pc98VideoClearScreen;
+    MachVtbl.VideoSetDisplayMode = Pc98VideoSetDisplayMode;
+    MachVtbl.VideoGetDisplaySize = Pc98VideoGetDisplaySize;
+    MachVtbl.VideoGetBufferSize = Pc98VideoGetBufferSize;
+    MachVtbl.VideoGetFontsFromFirmware = Pc98VideoGetFontsFromFirmware;
+    MachVtbl.VideoSetTextCursorPosition = Pc98VideoSetTextCursorPosition;
+    MachVtbl.VideoHideShowTextCursor = Pc98VideoHideShowTextCursor;
+    MachVtbl.VideoPutChar = Pc98VideoPutChar;
+    MachVtbl.VideoCopyOffScreenBufferToVRAM = 
Pc98VideoCopyOffScreenBufferToVRAM;
+    MachVtbl.VideoIsPaletteFixed = Pc98VideoIsPaletteFixed;
+    MachVtbl.VideoSetPaletteColor = Pc98VideoSetPaletteColor;
+    MachVtbl.VideoGetPaletteColor = Pc98VideoGetPaletteColor;
+    MachVtbl.VideoSync = Pc98VideoSync;
+    MachVtbl.Beep = Pc98Beep;
+    MachVtbl.PrepareForReactOS = Pc98PrepareForReactOS;
+    MachVtbl.GetMemoryMap = Pc98MemGetMemoryMap;
+    MachVtbl.GetExtendedBIOSData = Pc98GetExtendedBIOSData;
+    MachVtbl.GetFloppyCount = Pc98GetFloppyCount;
+    MachVtbl.DiskReadLogicalSectors = Pc98DiskReadLogicalSectors;
+    MachVtbl.DiskGetDriveGeometry = Pc98DiskGetDriveGeometry;
+    MachVtbl.DiskGetCacheableBlockCount = Pc98DiskGetCacheableBlockCount;
+    MachVtbl.GetTime = Pc98GetTime;
+    MachVtbl.InitializeBootDevices = Pc98InitializeBootDevices;
+    MachVtbl.HwDetect = Pc98HwDetect;
+    MachVtbl.HwIdle = Pc98HwIdle;
+
+    HiResoMachine = *(PUCHAR)MEM_BIOS_FLAG1 & HIGH_RESOLUTION_FLAG;
+
+    HalpCalibrateStallExecution();
+    Pc98VideoInit();
+}
diff --git a/boot/freeldr/freeldr/arch/i386/pc98/pc98beep.c 
b/boot/freeldr/freeldr/arch/i386/pc98/pc98beep.c
new file mode 100644
index 00000000000..c13c0a1da1b
--- /dev/null
+++ b/boot/freeldr/freeldr/arch/i386/pc98/pc98beep.c
@@ -0,0 +1,27 @@
+/*
+ * PROJECT:     FreeLoader
+ * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE:     Beep routine for NEC PC-98 series
+ * COPYRIGHT:   Copyright 2020 Dmitry Borisov ([email protected])
+ */
+
+#include <freeldr.h>
+
+VOID Pc98Beep(VOID)
+{
+    REGS Regs;
+
+    /* Int 18h AH=17h
+     * CRT BIOS - Beep on
+     */
+    Regs.b.ah = 0x17;
+    Int386(0x18, &Regs, &Regs);
+
+    StallExecutionProcessor(100000);
+
+    /* Int 18h AH=18h
+     * CRT BIOS - Beep off
+     */
+    Regs.b.ah = 0x18;
+    Int386(0x18, &Regs, &Regs);
+}
diff --git a/boot/freeldr/freeldr/arch/i386/pc98/pc98cons.c 
b/boot/freeldr/freeldr/arch/i386/pc98/pc98cons.c
new file mode 100644
index 00000000000..d4922f972aa
--- /dev/null
+++ b/boot/freeldr/freeldr/arch/i386/pc98/pc98cons.c
@@ -0,0 +1,122 @@
+/*
+ * PROJECT:     FreeLoader
+ * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE:     Console routines for NEC PC-98 series
+ * COPYRIGHT:   Copyright 2020 Dmitry Borisov ([email protected])
+ */
+
+/* INCLUDES 
*******************************************************************/
+
+#include <freeldr.h>
+
+extern ULONG VramText;
+extern UCHAR TextCols;
+extern UCHAR TextLines;
+
+/* GLOBALS 
********************************************************************/
+
+#define TEXT_CHAR_SIZE 2
+
+static USHORT CursorPosition = 0;
+
+/* FUNCTIONS 
******************************************************************/
+
+VOID
+Pc98ConsPutChar(int Ch)
+{
+    /* If scrolling is needed */
+    if (CursorPosition >= TextCols * TextLines)
+    {
+        RtlCopyMemory((PUSHORT)VramText,
+                      (PUSHORT)(VramText + TextCols * TEXT_CHAR_SIZE),
+                      TextCols * TextLines * TEXT_CHAR_SIZE);
+
+        CursorPosition -= TextCols;
+    }
+
+    if (Ch == '\n')
+    {
+        if (CursorPosition % TextCols != 0)
+            CursorPosition += TextCols - (CursorPosition % TextCols);
+
+        return;
+    }
+
+    if (Ch == '\t')
+    {
+        Pc98ConsPutChar(' ');
+        Pc98ConsPutChar(' ');
+        Pc98ConsPutChar(' ');
+        Pc98ConsPutChar(' ');
+        Pc98ConsPutChar(' ');
+        Pc98ConsPutChar(' ');
+        Pc98ConsPutChar(' ');
+        Pc98ConsPutChar(' ');
+
+        return;
+    }
+
+    *(PUSHORT)(VramText + CursorPosition * TEXT_CHAR_SIZE) = Ch;
+    ++CursorPosition;
+}
+
+BOOLEAN
+Pc98ConsKbHit(VOID)
+{
+    REGS Regs;
+
+    /* Int 18h AH=01h
+     * KEYBOARD - CHECK FOR KEYSTROKE
+     *
+     * Return:
+     * BH - status
+     *    00h - if no keystroke available
+     *    01h - if keystroke available
+     * AH - BIOS scan code
+     * AL - ASCII character
+     */
+    Regs.b.ah = 0x01;
+    Int386(0x18, &Regs, &Regs);
+
+    return Regs.b.bh == 1;
+}
+
+int
+Pc98ConsGetCh(VOID)
+{
+    static BOOLEAN ExtendedKey = FALSE;
+    static UCHAR ExtendedScanCode = 0;
+    REGS Regs;
+
+    /*
+     * If the last time we were called an
+     * extended key was pressed then return
+     * that keys scan code.
+     */
+    if (ExtendedKey)
+    {
+        ExtendedKey = FALSE;
+
+        return ExtendedScanCode;
+    }
+
+    /* Int 18h AH=00h
+     * KEYBOARD - GET KEYSTROKE
+     *
+     * Return:
+     * AH - BIOS scan code
+     * AL - ASCII character
+     */
+    Regs.b.ah = 0x00;
+    Int386(0x18, &Regs, &Regs);
+
+    /* Check for an extended keystroke */
+    if (Regs.b.al == 0)
+    {
+        ExtendedKey = TRUE;
+        ExtendedScanCode = Regs.b.ah;
+    }
+
+    /* Return keystroke */
+    return Regs.b.al;
+}
diff --git a/boot/freeldr/freeldr/arch/i386/pc98/pc98disk.c 
b/boot/freeldr/freeldr/arch/i386/pc98/pc98disk.c
new file mode 100644
index 00000000000..71a21e36267
--- /dev/null
+++ b/boot/freeldr/freeldr/arch/i386/pc98/pc98disk.c
@@ -0,0 +1,902 @@
+/*
+ * PROJECT:     FreeLoader
+ * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE:     Drive access routines for NEC PC-98 series
+ * COPYRIGHT:   Copyright 2020 Dmitry Borisov ([email protected])
+ */
+
+/* INCLUDES 
*******************************************************************/
+
+#include <freeldr.h>
+#include <hwide.h>
+
+#include <debug.h>
+DBG_DEFAULT_CHANNEL(DISK);
+
+/* GLOBALS 
********************************************************************/
+
+/* Maximum number of disks, indexed from 0x00 to 0xFF */
+#define MAX_DRIVES 0x100
+
+/* Cache of all possible PC disk drives */
+PC98_DISK_DRIVE Pc98DiskDrive[MAX_DRIVES];
+
+/* DISK IO ERROR SUPPORT 
******************************************************/
+
+static LONG lReportError = 0; /* >= 0: display errors; < 0: hide errors */
+
+LONG
+DiskReportError(BOOLEAN bShowError)
+{
+    /* Set the reference count */
+    if (bShowError)
+        ++lReportError;
+    else
+        --lReportError;
+    return lReportError;
+}
+
+static PCSTR
+DiskGetErrorCodeString(ULONG ErrorCode)
+{
+    switch (ErrorCode & 0xF0)
+    {
+        case 0x00: return "No error";
+        case 0x10: return "Drive write protection error";
+        case 0x20: return "DMA access across 64 kB boundary";
+        case 0x30: return "End of cylinder";
+        case 0x40: return "The drive name is invalid or the device have low 
health";
+        case 0x50: return "Time out, data not written";
+        case 0x60: return "Time out, drive not ready";
+        case 0x70:
+            if (ErrorCode == 0x78)
+                return "Illegal disk address";
+            else
+                return "Drive write protection error";
+        case 0x80: return "Undefined error";
+        case 0x90: return "Time out error";
+        case 0xA0: return "CRC error in the ID section";
+        case 0xB0: return "CRC error in the DATA section";
+        case 0xC0:
+            if (ErrorCode == 0xC8)
+                return "Seek failure";
+            else
+                return "No data (Sector not found)";
+        case 0xD0: return "Bad cylinder";
+        case 0xE0: return "No ID address mark was found";
+        case 0xF0: return "No DATA address mark was found";
+
+        default: return "Unknown error code";
+    }
+}
+
+static VOID
+DiskError(PCSTR ErrorString, ULONG ErrorCode)
+{
+    CHAR ErrorCodeString[200];
+
+    if (lReportError < 0)
+        return;
+
+    RtlStringCbPrintfA(ErrorCodeString, sizeof(ErrorCodeString), "%s\n\nError 
Code: 0x%lx\nError: %s",
+                       ErrorString, ErrorCode, 
DiskGetErrorCodeString(ErrorCode));
+
+    ERR("%s\n", ErrorCodeString);
+
+    UiMessageBox(ErrorCodeString);
+}
+
+/* FUNCTIONS 
******************************************************************/
+
+BOOLEAN DiskResetController(IN PPC98_DISK_DRIVE DiskDrive)
+{
+    REGS Regs;
+
+    if (DiskDrive->Type & DRIVE_FDD)
+    {
+        /* Int 1Bh AH=07h
+         * DISK BIOS - Recalibrate
+         *
+         * Call with:
+         * AL - drive number
+         *
+         * Return:
+         * CF - set on error, clear if successful
+         * AH - status
+         */
+        Regs.b.ah = 0x07;
+    }
+    else if (DiskDrive->Type != (DRIVE_IDE | DRIVE_CDROM))
+    {
+        /* Int 1Bh AH=03h
+         * DISK BIOS - Initialize
+         *
+         * Call with:
+         * AL - drive number
+         *
+         * Return:
+         * CF - set on error, clear if successful
+         * AH - status
+         */
+        Regs.b.ah = 0x03;
+    }
+    else
+    {
+        return FALSE;
+    }
+
+    WARN("DiskResetController(0x%x) DISK OPERATION FAILED -- RESETTING 
CONTROLLER\n", DiskDrive->DaUa);
+
+    Regs.b.al = DiskDrive->DaUa;
+    Int386(0x1B, &Regs, &Regs);
+    return INT386_SUCCESS(Regs);
+}
+
+VOID Pc98DiskPrepareForReactOS(VOID)
+{
+    AtaFree();
+}
+
+PPC98_DISK_DRIVE
+Pc98DiskDriveNumberToDrive(IN UCHAR DriveNumber)
+{
+    PPC98_DISK_DRIVE DiskDrive;
+
+    ASSERT((0 <= DriveNumber) && (DriveNumber < RTL_NUMBER_OF(Pc98DiskDrive)));
+
+    /* Retrieve a slot */
+    DiskDrive = &Pc98DiskDrive[DriveNumber];
+
+    /* The pre-initialization of the BIOS disks was already done in 
Pc98InitializeBootDevices() */
+    if (DiskDrive->Initialized)
+        return DiskDrive;
+    else
+        return NULL;
+}
+
+static inline
+UCHAR
+BytesPerSectorToSectorLengthCode(IN ULONG BytesPerSector)
+{
+    switch (BytesPerSector)
+    {
+        case 128:
+            return 0;
+        case 256:
+            return 1;
+        case 512:
+            return 2;
+        case 1024:
+            return 3;
+        case 2048:
+            return 4;
+        default:
+            return 0;
+    }
+}
+
+static BOOLEAN
+Pc98DiskReadLogicalSectorsLBA(
+    IN PPC98_DISK_DRIVE DiskDrive,
+    IN ULONGLONG SectorNumber,
+    IN ULONG SectorCount,
+    OUT PVOID Buffer)
+{
+    REGS RegsIn, RegsOut;
+    ULONG RetryCount;
+
+    if (DiskDrive->Type & DRIVE_IDE && DiskDrive->Type & DRIVE_CDROM)
+    {
+        return 
AtaAtapiReadLogicalSectorsLBA(AtaGetDevice(DiskDrive->IdeUnitNumber), 
SectorNumber, SectorCount, Buffer);
+    }
+    else
+    {
+        /* Int 1Bh AH=06h
+         * DISK BIOS - Read data
+         *
+         * Call with:
+         * AL - drive number
+         * BX - bytes to read
+         * CX - cylinder number
+         * DH - head number
+         * DL - sector number
+         * ES:BP -> buffer to read data into
+         *
+         * Return:
+         * CF - set on error, clear if successful
+         * AH - status
+         */
+        RegsIn.b.al = DiskDrive->DaUa;
+        RegsIn.b.ah = 0x06;
+        RegsIn.w.bx = DiskDrive->Geometry.BytesPerSector * SectorCount;
+        RegsIn.w.cx = SectorNumber & 0xFFFF;
+        RegsIn.w.dx = (SectorNumber >> 16) & 0xFFFF;
+        RegsIn.w.es = (USHORT)(((ULONG_PTR)Buffer) >> 4);
+        RegsIn.w.bp = ((ULONG_PTR)Buffer) & 0x0F;
+
+        /* Retry 3 times */
+        for (RetryCount = 0; RetryCount < 3; RetryCount++)
+        {
+            Int386(0x1B, &RegsIn, &RegsOut);
+
+            /* If it worked return TRUE */
+            if (INT386_SUCCESS(RegsOut))
+            {
+                return TRUE;
+            }
+            /* If it was a corrected ECC error then the data is still good */
+            else if (RegsOut.b.ah == 0x08)
+            {
+                return TRUE;
+            }
+            /* If it failed the do the next retry */
+            else
+            {
+                DiskResetController(DiskDrive);
+                continue;
+            }
+        }
+    }
+
+    /* If we get here then the read failed */
+    DiskError("Disk Read Failed in LBA mode", RegsOut.b.ah);
+    ERR("Disk Read Failed in LBA mode: %x (%s) (DriveNumber: 0x%x 
SectorNumber: %I64d SectorCount: %d)\n",
+        RegsOut.b.ah, DiskGetErrorCodeString(RegsOut.b.ah),
+        DiskDrive->DaUa, SectorNumber, SectorCount);
+
+    return FALSE;
+}
+
+static BOOLEAN
+Pc98DiskReadLogicalSectorsCHS(
+    IN PPC98_DISK_DRIVE DiskDrive,
+    IN ULONGLONG SectorNumber,
+    IN ULONG SectorCount,
+    OUT PVOID Buffer)
+{
+    UCHAR PhysicalSector;
+    UCHAR PhysicalHead;
+    ULONG PhysicalTrack;
+    GEOMETRY DriveGeometry;
+    ULONG NumberOfSectorsToRead;
+    REGS RegsIn, RegsOut;
+    ULONG RetryCount;
+
+    DriveGeometry = DiskDrive->Geometry;
+
+    while (SectorCount > 0)
+    {
+        /*
+         * Calculate the physical disk offsets.
+         * Note: DriveGeometry.Sectors < 64
+         */
+        PhysicalSector = (UCHAR)(SectorNumber % DriveGeometry.Sectors);
+        PhysicalHead = (UCHAR)((SectorNumber / DriveGeometry.Sectors) % 
DriveGeometry.Heads);
+        PhysicalTrack = (ULONG)((SectorNumber / DriveGeometry.Sectors) / 
DriveGeometry.Heads);
+
+        /* Floppy sectors value always start at 1 */
+        if (DiskDrive->Type & DRIVE_FDD)
+            ++PhysicalSector;
+
+        /* Calculate how many sectors we need to read this round */
+        if (PhysicalSector > 1)
+        {
+            if (SectorCount >= (DriveGeometry.Sectors - (PhysicalSector - 1)))
+                NumberOfSectorsToRead = (DriveGeometry.Sectors - 
(PhysicalSector - 1));
+            else
+                NumberOfSectorsToRead = SectorCount;
+        }
+        else
+        {
+            if (SectorCount >= DriveGeometry.Sectors)
+                NumberOfSectorsToRead = DriveGeometry.Sectors;
+            else
+                NumberOfSectorsToRead = SectorCount;
+        }
+
+        /* Make sure the read is within the geometry boundaries */
+        if ((PhysicalHead >= DriveGeometry.Heads) ||
+            (PhysicalTrack >= DriveGeometry.Cylinders) ||
+            ((NumberOfSectorsToRead + PhysicalSector) > (DriveGeometry.Sectors 
+ 1)) ||
+            (PhysicalSector > DriveGeometry.Sectors))
+        {
+            DiskError("Disk read exceeds drive geometry limits.", 0);
+            return FALSE;
+        }
+
+        if (DiskDrive->Type & DRIVE_FDD)
+        {
+            /* Int 1Bh AH=x6h
+             * DISK BIOS - Read data
+             *
+             * Call with:
+             * AL - drive number
+             * BX - bytes to read
+             * CH - sector length code
+             * CL - cylinder number
+             * DH - head number
+             * DL - sector number
+             * ES:BP -> buffer to read data into
+             *
+             * Return:
+             * CF - set on error, clear if successful
+             * AH - status
+             */
+            RegsIn.b.al = DiskDrive->DaUa;
+            RegsIn.b.ah = 0x56;    /* With SEEK, and use double-density format 
(MFM) */
+            RegsIn.w.bx = DriveGeometry.BytesPerSector * 
(UCHAR)NumberOfSectorsToRead;
+            RegsIn.b.cl = PhysicalTrack & 0xFFFF;
+            RegsIn.b.ch = 
BytesPerSectorToSectorLengthCode(DriveGeometry.BytesPerSector);
+            RegsIn.b.dl = PhysicalSector;
+            RegsIn.b.dh = PhysicalHead;
+            RegsIn.w.es = (USHORT)(((ULONG_PTR)Buffer) >> 4);
+            RegsIn.w.bp = ((ULONG_PTR)Buffer) & 0x0F;
+        }
+        else
+        {
+            /* Int 1Bh AH=06h
+             * DISK BIOS - Read data
+             *
+             * Call with:
+             * AL - drive number
+             * BX - bytes to read
+             * CX - cylinder number
+             * DH - head number
+             * DL - sector number
+             * ES:BP -> buffer to read data into
+             *
+             * Return:
+             * CF - set on error, clear if successful
+             * AH - status
+             */
+            RegsIn.b.al = DiskDrive->DaUa;
+            RegsIn.b.ah = 0x06;
+            RegsIn.w.bx = DriveGeometry.BytesPerSector * 
(UCHAR)NumberOfSectorsToRead;
+            RegsIn.w.cx = PhysicalTrack & 0xFFFF;
+            RegsIn.b.dl = PhysicalSector;
+            RegsIn.b.dh = PhysicalHead;
+            RegsIn.w.es = (USHORT)(((ULONG_PTR)Buffer) >> 4);
+            RegsIn.w.bp = ((ULONG_PTR)Buffer) & 0x0F;
+        }
+
+        /* Perform the read. Retry 3 times. */
+        for (RetryCount = 0; RetryCount < 3; RetryCount++)
+        {
+            Int386(0x1B, &RegsIn, &RegsOut);
+
+            /* If it worked break out */
+            if (INT386_SUCCESS(RegsOut))
+            {
+                break;
+            }
+            /* If it was a corrected ECC error then the data is still good */
+            else if (RegsOut.b.ah == 0x08)
+            {
+                break;
+            }
+            /* If it failed then do the next retry */
+            else
+            {
+                DiskResetController(DiskDrive);
+                continue;
+            }
+        }
+
+        /* If we retried 3 times then fail */
+        if (RetryCount >= 3)
+        {
+            DiskError("Disk Read Failed in CHS mode, after retrying 3 times", 
RegsOut.b.ah);
+            ERR("Disk Read Failed in CHS mode, after retrying 3 times: %x (%s) 
(DriveNumber: 0x%x SectorNumber: %I64d SectorCount: %d)\n",
+                RegsOut.b.ah, DiskGetErrorCodeString(RegsOut.b.ah),
+                DiskDrive->DaUa, SectorNumber, SectorCount);
+            return FALSE;
+        }
+
+        Buffer = (PVOID)((ULONG_PTR)Buffer + (NumberOfSectorsToRead * 
DriveGeometry.BytesPerSector));
+        SectorCount -= NumberOfSectorsToRead;
+        SectorNumber += NumberOfSectorsToRead;
+    }
+
+    return TRUE;
+}
+
+static BOOLEAN
+InitScsiDrive(
+    IN UCHAR DaUa,
+    IN OUT PPC98_DISK_DRIVE DiskDrive)
+{
+    REGS RegsIn, RegsOut;
+    UCHAR UnitAddress = DaUa & 0x0F;
+    USHORT DiskEquipment = *(PUCHAR)MEM_DISK_EQUIPS;
+    ULONG ScsiParameters = *(PULONG)(MEM_SCSI_TABLE + UnitAddress * 
sizeof(ULONG));
+    UCHAR DeviceType;
+
+    /* Hard drives */
+    if (DiskEquipment & (1 << UnitAddress))
+    {
+        /* Int 1Bh AH=84h
+         * DISK BIOS - Sense
+         *
+         * Call with:
+         * AL - drive number
+         *
+         * Return:
+         * BX - bytes per sector
+         * CX - cylinders number
+         * DH - heads number
+         * DL - sectors number
+         * CF - set on error, clear if successful
+         * AH - status
+         */
+        RegsIn.b.al = DaUa;
+        RegsIn.b.ah = 0x84;
+        Int386(0x1B, &RegsIn, &RegsOut);
+        if (!INT386_SUCCESS(RegsOut) || RegsOut.w.cx == 0)
+        {
+            DiskDrive->Initialized = FALSE;
+            return FALSE;
+        }
+
+        DiskDrive->Geometry.Cylinders = RegsOut.w.cx;
+        DiskDrive->Geometry.Heads = RegsOut.b.dh;
+        DiskDrive->Geometry.Sectors = RegsOut.b.dl;
+        DiskDrive->Geometry.BytesPerSector = RegsOut.w.bx;
+        DiskDrive->LBASupported = FALSE;
+        DiskDrive->IsRemovable = FALSE;
+    }
+    /* Other devices */
+    else if (ScsiParameters)
+    {
+        DeviceType = ScsiParameters & 0x1F;
+        switch (DeviceType)
+        {
+            case 0x05:
+                /* CD-ROM */
+                DiskDrive->Geometry.Cylinders = 0xFFFF;
+                DiskDrive->Geometry.Heads = 0xFFFF;
+                DiskDrive->Geometry.Sectors = 0xFFFF;
+                DiskDrive->Geometry.BytesPerSector = 2048;
+                DiskDrive->Type = DRIVE_CDROM;
+                DiskDrive->LBASupported = TRUE;
+                DiskDrive->IsRemovable = TRUE;
+                break;
+
+            case 0x07:
+                /* Magneto-optical drive */
+                DiskDrive->Geometry.Cylinders = 0xFFFF;
+                DiskDrive->Geometry.Heads = 8;
+                DiskDrive->Geometry.Sectors = 32;
+                DiskDrive->Geometry.BytesPerSector = 512;
+                DiskDrive->Type = DRIVE_MO;
+                DiskDrive->LBASupported = TRUE;
+                DiskDrive->IsRemovable = TRUE;
+                break;
+
+            default:
+                DiskDrive->Initialized = FALSE;
+                return FALSE;
+        }
+    }
+    else
+    {
+        DiskDrive->Initialized = FALSE;
+        return FALSE;
+    }
+
+    DiskDrive->DaUa = DaUa;
+    DiskDrive->Type |= DRIVE_SCSI;
+    DiskDrive->Initialized = TRUE;
+
+    TRACE("InitScsiDrive(0x%x) returned:\n"
+          "Cylinders  : 0x%x\n"
+          "Heads      : 0x%x\n"
+          "Sects/Track: 0x%x\n"
+          "Bytes/Sect : 0x%x\n",
+          DaUa,
+          DiskDrive->Geometry.Cylinders,
+          DiskDrive->Geometry.Heads,
+          DiskDrive->Geometry.Sectors,
+          DiskDrive->Geometry.BytesPerSector);
+
+    return TRUE;
+}
+
+static BOOLEAN
+InitIdeDrive(
+    IN UCHAR UnitNumber,
+    IN OUT PPC98_DISK_DRIVE DiskDrive)
+{
+    PDEVICE_UNIT DeviceUnit = AtaGetDevice(UnitNumber);
+
+    /* We work directly only with ATAPI drives because BIOS has ATA support */
+    if (DeviceUnit && DeviceUnit->Flags & ATA_DEVICE_ATAPI)
+    {
+        DiskDrive->Geometry.Cylinders = DeviceUnit->Cylinders;
+        DiskDrive->Geometry.Heads = DeviceUnit->Heads;
+        DiskDrive->Geometry.Sectors = DeviceUnit->Sectors;
+        DiskDrive->Geometry.BytesPerSector = DeviceUnit->SectorSize;
+        DiskDrive->DaUa = 0xFF;
+        DiskDrive->IdeUnitNumber = UnitNumber;
+        DiskDrive->Type = DRIVE_IDE | DRIVE_CDROM;
+        DiskDrive->LBASupported = TRUE;
+        DiskDrive->IsRemovable = TRUE;
+        DiskDrive->Initialized = TRUE;
+
+        TRACE("InitIdeDrive(0x%x) returned:\n"
+              "Cylinders  : 0x%x\n"
+              "Heads      : 0x%x\n"
+              "Sects/Track: 0x%x\n"
+              "Bytes/Sect : 0x%x\n",
+              UnitNumber,
+              DiskDrive->Geometry.Cylinders,
+              DiskDrive->Geometry.Heads,
+              DiskDrive->Geometry.Sectors,
+              DiskDrive->Geometry.BytesPerSector);
+
+        return TRUE;
+    }
+
+    DiskDrive->Initialized = FALSE;
+    return FALSE;
+}
+
+static BOOLEAN
+InitHardDrive(
+    IN UCHAR DaUa,
+    IN OUT PPC98_DISK_DRIVE DiskDrive)
+{
+    REGS RegsIn, RegsOut;
+
+    /* Int 1Bh AH=8Eh
+     * DISK BIOS - Set half-height operation mode
+     *
+     * Call with:
+     * AL - drive number
+     */
+    RegsIn.b.al = DaUa;
+    RegsIn.b.ah = 0x8E;
+    Int386(0x1B, &RegsIn, &RegsOut);
+
+    /* Int 1Bh AH=84h
+     * DISK BIOS - Sense
+     *
+     * Call with:
+     * AL - drive number
+     *
+     * Return:
+     * BX - bytes per sector
+     * CX - cylinders number
+     * DH - heads number
+     * DL - sectors number
+     * CF - set on error, clear if successful
+     * AH - status
+     */
+    RegsIn.b.al = DaUa;
+    RegsIn.b.ah = 0x84;
+    Int386(0x1B, &RegsIn, &RegsOut);
+    if (!INT386_SUCCESS(RegsOut) || RegsOut.w.cx == 0)
+    {
+        DiskDrive->Initialized = FALSE;
+        return FALSE;
+    }
+
+    DiskDrive->Geometry.Cylinders = RegsOut.w.cx;
+    DiskDrive->Geometry.Heads = RegsOut.b.dh;
+    DiskDrive->Geometry.Sectors = RegsOut.b.dl;
+    DiskDrive->Geometry.BytesPerSector = RegsOut.w.bx;
+    DiskDrive->DaUa = DaUa;
+    DiskDrive->Type = DRIVE_IDE;
+    DiskDrive->LBASupported = FALSE;
+    DiskDrive->IsRemovable = FALSE;
+    DiskDrive->Initialized = TRUE;
+
+    TRACE("InitHardDrive(0x%x) returned:\n"
+          "Cylinders  : 0x%x\n"
+          "Heads      : 0x%x\n"
+          "Sects/Track: 0x%x\n"
+          "Bytes/Sect : 0x%x\n",
+          DaUa,
+          DiskDrive->Geometry.Cylinders,
+          DiskDrive->Geometry.Heads,
+          DiskDrive->Geometry.Sectors,
+          DiskDrive->Geometry.BytesPerSector);
+
+    return TRUE;
+}
+
+static BOOLEAN
+InitFloppyDrive(
+    IN UCHAR DaUa,
+    IN OUT PPC98_DISK_DRIVE DiskDrive)
+{
+    REGS RegsIn, RegsOut;
+    USHORT BytesPerSector;
+    UCHAR DeviceAddress = DaUa & 0xF0;
+
+    /* There's no way to obtain floppy disk geometry in BIOS */
+
+    /* Int 1Bh AH=4Ah
+     * DISK BIOS - Read ID
+     *
+     * Call with:
+     * AL - drive number
+     *
+     * Return:
+     * CH - sector size
+     * CL - cylinder
+     * DH - head
+     * DL - sector
+     * CF - set on error, clear if successful
+     * AH - status
+     */
+    RegsIn.b.ah = 0x4A;
+    RegsIn.b.al = DaUa;
+    Int386(0x1B, &RegsIn, &RegsOut);
+    if (!INT386_SUCCESS(RegsOut))
+    {
+        DiskDrive->Initialized = FALSE;
+        return FALSE;
+    }
+
+    BytesPerSector = 128 << RegsOut.b.ch;
+    switch (BytesPerSector)
+    {
+        case 256:
+            if (DeviceAddress == 0x50)
+            {
+                /* 320 kB 2DD */
+                DiskDrive->Geometry.Cylinders = 80;
+                DiskDrive->Geometry.Heads = 2;
+                DiskDrive->Geometry.Sectors = 16;
+            }
+            else
+            {
+                /* 1 MB 2HD */
+                DiskDrive->Geometry.Cylinders = 77;
+                DiskDrive->Geometry.Heads = 2;
+                DiskDrive->Geometry.Sectors = 26;
+            }
+            break;
+
+        case 512:
+            if (DeviceAddress == 0x30 || DeviceAddress == 0xB0)
+            {
+                /* 1.44 MB 2HD */
+                DiskDrive->Geometry.Cylinders = 80;
+                DiskDrive->Geometry.Heads = 2;
+                DiskDrive->Geometry.Sectors = 18;
+            }
+            else if (DeviceAddress == 0x70 || DeviceAddress == 0xF0)
+            {
+                /* 720/640 kB 2DD */
+                DiskDrive->Geometry.Cylinders = 80;
+                DiskDrive->Geometry.Heads = 2;
+                DiskDrive->Geometry.Sectors = 8;
+            }
+            else
+            {
+                /* 1.2 MB 2HC */
+                DiskDrive->Geometry.Cylinders = 80;
+                DiskDrive->Geometry.Heads = 2;
+                DiskDrive->Geometry.Sectors = 15;
+            }
+            break;
+
+        case 1024:
+            /* 1.25 MB 2HD */
+            DiskDrive->Geometry.Cylinders = 77;
+            DiskDrive->Geometry.Heads = 2;
+            DiskDrive->Geometry.Sectors = 8;
+            break;
+
+        default:
+            DiskDrive->Initialized = FALSE;
+            return FALSE;
+    }
+
+    DiskDrive->Geometry.BytesPerSector = BytesPerSector;
+    DiskDrive->DaUa = DaUa;
+    DiskDrive->Type = DRIVE_FDD;
+    DiskDrive->LBASupported = FALSE;
+    DiskDrive->IsRemovable = TRUE;
+    DiskDrive->Initialized = TRUE;
+
+    TRACE("InitFloppyDrive(0x%x) returned:\n"
+          "Cylinders  : 0x%x\n"
+          "Heads      : 0x%x\n"
+          "Sects/Track: 0x%x\n"
+          "Bytes/Sect : 0x%x\n",
+          DaUa,
+          DiskDrive->Geometry.Cylinders,
+          DiskDrive->Geometry.Heads,
+          DiskDrive->Geometry.Sectors,
+          DiskDrive->Geometry.BytesPerSector);
+
+    return TRUE;
+}
+
+/* We emulate PC BIOS drive numbers here */
+BOOLEAN
+Pc98InitializeBootDevices(VOID)
+{
+    PPC98_DISK_DRIVE DiskDrive;
+    UCHAR FakeFloppyDriveNumber = 0x30;
+    UCHAR FakeHardDriveDriveNumber = 0x80;
+    UCHAR FakeCdRomDriveNumber = 0xE0;
+    USHORT DiskEquipment = *(PUSHORT)MEM_DISK_EQUIP & 
~(*(PUCHAR)MEM_RDISK_EQUIP);
+    UCHAR IdeDetectedCount;
+    UCHAR i;
+
+    TRACE("Pc98InitializeBootDevices()\n");
+
+    RtlZeroMemory(&Pc98DiskDrive, sizeof(Pc98DiskDrive));
+
+    /*
+     * Map DA/UA to drive number, i.e.
+     * 0x90 -> 0x30
+     * 0x80 -> 0x80
+     * 0xA0 -> 0x81, etc.
+     */
+
+    /* Map floppies */
+
+    for (i = 0; i < 4; i++)
+    {
+        DiskDrive = &Pc98DiskDrive[FakeFloppyDriveNumber];
+        if (FIRSTBYTE(DiskEquipment) & (1 << i))
+        {
+            if (InitFloppyDrive(0x30 + i, DiskDrive) || InitFloppyDrive(0xB0 + 
i, DiskDrive) ||
+                InitFloppyDrive(0x90 + i, DiskDrive) || InitFloppyDrive(0x10 + 
i, DiskDrive))
+                ++FakeFloppyDriveNumber;
+        }
+    }
+
+    for (i = 0; i < 4; i++)
+    {
+        DiskDrive = &Pc98DiskDrive[FakeFloppyDriveNumber];
+        if (FIRSTBYTE(DiskEquipment) & (16 << i))
+        {
+            if (InitFloppyDrive(0x50 + i, DiskDrive))
+                ++FakeFloppyDriveNumber;
+        }
+    }
+
+    for (i = 0; i < 4; i++)
+    {
+        DiskDrive = &Pc98DiskDrive[FakeFloppyDriveNumber];
+        if (SECONDBYTE(DiskEquipment) & (16 << i))
+        {
+            if (InitFloppyDrive(0x70 + i, DiskDrive) || InitFloppyDrive(0xF0 + 
i, DiskDrive))
+                ++FakeFloppyDriveNumber;
+        }
+    }
+
+    /* Map IDE/SASI drives */
+
+    for (i = 0; i < 4; i++)
+    {
+        DiskDrive = &Pc98DiskDrive[FakeHardDriveDriveNumber];
+        if (InitHardDrive(0x80 + i, DiskDrive) || InitHardDrive(0x00 + i, 
DiskDrive))
+            ++FakeHardDriveDriveNumber;
+    }
+
+    AtaInit(&IdeDetectedCount);
+    for (i = 0; i <= IdeDetectedCount; i++)
+    {
+        DiskDrive = &Pc98DiskDrive[FakeCdRomDriveNumber];
+        if (InitIdeDrive(i, DiskDrive))
+            ++FakeCdRomDriveNumber;
+    }
+
+    /* Map SCSI drives */
+
+    for (i = 0; i < 7; i++)
+    {
+        DiskDrive = &Pc98DiskDrive[FakeHardDriveDriveNumber];
+        if (InitScsiDrive(0xA0 + i, DiskDrive) || InitScsiDrive(0x20 + i, 
DiskDrive))
+        {
+            if (DiskDrive->Type & DRIVE_CDROM || DiskDrive->Type & DRIVE_MO)
+            {
+                /* Move to CD-ROM area */
+                Pc98DiskDrive[FakeCdRomDriveNumber] = *DiskDrive;
+                RtlZeroMemory(DiskDrive, sizeof(PC98_DISK_DRIVE));
+                ++FakeCdRomDriveNumber;
+            }
+            else
+            {
+                ++FakeHardDriveDriveNumber;
+            }
+        }
+    }
+
+#if 1
+    // Ugly HACK: Force ISO boot
+    // FIXME: Fill ARC disk blocks completely
+    // to allow usage of CD-ROM root path (See floppy_pc98.ini).
+    FrldrBootDrive = 0xE0;
+    FrldrBootPartition = 0xFF;
+#else
+    /* Reassign boot drive */
+    for (i = 0; i < MAX_DRIVES - 1; i++)
+    {
+        DiskDrive = &Pc98DiskDrive[i];
+        if (DiskDrive->Initialized && DiskDrive->DaUa == FrldrBootDrive)
+        {
+            TRACE("Boot drive: old 0x%x, new 0x%x\n", FrldrBootDrive, i);
+            FrldrBootDrive = i;
+            break;
+        }
+    }
+#endif
+
+    /* Call PC version */
+    return PcInitializeBootDevices();
+}
+
+BOOLEAN
+Pc98DiskReadLogicalSectors(
+    IN UCHAR DriveNumber,
+    IN ULONGLONG SectorNumber,
+    IN ULONG SectorCount,
+    OUT PVOID Buffer)
+{
+    PPC98_DISK_DRIVE DiskDrive;
+
+    TRACE("Pc98DiskReadLogicalSectors() DriveNumber: 0x%x SectorNumber: %I64d 
SectorCount: %d Buffer: 0x%x\n",
+          DriveNumber, SectorNumber, SectorCount, Buffer);
+
+    /* 16-bit BIOS addressing limitation */
+    ASSERT(((ULONG_PTR)Buffer) <= 0xFFFFF);
+
+    DiskDrive = Pc98DiskDriveNumberToDrive(DriveNumber);
+    if (!DiskDrive)
+        return FALSE;
+
+    if (DiskDrive->LBASupported)
+    {
+        /* LBA is easy, nothing to calculate. Just do the read. */
+        TRACE("--> Using LBA\n");
+        return Pc98DiskReadLogicalSectorsLBA(DiskDrive, SectorNumber, 
SectorCount, Buffer);
+    }
+    else
+    {
+        /* LBA is not supported, default to CHS */
+        TRACE("--> Using CHS\n");
+        return Pc98DiskReadLogicalSectorsCHS(DiskDrive, SectorNumber, 
SectorCount, Buffer);
+    }
+}
+
+BOOLEAN
+Pc98DiskGetDriveGeometry(UCHAR DriveNumber, PGEOMETRY Geometry)
+{
+    PPC98_DISK_DRIVE DiskDrive;
+
+    TRACE("Pc98DiskGetDriveGeometry(0x%x)\n", DriveNumber);
+
+    DiskDrive = Pc98DiskDriveNumberToDrive(DriveNumber);
+    if (!DiskDrive)
+        return FALSE;
+
+    *Geometry = DiskDrive->Geometry;
+
+    return TRUE;
+}
+
+ULONG
+Pc98DiskGetCacheableBlockCount(UCHAR DriveNumber)
+{
+    PPC98_DISK_DRIVE DiskDrive;
+
+    DiskDrive = Pc98DiskDriveNumberToDrive(DriveNumber);
+    if (!DiskDrive)
+        return 1; // Unknown count.
+
+    /*
+     * If LBA is supported then the block size will be 64 sectors (32k).
+     * If not then the block size is the size of one track.
+     */
+    if (DiskDrive->LBASupported)
+        return 64;
+    else
+        return DiskDrive->Geometry.Sectors;
+}
diff --git a/boot/freeldr/freeldr/arch/i386/pc98/pc98hw.c 
b/boot/freeldr/freeldr/arch/i386/pc98/pc98hw.c
new file mode 100644
index 00000000000..457981c5fde
--- /dev/null
+++ b/boot/freeldr/freeldr/arch/i386/pc98/pc98hw.c
@@ -0,0 +1,1292 @@
+/*
+ * PROJECT:     FreeLoader
+ * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE:     Hardware detection routines for NEC PC-98 series
+ * COPYRIGHT:   Copyright 2020 Dmitry Borisov ([email protected])
+ */
+
+/* INCLUDES 
*******************************************************************/
+
+#include <freeldr.h>
+#include <cportlib/cportlib.h>
+#include <drivers/pc98/pit.h>
+#include <drivers/pc98/fdc.h>
+
+#include <debug.h>
+DBG_DEFAULT_CHANNEL(HWDETECT);
+
+/* Used for BIOS disks pre-enumeration performed when detecting the boot 
devices in InitializeBootDevices() */
+extern UCHAR PcBiosDiskCount;
+
+extern BOOLEAN HiResoMachine;
+
+GET_HARDDISK_CONFIG_DATA GetHarddiskConfigurationData = NULL;
+
+/* GLOBALS 
********************************************************************/
+
+#define MILLISEC     10
+#define PRECISION    8
+#define HZ           100
+
+static unsigned int delay_count = 1;
+
+PCHAR
+GetHarddiskIdentifier(UCHAR DriveNumber);
+
+/* FUNCTIONS 
******************************************************************/
+
+static VOID
+__StallExecutionProcessor(ULONG Loops)
+{
+    register volatile unsigned int i;
+    for (i = 0; i < Loops; i++);
+}
+
+VOID StallExecutionProcessor(ULONG Microseconds)
+{
+    ULONGLONG LoopCount = ((ULONGLONG)delay_count * (ULONGLONG)Microseconds) / 
1000ULL;
+    __StallExecutionProcessor((ULONG)LoopCount);
+}
+
+static VOID
+WaitFor8253Wraparound(VOID)
+{
+    ULONG CurrentCount;
+    ULONG PreviousCount = ~0;
+    LONG Delta;
+
+    CurrentCount = Read8253Timer(PitChannel0);
+
+    do
+    {
+        PreviousCount = CurrentCount;
+        CurrentCount = Read8253Timer(PitChannel0);
+        Delta = CurrentCount - PreviousCount;
+    }
+    while (Delta < 300);
+}
+
+VOID
+HalpCalibrateStallExecution(VOID)
+{
+    ULONG i;
+    ULONG calib_bit;
+    ULONG CurCount;
+    TIMER_CONTROL_PORT_REGISTER TimerControl;
+    USHORT Count = (*(PUCHAR)MEM_BIOS_FLAG1 & SYSTEM_CLOCK_8MHZ_FLAG) ?
+                    (TIMER_FREQUENCY_1 / HZ) : (TIMER_FREQUENCY_2 / HZ);
+
+    /* Initialize timer interrupt with MILLISECOND ms interval */
+    TimerControl.BcdMode = FALSE;
+    TimerControl.OperatingMode = PitOperatingMode2;
+    TimerControl.AccessMode = PitAccessModeLowHigh;
+    TimerControl.Channel = PitChannel0;
+    Write8253Timer(TimerControl, Count);
+
+    /* Stage 1: Coarse calibration */
+
+    delay_count = 1;
+
+    do
+    {
+        /* Next delay count to try */
+        delay_count <<= 1;
+
+        WaitFor8253Wraparound();
+
+        /* Do the delay */
+        __StallExecutionProcessor(delay_count);
+
+        CurCount = Read8253Timer(PitChannel0);
+    }
+    while (CurCount > Count / 2);
+
+    /* Get bottom value for delay */
+    delay_count >>= 1;
+
+    /* Stage 2: Fine calibration */
+
+    /* Which bit are we going to test */
+    calib_bit = delay_count;
+
+    for (i = 0; i < PRECISION; i++)
+    {
+        /* Next bit to calibrate */
+        calib_bit >>= 1;
+
+        /* If we have done all bits, stop */
+        if (!calib_bit)
+            break;
+
+        /* Set the bit in delay_count */
+        delay_count |= calib_bit;
+
+        WaitFor8253Wraparound();
+
+        /* Do the delay */
+        __StallExecutionProcessor(delay_count);
+
+        CurCount = Read8253Timer(PitChannel0);
+
+        /* If a tick has passed, turn the calibrated bit back off */
+        if (CurCount <= Count / 2)
+            delay_count &= ~calib_bit;
+    }
+
+    /* We're finished: Do the finishing touches */
+
+    /* Calculate delay_count for 1ms */
+    delay_count /= (MILLISEC / 2);
+}
+
+static UCHAR
+GetFloppyType(UCHAR FloppyNumber)
+{
+    /* FIXME */
+    return 5;
+}
+
+static VOID
+DetectBiosFloppyPeripheral(PCONFIGURATION_COMPONENT_DATA ControllerKey)
+{
+    PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
+    PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor;
+    PCM_FLOPPY_DEVICE_DATA FloppyData;
+    CHAR Identifier[20];
+    PCONFIGURATION_COMPONENT_DATA PeripheralKey;
+    ULONG Size;
+    UCHAR FloppyNumber;
+    UCHAR FloppyType;
+    ULONG MaxDensity[6] = {0, 360, 1200, 720, 1440, 2880};
+
+    for (FloppyNumber = 0; FloppyNumber < Pc98GetFloppyCount(); FloppyNumber++)
+    {
+        FloppyType = GetFloppyType(FloppyNumber);
+
+        if ((FloppyType > 5) || (FloppyType == 0))
+            continue;
+
+        /* TODO: Properly detect */
+
+        RtlStringCbPrintfA(Identifier, sizeof(Identifier), "FLOPPY%d", 
FloppyNumber + 1);
+
+        /* Set 'Configuration Data' value */
+        Size = sizeof(CM_PARTIAL_RESOURCE_LIST) +
+               sizeof(CM_FLOPPY_DEVICE_DATA);
+        PartialResourceList = FrLdrHeapAlloc(Size, TAG_HW_RESOURCE_LIST);
+        if (PartialResourceList == NULL)
+        {
+            ERR("Failed to allocate resource descriptor\n");
+            return;
+        }
+        RtlZeroMemory(PartialResourceList, Size);
+        PartialResourceList->Version = 1;
+        PartialResourceList->Revision = 1;
+        PartialResourceList->Count = 1;
+
+        PartialDescriptor = &PartialResourceList->PartialDescriptors[0];
+        PartialDescriptor->Type = CmResourceTypeDeviceSpecific;
+        PartialDescriptor->ShareDisposition = CmResourceShareUndetermined;
+        PartialDescriptor->u.DeviceSpecificData.DataSize = 
sizeof(CM_FLOPPY_DEVICE_DATA);
+
+        /* FIXME: Don't use default parameters for 1.44 MB floppy */
+        FloppyData = (PVOID)(((ULONG_PTR)PartialResourceList) + 
sizeof(CM_PARTIAL_RESOURCE_LIST));
+        FloppyData->Version = 2;
+        FloppyData->Revision = 0;
+        FloppyData->MaxDensity = MaxDensity[FloppyType];
+        FloppyData->MountDensity = 0;
+        FloppyData->StepRateHeadUnloadTime = 175;
+        FloppyData->HeadLoadTime = 2;
+        FloppyData->MotorOffTime = 37;
+        FloppyData->SectorLengthCode = 2;
+        FloppyData->SectorPerTrack = 18;
+        FloppyData->ReadWriteGapLength = 27;
+        FloppyData->DataTransferLength = 255;
+        FloppyData->FormatGapLength = 108;
+        FloppyData->FormatFillCharacter = 0xF6;
+        FloppyData->HeadSettleTime = 15;
+        FloppyData->MotorSettleTime = 8;
+        FloppyData->MaximumTrackValue = (FloppyType == 1) ? 39 : 79;
+        FloppyData->DataTransferRate = 0;
+
+        FldrCreateComponentKey(ControllerKey,
+                               PeripheralClass,
+                               FloppyDiskPeripheral,
+                               Input | Output,
+                               FloppyNumber,
+                               0xFFFFFFFF,
+                               Identifier,
+                               PartialResourceList,
+                               Size,
+                               &PeripheralKey);
+        TRACE("Created key: FloppyDiskPeripheral\\%d\n", FloppyNumber);
+    }
+}
+
+static PCONFIGURATION_COMPONENT_DATA
+DetectBiosFloppyController(PCONFIGURATION_COMPONENT_DATA BusKey)
+{
+    PCONFIGURATION_COMPONENT_DATA ControllerKey;
+    PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
+    PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor;
+    ULONG Size;
+    ULONG FloppyCount;
+    UCHAR i;
+    UCHAR Index = 0;
+
+    FloppyCount = Pc98GetFloppyCount();
+
+    /* Always create a BIOS disk controller, no matter if we have floppy 
drives or not */
+    Size = sizeof(CM_PARTIAL_RESOURCE_LIST) +
+           6 * sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
+    PartialResourceList = FrLdrHeapAlloc(Size, TAG_HW_RESOURCE_LIST);
+    if (PartialResourceList == NULL)
+    {
+        ERR("Failed to allocate resource descriptor\n");
+        return NULL;
+    }
+    RtlZeroMemory(PartialResourceList, Size);
+    PartialResourceList->Version = 1;
+    PartialResourceList->Revision = 1;
+    PartialResourceList->Count = 7;
+
+    /* Set I/O ports */
+    for (i = 0; i < 3; i++)
+    {
+        PartialDescriptor = &PartialResourceList->PartialDescriptors[Index++];
+        PartialDescriptor->Type = CmResourceTypePort;
+        PartialDescriptor->ShareDisposition = CmResourceShareDeviceExclusive;
+        PartialDescriptor->Flags = CM_RESOURCE_PORT_IO;
+        PartialDescriptor->u.Port.Start.LowPart = 0x90 + i * 2;
+        PartialDescriptor->u.Port.Start.HighPart = 0;
+        PartialDescriptor->u.Port.Length = 1;
+    }
+    PartialDescriptor = &PartialResourceList->PartialDescriptors[Index++];
+    PartialDescriptor->Type = CmResourceTypePort;
+    PartialDescriptor->ShareDisposition = CmResourceShareDeviceExclusive;
+    PartialDescriptor->Flags = CM_RESOURCE_PORT_IO;
+    PartialDescriptor->u.Port.Start.LowPart = 0xBE;
+    PartialDescriptor->u.Port.Start.HighPart = 0;
+    PartialDescriptor->u.Port.Length = 1;
+
+    PartialDescriptor = &PartialResourceList->PartialDescriptors[Index++];
+    PartialDescriptor->Type = CmResourceTypePort;
+    PartialDescriptor->ShareDisposition = CmResourceShareDeviceExclusive;
+    PartialDescriptor->Flags = CM_RESOURCE_PORT_IO;
+    PartialDescriptor->u.Port.Start.LowPart = 0x4BE;
+    PartialDescriptor->u.Port.Start.HighPart = 0;
+    PartialDescriptor->u.Port.Length = 1;
+
+    /* Set Interrupt */
+    PartialDescriptor = &PartialResourceList->PartialDescriptors[Index++];
+    PartialDescriptor->Type = CmResourceTypeInterrupt;
+    PartialDescriptor->ShareDisposition = CmResourceShareUndetermined;
+    PartialDescriptor->Flags = CM_RESOURCE_INTERRUPT_LATCHED;
+    PartialDescriptor->u.Interrupt.Level = 11;
+    PartialDescriptor->u.Interrupt.Vector = 11;
+    PartialDescriptor->u.Interrupt.Affinity = 0xFFFFFFFF;
+
+    /* Set DMA channel */
+    PartialDescriptor = &PartialResourceList->PartialDescriptors[Index++];
+    PartialDescriptor->Type = CmResourceTypeDma;
+    PartialDescriptor->ShareDisposition = CmResourceShareUndetermined;
+    PartialDescriptor->Flags = 0;
+    PartialDescriptor->u.Dma.Channel = 2;
+    PartialDescriptor->u.Dma.Port = 0;
+
+    /* Create floppy disk controller */
+    FldrCreateComponentKey(BusKey,
+                           ControllerClass,
+                           DiskController,
+                           Output | Input,
+                           0,
+                           0xFFFFFFFF,
+                           NULL,
+                           PartialResourceList,
+                           Size,
+                           &ControllerKey);
+    TRACE("Created key: DiskController\\0\n");
+
+    if (FloppyCount)
+        DetectBiosFloppyPeripheral(ControllerKey);
+
+    return ControllerKey;
+}
+
+static PCM_PARTIAL_RESOURCE_LIST
+Pc98GetHarddiskConfigurationData(UCHAR DriveNumber, ULONG* pSize)
+{
+    PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
+    PCM_DISK_GEOMETRY_DEVICE_DATA DiskGeometry;
+    GEOMETRY Geometry;
+    ULONG Size;
+
+    *pSize = 0;
+
+    /* Set 'Configuration Data' value */
+    Size = sizeof(CM_PARTIAL_RESOURCE_LIST) +
+           sizeof(CM_DISK_GEOMETRY_DEVICE_DATA);
+    PartialResourceList = FrLdrHeapAlloc(Size, TAG_HW_RESOURCE_LIST);
+    if (PartialResourceList == NULL)
+    {
+        ERR("Failed to allocate resource descriptor\n");
+        return NULL;
+    }
+    RtlZeroMemory(PartialResourceList, Size);
+    PartialResourceList->Version = 1;
+    PartialResourceList->Revision = 1;
+    PartialResourceList->Count = 1;
+    PartialResourceList->PartialDescriptors[0].Type = 
CmResourceTypeDeviceSpecific;
+//  PartialResourceList->PartialDescriptors[0].ShareDisposition =
+//  PartialResourceList->PartialDescriptors[0].Flags =
+    PartialResourceList->PartialDescriptors[0].u.DeviceSpecificData.DataSize =
+        sizeof(CM_DISK_GEOMETRY_DEVICE_DATA);
+
+    /* Get pointer to geometry data */
+    DiskGeometry = (PVOID)(((ULONG_PTR)PartialResourceList) + 
sizeof(CM_PARTIAL_RESOURCE_LIST));
+
+    /* Get the disk geometry. Extended geometry isn't supported by hardware */
+    if (Pc98DiskGetDriveGeometry(DriveNumber, &Geometry))
+    {
+        DiskGeometry->BytesPerSector = Geometry.BytesPerSector;
+        DiskGeometry->NumberOfCylinders = Geometry.Cylinders;
+        DiskGeometry->SectorsPerTrack = Geometry.Sectors;
+        DiskGeometry->NumberOfHeads = Geometry.Heads;
+    }
+    else
+    {
+        TRACE("Reading disk geometry failed\n");
+        FrLdrHeapFree(PartialResourceList, TAG_HW_RESOURCE_LIST);
+        return NULL;
+    }
+    TRACE("Disk %x: %u Cylinders  %u Heads  %u Sectors  %u Bytes\n",
+          DriveNumber,
+          DiskGeometry->NumberOfCylinders,
+          DiskGeometry->NumberOfHeads,
+          DiskGeometry->SectorsPerTrack,
+          DiskGeometry->BytesPerSector);
+
+    *pSize = Size;
+    return PartialResourceList;
+}
+
+VOID
+DetectBiosDisks(
+    PCONFIGURATION_COMPONENT_DATA SystemKey,
+    PCONFIGURATION_COMPONENT_DATA BusKey)
+{
+    PCONFIGURATION_COMPONENT_DATA ControllerKey, DiskKey;
+    PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
+    PCM_INT13_DRIVE_PARAMETER Int13Drives;
+    GEOMETRY Geometry;
+    UCHAR DiskCount, DriveNumber;
+    USHORT i;
+    ULONG Size;
+
+    /* The pre-enumeration of the BIOS disks was already done in 
InitializeBootDevices() */
+    DiskCount = PcBiosDiskCount;
+
+    /* Use the floppy disk controller as our controller */
+    ControllerKey = DetectBiosFloppyController(BusKey);
+    if (!ControllerKey)
+    {
+        ERR("Failed to detect BIOS disk controller\n");
+        return;
+    }
+
+    /* Set 'Configuration Data' value */
+    Size = sizeof(CM_PARTIAL_RESOURCE_LIST) +
+           sizeof(CM_INT13_DRIVE_PARAMETER) * DiskCount;
+    PartialResourceList = FrLdrHeapAlloc(Size, TAG_HW_RESOURCE_LIST);
+    if (PartialResourceList == NULL)
+    {
+        ERR("Failed to allocate resource descriptor\n");
+        return;
+    }
+    RtlZeroMemory(PartialResourceList, Size);
+    PartialResourceList->Version = 1;
+    PartialResourceList->Revision = 1;
+    PartialResourceList->Count = 1;
+
+    PartialResourceList->PartialDescriptors[0].Type = 
CmResourceTypeDeviceSpecific;
+    PartialResourceList->PartialDescriptors[0].ShareDisposition = 0;
+    PartialResourceList->PartialDescriptors[0].Flags = 0;
+    PartialResourceList->PartialDescriptors[0].u.DeviceSpecificData.DataSize =
+        sizeof(CM_INT13_DRIVE_PARAMETER) * DiskCount;
+
+    /* Get hard disk Int13 geometry data */
+    Int13Drives = (PVOID)(((ULONG_PTR)PartialResourceList) + 
sizeof(CM_PARTIAL_RESOURCE_LIST));
+    for (i = 0; i < DiskCount; i++)
+    {
+        DriveNumber = 0x80 + i;
+
+        if (Pc98DiskGetDriveGeometry(DriveNumber, &Geometry))
+        {
+            Int13Drives[i].DriveSelect = DriveNumber;
+            Int13Drives[i].MaxCylinders = Geometry.Cylinders - 1;
+            Int13Drives[i].SectorsPerTrack = (USHORT)Geometry.Sectors;
+            Int13Drives[i].MaxHeads = (USHORT)Geometry.Heads - 1;
+            Int13Drives[i].NumberDrives = DiskCount;
+
+            TRACE("Disk %x: %u Cylinders  %u Heads  %u Sectors  %u Bytes\n",
+                  DriveNumber,
+                  Geometry.Cylinders - 1,
+                  Geometry.Heads - 1,
+                  Geometry.Sectors,
+                  Geometry.BytesPerSector);
+        }
+    }
+
+    /* Update the 'System' key's configuration data with BIOS INT13h 
information */
+    FldrSetConfigurationData(SystemKey, PartialResourceList, Size);
+
+    /* Create and fill subkey for each harddisk */
+    for (i = 0; i < DiskCount; i++)
+    {
+        PCHAR Identifier;
+
+        DriveNumber = 0x80 + i;
+
+        /* Get disk values */
+        PartialResourceList = GetHarddiskConfigurationData(DriveNumber, &Size);
+        Identifier = GetHarddiskIdentifier(DriveNumber);
+
+        /* Create disk key */
+        FldrCreateComponentKey(ControllerKey,
+                               PeripheralClass,
+                               DiskPeripheral,
+                               Output | Input,
+                               i,
+                               0xFFFFFFFF,
+                               Identifier,
+                               PartialResourceList,
+                               Size,
+                               &DiskKey);
+        TRACE("Created key: DiskPeripheral\\%d\n", i);
+    }
+}
+
+static VOID
+DetectPointerPeripheral(PCONFIGURATION_COMPONENT_DATA ControllerKey)
+{
+    PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
+    PCONFIGURATION_COMPONENT_DATA PeripheralKey;
+    ULONG Size;
+
+    /* TODO: Properly detect */
+
+    /* Set 'Configuration Data' value */
+    Size = sizeof(CM_PARTIAL_RESOURCE_LIST) -
+           sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
+    PartialResourceList = FrLdrHeapAlloc(Size, TAG_HW_RESOURCE_LIST);
+    if (PartialResourceList == NULL)
+    {
+        ERR("Failed to allocate resource descriptor\n");
+        return;
+    }
+    RtlZeroMemory(PartialResourceList, Size);
+    PartialResourceList->Version = 1;
+    PartialResourceList->Revision = 1;
+    PartialResourceList->Count = 0;
+
+    /* Create 'PointerPeripheral' key */
+    FldrCreateComponentKey(ControllerKey,
+                           PeripheralClass,
+                           PointerPeripheral,
+                           Input,
+                           0,
+                           0xFFFFFFFF,
+                           "NEC PC-9800 BUS MOUSE",
+                           PartialResourceList,
+                           Size,
+                           &PeripheralKey);
+    TRACE("Created key: PointerPeripheral\\0\n");
+}
+
+static VOID
+DetectPointerController(PCONFIGURATION_COMPONENT_DATA BusKey)
+{
+    PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
+    PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor;
+    PCONFIGURATION_COMPONENT_DATA ControllerKey;
+    ULONG Size;
+    UCHAR i;
+    UCHAR Index = 0;
+
+    /* Set 'Configuration Data' value */
+    Size = sizeof(CM_PARTIAL_RESOURCE_LIST) +
+           (HiResoMachine ? 6 : 5) * sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
+    PartialResourceList = FrLdrHeapAlloc(Size, TAG_HW_RESOURCE_LIST);
+    if (PartialResourceList == NULL)
+    {
+        ERR("Failed to allocate resource descriptor\n");
+        return;
+    }
+    RtlZeroMemory(PartialResourceList, Size);
+    PartialResourceList->Version = 1;
+    PartialResourceList->Revision = 1;
+    PartialResourceList->Count = (HiResoMachine ? 7 : 6);
+
+    /* Set I/O ports */
+    for (i = 0; i < 4; i++)
+    {
+        PartialDescriptor = &PartialResourceList->PartialDescriptors[Index++];
+        PartialDescriptor->Type = CmResourceTypePort;
+        PartialDescriptor->ShareDisposition = CmResourceShareDeviceExclusive;
+        PartialDescriptor->Flags = CM_RESOURCE_PORT_IO;
+        PartialDescriptor->u.Port.Start.LowPart = (HiResoMachine ? 0x61 : 
0x7FD9) + i * 2;
+        PartialDescriptor->u.Port.Start.HighPart = 0;
+        PartialDescriptor->u.Port.Length = 1;
+    }
+    PartialDescriptor = &PartialResourceList->PartialDescriptors[Index++];
+    PartialDescriptor->Type = CmResourceTypePort;
+    PartialDescriptor->ShareDisposition = CmResourceShareDeviceExclusive;
+    PartialDescriptor->Flags = CM_RESOURCE_PORT_IO;
+    PartialDescriptor->u.Port.Start.LowPart = (HiResoMachine ? 0x869 : 0xBFDB);
+    PartialDescriptor->u.Port.Start.HighPart = 0;
+    PartialDescriptor->u.Port.Length = 1;
+    if (HiResoMachine)
+    {
+        PartialDescriptor = &PartialResourceList->PartialDescriptors[Index++];
+        PartialDescriptor->Type = CmResourceTypePort;
+        PartialDescriptor->ShareDisposition = CmResourceShareDeviceExclusive;
+        PartialDescriptor->Flags = CM_RESOURCE_PORT_IO;
+        PartialDescriptor->u.Port.Start.LowPart = 0x98D7;
+        PartialDescriptor->u.Port.Start.HighPart = 0;
+        PartialDescriptor->u.Port.Length = 1;
+    }
+
+    /* Set Interrupt */
+    PartialDescriptor = &PartialResourceList->PartialDescriptors[Index++];
+    PartialDescriptor->Type = CmResourceTypeInterrupt;
+    PartialDescriptor->ShareDisposition = CmResourceShareUndetermined;
+    PartialDescriptor->Flags = CM_RESOURCE_INTERRUPT_LATCHED;
+    PartialDescriptor->u.Interrupt.Level = 13;
+    PartialDescriptor->u.Interrupt.Vector = 13;
+    PartialDescriptor->u.Interrupt.Affinity = 0xFFFFFFFF;
+
+    /* Create controller key */
+    FldrCreateComponentKey(BusKey,
+                           ControllerClass,
+                           PointerController,
+                           Input,
+                           0,
+                           0xFFFFFFFF,
+                           NULL,
+                           PartialResourceList,
+                           Size,
+                           &ControllerKey);
+    TRACE("Created key: PointerController\\0\n");
+
+    DetectPointerPeripheral(ControllerKey);
+}
+
+static VOID
+DetectKeyboardPeripheral(PCONFIGURATION_COMPONENT_DATA ControllerKey)
+{
+    PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
+    PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor;
+    PCM_KEYBOARD_DEVICE_DATA KeyboardData;
+    PCONFIGURATION_COMPONENT_DATA PeripheralKey;
+    CHAR Identifier[80];
+    ULONG Size;
+    REGS Regs;
+    UCHAR KeyboardType = ((*(PUCHAR)MEM_KEYB_TYPE & 0x40) >> 5) |
+                         ((*(PUCHAR)MEM_KEYB_TYPE & 0x08) >> 3);
+
+    /* TODO: Properly detect */
+
+    /* Set 'Configuration Data' value */
+    Size = sizeof(CM_PARTIAL_RESOURCE_LIST) +
+           sizeof(CM_KEYBOARD_DEVICE_DATA);
+    PartialResourceList = FrLdrHeapAlloc(Size, TAG_HW_RESOURCE_LIST);
+    if (PartialResourceList == NULL)
+    {
+        ERR("Failed to allocate resource descriptor\n");
+        return;
+    }
+    RtlZeroMemory(PartialResourceList, Size);
+    PartialResourceList->Version = 1;
+    PartialResourceList->Revision = 1;
+    PartialResourceList->Count = 1;
+
+    PartialDescriptor = &PartialResourceList->PartialDescriptors[0];
+    PartialDescriptor->Type = CmResourceTypeDeviceSpecific;
+    PartialDescriptor->ShareDisposition = CmResourceShareUndetermined;
+    PartialDescriptor->u.DeviceSpecificData.DataSize = 
sizeof(CM_KEYBOARD_DEVICE_DATA);
+
+    /* Int 18h AH=02h
+     * KEYBOARD - GET SHIFT FLAGS
+     *
+     * Return:
+     * AL - shift flags
+     */
+    Regs.b.ah = 0x02;
+    Int386(0x18, &Regs, &Regs);
+
+    KeyboardData = (PCM_KEYBOARD_DEVICE_DATA)(PartialDescriptor + 1);
+    KeyboardData->Version = 1;
+    KeyboardData->Revision = 1;
+    KeyboardData->Type = 7;
+    KeyboardData->Subtype = 1;
+    KeyboardData->KeyboardFlags = (Regs.b.al & 0x08) |
+                                  ((Regs.b.al & 0x02) << 6) |
+                                  ((Regs.b.al & 0x10) << 2) |
+                                  ((Regs.b.al & 0x01) << 1);
+
+    if (KeyboardType == 0)
+        RtlStringCbPrintfA(Identifier, sizeof(Identifier), "PC98_NmodeKEY");
+    else if (KeyboardType == 2)
+        RtlStringCbPrintfA(Identifier, sizeof(Identifier), "PC98_106KEY");
+    else
+        RtlStringCbPrintfA(Identifier, sizeof(Identifier), "PC98_LaptopKEY");
+
+    /* Create controller key */
+    FldrCreateComponentKey(ControllerKey,
+                           PeripheralClass,
+                           KeyboardPeripheral,
+                           Input | ConsoleIn,
+                           0,
+                           0xFFFFFFFF,
+                           Identifier,
+                           PartialResourceList,
+                           Size,
+                           &PeripheralKey);
+    TRACE("Created key: KeyboardPeripheral\\0\n");
+}
+
+static VOID
+DetectKeyboardController(PCONFIGURATION_COMPONENT_DATA BusKey)
+{
+    PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
+    PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor;
+    PCONFIGURATION_COMPONENT_DATA ControllerKey;
+    ULONG Size;
+    UCHAR i;
+
+    if (!CpDoesPortExist((PUCHAR)0x41))
+        return;
+
+    /* Set 'Configuration Data' value */
+    Size = sizeof(CM_PARTIAL_RESOURCE_LIST) +
+           2 * sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
+    PartialResourceList = FrLdrHeapAlloc(Size, TAG_HW_RESOURCE_LIST);
+    if (PartialResourceList == NULL)
+    {
+        ERR("Failed to allocate resource descriptor\n");
+        return;
+    }
+    RtlZeroMemory(PartialResourceList, Size);
+    PartialResourceList->Version = 1;
+    PartialResourceList->Revision = 1;
+    PartialResourceList->Count = 3;
+
+    /* Set I/O ports */
+    for (i = 0; i < 2; i++)
+    {
+        PartialDescriptor = &PartialResourceList->PartialDescriptors[i];
+        PartialDescriptor->Type = CmResourceTypePort;
+        PartialDescriptor->ShareDisposition = CmResourceShareDeviceExclusive;
+        PartialDescriptor->Flags = CM_RESOURCE_PORT_IO;
+        PartialDescriptor->u.Port.Start.LowPart = 0x41 + i * 2;
+        PartialDescriptor->u.Port.Start.HighPart = 0;
+        PartialDescriptor->u.Port.Length = 1;
+    }
+
+    /* Set Interrupt */
+    PartialDescriptor = &PartialResourceList->PartialDescriptors[2];
+    PartialDescriptor->Type = CmResourceTypeInterrupt;
+    PartialDescriptor->ShareDisposition = CmResourceShareUndetermined;
+    PartialDescriptor->Flags = CM_RESOURCE_INTERRUPT_LATCHED;
+    PartialDescriptor->u.Interrupt.Level = 1;
+    PartialDescriptor->u.Interrupt.Vector = 1;
+    PartialDescriptor->u.Interrupt.Affinity = 0xFFFFFFFF;
+
+    /* Create controller key */
+    FldrCreateComponentKey(BusKey,
+                           ControllerClass,
+                           KeyboardController,
+                           Input | ConsoleIn,
+                           0,
+                           0xFFFFFFFF,
+                           NULL,
+                           PartialResourceList,
+                           Size,
+                           &ControllerKey);
+    TRACE("Created key: KeyboardController\\0\n");
+
+    DetectKeyboardPeripheral(ControllerKey);
+}
+
+static VOID
+DetectParallelPorts(PCONFIGURATION_COMPONENT_DATA BusKey)
+{
+    PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
+    PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor;
+    PCONFIGURATION_COMPONENT_DATA ControllerKey;
+    ULONG Size;
+    UCHAR i;
+    UCHAR Index = 0;
+
+    /* TODO: Properly detect */
+
+    /* Set 'Configuration Data' value */
+    Size = sizeof(CM_PARTIAL_RESOURCE_LIST) +
+           7 * sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
+    PartialResourceList = FrLdrHeapAlloc(Size, TAG_HW_RESOURCE_LIST);
+    if (PartialResourceList == NULL)
+    {
+        ERR("Failed to allocate resource descriptor\n");
+        return;
+    }
+    RtlZeroMemory(PartialResourceList, Size);
+    PartialResourceList->Version = 1;
+    PartialResourceList->Revision = 1;
+    PartialResourceList->Count = 8;
+
+    /* Set I/O ports */
+    for (i = 0; i < 4; i++)
+    {
+        PartialDescriptor = &PartialResourceList->PartialDescriptors[Index++];
+        PartialDescriptor->Type = CmResourceTypePort;
+        PartialDescriptor->ShareDisposition = CmResourceShareDeviceExclusive;
+        PartialDescriptor->Flags = CM_RESOURCE_PORT_IO;
+        PartialDescriptor->u.Port.Start.LowPart = 0x40 + i * 2;
+        PartialDescriptor->u.Port.Start.HighPart = 0;
+        PartialDescriptor->u.Port.Length = 3;
+    }
+
+    PartialDescriptor = &PartialResourceList->PartialDescriptors[Index++];
+    PartialDescriptor->Type = CmResourceTypePort;
+    PartialDescriptor->ShareDisposition = CmResourceShareDeviceExclusive;
+    PartialDescriptor->Flags = CM_RESOURCE_PORT_IO;
+    PartialDescriptor->u.Port.Start.LowPart = 0x140;
+    PartialDescriptor->u.Port.Start.HighPart = 0;
+    PartialDescriptor->u.Port.Length = 3;
+
+    PartialDescriptor = &PartialResourceList->PartialDescriptors[Index++];
+    PartialDescriptor->Type = CmResourceTypePort;
+    PartialDescriptor->ShareDisposition = CmResourceShareDeviceExclusive;
+    PartialDescriptor->Flags = CM_RESOURCE_PORT_IO;
+    PartialDescriptor->u.Port.Start.LowPart = 0x149;
+    PartialDescriptor->u.Port.Start.HighPart = 0;
+    PartialDescriptor->u.Port.Length = 1;
+
+    PartialDescriptor = &PartialResourceList->PartialDescriptors[Index++];
+    PartialDescriptor->Type = CmResourceTypePort;
+    PartialDescriptor->ShareDisposition = CmResourceShareDeviceExclusive;
+    PartialDescriptor->Flags = CM_RESOURCE_PORT_IO;
+    PartialDescriptor->u.Port.Start.LowPart = 0x14B;
+    PartialDescriptor->u.Port.Start.HighPart = 0;
+    PartialDescriptor->u.Port.Length = 4;
+
+    /* Set Interrupt */
+    PartialDescriptor = &PartialResourceList->PartialDescriptors[Index++];
+    PartialDescriptor->Type = CmResourceTypeInterrupt;
+    PartialDescriptor->ShareDisposition = CmResourceShareUndetermined;
+    PartialDescriptor->Flags = CM_RESOURCE_INTERRUPT_LATCHED;
+    PartialDescriptor->u.Interrupt.Level = 14;
+    PartialDescriptor->u.Interrupt.Vector = 14;
+    PartialDescriptor->u.Interrupt.Affinity = 0xFFFFFFFF;
+
+    /* Create controller key */
+    FldrCreateComponentKey(BusKey,
+                           ControllerClass,
+                           ParallelController,
+                           Output,
+                           0,
+                           0xFFFFFFFF,
+                           "PARALLEL1",
+                           PartialResourceList,
+                           Size,
+                           &ControllerKey);
+    TRACE("Created key: ParallelController\\0\n");
+}
+
+static VOID
+DetectSerialPorts(PCONFIGURATION_COMPONENT_DATA BusKey)
+{
+    PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
+    PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor;
+    PCM_SERIAL_DEVICE_DATA SerialDeviceData;
+    PCONFIGURATION_COMPONENT_DATA ControllerKey;
+    CHAR Identifier[80];
+    UCHAR i;
+    ULONG Size;
+    UCHAR FifoStatus;
+    BOOLEAN HasFifo;
+    UCHAR Index = 0;
+    ULONG ControllerNumber = 0;
+
+    if (CpDoesPortExist((PUCHAR)0x30))
+    {
+        RtlStringCbPrintfA(Identifier, sizeof(Identifier), "COM%d", 
ControllerNumber + 1);
+
+        FifoStatus = READ_PORT_UCHAR((PUCHAR)0x136) & 0x40;
+        StallExecutionProcessor(5);
+        HasFifo = ((READ_PORT_UCHAR((PUCHAR)0x136) & 0x40) != FifoStatus);
+
+        /* Set 'Configuration Data' value */
+        Size = sizeof(CM_PARTIAL_RESOURCE_LIST) +
+               (HasFifo ? 10 : 3) * sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR) +
+               sizeof(CM_SERIAL_DEVICE_DATA);
+        PartialResourceList = FrLdrHeapAlloc(Size, TAG_HW_RESOURCE_LIST);
+        if (PartialResourceList == NULL)
+        {
+            ERR("Failed to allocate resource descriptor\n");
+            return;
+        }
+        RtlZeroMemory(PartialResourceList, Size);
+        PartialResourceList->Version = 1;
+        PartialResourceList->Revision = 1;
+        PartialResourceList->Count = (HasFifo ? 11 : 4);
+
+        /* Set I/O ports */
+        for (i = 0; i < 2; i++)
+        {
+            PartialDescriptor = 
&PartialResourceList->PartialDescriptors[Index++];
+            PartialDescriptor->Type = CmResourceTypePort;
+            PartialDescriptor->ShareDisposition = 
CmResourceShareDeviceExclusive;
+            PartialDescriptor->Flags = CM_RESOURCE_PORT_IO;
+            PartialDescriptor->u.Port.Start.LowPart = 0x30 + i * 2;
+            PartialDescriptor->u.Port.Start.HighPart = 0;
+            PartialDescriptor->u.Port.Length = 1;
+        }
+        if (HasFifo)
+        {
+            for (i = 0; i < 7; i++)
+            {
+                PartialDescriptor = 
&PartialResourceList->PartialDescriptors[Index++];
+                PartialDescriptor->Type = CmResourceTypePort;
+                PartialDescriptor->ShareDisposition = 
CmResourceShareDeviceExclusive;
+                PartialDescriptor->Flags = CM_RESOURCE_PORT_IO;
+                PartialDescriptor->u.Port.Start.LowPart = 0x130 + i * 2;
+                PartialDescriptor->u.Port.Start.HighPart = 0;
+                PartialDescriptor->u.Port.Length = 1;
+            }
+        }
+
+        /* Set Interrupt */
+        PartialDescriptor = &PartialResourceList->PartialDescriptors[Index++];
+        PartialDescriptor->Type = CmResourceTypeInterrupt;
+        PartialDescriptor->ShareDisposition = CmResourceShareUndetermined;
+        PartialDescriptor->Flags = CM_RESOURCE_INTERRUPT_LATCHED;
+        PartialDescriptor->u.Interrupt.Level = 4;
+        PartialDescriptor->u.Interrupt.Vector = 4;
+        PartialDescriptor->u.Interrupt.Affinity = 0xFFFFFFFF;
+
+        /* Set serial data (device specific) */
+        PartialDescriptor = &PartialResourceList->PartialDescriptors[Index++];
+        PartialDescriptor->Type = CmResourceTypeDeviceSpecific;
+        PartialDescriptor->ShareDisposition = CmResourceShareUndetermined;
+        PartialDescriptor->Flags = 0;
+        PartialDescriptor->u.DeviceSpecificData.DataSize = 
sizeof(CM_SERIAL_DEVICE_DATA);
+
+        SerialDeviceData = 
(PCM_SERIAL_DEVICE_DATA)&PartialResourceList->PartialDescriptors[Index++];
+        SerialDeviceData->BaudClock = (*(PUCHAR)MEM_BIOS_FLAG1 & 
SYSTEM_CLOCK_8MHZ_FLAG) ?
+                                       TIMER_FREQUENCY_1 : TIMER_FREQUENCY_2;
+
+        /* Create controller key */
+        FldrCreateComponentKey(BusKey,
+                               ControllerClass,
+                               SerialController,
+                               Output | Input | ConsoleIn | ConsoleOut,
+                               ControllerNumber,
+                               0xFFFFFFFF,
+                               Identifier,
+                               PartialResourceList,
+                               Size,
+                               &ControllerKey);
+        TRACE("Created key: SerialController\\%d\n", ControllerNumber);
+
+        ++ControllerNumber;
+    }
+
+    if (CpDoesPortExist((PUCHAR)0x238))
+    {
+        Index = 0;
+
+        RtlStringCbPrintfA(Identifier, sizeof(Identifier), "COM%d", 
ControllerNumber + 1);
+
+        /* Set 'Configuration Data' value */
+        Size = sizeof(CM_PARTIAL_RESOURCE_LIST) +
+               2 * sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR) +
+               sizeof(CM_SERIAL_DEVICE_DATA);
+        PartialResourceList = FrLdrHeapAlloc(Size, TAG_HW_RESOURCE_LIST);
+        if (PartialResourceList == NULL)
+        {
+            ERR("Failed to allocate resource descriptor\n");
+            return;
+        }
+        RtlZeroMemory(PartialResourceList, Size);
+        PartialResourceList->Version = 1;
+        PartialResourceList->Revision = 1;
+        PartialResourceList->Count = 3;
+
+        /* Set I/O ports */
+        PartialDescriptor = &PartialResourceList->PartialDescriptors[Index++];
+        PartialDescriptor->Type = CmResourceTypePort;
+        PartialDescriptor->ShareDisposition = CmResourceShareDeviceExclusive;
+        PartialDescriptor->Flags = CM_RESOURCE_PORT_IO;
+        PartialDescriptor->u.Port.Start.LowPart = 0x238;
+        PartialDescriptor->u.Port.Start.HighPart = 0;
+        PartialDescriptor->u.Port.Length = 8;
+
+        /* Set Interrupt */
+        PartialDescriptor = &PartialResourceList->PartialDescriptors[Index++];
+        PartialDescriptor->Type = CmResourceTypeInterrupt;
+        PartialDescriptor->ShareDisposition = CmResourceShareUndetermined;
+        PartialDescriptor->Flags = CM_RESOURCE_INTERRUPT_LATCHED;
+        PartialDescriptor->u.Interrupt.Level = 5;
+        PartialDescriptor->u.Interrupt.Vector = 5;
+        PartialDescriptor->u.Interrupt.Affinity = 0xFFFFFFFF;
+
+        /* Set serial data (device specific) */
+        PartialDescriptor = &PartialResourceList->PartialDescriptors[Index++];
+        PartialDescriptor->Type = CmResourceTypeDeviceSpecific;
+        PartialDescriptor->ShareDisposition = CmResourceShareUndetermined;
+        PartialDescriptor->Flags = 0;
+        PartialDescriptor->u.DeviceSpecificData.DataSize = 
sizeof(CM_SERIAL_DEVICE_DATA);
+
+        SerialDeviceData = 
(PCM_SERIAL_DEVICE_DATA)&PartialResourceList->PartialDescriptors[Index++];
+        SerialDeviceData->BaudClock = 1843200;
+
+        /* Create controller key */
+        FldrCreateComponentKey(BusKey,
+                               ControllerClass,
+                               SerialController,
+                               Output | Input | ConsoleIn | ConsoleOut,
+                               ControllerNumber,
+                               0xFFFFFFFF,
+                               Identifier,
+                               PartialResourceList,
+                               Size,
+                               &ControllerKey);
+        TRACE("Created key: SerialController\\%d\n", ControllerNumber);
+
+        ++ControllerNumber;
+    }
+}
+
+static VOID
+DetectCBusBios(PCONFIGURATION_COMPONENT_DATA SystemKey, ULONG *BusNumber)
+{
+    PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
+    PCONFIGURATION_COMPONENT_DATA BusKey;
+    ULONG Size;
+
+    /* Set 'Configuration Data' value */
+    Size = sizeof(CM_PARTIAL_RESOURCE_LIST) -
+           sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
+    PartialResourceList = FrLdrHeapAlloc(Size, TAG_HW_RESOURCE_LIST);
+    if (PartialResourceList == NULL)
+    {
+        ERR("Failed to allocate resource descriptor\n");
+        return;
+    }
+    RtlZeroMemory(PartialResourceList, Size);
+    PartialResourceList->Version = 1;
+    PartialResourceList->Revision = 1;
+    PartialResourceList->Count = 0;
+
+    /* Create bus key */
+    FldrCreateComponentKey(SystemKey,
+                           AdapterClass,
+                           MultiFunctionAdapter,
+                           0x0,
+                           0,
+                           0xFFFFFFFF,
+                           "ISA",
+                           PartialResourceList,
+                           Size,
+                           &BusKey);
+
+    /* Increment bus number */
+    (*BusNumber)++;
+
+    /* Detect C-bus/BIOS devices */
+    DetectBiosDisks(SystemKey, BusKey);
+    DetectSerialPorts(BusKey);
+    DetectParallelPorts(BusKey);
+    DetectKeyboardController(BusKey);
+    DetectPointerController(BusKey);
+
+    /* FIXME: Detect more C-bus devices */
+}
+
+static VOID
+DetectNesaBios(PCONFIGURATION_COMPONENT_DATA SystemKey, ULONG *BusNumber)
+{
+    PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
+    PCONFIGURATION_COMPONENT_DATA BusKey;
+    ULONG Size;
+
+    if (!((*(PUCHAR)MEM_BIOS_FLAG5) & NESA_BUS_FLAG))
+        return;
+
+    /* Set 'Configuration Data' value */
+    Size = sizeof(CM_PARTIAL_RESOURCE_LIST) -
+           sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
+    PartialResourceList = FrLdrHeapAlloc(Size, TAG_HW_RESOURCE_LIST);
+    if (PartialResourceList == NULL)
+    {
+        ERR("Failed to allocate resource descriptor\n");
+        return;
+    }
+    RtlZeroMemory(PartialResourceList, Size);
+    PartialResourceList->Version = 1;
+    PartialResourceList->Revision = 1;
+    PartialResourceList->Count = 0;
+
+    /* Create bus key */
+    FldrCreateComponentKey(SystemKey,
+                           AdapterClass,
+                           MultiFunctionAdapter,
+                           0x0,
+                           0,
+                           0xFFFFFFFF,
+                           "EISA",
+                           PartialResourceList,
+                           Size,
+                           &BusKey);
+
+    /* Increment bus number */
+    (*BusNumber)++;
+}
+
+// FIXME: Copied from machpc.c
+static VOID
+DetectPnpBios(PCONFIGURATION_COMPONENT_DATA SystemKey, ULONG *BusNumber)
+{
+    PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
+    PCM_PNP_BIOS_DEVICE_NODE DeviceNode;
+    PCM_PNP_BIOS_INSTALLATION_CHECK InstData;
+    PCONFIGURATION_COMPONENT_DATA BusKey;
+    ULONG x;
+    ULONG NodeSize = 0;
+    ULONG NodeCount = 0;
+    UCHAR NodeNumber;
+    ULONG FoundNodeCount;
+    int i;
+    ULONG PnpBufferSize;
+    ULONG PnpBufferSizeLimit;
+    ULONG Size;
+    char *Ptr;
+
+    InstData = (PCM_PNP_BIOS_INSTALLATION_CHECK)PnpBiosSupported();
+    if (InstData == NULL || strncmp((CHAR*)InstData->Signature, "$PnP", 4))
+    {
+        TRACE("PnP-BIOS not supported\n");
+        return;
+    }
+
+    TRACE("PnP-BIOS supported\n");
+    TRACE("Signature '%c%c%c%c'\n",
+          InstData->Signature[0], InstData->Signature[1],
+          InstData->Signature[2], InstData->Signature[3]);
+
+    x = PnpBiosGetDeviceNodeCount(&NodeSize, &NodeCount);
+    if (x == 0x82)
+    {
+        TRACE("PnP-BIOS function 'Get Number of System Device Nodes' not 
supported\n");
+        return;
+    }
+
+    NodeCount &= 0xFF; // needed since some fscked up BIOSes return
+    // wrong info (e.g. Mac Virtual PC)
+    // e.g. look: http://my.execpc.com/~geezer/osd/pnp/pnp16.c
+    if (x != 0 || NodeSize == 0 || NodeCount == 0)
+    {
+        ERR("PnP-BIOS failed to enumerate device nodes\n");
+        return;
+    }
+    TRACE("MaxNodeSize %u  NodeCount %u\n", NodeSize, NodeCount);
+    TRACE("Estimated buffer size %u\n", NodeSize * NodeCount);
+
+    /* Set 'Configuration Data' value */
+    PnpBufferSizeLimit = sizeof(CM_PNP_BIOS_INSTALLATION_CHECK)
+                         + (NodeSize * NodeCount);
+    Size = sizeof(CM_PARTIAL_RESOURCE_LIST) + PnpBufferSizeLimit;
+    PartialResourceList = FrLdrHeapAlloc(Size, TAG_HW_RESOURCE_LIST);
+    if (PartialResourceList == NULL)
+    {
+        ERR("Failed to allocate resource descriptor\n");
+        return;
+    }
+    RtlZeroMemory(PartialResourceList, Size);
+
+    /* Initialize resource descriptor */
+    PartialResourceList->Version = 1;
+    PartialResourceList->Revision = 1;
+    PartialResourceList->Count = 1;
+    PartialResourceList->PartialDescriptors[0].Type =
+        CmResourceTypeDeviceSpecific;
+    PartialResourceList->PartialDescriptors[0].ShareDisposition =
+        CmResourceShareUndetermined;
+
+    /* The buffer starts after PartialResourceList->PartialDescriptors[0] */
+    Ptr = (char *)(PartialResourceList + 1);
+
+    /* Set installation check data */
+    RtlCopyMemory(Ptr, InstData, sizeof(CM_PNP_BIOS_INSTALLATION_CHECK));
+    Ptr += sizeof(CM_PNP_BIOS_INSTALLATION_CHECK);
+    PnpBufferSize = sizeof(CM_PNP_BIOS_INSTALLATION_CHECK);
+
+    /* Copy device nodes */
+    FoundNodeCount = 0;
+    for (i = 0; i < 0xFF; i++)
+    {
+        NodeNumber = (UCHAR)i;
+
+        x = PnpBiosGetDeviceNode(&NodeNumber, DiskReadBuffer);
+        if (x == 0)
+        {
+            DeviceNode = (PCM_PNP_BIOS_DEVICE_NODE)DiskReadBuffer;
+
+            TRACE("Node: %u  Size %u (0x%x)\n",
+                  DeviceNode->Node,
+                  DeviceNode->Size,
+                  DeviceNode->Size);
+
+            if (PnpBufferSize + DeviceNode->Size > PnpBufferSizeLimit)
+            {
+                ERR("Buffer too small! Ignoring remaining device nodes.\n");
+                break;
+            }
+
+            RtlCopyMemory(Ptr, DeviceNode, DeviceNode->Size);
+
+            Ptr += DeviceNode->Size;
+            PnpBufferSize += DeviceNode->Size;
+
+            FoundNodeCount++;
+            if (FoundNodeCount >= NodeCount)
+                break;
+        }
+    }
+
+    /* Set real data size */
+    PartialResourceList->PartialDescriptors[0].u.DeviceSpecificData.DataSize =
+        PnpBufferSize;
+    Size = sizeof(CM_PARTIAL_RESOURCE_LIST) + PnpBufferSize;
+
+    TRACE("Real buffer size: %u\n", PnpBufferSize);
+    TRACE("Resource size: %u\n", Size);
+
+    /* Create component key */
+    FldrCreateComponentKey(SystemKey,
+                           AdapterClass,
+                           MultiFunctionAdapter,
+                           0x0,
+                           0x0,
+                           0xFFFFFFFF,
+                           "PNP BIOS",
+                           PartialResourceList,
+                           Size,
+                           &BusKey);
+
+    (*BusNumber)++;
+}
+
+PCONFIGURATION_COMPONENT_DATA
+Pc98HwDetect(VOID)
+{
+    PCONFIGURATION_COMPONENT_DATA SystemKey;
+    ULONG BusNumber = 0;
+
+    TRACE("DetectHardware()\n");
+
+    /* Create the 'System' key */
+    FldrCreateSystemKey(&SystemKey);
+    FldrSetIdentifier(SystemKey, "NEC PC-98");
+
+    GetHarddiskConfigurationData = Pc98GetHarddiskConfigurationData;
+    FindPciBios = PcFindPciBios;
+
+    /* Detect buses */
+    DetectPciBios(SystemKey, &BusNumber);
+    DetectApmBios(SystemKey, &BusNumber);
+    DetectPnpBios(SystemKey, &BusNumber);
+    DetectNesaBios(SystemKey, &BusNumber);
+    DetectCBusBios(SystemKey, &BusNumber);
+    DetectAcpiBios(SystemKey, &BusNumber);
+    // TODO: Detect more buses
+
+    // TODO: Collect the ROM blocks and append their
+    // CM_ROM_BLOCK data into the 'System' key's configuration data.
+
+    TRACE("DetectHardware() Done\n");
+    return SystemKey;
+}
+
+UCHAR
+Pc98GetFloppyCount(VOID)
+{
+    USHORT DiskEquipment = *(PUSHORT)MEM_DISK_EQUIP & 
~(*(PUCHAR)MEM_RDISK_EQUIP);
+    UCHAR DiskMask;
+    UCHAR FloppyCount = 0;
+
+    for (DiskMask = 0x01; DiskMask != 0; DiskMask <<= 1)
+    {
+        if (FIRSTBYTE(DiskEquipment) & DiskMask)
+            ++FloppyCount;
+    }
+
+    for (DiskMask = 0x10; DiskMask != 0; DiskMask <<= 1)
+    {
+        if (SECONDBYTE(DiskEquipment) & DiskMask)
+            ++FloppyCount;
+    }
+
+    return FloppyCount;
+}
+
+VOID __cdecl DiskStopFloppyMotor(VOID)
+{
+    WRITE_PORT_UCHAR((PUCHAR)(FDC1_IO_BASE + FDC_o_CONTROL), 0);
+    WRITE_PORT_UCHAR((PUCHAR)(FDC2_IO_BASE + FDC_o_CONTROL), 0);
+}
+
+// FIXME: 1) Copied from pchw.c 2) Should be done inside MachInit.
+VOID
+FrLdrCheckCpuCompatibility(VOID)
+{
+    INT CpuInformation[4] = {-1};
+    ULONG NumberOfIds;
+
+    /* Check if the processor first supports ID 1 */
+    __cpuid(CpuInformation, 0);
+
+    NumberOfIds = CpuInformation[0];
+
+    if (NumberOfIds == 0)
+    {
+        FrLdrBugCheckWithMessage(MISSING_HARDWARE_REQUIREMENTS,
+                                 __FILE__,
+                                 __LINE__,
+                                 "ReactOS requires the CPUID instruction to 
return "
+                                 "more than one supported ID.\n\n");
+    }
+
+    /* NumberOfIds will be greater than 1 if the processor is new enough */
+    if (NumberOfIds == 1)
+    {
+        INT ProcessorFamily;
+
+        /* Get information */
+        __cpuid(CpuInformation, 1);
+
+        ProcessorFamily = (CpuInformation[0] >> 8) & 0xF;
+
+        /* If it's Family 4 or lower, bugcheck */
+        if (ProcessorFamily < 5)
+        {
+            FrLdrBugCheckWithMessage(MISSING_HARDWARE_REQUIREMENTS,
+                                     __FILE__,
+                                     __LINE__,
+                                     "Processor is too old (family %u < 5)\n"
+                                     "ReactOS requires a Pentium-level 
processor or newer.",
+                                     ProcessorFamily);
+        }
+    }
+}
diff --git a/boot/freeldr/freeldr/arch/i386/pc98/pc98mem.c 
b/boot/freeldr/freeldr/arch/i386/pc98/pc98mem.c
new file mode 100644
index 00000000000..3805470b8f4
--- /dev/null
+++ b/boot/freeldr/freeldr/arch/i386/pc98/pc98mem.c
@@ -0,0 +1,105 @@
+/*
+ * PROJECT:     FreeLoader
+ * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE:     Hardware-specific creating a memory map routine for NEC PC-98 
series
+ * COPYRIGHT:   Copyright 2020 Dmitry Borisov ([email protected])
+ */
+
+/* INCLUDES 
*******************************************************************/
+
+#include <freeldr.h>
+
+#include <debug.h>
+DBG_DEFAULT_CHANNEL(MEMORY);
+
+/* pcmem.c */
+extern VOID
+SetMemory(
+    PFREELDR_MEMORY_DESCRIPTOR MemoryMap,
+    ULONG_PTR BaseAddress,
+    SIZE_T Size,
+    TYPE_OF_MEMORY MemoryType);
+
+/* pcmem.c */
+extern VOID
+ReserveMemory(
+    PFREELDR_MEMORY_DESCRIPTOR MemoryMap,
+    ULONG_PTR BaseAddress,
+    SIZE_T Size,
+    TYPE_OF_MEMORY MemoryType,
+    PCHAR Usage);
+
+/* pcmem.c */
+extern ULONG
+PcMemFinalizeMemoryMap(PFREELDR_MEMORY_DESCRIPTOR MemoryMap);
+
+extern BOOLEAN HiResoMachine;
+
+/* GLOBALS 
********************************************************************/
+
+#define KB 1024
+#define MB (KB * KB)
+
+static FREELDR_MEMORY_DESCRIPTOR Pc98MemoryMap[MAX_BIOS_DESCRIPTORS + 1];
+
+/* FUNCTIONS 
******************************************************************/
+
+PFREELDR_MEMORY_DESCRIPTOR
+Pc98MemGetMemoryMap(ULONG *MemoryMapSize)
+{
+    USHORT ConventionalMemory, ExtendedMemory;
+    ULONG ExtendedMemory16;
+
+    TRACE("Pc98MemGetMemoryMap()\n");
+
+    RtlZeroMemory(&PcBiosMemoryMap, sizeof(BIOS_MEMORY_MAP) * 
MAX_BIOS_DESCRIPTORS);
+    PcBiosMapCount = 0;
+
+    ConventionalMemory = ((*(PUCHAR)MEM_BIOS_FLAG1 & CONVENTIONAL_MEMORY_SIZE) 
+ 1) * 128;
+    ExtendedMemory = *(PUCHAR)MEM_EXPMMSZ * 128;
+    ExtendedMemory16 = (*(PUCHAR)MEM_EXPMMSZ16M_LOW + 
(*(PUCHAR)MEM_EXPMMSZ16M_HIGH << 8)) * 1024;
+
+    if (ConventionalMemory > 640 && !HiResoMachine)
+        ConventionalMemory = 640;
+
+    TRACE("Total conventional memory %d kB available.\n", ConventionalMemory);
+    TRACE("Total extended memory %d kB available.\n", ExtendedMemory);
+    TRACE("Total extended high memory %d kB available.\n", ExtendedMemory16);
+    TRACE("Installed physical memory %d kB.\n", ConventionalMemory + 
ExtendedMemory + ExtendedMemory16);
+
+    /* First, setup allowed ranges */
+    SetMemory(Pc98MemoryMap, 0x0000600, ConventionalMemory * KB, LoaderFree);
+    SetMemory(Pc98MemoryMap, 0x0100000, ExtendedMemory * KB, LoaderFree);
+    SetMemory(Pc98MemoryMap, 0x1000000, ExtendedMemory16 * KB, LoaderFree);
+
+    /* Next, setup some protected ranges */
+    if (HiResoMachine)
+    {
+        SetMemory(Pc98MemoryMap, 0x000000, 1 * KB, LoaderFirmwarePermanent);  
/* Real mode IVT */
+        SetMemory(Pc98MemoryMap, 0x000400, 512, LoaderFirmwarePermanent);     
/* Real mode BDA */
+        SetMemory(Pc98MemoryMap, 0x080000, 256 * KB, 
LoaderFirmwarePermanent);/* Memory Window */
+        SetMemory(Pc98MemoryMap, 0x0C0000, 128 * KB, 
LoaderFirmwarePermanent);/* VRAM */
+        SetMemory(Pc98MemoryMap, 0x0E0000, 16 * KB, LoaderFirmwarePermanent); 
/* Text VRAM */
+        SetMemory(Pc98MemoryMap, 0x0E4000, 4 * KB, LoaderFirmwarePermanent);  
/* CG Window */
+        SetMemory(Pc98MemoryMap, 0x0E5000, 103 * KB, LoaderSpecialMemory);    
/* BIOS ROM */
+        SetMemory(Pc98MemoryMap, 0xF00000, 640 * KB, LoaderSpecialMemory);    
/* Reserved */
+    }
+    else
+    {
+        SetMemory(Pc98MemoryMap, 0x000000, 1 * KB, LoaderFirmwarePermanent);  
/* Real mode IVT */
+        SetMemory(Pc98MemoryMap, 0x000400, 512, LoaderFirmwarePermanent);     
/* Real mode BDA */
+        SetMemory(Pc98MemoryMap, 0x000600 + ConventionalMemory * KB,
+                  (640 - ConventionalMemory) * KB, LoaderSpecialMemory);      
/* External bus */
+        SetMemory(Pc98MemoryMap, 0x0A0000, 16 * KB, LoaderFirmwarePermanent); 
/* Text VRAM */
+        SetMemory(Pc98MemoryMap, 0x0A4000, 4 * KB, LoaderFirmwarePermanent);  
/* CG Window */
+        SetMemory(Pc98MemoryMap, 0x0A5000, 12 * KB, LoaderFirmwarePermanent); 
/* Reserved */
+        SetMemory(Pc98MemoryMap, 0x0A8000, 96 * KB, LoaderFirmwarePermanent); 
/* VRAM (Plane B, R, G) */
+        SetMemory(Pc98MemoryMap, 0x0C0000, 128 * KB, LoaderSpecialMemory);    
/* BIOS ROM (Peripherals) */
+        SetMemory(Pc98MemoryMap, 0x0E0000, 32 * KB, LoaderFirmwarePermanent); 
/* VRAM (Plane I) */
+        SetMemory(Pc98MemoryMap, 0x0E8000, 96 * KB, LoaderSpecialMemory);     
/* BIOS ROM */
+        SetMemory(Pc98MemoryMap, 0xF00000, 640 * KB, LoaderSpecialMemory);    
/* Reserved */
+    }
+
+    *MemoryMapSize = PcMemFinalizeMemoryMap(Pc98MemoryMap);
+    return Pc98MemoryMap;
+}
diff --git a/boot/freeldr/freeldr/arch/i386/pc98/pc98rtc.c 
b/boot/freeldr/freeldr/arch/i386/pc98/pc98rtc.c
new file mode 100644
index 00000000000..35c23a3716c
--- /dev/null
+++ b/boot/freeldr/freeldr/arch/i386/pc98/pc98rtc.c
@@ -0,0 +1,42 @@
+/*
+ * PROJECT:     FreeLoader
+ * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE:     Real-time clock access routine for NEC PC-98 series
+ * COPYRIGHT:   Copyright 2020 Dmitry Borisov ([email protected])
+ */
+
+#include <freeldr.h>
+
+#define BCD_INT(bcd) (((bcd & 0xF0) >> 4) * 10 + (bcd & 0x0F))
+
+TIMEINFO*
+Pc98GetTime(VOID)
+{
+    static TIMEINFO TimeInfo;
+    REGS Regs;
+    UCHAR SysTime[6];
+
+    /* Int 1Ch AH=00h
+     * TIMER BIOS - Read system time
+     *
+     * Call with:
+     * ES:BX -> data buffer
+     */
+    Regs.b.ah = 0x00;
+    Regs.w.es = ((ULONG_PTR)SysTime) >> 4;
+    Regs.w.bx = ((ULONG_PTR)SysTime) & 0x0F;
+    Int386(0x1C, &Regs, &Regs);
+
+    TimeInfo.Year = BCD_INT(SysTime[0]);
+    TimeInfo.Month = BCD_INT(SysTime[1] >> 4);
+    TimeInfo.Day = BCD_INT(SysTime[2]);
+    TimeInfo.Hour = BCD_INT(SysTime[3]);
+    TimeInfo.Minute = BCD_INT(SysTime[4]);
+    TimeInfo.Second = BCD_INT(SysTime[5]);
+    if (TimeInfo.Year >= 80)
+        TimeInfo.Year += 1900;
+    else
+        TimeInfo.Year += 2000;
+
+    return &TimeInfo;
+}
diff --git a/boot/freeldr/freeldr/arch/i386/pc98/pc98video.c 
b/boot/freeldr/freeldr/arch/i386/pc98/pc98video.c
new file mode 100644
index 00000000000..cb21eaaa70a
--- /dev/null
+++ b/boot/freeldr/freeldr/arch/i386/pc98/pc98video.c
@@ -0,0 +1,339 @@
+/*
+ * PROJECT:     FreeLoader
+ * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE:     Video support for NEC PC-98 series
+ * COPYRIGHT:   Copyright 2020 Dmitry Borisov ([email protected])
+ */
+
+/* INCLUDES 
*******************************************************************/
+
+#include <freeldr.h>
+#include <drivers/pc98/video.h>
+
+extern UCHAR XboxFont8x16[];
+extern BOOLEAN HiResoMachine;
+
+/* GLOBALS 
********************************************************************/
+
+#define VGA_CHAR_SIZE 2
+
+#define TEXT_CHAR_SIZE 2
+UCHAR TextCols;
+UCHAR TextLines;
+
+#define CHAR_WIDTH  8
+#define CHAR_HEIGHT 16
+
+#define SCREEN_WIDTH  640
+#define SCREEN_HEIGHT 400
+#define BYTES_PER_SCANLINE (SCREEN_WIDTH / 8)
+
+ULONG VramText;
+static ULONG VramPlaneB;
+static ULONG VramPlaneG;
+static ULONG VramPlaneR;
+static ULONG VramPlaneI;
+
+static const PALETTE_ENTRY CgaPalette[] =
+{
+    {0x00, 0x00, 0x00},
+    {0x00, 0x00, 0x0A},
+    {0x00, 0x0A, 0x00},
+    {0x00, 0x0A, 0x0A},
+    {0x0A, 0x00, 0x00},
+    {0x0A, 0x00, 0x0A},
+    {0x0A, 0x05, 0x00},
+    {0x0A, 0x0A, 0x0A},
+    {0x05, 0x05, 0x05},
+    {0x05, 0x05, 0x0F},
+    {0x05, 0x0F, 0x05},
+    {0x05, 0x0F, 0x0F},
+    {0x0F, 0x05, 0x05},
+    {0x0F, 0x05, 0x0F},
+    {0x0F, 0x0F, 0x05},
+    {0x0F, 0x0F, 0x0F}
+};
+
+/* FUNCTIONS 
******************************************************************/
+
+VOID
+Pc98VideoInit(VOID)
+{
+    REGS Regs;
+    USHORT i;
+
+    if (HiResoMachine)
+    {
+        VramPlaneB = VRAM_HI_RESO_PLANE_B;
+        VramPlaneG = VRAM_HI_RESO_PLANE_G;
+        VramPlaneR = VRAM_HI_RESO_PLANE_R;
+        VramPlaneI = VRAM_HI_RESO_PLANE_I;
+        VramText = VRAM_HI_RESO_TEXT;
+        TextCols = 80;
+        TextLines = 31;
+    }
+    else
+    {
+        VramPlaneB = VRAM_NORMAL_PLANE_B;
+        VramPlaneG = VRAM_NORMAL_PLANE_G;
+        VramPlaneR = VRAM_NORMAL_PLANE_R;
+        VramPlaneI = VRAM_NORMAL_PLANE_I;
+        VramText = VRAM_NORMAL_TEXT;
+        TextCols = 80;
+        TextLines = 25;
+    }
+
+    for (i = 0; i < VRAM_ATTR_SIZE; i += TEXT_CHAR_SIZE)
+        *(PUCHAR)(VramText + VRAM_TEXT_ATTR_OFFSET + i) = GDC_ATTR_WHITE | 
GDC_ATTR_VISIBLE;
+
+    /* Int 18h AH=41h
+     * CRT BIOS - Stop displaying graphics
+     */
+    Regs.b.ah = 0x41;
+    Int386(0x18, &Regs, &Regs);
+
+    /* Int 18h AH=42h
+     * CRT BIOS - Set display area
+     *
+     * CH0-CH3 - always zero
+     * CH4 - video page
+     * CH5 - CRT display mode
+     *    0 - color
+     *    1 - monochrome
+     * CH6-CH7 - VRAM area
+     *    01 - Upper-half (16-32 kB), 640x200
+     *    10 - Lower-half (0-16 kB), 640x200
+     *    11 - All (0-32 kB), 640x400
+     */
+    Regs.b.ah = 0x42;
+    Regs.b.ch = 0xC0;
+    Int386(0x18, &Regs, &Regs); /* 640x400 */
+
+    WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_MODE_FLIPFLOP2, GDC2_MODE_COLORS_16);
+    WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_VIDEO_PAGE, 0);
+    WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_VIDEO_PAGE_ACCESS, 0);
+
+    Pc98VideoSync();
+    for (i = 0; i < RTL_NUMBER_OF(CgaPalette); i++)
+        Pc98VideoSetPaletteColor(i, CgaPalette[i].Red, CgaPalette[i].Green, 
CgaPalette[i].Blue);
+
+    /* Int 18h AH=A0h
+     * CRT BIOS - Set text screen mode
+     *
+     * AL0 - text rows
+     *    0 - 25
+     *    1 - 20
+     * AL1 - text cols
+     *    0 - 80
+     *    1 - 40
+     * AL2 - text attribute
+     *    0 - with vertical line
+     *    1 - normal
+     * AL3 - KCG access mode
+     *    0 - code
+     *    1 - bitmap
+     * AL4-AL7 - always zero
+     *
+     * High-resolution machine:
+     * AL4 - text rows, AL3 - KCG access mode
+     */
+    Regs.b.ah = 0xA0;
+    Regs.b.al = HiResoMachine ? 0x10 : 0x00;
+    Int386(0x18, &Regs, &Regs); /* 80x25(31) */
+
+    /* Int 18h AH=0Ch
+     * CRT BIOS - Start displaying text
+     */
+    Regs.b.ah = 0x0C;
+    Int386(0x18, &Regs, &Regs);
+
+    /* Int 18h AH=40h
+     * CRT BIOS - Start displaying graphics
+     */
+    Regs.b.ah = 0x40;
+    Int386(0x18, &Regs, &Regs);
+}
+
+VOID
+Pc98VideoClearScreen(UCHAR Attr)
+{
+    USHORT i;
+    USHORT B = (Attr & 0x10) ? 0xFFFF : 0;
+    USHORT G = (Attr & 0x20) ? 0xFFFF : 0;
+    USHORT R = (Attr & 0x40) ? 0xFFFF : 0;
+    USHORT I = (Attr & 0x80) ? 0xFFFF : 0;
+
+    for (i = 0; i < VRAM_TEXT_SIZE; i += TEXT_CHAR_SIZE)
+        *(PUSHORT)(VramText + i) = ' ';
+
+    for (i = 0; i < BYTES_PER_SCANLINE * SCREEN_HEIGHT; i += sizeof(USHORT))
+    {
+        *(PUSHORT)(VramPlaneB + i) = B;
+        *(PUSHORT)(VramPlaneG + i) = G;
+        *(PUSHORT)(VramPlaneR + i) = R;
+        *(PUSHORT)(VramPlaneI + i) = I;
+    }
+}
+
+VIDEODISPLAYMODE
+Pc98VideoSetDisplayMode(char *DisplayModeName, BOOLEAN Init)
+{
+    /* Not supported by hardware */
+    return VideoTextMode;
+}
+
+VOID
+Pc98VideoGetDisplaySize(PULONG Width, PULONG Height, PULONG Depth)
+{
+    *Width = SCREEN_WIDTH / CHAR_WIDTH;
+    *Height = SCREEN_HEIGHT / CHAR_HEIGHT;
+    *Depth = 0;
+}
+
+ULONG
+Pc98VideoGetBufferSize(VOID)
+{
+    return (SCREEN_WIDTH / CHAR_WIDTH) * (SCREEN_HEIGHT / CHAR_HEIGHT) * 
VGA_CHAR_SIZE;
+}
+
+VOID
+Pc98VideoGetFontsFromFirmware(PULONG RomFontPointers)
+{
+    *RomFontPointers = VramText + 0x4000;
+}
+
+VOID
+Pc98VideoSetTextCursorPosition(UCHAR X, UCHAR Y)
+{
+    CSRWPARAM CursorParameters;
+
+    RtlZeroMemory(&CursorParameters, sizeof(CSRWPARAM));
+    CursorParameters.CursorAdress = X + Y * TextCols;
+    CursorParameters.DotAddress = 0;
+
+    WRITE_PORT_UCHAR((PUCHAR)GDC1_IO_o_COMMAND, GDC_COMMAND_CSRW);
+    WRITE_GDC_CSRW((PUCHAR)GDC1_IO_o_PARAM, &CursorParameters);
+}
+
+VOID
+Pc98VideoHideShowTextCursor(BOOLEAN Show)
+{
+    CSRFORMPARAM CursorParameters;
+
+    RtlZeroMemory(&CursorParameters, sizeof(CSRFORMPARAM));
+    CursorParameters.Show = Show;
+    CursorParameters.Blink = TRUE;
+    CursorParameters.BlinkRate = 12;
+    CursorParameters.LinesPerRow = 16;
+    CursorParameters.StartScanLine = 12;
+    CursorParameters.EndScanLine = 15;
+
+    WRITE_PORT_UCHAR((PUCHAR)GDC1_IO_o_COMMAND, GDC_COMMAND_CSRFORM);
+    WRITE_GDC_CSRFORM((PUCHAR)GDC1_IO_o_PARAM, &CursorParameters);
+}
+
+VOID
+Pc98VideoPutChar(int Ch, UCHAR Attr, unsigned X, unsigned Y)
+{
+    UCHAR Line;
+    UCHAR B = (Attr & 0x10) ? 0xFF : 0;
+    UCHAR G = (Attr & 0x20) ? 0xFF : 0;
+    UCHAR R = (Attr & 0x40) ? 0xFF : 0;
+    UCHAR I = (Attr & 0x80) ? 0xFF : 0;
+    ULONG VramOffset = X + (Y * CHAR_HEIGHT) * BYTES_PER_SCANLINE;
+    PUCHAR FontPtr = XboxFont8x16 + Ch * 16;
+
+    for (Line = 0; Line < CHAR_HEIGHT; Line++)
+    {
+        if (Attr & 0x0F)
+        {
+            *(PUCHAR)(VramPlaneB + VramOffset + Line * BYTES_PER_SCANLINE) = B 
| ((Attr & 0x01) ? FontPtr[Line] : 0);
+            *(PUCHAR)(VramPlaneG + VramOffset + Line * BYTES_PER_SCANLINE) = G 
| ((Attr & 0x02) ? FontPtr[Line] : 0);
+            *(PUCHAR)(VramPlaneR + VramOffset + Line * BYTES_PER_SCANLINE) = R 
| ((Attr & 0x04) ? FontPtr[Line] : 0);
+            *(PUCHAR)(VramPlaneI + VramOffset + Line * BYTES_PER_SCANLINE) = I 
| ((Attr & 0x08) ? FontPtr[Line] : 0);
+        }
+        else
+        {
+            *(PUCHAR)(VramPlaneB + VramOffset + Line * BYTES_PER_SCANLINE) = B 
& ~FontPtr[Line];
+            *(PUCHAR)(VramPlaneG + VramOffset + Line * BYTES_PER_SCANLINE) = G 
& ~FontPtr[Line];
+            *(PUCHAR)(VramPlaneR + VramOffset + Line * BYTES_PER_SCANLINE) = R 
& ~FontPtr[Line];
+            *(PUCHAR)(VramPlaneI + VramOffset + Line * BYTES_PER_SCANLINE) = I 
& ~FontPtr[Line];
+        }
+    }
+}
+
+VOID
+Pc98VideoCopyOffScreenBufferToVRAM(PVOID Buffer)
+{
+    PUCHAR OffScreenBuffer = (PUCHAR)Buffer;
+    USHORT X, Y;
+
+    for (Y = 0; Y < SCREEN_HEIGHT / CHAR_HEIGHT; Y++)
+    {
+        for (X = 0; X < SCREEN_WIDTH / CHAR_WIDTH; X++)
+        {
+            Pc98VideoPutChar(OffScreenBuffer[0], OffScreenBuffer[1], X, Y);
+            OffScreenBuffer += VGA_CHAR_SIZE;
+        }
+    }
+}
+
+BOOLEAN
+Pc98VideoIsPaletteFixed(VOID)
+{
+    return FALSE;
+}
+
+VOID
+Pc98VideoSetPaletteColor(UCHAR Color, UCHAR Red, UCHAR Green, UCHAR Blue)
+{
+    if (Color < 16)
+    {
+        WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_PALETTE_INDEX, Color);
+        WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_RED, Red);
+        WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_GREEN, Green);
+        WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_BLUE, Blue);
+    }
+}
+
+VOID
+Pc98VideoGetPaletteColor(UCHAR Color, UCHAR* Red, UCHAR* Green, UCHAR* Blue)
+{
+    if (Color < 16)
+    {
+        WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_PALETTE_INDEX, Color);
+        *Red = READ_PORT_UCHAR((PUCHAR)GDC2_IO_i_RED);
+        *Green = READ_PORT_UCHAR((PUCHAR)GDC2_IO_i_GREEN);
+        *Blue = READ_PORT_UCHAR((PUCHAR)GDC2_IO_i_BLUE);
+    }
+    else
+    {
+        *Red = 0;
+        *Green = 0;
+        *Blue = 0;
+    }
+}
+
+VOID
+Pc98VideoSync(VOID)
+{
+    while (READ_PORT_UCHAR((PUCHAR)GDC2_IO_i_STATUS) & GDC_STATUS_VSYNC)
+        NOTHING;
+
+    while (!(READ_PORT_UCHAR((PUCHAR)GDC2_IO_i_STATUS) & GDC_STATUS_VSYNC))
+        NOTHING;
+}
+
+VOID
+Pc98VideoPrepareForReactOS(VOID)
+{
+    REGS Regs;
+
+    /* Int 18h AH=41h
+     * CRT BIOS - Stop displaying graphics
+     */
+    Regs.b.ah = 0x41;
+    Int386(0x18, &Regs, &Regs);
+
+    Pc98VideoHideShowTextCursor(FALSE);
+}
diff --git a/boot/freeldr/freeldr/arch/realmode/helpers_pc98.inc 
b/boot/freeldr/freeldr/arch/realmode/helpers_pc98.inc
new file mode 100644
index 00000000000..f935d370df8
--- /dev/null
+++ b/boot/freeldr/freeldr/arch/realmode/helpers_pc98.inc
@@ -0,0 +1,245 @@
+/*
+ * PROJECT:     FreeLoader
+ * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE:     Real mode helper code for NEC PC-98 series
+ * COPYRIGHT:   Copyright 2020 Dmitry Borisov ([email protected])
+ */
+
+/*
+ * Enable the A20 address line
+ */
+EnableA20:
+    push ax
+
+    /* Unmask A20 line */
+    xor ax, ax
+    out HEX(0F2), al
+    mov al, HEX(02)
+    out HEX(0F6), al
+
+    pop ax
+    ret
+
+/*
+ * Disable the A20 address line
+ */
+DisableA20:
+    push ax
+
+    /* Mask A20 line */
+    mov al, HEX(03)
+    out HEX(0F6), al
+
+    pop ax
+    ret
+
+/*
+ * Prints a string
+ * SI = pointer to zero terminated string
+ */
+writestr:
+    pushfd
+    pushad
+
+.writestr_loop:
+    lodsb
+
+    /* Null test */
+    or al, al
+    jz short .writestr_end
+
+    /* CR test */
+    cmp al, HEX(0D)
+    jz short .writestr_cr
+
+    call writechr
+    jmp short .writestr_loop
+
+.writestr_cr:
+    mov ax, word ptr VramOffset
+    mov dl, 80 * 2
+    div dl
+    inc ax
+    mul dl
+    mov word ptr VramOffset, ax
+
+    /* Skip the next LF character */
+    inc si
+    jmp short .writestr_loop
+
+.writestr_end:
+    popad
+    popfd
+    ret
+
+/*
+ * writechr
+ * AL = character to output
+ */
+writechr:
+    pushf
+    pusha
+
+    /* High-resolution mode check */
+    test byte ptr ds:[HEX(501)], HEX(08)
+    jz .writechr_normal
+    push HEX(0E000)
+    jmp short .writechr_test_done
+.writechr_normal:
+    push HEX(0A000)
+
+.writechr_test_done:
+    pop es
+    mov di, word ptr VramOffset
+    xor ah, ah
+    stosw
+    mov word ptr VramOffset, di
+
+    popa
+    popf
+    ret
+
+VramOffset:
+    .word 0
+
+/*
+ * Writes a hex number in (AL, AX, EAX) to the console
+ */
+writehex2:
+    pushfd
+    pushad
+    shl eax, 24
+    mov cx, 2
+    jmp short writehex_common
+writehex4:
+    pushfd
+    pushad
+    shl eax, 16
+    mov cx, 4
+    jmp short writehex_common
+writehex8:
+    pushfd
+    pushad
+    mov cx, 8
+writehex_common:
+.loop:
+    rol eax, 4
+    push eax
+    and al, HEX(0F)
+    cmp al, 10
+    jae .high
+.low:
+    add al, '0'
+    jmp short .ischar
+.high:
+    add al, 'A'-10
+.ischar:
+    call writechr
+    pop eax
+    loop .loop
+    popad
+    popfd
+    ret
+
+/*
+ * Reboots the computer
+ */
+Reboot:
+    cli
+
+    /* Disable A20 address line */
+    call DisableA20
+
+    /* Enable SHUT0 */
+    mov al, HEX(0F)
+    out HEX(37), al
+
+    /* Enable SHUT1 */
+    mov al, HEX(0B)
+    out HEX(37), al
+
+    /* Activate the CPU reset line */
+    xor ax, ax
+    out HEX(0F0), al
+
+.RebootLoop:
+    hlt
+    jmp short .RebootLoop
+
+/*
+ * Jumps to the bootsector code
+ */
+Relocator16Boot:
+    cli
+
+    /* Disable A20 address line */
+    call DisableA20
+
+    /* Stop displaying graphics */
+    mov ax, HEX(4100)
+    int HEX(18)
+
+    /* Cursor off */
+    mov ax, HEX(1200)
+    int HEX(18)
+
+    /* Clear text screen */
+    mov ax, HEX(1600)
+    mov dx, HEX(0E120)
+    int HEX(18)
+
+    /* Start displaying text */
+    mov ax, HEX(0C00)
+    int HEX(18)
+
+    /* Get current EFLAGS and mask CF, ZF and SF */
+    pushf
+    pop cx
+    and cx, not (EFLAGS_CF or EFLAGS_ZF or EFLAGS_SF)
+
+    /* Get flags CF, ZF and SF from the REGS structure */
+    mov ax, word ptr cs:[BSS_RegisterSet + REGS_EFLAGS]
+    and ax, (EFLAGS_CF or EFLAGS_ZF or EFLAGS_SF)
+
+    /* Combine flags and set them */
+    or ax, cx
+    push ax
+    popf
+
+    /* Setup the segment registers */
+    mov ax, word ptr cs:[BSS_RegisterSet + REGS_DS]
+    mov ds, ax
+    mov ax, word ptr cs:[BSS_RegisterSet + REGS_ES]
+    mov es, ax
+    mov ax, word ptr cs:[BSS_RegisterSet + REGS_FS]
+    mov fs, ax
+    mov ax, word ptr cs:[BSS_RegisterSet + REGS_GS]
+    mov gs, ax
+
+    /* Patch the jump address (segment:offset) */
+    mov eax, dword ptr cs:[BSS_RealModeEntry]
+    mov dword ptr cs:[Relocator16Address], eax
+
+    /* Switch the stack (segment:offset) */
+    mov eax, dword ptr cs:[BSS_CallbackReturn]
+    shr eax, 16
+    mov ss, ax
+    mov eax, dword ptr cs:[BSS_CallbackReturn]
+    and eax, HEX(0FFFF)
+    mov esp, eax
+
+    /* Setup the registers */
+    mov eax, dword ptr cs:[BSS_RegisterSet + REGS_EAX]
+    mov ebx, dword ptr cs:[BSS_RegisterSet + REGS_EBX]
+    mov ecx, dword ptr cs:[BSS_RegisterSet + REGS_ECX]
+    mov edx, dword ptr cs:[BSS_RegisterSet + REGS_EDX]
+    mov esi, dword ptr cs:[BSS_RegisterSet + REGS_ESI]
+    mov edi, dword ptr cs:[BSS_RegisterSet + REGS_EDI]
+    mov ebp, dword ptr cs:[BSS_RegisterSet + REGS_EBP]
+
+    /* Jump to the new CS:IP */
+    .byte HEX(0EA) /* ljmp16 segment:offset */
+Relocator16Address:
+    .word HEX(0000) /* Default offset */
+    .word HEX(1FC0) /* Default segment */
+    nop
diff --git a/boot/freeldr/freeldr/arch/realmode/i386.S 
b/boot/freeldr/freeldr/arch/realmode/i386.S
index f76a38ae464..5f4d35f70db 100644
--- a/boot/freeldr/freeldr/arch/realmode/i386.S
+++ b/boot/freeldr/freeldr/arch/realmode/i386.S
@@ -197,7 +197,11 @@ rmode_idtptr:
     .long 0             /* Base Address */
 
 #include "int386.inc"
+#if defined(SARCH_PC98)
+#include "helpers_pc98.inc"
+#else
 #include "helpers.inc"
+#endif
 #include "pxe.inc"
 #include "pnp.inc"
 
diff --git a/boot/freeldr/freeldr/arch/realmode/int386.inc 
b/boot/freeldr/freeldr/arch/realmode/int386.inc
index ea3a822424d..07f59855438 100644
--- a/boot/freeldr/freeldr/arch/realmode/int386.inc
+++ b/boot/freeldr/freeldr/arch/realmode/int386.inc
@@ -50,6 +50,12 @@ Int386_set_registers:
     mov edx, dword ptr cs:[BSS_RegisterSet + REGS_EDX]
     mov esi, dword ptr cs:[BSS_RegisterSet + REGS_ESI]
     mov edi, dword ptr cs:[BSS_RegisterSet + REGS_EDI]
+#if defined(SARCH_PC98)
+    /* Always set EBP register even if its equals zero.
+     * Otherwise the DISK BIOS calls will store garbage data in a output 
buffer.
+     */
+    mov ebp, dword ptr cs:[BSS_RegisterSet + REGS_EBP]
+#endif
 
     /* Call the interrupt vector */
     /*int       Int386_vector*/
diff --git a/boot/freeldr/freeldr/include/arch/i386/machpc98.h 
b/boot/freeldr/freeldr/include/arch/i386/machpc98.h
new file mode 100644
index 00000000000..07881b83319
--- /dev/null
+++ b/boot/freeldr/freeldr/include/arch/i386/machpc98.h
@@ -0,0 +1,153 @@
+/*
+ * PROJECT:     FreeLoader
+ * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE:     Header file for NEC PC-98 series
+ * COPYRIGHT:   Copyright 2020 Dmitry Borisov ([email protected])
+ */
+
+#pragma once
+
+#ifndef __MEMORY_H
+#include "mm.h"
+#endif
+
+/*
+ * BIOS work area memory
+ */
+
+/* Extended RAM between 0x100000 and 0xFFFFFF in 128 kB */
+#define MEM_EXPMMSZ 0x401
+
+#define MEM_BIOS_FLAG5 0x458
+    #define NESA_BUS_FLAG    0x80
+
+#define MEM_SCSI_TABLE 0x460
+
+/* Bit 3 and bit 6 - keyboard type */
+#define MEM_KEYB_TYPE 0x481
+
+/* Status about connected SCSI hard drives */
+#define MEM_DISK_EQUIPS 0x482
+
+/* Status about RAM drives */
+#define MEM_RDISK_EQUIP 0x488
+
+#define MEM_BIOS_FLAG1 0x501
+    #define CONVENTIONAL_MEMORY_SIZE 0x07 /* In 128 kB */
+    #define HIGH_RESOLUTION_FLAG     0x08
+    #define SYSTEM_CLOCK_8MHZ_FLAG   0x80 /* 0 = PIT runs at 2.4576 MHz, 1 = 
at 1.9968 MHz */
+
+/* Status about connected floppies */
+#define MEM_DISK_EQUIP 0x55C
+
+/* Device Address/Unit Address (DA/UA) */
+#define MEM_DISK_BOOT 0x584
+
+/* Extended RAM after 0x1000000, low part, in 1 MB */
+#define MEM_EXPMMSZ16M_LOW 0x594
+
+/* Extended RAM after 0x1000000, high part, in 1 MB */
+#define MEM_EXPMMSZ16M_HIGH 0x595
+
+/* Status about connected 1.44 MB floppies */
+#define MEM_F144_SUPPORT 0x5AE
+
+#define MEM_EXTENDED_NORMAL    0xF8E80
+#define MEM_EXTENDED_HIGH_RESO 0xFFE80
+
+VOID Pc98Beep(VOID);
+
+VOID Pc98ConsPutChar(int Ch);
+BOOLEAN Pc98ConsKbHit(VOID);
+int Pc98ConsGetCh(VOID);
+
+VOID Pc98VideoInit(VOID);
+VOID Pc98VideoClearScreen(UCHAR Attr);
+VIDEODISPLAYMODE Pc98VideoSetDisplayMode(char *DisplayMode, BOOLEAN Init);
+VOID Pc98VideoGetDisplaySize(PULONG Width, PULONG Height, PULONG Depth);
+ULONG Pc98VideoGetBufferSize(VOID);
+VOID Pc98VideoGetFontsFromFirmware(PULONG RomFontPointers);
+VOID Pc98VideoSetTextCursorPosition(UCHAR X, UCHAR Y);
+VOID Pc98VideoHideShowTextCursor(BOOLEAN Show);
+VOID Pc98VideoPutChar(int Ch, UCHAR Attr, unsigned X, unsigned Y);
+VOID Pc98VideoCopyOffScreenBufferToVRAM(PVOID Buffer);
+BOOLEAN Pc98VideoIsPaletteFixed(VOID);
+VOID Pc98VideoSetPaletteColor(UCHAR Color, UCHAR Red, UCHAR Green, UCHAR Blue);
+VOID Pc98VideoGetPaletteColor(UCHAR Color, UCHAR* Red, UCHAR* Green, UCHAR* 
Blue);
+VOID Pc98VideoSync(VOID);
+VOID Pc98VideoPrepareForReactOS(VOID);
+
+VOID Pc98PrepareForReactOS(VOID);
+TIMEINFO* Pc98GetTime(VOID);
+BOOLEAN Pc98InitializeBootDevices(VOID);
+PCONFIGURATION_COMPONENT_DATA Pc98HwDetect(VOID);
+VOID Pc98HwIdle(VOID);
+
+/* pcmem.c */
+extern BIOS_MEMORY_MAP PcBiosMemoryMap[];
+extern ULONG PcBiosMapCount;
+
+PFREELDR_MEMORY_DESCRIPTOR Pc98MemGetMemoryMap(ULONG *MemoryMapSize);
+
+/* hwpci.c */
+BOOLEAN PcFindPciBios(PPCI_REGISTRY_INFO BusData);
+
+/*
+ * Disk Variables and Functions
+ */
+
+typedef struct _PC98_DISK_DRIVE
+{
+    /* Disk geometry */
+    GEOMETRY Geometry;
+
+    /* BIOS drive number */
+    UCHAR DaUa;
+
+    /* IDE driver drive number */
+    UCHAR IdeUnitNumber;
+
+    /* Drive type flags */
+    UCHAR Type;
+#define DRIVE_SASI     0x00
+#define DRIVE_IDE      0x01
+#define DRIVE_SCSI     0x02
+#define DRIVE_CDROM    0x04
+#define DRIVE_FDD      0x08
+#define DRIVE_MO       0x10
+#define DRIVE_RAM      0x20
+
+    /* TRUE when LBA access are supported */
+    BOOLEAN LBASupported;
+
+    /*
+     * 'IsRemovable' flag: TRUE when the drive is removable (e.g. floppy, 
CD-ROM...).
+     * In that case some of the cached information might need to be refreshed 
regularly.
+     */
+    BOOLEAN IsRemovable;
+
+    /*
+     * 'Initialized' flag: if TRUE then the drive has been initialized;
+     * if FALSE then the disk isn't detected by BIOS/FreeLoader.
+     */
+    BOOLEAN Initialized;
+} PC98_DISK_DRIVE, *PPC98_DISK_DRIVE;
+
+/* Platform-specific boot drive and partition numbers */
+extern UCHAR FrldrBootDrive;
+extern ULONG FrldrBootPartition;
+
+LONG DiskReportError(BOOLEAN bShowError);
+BOOLEAN DiskResetController(IN PPC98_DISK_DRIVE DiskDrive);
+
+BOOLEAN Pc98DiskReadLogicalSectors(UCHAR DriveNumber, ULONGLONG SectorNumber, 
ULONG SectorCount, PVOID Buffer);
+BOOLEAN Pc98DiskGetDriveGeometry(UCHAR DriveNumber, PGEOMETRY DriveGeometry);
+ULONG Pc98DiskGetCacheableBlockCount(UCHAR DriveNumber);
+UCHAR Pc98GetFloppyCount(VOID);
+PPC98_DISK_DRIVE Pc98DiskDriveNumberToDrive(IN UCHAR DriveNumber);
+
+ULONG Pc98GetBootSectorLoadAddress(IN UCHAR DriveNumber);
+VOID Pc98DiskPrepareForReactOS(VOID);
+
+/* hwdisk.c */
+BOOLEAN PcInitializeBootDevices(VOID);
diff --git a/boot/freeldr/freeldr/include/freeldr.h 
b/boot/freeldr/freeldr/include/freeldr.h
index 67966616442..ac2a5e8b13f 100644
--- a/boot/freeldr/freeldr/include/freeldr.h
+++ b/boot/freeldr/freeldr/include/freeldr.h
@@ -104,16 +104,23 @@
 #if defined(_M_IX86) || defined(_M_AMD64)
 #include <arch/pc/hardware.h>
 #include <arch/pc/pcbios.h>
-#include <arch/pc/machpc.h>
 #include <arch/pc/x86common.h>
 #include <arch/pc/pxe.h>
 #include <arch/i386/drivemap.h>
 #endif
 #if defined(_M_IX86)
-#include <arch/i386/i386.h>
+#if defined(SARCH_PC98)
+#include <arch/i386/machpc98.h>
+#elif defined(SARCH_XBOX)
+#include <arch/pc/machpc.h>
 #include <arch/i386/machxbox.h>
+#else
+#include <arch/pc/machpc.h>
+#endif
+#include <arch/i386/i386.h>
 #include <internal/i386/intrin_i.h>
 #elif defined(_M_AMD64)
+#include <arch/pc/machpc.h>
 #include <arch/amd64/amd64.h>
 #include <internal/amd64/intrin_i.h>
 #elif defined(_M_PPC)
diff --git a/boot/freeldr/freeldr/include/keycodes.h 
b/boot/freeldr/freeldr/include/keycodes.h
index 5582c90e2b2..f787b669cdd 100644
--- a/boot/freeldr/freeldr/include/keycodes.h
+++ b/boot/freeldr/freeldr/include/keycodes.h
@@ -1,25 +1,40 @@
 /*
- *  FreeLoader
- *  Copyright (C) 1998-2003  Brian Palmer  <[email protected]>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, write to the Free Software Foundation, Inc.,
- *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * PROJECT:     FreeLoader
+ * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE:     Key codes header file
+ * COPYRIGHT:   Copyright 1998-2003 Brian Palmer ([email protected])
+ *              Copyright 2020 Dmitry Borisov ([email protected])
  */
 
 #pragma once
 
-// Key codes
+#if defined(SARCH_PC98)
+#define KEY_EXTENDED    0x00
+#define KEY_ENTER       0x0D
+#define KEY_BACKSPACE   0x08
+#define KEY_DELETE      0x39
+#define KEY_SPACE       0x20
+#define KEY_LEFTSHIFT   0x70
+#define KEY_HOME        0x3E
+#define KEY_UP          0x3A
+#define KEY_DOWN        0x3D
+#define KEY_LEFT        0x3B
+#define KEY_RIGHT       0x3C
+#define KEY_ESC         0x1B
+#define KEY_CAPS_LOCK   0x71
+#define KEY_F1          0x62
+#define KEY_F2          0x63
+#define KEY_F3          0x64
+#define KEY_F4          0x65
+#define KEY_F5          0x66
+#define KEY_F6          0x67
+#define KEY_F7          0x68
+#define KEY_F8          0x69
+#define KEY_F9          0x6A
+#define KEY_F10         0x6B
+#define KEY_KEYPAD_PLUS 0x2B
+#define KEY_END         0x3F
+#else /* SARCH_PC98 */
 #define KEY_EXTENDED    0x00
 #define KEY_ENTER       0x0D
 #define KEY_BACKSPACE   0x08
@@ -48,3 +63,4 @@
 #define KEY_KEYPAD_PLUS 0x4E
 #define KEY_END         0x4F
 #define KEY_SEND        0xE7
+#endif
diff --git a/boot/freeldr/freeldr/miscboot.c b/boot/freeldr/freeldr/miscboot.c
index a00072d3efd..6aca738b773 100644
--- a/boot/freeldr/freeldr/miscboot.c
+++ b/boot/freeldr/freeldr/miscboot.c
@@ -39,6 +39,7 @@ LoadBootSector(
     ULONG FileId;
     ULONG BytesRead;
     CHAR ArcPath[MAX_PATH];
+    ULONG LoadAddress;
 
     *DriveNumber = 0;
     *PartitionNumber = 0;
@@ -98,8 +99,14 @@ LoadBootSector(
         return Status;
     }
 
+#if defined(SARCH_PC98)
+    LoadAddress = Pc98GetBootSectorLoadAddress(*DriveNumber);
+#else
+    LoadAddress = 0x7C00;
+#endif
+
     /* Now try to load the boot sector. If this fails then abort. */
-    Status = ArcRead(FileId, (PVOID)0x7c00, 512, &BytesRead);
+    Status = ArcRead(FileId, UlongToPtr(LoadAddress), 512, &BytesRead);
     ArcClose(FileId);
     if ((Status != ESUCCESS) || (BytesRead != 512))
     {
@@ -108,7 +115,7 @@ LoadBootSector(
     }
 
     /* Check for validity */
-    if (*((USHORT*)(0x7c00 + 0x1fe)) != 0xaa55)
+    if (*(USHORT*)UlongToPtr(LoadAddress + 0x1FE) != 0xAA55)
     {
         UiMessageBox("Invalid boot sector magic (0xaa55)");
         return ENOEXEC;
@@ -131,6 +138,7 @@ LoadPartitionOrDrive(
     ULONG FileId;
     ULONG BytesRead;
     CHAR ArcPath[MAX_PATH];
+    ULONG LoadAddress;
 
     /*
      * The ARC "BootPath" value takes precedence over
@@ -167,11 +175,17 @@ LoadPartitionOrDrive(
         return Status;
     }
 
+#if defined(SARCH_PC98)
+    LoadAddress = Pc98GetBootSectorLoadAddress(*DriveNumber);
+#else
+    LoadAddress = 0x7C00;
+#endif
+
     /*
      * Now try to load the partition boot sector or the MBR (when 
PartitionNumber == 0).
      * If this fails then abort.
      */
-    Status = ArcRead(FileId, (PVOID)0x7c00, 512, &BytesRead);
+    Status = ArcRead(FileId, UlongToPtr(LoadAddress), 512, &BytesRead);
     ArcClose(FileId);
     if ((Status != ESUCCESS) || (BytesRead != 512))
     {
@@ -183,7 +197,7 @@ LoadPartitionOrDrive(
     }
 
     /* Check for validity */
-    if (*((USHORT*)(0x7c00 + 0x1fe)) != 0xaa55)
+    if (*(USHORT*)UlongToPtr(LoadAddress + 0x1FE) != 0xAA55)
     {
         UiMessageBox("Invalid boot sector magic (0xaa55)");
         return ENOEXEC;
@@ -325,7 +339,7 @@ LoadAndBootDevice(
     UiUnInitialize("Booting...");
     IniCleanup();
 
-    /* Boot the loaded sector code at 0x7C00 */
+    /* Boot the loaded sector code */
     ChainLoadBiosBootSectorCode(DriveNumber, PartitionNumber);
     /* Must not return! */
     return ESUCCESS;
diff --git a/sdk/include/reactos/drivers/pc98/fdc.h 
b/sdk/include/reactos/drivers/pc98/fdc.h
new file mode 100644
index 00000000000..2d3ce06b13f
--- /dev/null
+++ b/sdk/include/reactos/drivers/pc98/fdc.h
@@ -0,0 +1,26 @@
+/*
+ * PROJECT:     NEC PC-98 series onboard hardware
+ * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE:     NEC uPD765A FDC header file
+ * COPYRIGHT:   Copyright 2020 Dmitry Borisov ([email protected])
+ */
+
+#pragma once
+
+#define FDC1_IO_BASE             0x90
+#define FDC2_IO_BASE             0xC8
+
+#define FDC_IO_o_MODE_SWITCH     0xBE
+#define FDC_IO_o_EMODE_SWITCH    0x4BE
+#define FDC_IO_i_MODE            0xBE
+#define FDC_IO_i_EMODE           0x4BE
+
+/*
+ * FDC registers offsets
+ */
+#define FDC_o_DATA           0x02
+#define FDC_o_CONTROL        0x04
+
+#define FDC_i_STATUS         0x00
+#define FDC_i_DATA           0x02
+#define FDC_i_READ_SWITCH    0x04
diff --git a/sdk/include/reactos/drivers/pc98/video.h 
b/sdk/include/reactos/drivers/pc98/video.h
new file mode 100644
index 00000000000..42132f39ef8
--- /dev/null
+++ b/sdk/include/reactos/drivers/pc98/video.h
@@ -0,0 +1,279 @@
+/*
+ * PROJECT:     NEC PC-98 series onboard hardware
+ * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE:     Graphics system header file
+ * COPYRIGHT:   Copyright 2020 Dmitry Borisov ([email protected])
+ */
+
+#pragma once
+
+/* Video memory 
***************************************************************/
+
+#define VRAM_NORMAL_PLANE_B      0xA8000
+#define VRAM_NORMAL_PLANE_G      0xB0000
+#define VRAM_NORMAL_PLANE_R      0xB8000
+#define VRAM_NORMAL_PLANE_I      0xE0000
+#define VRAM_PLANE_SIZE          0x08000
+#define VRAM_NORMAL_TEXT         0xA0000
+#define VRAM_TEXT_ATTR_OFFSET    0x02000
+#define VRAM_TEXT_SIZE           0x02000
+#define VRAM_ATTR_SIZE           0x02000
+
+/* High-resolution machine */
+#define VRAM_HI_RESO_PLANE_B     0xC0000
+#define VRAM_HI_RESO_PLANE_G     0xC8000
+#define VRAM_HI_RESO_PLANE_R     0xD0000
+#define VRAM_HI_RESO_PLANE_I     0xD8000
+#define VRAM_HI_RESO_TEXT        0xE0000
+
+/* GDC definitions 
************************************************************/
+
+#define GDC_STATUS_DRDY           0x01
+#define GDC_STATUS_FIFO_FULL      0x02
+#define GDC_STATUS_FIFO_EMPTY     0x04
+#define GDC_STATUS_DRAWING        0x08
+#define GDC_STATUS_DMA_EXECUTE    0x10
+#define GDC_STATUS_VSYNC          0x20
+#define GDC_STATUS_HBLANK         0x40
+#define GDC_STATUS_LPEN           0x80
+
+#define GDC_ATTR_VISIBLE          0x01
+#define GDC_ATTR_BLINK            0x02
+#define GDC_ATTR_REVERSE          0x04
+#define GDC_ATTR_UNDERLINE        0x08
+#define GDC_ATTR_VERTICAL_LINE    0x10
+
+#define GDC_ATTR_BLACK            0x00
+#define GDC_ATTR_BLUE             0x20
+#define GDC_ATTR_RED              0x40
+#define GDC_ATTR_PURPLE           0x60
+#define GDC_ATTR_GREEN            0x80
+#define GDC_ATTR_LIGHTBLUE        0xA0
+#define GDC_ATTR_YELLOW           0xC0
+#define GDC_ATTR_WHITE            0xE0
+
+#define GDC_COMMAND_RESET         0x00
+#define GDC_COMMAND_BCTRL_STOP    0x0C
+#define GDC_COMMAND_BCTRL_START   0x0D
+#define GDC_COMMAND_SYNC_ON       0x0E
+#define GDC_COMMAND_SYNC_OFF      0x0F
+#define GDC_COMMAND_WRITE         0x20
+#define GDC_COMMAND_SLAVE         0x6E
+#define GDC_COMMAND_MASTER        0x6F
+
+#define GDC_COMMAND_CSRFORM       0x4B
+typedef struct _CSRFORMPARAM
+{
+    BOOLEAN Show;
+    BOOLEAN Blink;
+    UCHAR BlinkRate;
+    UCHAR LinesPerRow;
+    UCHAR StartScanLine;
+    UCHAR EndScanLine;
+} CSRFORMPARAM, *PCSRFORMPARAM;
+
+FORCEINLINE
+VOID
+WRITE_GDC_CSRFORM(PUCHAR Port, PCSRFORMPARAM CursorParameters)
+{
+    WRITE_PORT_UCHAR(Port, ((CursorParameters->Show & 0x01) << 7) |
+                     (CursorParameters->LinesPerRow - 1));
+    WRITE_PORT_UCHAR(Port, ((CursorParameters->BlinkRate & 0x03) << 6) |
+                     ((!CursorParameters->Blink & 0x01) << 5) | 
CursorParameters->StartScanLine);
+    WRITE_PORT_UCHAR(Port, (CursorParameters->EndScanLine << 3) | 
((CursorParameters->BlinkRate & 0x1C) >> 2));
+}
+
+#define GDC_COMMAND_START         0x6B
+#define GDC_COMMAND_ZOOM          0x46
+
+#define GDC_COMMAND_CSRW          0x49
+typedef struct _CSRWPARAM
+{
+    ULONG CursorAdress;
+    UCHAR DotAddress;
+} CSRWPARAM, *PCSRWPARAM;
+
+FORCEINLINE
+VOID
+WRITE_GDC_CSRW(PUCHAR Port, PCSRWPARAM CursorParameters)
+{
+    ASSERT(CursorParameters->CursorAdress < 0xF00000);
+    ASSERT(CursorParameters->DotAddress < 0x10);
+
+    WRITE_PORT_UCHAR(Port, CursorParameters->CursorAdress & 0xFF);
+    WRITE_PORT_UCHAR(Port, (CursorParameters->CursorAdress >> 8) & 0xFF);
+    WRITE_PORT_UCHAR(Port, (CursorParameters->DotAddress << 4) |
+                     ((CursorParameters->CursorAdress >> 16) & 0x03));
+}
+
+#define GDC_COMMAND_PRAM          0x70
+#define GDC_COMMAND_PITCH         0x47
+#define GDC_COMMAND_MASK          0x4A
+#define GDC_COMMAND_FIGS          0x4C
+#define GDC_COMMAND_FIGD          0x6C
+#define GDC_COMMAND_GCHRD         0x68
+#define GDC_COMMAND_READ          0xA0
+#define GDC_COMMAND_CURD          0xE0
+#define GDC_COMMAND_LPRD          0xC0
+#define GDC_COMMAND_DMAR          0xA4
+#define GDC_COMMAND_DMAW          0x24
+
+/* Master GDC 
*****************************************************************/
+
+#define GDC1_IO_i_STATUS          0x60
+#define GDC1_IO_i_DATA            0x62
+#define GDC1_IO_i_MODE_FLIPFLOP1  0x68
+
+#define GDC1_IO_o_PARAM           0x60
+#define GDC1_IO_o_COMMAND         0x62
+#define GDC1_IO_o_VSYNC           0x64 /* CRT interrupt reset */
+
+#define GDC1_IO_o_MODE_FLIPFLOP1  0x68
+    #define GDC1_MODE_VERTICAL_LINE    0x00 /* Character attribute */
+    #define GDC1_MODE_SIMPLE_GRAPHICS  0x01
+    #define GDC1_MODE_COLORED          0x02
+    #define GDC1_MODE_MONOCHROME       0x03
+    #define GDC1_MODE_COLS_80          0x04
+    #define GDC1_MODE_COLS_40          0x05
+    #define GDC1_MODE_ANK_6_8          0x06
+    #define GDC1_MODE_ANK_7_13         0x07
+    #define GDC1_MODE_LINES_400        0x08
+    #define GDC1_MODE_LINES_200        0x09 /* Hide odd raster line */
+    #define GDC1_MODE_KCG_CODE         0x0A /* CG access during V-SYNC */
+    #define GDC1_MODE_KCG_BITMAP       0x0B
+    #define GDC1_NVMW_PROTECT          0x0C
+    #define GDC1_NVMW_UNPROTECT        0x0D /* Memory at 
TextVramSegment:(3FE2-3FFEh) */
+    #define GDC1_MODE_DISPLAY_DISABLE  0x0E
+    #define GDC1_MODE_DISPLAY_ENABLE   0x0F
+
+#define GDC1_IO_o_BORDER_COLOR    0x6C /* PC-H98 */
+
+/* Slave GDC 
******************************************************************/
+
+#define GDC2_IO_i_STATUS               0xA0
+#define GDC2_IO_i_DATA                 0xA2
+#define GDC2_IO_i_VIDEO_PAGE           0xA4
+#define GDC2_IO_i_VIDEO_PAGE_ACCESS    0xA6
+#define GDC2_IO_i_PALETTE_INDEX        0xA8
+#define GDC2_IO_i_GREEN                0xAA
+#define GDC2_IO_i_RED                  0xAC
+#define GDC2_IO_i_BLUE                 0xAE
+#define GDC2_IO_i_MODE_FLIPFLOP2       0x6A
+#define GDC2_IO_i_MODE_FLIPFLOP3       0x6E
+
+#define GDC2_IO_o_PARAM                0xA0
+#define GDC2_IO_o_COMMAND              0xA2
+#define GDC2_IO_o_VIDEO_PAGE           0xA4 /* Video page to display (invalid 
in 480 height mode) */
+#define GDC2_IO_o_VIDEO_PAGE_ACCESS    0xA6 /* Video page to CPU access */
+#define GDC2_IO_o_PALETTE_INDEX        0xA8
+#define GDC2_IO_o_GREEN                0xAA
+#define GDC2_IO_o_RED                  0xAC
+#define GDC2_IO_o_BLUE                 0xAE
+
+#define GDC2_IO_o_MODE_FLIPFLOP2  0x6A
+    #define GDC2_MODE_COLORS_8         0x00
+    #define GDC2_MODE_COLORS_16        0x01
+    #define GDC2_MODE_GRCG             0x04
+    #define GDC2_MODE_EGC              0x05
+    #define GDC2_EGC_FF_PROTECT        0x06
+    #define GDC2_EGC_FF_UNPROTECT      0x07 /* Unprotect the EGC F/F registers 
*/
+    #define GDC2_MODE_PEGS_DISABLE     0x20
+    #define GDC2_MODE_PEGC_ENABLE      0x21
+    // #define GDC2_MODE_              0x26
+    // #define GDC2_MODE_              0x27
+    // #define GDC2_MODE_              0x28
+    // #define GDC2_MODE_              0x29
+    // #define GDC2_MODE_              0x2A
+    // #define GDC2_MODE_              0x2B
+    // #define GDC2_MODE_              0x2C
+    // #define GDC2_MODE_              0x2D
+    #define GDC2_MODE_CRT              0x40
+    #define GDC2_MODE_LCD              0x41
+    // #define GDC2_MODE_VRAM_PLAIN    0x62 /* PC-H98 */
+    // #define GDC2_MODE_VRAM_PACKED   0x63
+    #define GDC2_MODE_LINES_400        0x68 /* 128 kB VRAM boundary */
+    #define GDC2_MODE_LINES_800        0x69 /* 256 kB VRAM boundary */
+    // #define GDC2_MODE_              0x6C
+    // #define GDC2_MODE_              0x6D
+    #define GDC2_CLOCK1_2_5MHZ         0x82
+    #define GDC2_CLOCK1_5MHZ           0x83
+    #define GDC2_CLOCK2_2_5MHZ         0x84
+    #define GDC2_CLOCK2_5MHZ           0x85
+
+#define GDC2_IO_o_MODE_FLIPFLOP3  0x6E
+    // #define GDC2_MODE_              0x02
+    // #define GDC2_MODE_              0x03
+    // #define GDC2_MODE_              0x08
+    // #define GDC2_MODE_              0x09
+    // #define GDC2_MODE_              0x0A
+    // #define GDC2_MODE_              0x0B
+    // #define GDC2_MODE_              0x0C
+    // #define GDC2_MODE_              0x0D
+    // #define GDC2_MODE_              0x0E
+    // #define GDC2_MODE_              0x0F
+    // #define GDC2_MODE_              0x14
+    // #define GDC2_MODE_              0x15
+
+FORCEINLINE
+VOID
+WRITE_GDC1_COMMAND(UCHAR Command)
+{
+    while (!(READ_PORT_UCHAR((PUCHAR)GDC1_IO_i_STATUS) & 
GDC_STATUS_FIFO_EMPTY))
+        NOTHING;
+
+    WRITE_PORT_UCHAR((PUCHAR)GDC1_IO_o_COMMAND, Command);
+}
+
+FORCEINLINE
+VOID
+WRITE_GDC2_COMMAND(UCHAR Command)
+{
+    while (!(READ_PORT_UCHAR((PUCHAR)GDC2_IO_i_STATUS) & 
GDC_STATUS_FIFO_EMPTY))
+        NOTHING;
+
+    WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_COMMAND, Command);
+}
+
+/* CRT Controller 
*************************************************************/
+
+#define CRTC_IO_o_SCANLINE_START       0x70
+#define CRTC_IO_o_SCANLINE_END         0x72
+#define CRTC_IO_o_SCANLINE_BLANK_AT    0x74
+#define CRTC_IO_o_SCANLINES            0x76
+#define CRTC_IO_o_SUR                  0x78
+#define CRTC_IO_o_SDR                  0x7A
+
+/* GRCG (Graphic Charger) 
*****************************************************/
+
+#define GRCG_IO_i_MODE                 0x7C
+#define GRCG_IO_o_MODE                 0x7C
+typedef union _GRCG_MODE_REGISTER
+{
+    struct
+    {
+        UCHAR DisablePlaneB:1;
+        UCHAR DisablePlaneR:1;
+        UCHAR DisablePlaneG:1;
+        UCHAR DisablePlaneI:1;
+        UCHAR Unused:2;
+
+        UCHAR Mode:1;
+#define GRCG_MODE_TILE_DIRECT_WRITE 0
+#define GRCG_MODE_TILE_COMPARE_READ 0
+#define GRCG_MODE_READ_MODIFY_WRITE 1
+
+        UCHAR Enable:1;
+    };
+    UCHAR Bits;
+} GRCG_MODE_REGISTER, *PGRCG_MODE_REGISTER;
+
+#define GRCG_IO_o_TILE_PATTERN         0x7E
+
+/* CG Window 
******************************************************************/
+
+#define KCG_IO_o_CHARCODE_HIGH      0xA1
+#define KCG_IO_o_CHARCODE_LOW       0xA3
+#define KCG_IO_o_LINE               0xA5
+#define KCG_IO_o_PATTERN            0xA9
+
+#define KCG_IO_i_PATTERN            0xA9

Reply via email to