https://git.reactos.org/?p=reactos.git;a=commitdiff;h=c14cc22bfde4d775ea78d…
commit c14cc22bfde4d775ea78dd9d43193b20fdb1afa6
Author: Dmitry Borisov <di.sean(a)protonmail.com>
AuthorDate: Sun Nov 10 22:47:39 2019 +0600
Commit: Hermès BÉLUSCA - MAÏTO <hermes.belusca-maito(a)reactos.org>
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 (di.sean(a)protonmail.com)
+ */
+
+/* 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 (di.sean(a)protonmail.com)
+ */
+
+#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 (di.sean(a)protonmail.com)
+ */
+
+/* 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 (di.sean(a)protonmail.com)
+ */
+
+/* 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 (di.sean(a)protonmail.com)
+ */
+
+/* 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 (di.sean(a)protonmail.com)
+ */
+
+/* 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 (di.sean(a)protonmail.com)
+ */
+
+#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 (di.sean(a)protonmail.com)
+ */
+
+/* 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 (di.sean(a)protonmail.com)
+ */
+
+/*
+ * 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 (di.sean(a)protonmail.com)
+ */
+
+#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 <brianp(a)sginet.com>
- *
- * 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 (brianp(a)reactos.org)
+ * Copyright 2020 Dmitry Borisov (di.sean(a)protonmail.com)
*/
#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 (di.sean(a)protonmail.com)
+ */
+
+#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 (di.sean(a)protonmail.com)
+ */
+
+#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