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@protonmail.com) + */ + +/* INCLUDES *******************************************************************/ + +#include + +#include +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@protonmail.com) + */ + +#include + +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@protonmail.com) + */ + +/* INCLUDES *******************************************************************/ + +#include + +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@protonmail.com) + */ + +/* INCLUDES *******************************************************************/ + +#include +#include + +#include +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@protonmail.com) + */ + +/* INCLUDES *******************************************************************/ + +#include +#include +#include +#include + +#include +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@protonmail.com) + */ + +/* INCLUDES *******************************************************************/ + +#include + +#include +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@protonmail.com) + */ + +#include + +#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@protonmail.com) + */ + +/* INCLUDES *******************************************************************/ + +#include +#include + +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@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@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 #include -#include #include #include #include #endif #if defined(_M_IX86) -#include +#if defined(SARCH_PC98) +#include +#elif defined(SARCH_XBOX) +#include #include +#else +#include +#endif +#include #include #elif defined(_M_AMD64) +#include #include #include #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 - * - * 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@reactos.org) + * Copyright 2020 Dmitry Borisov (di.sean@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@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@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