[FREELDR] Add ARC-emulation support for NEC PC-98 series

- Add ARC-emulation support for NEC PC-98 series
- Add global definition for PC-98 port into CMakeLists.txt
- Add floppy verison of freeldr.ini for PC-98 CD boot
This commit is contained in:
Dmitry Borisov 2019-11-10 22:47:39 +06:00 committed by Hermès BÉLUSCA - MAÏTO
parent 9ccd179e4a
commit c14cc22bfd
22 changed files with 3853 additions and 29 deletions

View file

@ -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)

View file

@ -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

View file

@ -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()

View file

@ -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

View file

@ -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");

View file

@ -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 <freeldr.h>
#include <debug.h>
DBG_DEFAULT_CHANNEL(HWDETECT);
/* GLOBALS ********************************************************************/
BOOLEAN HiResoMachine;
/* FUNCTIONS ******************************************************************/
VOID
Pc98GetExtendedBIOSData(PULONG ExtendedBIOSDataArea, PULONG ExtendedBIOSDataSize)
{
*ExtendedBIOSDataArea = HiResoMachine ? MEM_EXTENDED_HIGH_RESO : MEM_EXTENDED_NORMAL;
*ExtendedBIOSDataSize = 64;
}
VOID
Pc98HwIdle(VOID)
{
/* Unimplemented */
}
VOID
Pc98PrepareForReactOS(VOID)
{
Pc98DiskPrepareForReactOS();
Pc98VideoPrepareForReactOS();
DiskStopFloppyMotor();
}
ULONG
Pc98GetBootSectorLoadAddress(IN UCHAR DriveNumber)
{
PPC98_DISK_DRIVE DiskDrive;
DiskDrive = Pc98DiskDriveNumberToDrive(DriveNumber);
if (!DiskDrive)
{
ERR("Failed to get drive 0x%x\n", DriveNumber);
return 0x1FC00;
}
if (((DiskDrive->DaUa & 0xF0) == 0x30) ||
((DiskDrive->DaUa & 0xF0) == 0xB0))
{
/* 1.44 MB floppy */
return 0x1FE00;
}
else if (DiskDrive->Type & DRIVE_FDD)
{
return 0x1FC00;
}
return 0x1F800;
}
VOID __cdecl ChainLoadBiosBootSectorCode(
IN UCHAR BootDrive OPTIONAL,
IN ULONG BootPartition OPTIONAL)
{
REGS Regs;
PPC98_DISK_DRIVE DiskDrive;
USHORT LoadAddress;
UCHAR DriveNumber = BootDrive ? BootDrive : FrldrBootDrive;
DiskDrive = Pc98DiskDriveNumberToDrive(DriveNumber);
if (!DiskDrive)
{
ERR("Failed to get drive 0x%x\n", DriveNumber);
return;
}
LoadAddress = (USHORT)(Pc98GetBootSectorLoadAddress(DriveNumber) >> 4);
RtlZeroMemory(&Regs, sizeof(Regs));
Regs.w.ax = DiskDrive->DaUa;
Regs.w.si = LoadAddress;
Regs.w.es = LoadAddress;
*(PUCHAR)MEM_DISK_BOOT = DiskDrive->DaUa;
Pc98VideoClearScreen(ATTR(COLOR_WHITE, COLOR_BLACK));
Relocator16Boot(&Regs,
/* Stack segment:pointer */
0x0020, 0x00FF,
/* Code segment:pointer */
LoadAddress, 0x0000);
}
static BOOLEAN
Pc98ArchTest(VOID)
{
REGS RegsIn, RegsOut;
/* Int 1Ah AX=1000h
* NEC PC-9800 series - Installation check
*/
RegsIn.w.ax = 0x1000;
Int386(0x1A, &RegsIn, &RegsOut);
return RegsOut.w.ax != 0x1000;
}
VOID
MachInit(const char *CmdLine)
{
if (!Pc98ArchTest())
{
_disable();
__halt();
while (TRUE)
NOTHING;
}
/* Setup vtbl */
RtlZeroMemory(&MachVtbl, sizeof(MACHVTBL));
MachVtbl.ConsPutChar = Pc98ConsPutChar;
MachVtbl.ConsKbHit = Pc98ConsKbHit;
MachVtbl.ConsGetCh = Pc98ConsGetCh;
MachVtbl.VideoClearScreen = Pc98VideoClearScreen;
MachVtbl.VideoSetDisplayMode = Pc98VideoSetDisplayMode;
MachVtbl.VideoGetDisplaySize = Pc98VideoGetDisplaySize;
MachVtbl.VideoGetBufferSize = Pc98VideoGetBufferSize;
MachVtbl.VideoGetFontsFromFirmware = Pc98VideoGetFontsFromFirmware;
MachVtbl.VideoSetTextCursorPosition = Pc98VideoSetTextCursorPosition;
MachVtbl.VideoHideShowTextCursor = Pc98VideoHideShowTextCursor;
MachVtbl.VideoPutChar = Pc98VideoPutChar;
MachVtbl.VideoCopyOffScreenBufferToVRAM = Pc98VideoCopyOffScreenBufferToVRAM;
MachVtbl.VideoIsPaletteFixed = Pc98VideoIsPaletteFixed;
MachVtbl.VideoSetPaletteColor = Pc98VideoSetPaletteColor;
MachVtbl.VideoGetPaletteColor = Pc98VideoGetPaletteColor;
MachVtbl.VideoSync = Pc98VideoSync;
MachVtbl.Beep = Pc98Beep;
MachVtbl.PrepareForReactOS = Pc98PrepareForReactOS;
MachVtbl.GetMemoryMap = Pc98MemGetMemoryMap;
MachVtbl.GetExtendedBIOSData = Pc98GetExtendedBIOSData;
MachVtbl.GetFloppyCount = Pc98GetFloppyCount;
MachVtbl.DiskReadLogicalSectors = Pc98DiskReadLogicalSectors;
MachVtbl.DiskGetDriveGeometry = Pc98DiskGetDriveGeometry;
MachVtbl.DiskGetCacheableBlockCount = Pc98DiskGetCacheableBlockCount;
MachVtbl.GetTime = Pc98GetTime;
MachVtbl.InitializeBootDevices = Pc98InitializeBootDevices;
MachVtbl.HwDetect = Pc98HwDetect;
MachVtbl.HwIdle = Pc98HwIdle;
HiResoMachine = *(PUCHAR)MEM_BIOS_FLAG1 & HIGH_RESOLUTION_FLAG;
HalpCalibrateStallExecution();
Pc98VideoInit();
}

View file

@ -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 <freeldr.h>
VOID Pc98Beep(VOID)
{
REGS Regs;
/* Int 18h AH=17h
* CRT BIOS - Beep on
*/
Regs.b.ah = 0x17;
Int386(0x18, &Regs, &Regs);
StallExecutionProcessor(100000);
/* Int 18h AH=18h
* CRT BIOS - Beep off
*/
Regs.b.ah = 0x18;
Int386(0x18, &Regs, &Regs);
}

View file

@ -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 <freeldr.h>
extern ULONG VramText;
extern UCHAR TextCols;
extern UCHAR TextLines;
/* GLOBALS ********************************************************************/
#define TEXT_CHAR_SIZE 2
static USHORT CursorPosition = 0;
/* FUNCTIONS ******************************************************************/
VOID
Pc98ConsPutChar(int Ch)
{
/* If scrolling is needed */
if (CursorPosition >= TextCols * TextLines)
{
RtlCopyMemory((PUSHORT)VramText,
(PUSHORT)(VramText + TextCols * TEXT_CHAR_SIZE),
TextCols * TextLines * TEXT_CHAR_SIZE);
CursorPosition -= TextCols;
}
if (Ch == '\n')
{
if (CursorPosition % TextCols != 0)
CursorPosition += TextCols - (CursorPosition % TextCols);
return;
}
if (Ch == '\t')
{
Pc98ConsPutChar(' ');
Pc98ConsPutChar(' ');
Pc98ConsPutChar(' ');
Pc98ConsPutChar(' ');
Pc98ConsPutChar(' ');
Pc98ConsPutChar(' ');
Pc98ConsPutChar(' ');
Pc98ConsPutChar(' ');
return;
}
*(PUSHORT)(VramText + CursorPosition * TEXT_CHAR_SIZE) = Ch;
++CursorPosition;
}
BOOLEAN
Pc98ConsKbHit(VOID)
{
REGS Regs;
/* Int 18h AH=01h
* KEYBOARD - CHECK FOR KEYSTROKE
*
* Return:
* BH - status
* 00h - if no keystroke available
* 01h - if keystroke available
* AH - BIOS scan code
* AL - ASCII character
*/
Regs.b.ah = 0x01;
Int386(0x18, &Regs, &Regs);
return Regs.b.bh == 1;
}
int
Pc98ConsGetCh(VOID)
{
static BOOLEAN ExtendedKey = FALSE;
static UCHAR ExtendedScanCode = 0;
REGS Regs;
/*
* If the last time we were called an
* extended key was pressed then return
* that keys scan code.
*/
if (ExtendedKey)
{
ExtendedKey = FALSE;
return ExtendedScanCode;
}
/* Int 18h AH=00h
* KEYBOARD - GET KEYSTROKE
*
* Return:
* AH - BIOS scan code
* AL - ASCII character
*/
Regs.b.ah = 0x00;
Int386(0x18, &Regs, &Regs);
/* Check for an extended keystroke */
if (Regs.b.al == 0)
{
ExtendedKey = TRUE;
ExtendedScanCode = Regs.b.ah;
}
/* Return keystroke */
return Regs.b.al;
}

View file

@ -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 <freeldr.h>
#include <hwide.h>
#include <debug.h>
DBG_DEFAULT_CHANNEL(DISK);
/* GLOBALS ********************************************************************/
/* Maximum number of disks, indexed from 0x00 to 0xFF */
#define MAX_DRIVES 0x100
/* Cache of all possible PC disk drives */
PC98_DISK_DRIVE Pc98DiskDrive[MAX_DRIVES];
/* DISK IO ERROR SUPPORT ******************************************************/
static LONG lReportError = 0; /* >= 0: display errors; < 0: hide errors */
LONG
DiskReportError(BOOLEAN bShowError)
{
/* Set the reference count */
if (bShowError)
++lReportError;
else
--lReportError;
return lReportError;
}
static PCSTR
DiskGetErrorCodeString(ULONG ErrorCode)
{
switch (ErrorCode & 0xF0)
{
case 0x00: return "No error";
case 0x10: return "Drive write protection error";
case 0x20: return "DMA access across 64 kB boundary";
case 0x30: return "End of cylinder";
case 0x40: return "The drive name is invalid or the device have low health";
case 0x50: return "Time out, data not written";
case 0x60: return "Time out, drive not ready";
case 0x70:
if (ErrorCode == 0x78)
return "Illegal disk address";
else
return "Drive write protection error";
case 0x80: return "Undefined error";
case 0x90: return "Time out error";
case 0xA0: return "CRC error in the ID section";
case 0xB0: return "CRC error in the DATA section";
case 0xC0:
if (ErrorCode == 0xC8)
return "Seek failure";
else
return "No data (Sector not found)";
case 0xD0: return "Bad cylinder";
case 0xE0: return "No ID address mark was found";
case 0xF0: return "No DATA address mark was found";
default: return "Unknown error code";
}
}
static VOID
DiskError(PCSTR ErrorString, ULONG ErrorCode)
{
CHAR ErrorCodeString[200];
if (lReportError < 0)
return;
RtlStringCbPrintfA(ErrorCodeString, sizeof(ErrorCodeString), "%s\n\nError Code: 0x%lx\nError: %s",
ErrorString, ErrorCode, DiskGetErrorCodeString(ErrorCode));
ERR("%s\n", ErrorCodeString);
UiMessageBox(ErrorCodeString);
}
/* FUNCTIONS ******************************************************************/
BOOLEAN DiskResetController(IN PPC98_DISK_DRIVE DiskDrive)
{
REGS Regs;
if (DiskDrive->Type & DRIVE_FDD)
{
/* Int 1Bh AH=07h
* DISK BIOS - Recalibrate
*
* Call with:
* AL - drive number
*
* Return:
* CF - set on error, clear if successful
* AH - status
*/
Regs.b.ah = 0x07;
}
else if (DiskDrive->Type != (DRIVE_IDE | DRIVE_CDROM))
{
/* Int 1Bh AH=03h
* DISK BIOS - Initialize
*
* Call with:
* AL - drive number
*
* Return:
* CF - set on error, clear if successful
* AH - status
*/
Regs.b.ah = 0x03;
}
else
{
return FALSE;
}
WARN("DiskResetController(0x%x) DISK OPERATION FAILED -- RESETTING CONTROLLER\n", DiskDrive->DaUa);
Regs.b.al = DiskDrive->DaUa;
Int386(0x1B, &Regs, &Regs);
return INT386_SUCCESS(Regs);
}
VOID Pc98DiskPrepareForReactOS(VOID)
{
AtaFree();
}
PPC98_DISK_DRIVE
Pc98DiskDriveNumberToDrive(IN UCHAR DriveNumber)
{
PPC98_DISK_DRIVE DiskDrive;
ASSERT((0 <= DriveNumber) && (DriveNumber < RTL_NUMBER_OF(Pc98DiskDrive)));
/* Retrieve a slot */
DiskDrive = &Pc98DiskDrive[DriveNumber];
/* The pre-initialization of the BIOS disks was already done in Pc98InitializeBootDevices() */
if (DiskDrive->Initialized)
return DiskDrive;
else
return NULL;
}
static inline
UCHAR
BytesPerSectorToSectorLengthCode(IN ULONG BytesPerSector)
{
switch (BytesPerSector)
{
case 128:
return 0;
case 256:
return 1;
case 512:
return 2;
case 1024:
return 3;
case 2048:
return 4;
default:
return 0;
}
}
static BOOLEAN
Pc98DiskReadLogicalSectorsLBA(
IN PPC98_DISK_DRIVE DiskDrive,
IN ULONGLONG SectorNumber,
IN ULONG SectorCount,
OUT PVOID Buffer)
{
REGS RegsIn, RegsOut;
ULONG RetryCount;
if (DiskDrive->Type & DRIVE_IDE && DiskDrive->Type & DRIVE_CDROM)
{
return AtaAtapiReadLogicalSectorsLBA(AtaGetDevice(DiskDrive->IdeUnitNumber), SectorNumber, SectorCount, Buffer);
}
else
{
/* Int 1Bh AH=06h
* DISK BIOS - Read data
*
* Call with:
* AL - drive number
* BX - bytes to read
* CX - cylinder number
* DH - head number
* DL - sector number
* ES:BP -> buffer to read data into
*
* Return:
* CF - set on error, clear if successful
* AH - status
*/
RegsIn.b.al = DiskDrive->DaUa;
RegsIn.b.ah = 0x06;
RegsIn.w.bx = DiskDrive->Geometry.BytesPerSector * SectorCount;
RegsIn.w.cx = SectorNumber & 0xFFFF;
RegsIn.w.dx = (SectorNumber >> 16) & 0xFFFF;
RegsIn.w.es = (USHORT)(((ULONG_PTR)Buffer) >> 4);
RegsIn.w.bp = ((ULONG_PTR)Buffer) & 0x0F;
/* Retry 3 times */
for (RetryCount = 0; RetryCount < 3; RetryCount++)
{
Int386(0x1B, &RegsIn, &RegsOut);
/* If it worked return TRUE */
if (INT386_SUCCESS(RegsOut))
{
return TRUE;
}
/* If it was a corrected ECC error then the data is still good */
else if (RegsOut.b.ah == 0x08)
{
return TRUE;
}
/* If it failed the do the next retry */
else
{
DiskResetController(DiskDrive);
continue;
}
}
}
/* If we get here then the read failed */
DiskError("Disk Read Failed in LBA mode", RegsOut.b.ah);
ERR("Disk Read Failed in LBA mode: %x (%s) (DriveNumber: 0x%x SectorNumber: %I64d SectorCount: %d)\n",
RegsOut.b.ah, DiskGetErrorCodeString(RegsOut.b.ah),
DiskDrive->DaUa, SectorNumber, SectorCount);
return FALSE;
}
static BOOLEAN
Pc98DiskReadLogicalSectorsCHS(
IN PPC98_DISK_DRIVE DiskDrive,
IN ULONGLONG SectorNumber,
IN ULONG SectorCount,
OUT PVOID Buffer)
{
UCHAR PhysicalSector;
UCHAR PhysicalHead;
ULONG PhysicalTrack;
GEOMETRY DriveGeometry;
ULONG NumberOfSectorsToRead;
REGS RegsIn, RegsOut;
ULONG RetryCount;
DriveGeometry = DiskDrive->Geometry;
while (SectorCount > 0)
{
/*
* Calculate the physical disk offsets.
* Note: DriveGeometry.Sectors < 64
*/
PhysicalSector = (UCHAR)(SectorNumber % DriveGeometry.Sectors);
PhysicalHead = (UCHAR)((SectorNumber / DriveGeometry.Sectors) % DriveGeometry.Heads);
PhysicalTrack = (ULONG)((SectorNumber / DriveGeometry.Sectors) / DriveGeometry.Heads);
/* Floppy sectors value always start at 1 */
if (DiskDrive->Type & DRIVE_FDD)
++PhysicalSector;
/* Calculate how many sectors we need to read this round */
if (PhysicalSector > 1)
{
if (SectorCount >= (DriveGeometry.Sectors - (PhysicalSector - 1)))
NumberOfSectorsToRead = (DriveGeometry.Sectors - (PhysicalSector - 1));
else
NumberOfSectorsToRead = SectorCount;
}
else
{
if (SectorCount >= DriveGeometry.Sectors)
NumberOfSectorsToRead = DriveGeometry.Sectors;
else
NumberOfSectorsToRead = SectorCount;
}
/* Make sure the read is within the geometry boundaries */
if ((PhysicalHead >= DriveGeometry.Heads) ||
(PhysicalTrack >= DriveGeometry.Cylinders) ||
((NumberOfSectorsToRead + PhysicalSector) > (DriveGeometry.Sectors + 1)) ||
(PhysicalSector > DriveGeometry.Sectors))
{
DiskError("Disk read exceeds drive geometry limits.", 0);
return FALSE;
}
if (DiskDrive->Type & DRIVE_FDD)
{
/* Int 1Bh AH=x6h
* DISK BIOS - Read data
*
* Call with:
* AL - drive number
* BX - bytes to read
* CH - sector length code
* CL - cylinder number
* DH - head number
* DL - sector number
* ES:BP -> buffer to read data into
*
* Return:
* CF - set on error, clear if successful
* AH - status
*/
RegsIn.b.al = DiskDrive->DaUa;
RegsIn.b.ah = 0x56; /* With SEEK, and use double-density format (MFM) */
RegsIn.w.bx = DriveGeometry.BytesPerSector * (UCHAR)NumberOfSectorsToRead;
RegsIn.b.cl = PhysicalTrack & 0xFFFF;
RegsIn.b.ch = BytesPerSectorToSectorLengthCode(DriveGeometry.BytesPerSector);
RegsIn.b.dl = PhysicalSector;
RegsIn.b.dh = PhysicalHead;
RegsIn.w.es = (USHORT)(((ULONG_PTR)Buffer) >> 4);
RegsIn.w.bp = ((ULONG_PTR)Buffer) & 0x0F;
}
else
{
/* Int 1Bh AH=06h
* DISK BIOS - Read data
*
* Call with:
* AL - drive number
* BX - bytes to read
* CX - cylinder number
* DH - head number
* DL - sector number
* ES:BP -> buffer to read data into
*
* Return:
* CF - set on error, clear if successful
* AH - status
*/
RegsIn.b.al = DiskDrive->DaUa;
RegsIn.b.ah = 0x06;
RegsIn.w.bx = DriveGeometry.BytesPerSector * (UCHAR)NumberOfSectorsToRead;
RegsIn.w.cx = PhysicalTrack & 0xFFFF;
RegsIn.b.dl = PhysicalSector;
RegsIn.b.dh = PhysicalHead;
RegsIn.w.es = (USHORT)(((ULONG_PTR)Buffer) >> 4);
RegsIn.w.bp = ((ULONG_PTR)Buffer) & 0x0F;
}
/* Perform the read. Retry 3 times. */
for (RetryCount = 0; RetryCount < 3; RetryCount++)
{
Int386(0x1B, &RegsIn, &RegsOut);
/* If it worked break out */
if (INT386_SUCCESS(RegsOut))
{
break;
}
/* If it was a corrected ECC error then the data is still good */
else if (RegsOut.b.ah == 0x08)
{
break;
}
/* If it failed then do the next retry */
else
{
DiskResetController(DiskDrive);
continue;
}
}
/* If we retried 3 times then fail */
if (RetryCount >= 3)
{
DiskError("Disk Read Failed in CHS mode, after retrying 3 times", RegsOut.b.ah);
ERR("Disk Read Failed in CHS mode, after retrying 3 times: %x (%s) (DriveNumber: 0x%x SectorNumber: %I64d SectorCount: %d)\n",
RegsOut.b.ah, DiskGetErrorCodeString(RegsOut.b.ah),
DiskDrive->DaUa, SectorNumber, SectorCount);
return FALSE;
}
Buffer = (PVOID)((ULONG_PTR)Buffer + (NumberOfSectorsToRead * DriveGeometry.BytesPerSector));
SectorCount -= NumberOfSectorsToRead;
SectorNumber += NumberOfSectorsToRead;
}
return TRUE;
}
static BOOLEAN
InitScsiDrive(
IN UCHAR DaUa,
IN OUT PPC98_DISK_DRIVE DiskDrive)
{
REGS RegsIn, RegsOut;
UCHAR UnitAddress = DaUa & 0x0F;
USHORT DiskEquipment = *(PUCHAR)MEM_DISK_EQUIPS;
ULONG ScsiParameters = *(PULONG)(MEM_SCSI_TABLE + UnitAddress * sizeof(ULONG));
UCHAR DeviceType;
/* Hard drives */
if (DiskEquipment & (1 << UnitAddress))
{
/* Int 1Bh AH=84h
* DISK BIOS - Sense
*
* Call with:
* AL - drive number
*
* Return:
* BX - bytes per sector
* CX - cylinders number
* DH - heads number
* DL - sectors number
* CF - set on error, clear if successful
* AH - status
*/
RegsIn.b.al = DaUa;
RegsIn.b.ah = 0x84;
Int386(0x1B, &RegsIn, &RegsOut);
if (!INT386_SUCCESS(RegsOut) || RegsOut.w.cx == 0)
{
DiskDrive->Initialized = FALSE;
return FALSE;
}
DiskDrive->Geometry.Cylinders = RegsOut.w.cx;
DiskDrive->Geometry.Heads = RegsOut.b.dh;
DiskDrive->Geometry.Sectors = RegsOut.b.dl;
DiskDrive->Geometry.BytesPerSector = RegsOut.w.bx;
DiskDrive->LBASupported = FALSE;
DiskDrive->IsRemovable = FALSE;
}
/* Other devices */
else if (ScsiParameters)
{
DeviceType = ScsiParameters & 0x1F;
switch (DeviceType)
{
case 0x05:
/* CD-ROM */
DiskDrive->Geometry.Cylinders = 0xFFFF;
DiskDrive->Geometry.Heads = 0xFFFF;
DiskDrive->Geometry.Sectors = 0xFFFF;
DiskDrive->Geometry.BytesPerSector = 2048;
DiskDrive->Type = DRIVE_CDROM;
DiskDrive->LBASupported = TRUE;
DiskDrive->IsRemovable = TRUE;
break;
case 0x07:
/* Magneto-optical drive */
DiskDrive->Geometry.Cylinders = 0xFFFF;
DiskDrive->Geometry.Heads = 8;
DiskDrive->Geometry.Sectors = 32;
DiskDrive->Geometry.BytesPerSector = 512;
DiskDrive->Type = DRIVE_MO;
DiskDrive->LBASupported = TRUE;
DiskDrive->IsRemovable = TRUE;
break;
default:
DiskDrive->Initialized = FALSE;
return FALSE;
}
}
else
{
DiskDrive->Initialized = FALSE;
return FALSE;
}
DiskDrive->DaUa = DaUa;
DiskDrive->Type |= DRIVE_SCSI;
DiskDrive->Initialized = TRUE;
TRACE("InitScsiDrive(0x%x) returned:\n"
"Cylinders : 0x%x\n"
"Heads : 0x%x\n"
"Sects/Track: 0x%x\n"
"Bytes/Sect : 0x%x\n",
DaUa,
DiskDrive->Geometry.Cylinders,
DiskDrive->Geometry.Heads,
DiskDrive->Geometry.Sectors,
DiskDrive->Geometry.BytesPerSector);
return TRUE;
}
static BOOLEAN
InitIdeDrive(
IN UCHAR UnitNumber,
IN OUT PPC98_DISK_DRIVE DiskDrive)
{
PDEVICE_UNIT DeviceUnit = AtaGetDevice(UnitNumber);
/* We work directly only with ATAPI drives because BIOS has ATA support */
if (DeviceUnit && DeviceUnit->Flags & ATA_DEVICE_ATAPI)
{
DiskDrive->Geometry.Cylinders = DeviceUnit->Cylinders;
DiskDrive->Geometry.Heads = DeviceUnit->Heads;
DiskDrive->Geometry.Sectors = DeviceUnit->Sectors;
DiskDrive->Geometry.BytesPerSector = DeviceUnit->SectorSize;
DiskDrive->DaUa = 0xFF;
DiskDrive->IdeUnitNumber = UnitNumber;
DiskDrive->Type = DRIVE_IDE | DRIVE_CDROM;
DiskDrive->LBASupported = TRUE;
DiskDrive->IsRemovable = TRUE;
DiskDrive->Initialized = TRUE;
TRACE("InitIdeDrive(0x%x) returned:\n"
"Cylinders : 0x%x\n"
"Heads : 0x%x\n"
"Sects/Track: 0x%x\n"
"Bytes/Sect : 0x%x\n",
UnitNumber,
DiskDrive->Geometry.Cylinders,
DiskDrive->Geometry.Heads,
DiskDrive->Geometry.Sectors,
DiskDrive->Geometry.BytesPerSector);
return TRUE;
}
DiskDrive->Initialized = FALSE;
return FALSE;
}
static BOOLEAN
InitHardDrive(
IN UCHAR DaUa,
IN OUT PPC98_DISK_DRIVE DiskDrive)
{
REGS RegsIn, RegsOut;
/* Int 1Bh AH=8Eh
* DISK BIOS - Set half-height operation mode
*
* Call with:
* AL - drive number
*/
RegsIn.b.al = DaUa;
RegsIn.b.ah = 0x8E;
Int386(0x1B, &RegsIn, &RegsOut);
/* Int 1Bh AH=84h
* DISK BIOS - Sense
*
* Call with:
* AL - drive number
*
* Return:
* BX - bytes per sector
* CX - cylinders number
* DH - heads number
* DL - sectors number
* CF - set on error, clear if successful
* AH - status
*/
RegsIn.b.al = DaUa;
RegsIn.b.ah = 0x84;
Int386(0x1B, &RegsIn, &RegsOut);
if (!INT386_SUCCESS(RegsOut) || RegsOut.w.cx == 0)
{
DiskDrive->Initialized = FALSE;
return FALSE;
}
DiskDrive->Geometry.Cylinders = RegsOut.w.cx;
DiskDrive->Geometry.Heads = RegsOut.b.dh;
DiskDrive->Geometry.Sectors = RegsOut.b.dl;
DiskDrive->Geometry.BytesPerSector = RegsOut.w.bx;
DiskDrive->DaUa = DaUa;
DiskDrive->Type = DRIVE_IDE;
DiskDrive->LBASupported = FALSE;
DiskDrive->IsRemovable = FALSE;
DiskDrive->Initialized = TRUE;
TRACE("InitHardDrive(0x%x) returned:\n"
"Cylinders : 0x%x\n"
"Heads : 0x%x\n"
"Sects/Track: 0x%x\n"
"Bytes/Sect : 0x%x\n",
DaUa,
DiskDrive->Geometry.Cylinders,
DiskDrive->Geometry.Heads,
DiskDrive->Geometry.Sectors,
DiskDrive->Geometry.BytesPerSector);
return TRUE;
}
static BOOLEAN
InitFloppyDrive(
IN UCHAR DaUa,
IN OUT PPC98_DISK_DRIVE DiskDrive)
{
REGS RegsIn, RegsOut;
USHORT BytesPerSector;
UCHAR DeviceAddress = DaUa & 0xF0;
/* There's no way to obtain floppy disk geometry in BIOS */
/* Int 1Bh AH=4Ah
* DISK BIOS - Read ID
*
* Call with:
* AL - drive number
*
* Return:
* CH - sector size
* CL - cylinder
* DH - head
* DL - sector
* CF - set on error, clear if successful
* AH - status
*/
RegsIn.b.ah = 0x4A;
RegsIn.b.al = DaUa;
Int386(0x1B, &RegsIn, &RegsOut);
if (!INT386_SUCCESS(RegsOut))
{
DiskDrive->Initialized = FALSE;
return FALSE;
}
BytesPerSector = 128 << RegsOut.b.ch;
switch (BytesPerSector)
{
case 256:
if (DeviceAddress == 0x50)
{
/* 320 kB 2DD */
DiskDrive->Geometry.Cylinders = 80;
DiskDrive->Geometry.Heads = 2;
DiskDrive->Geometry.Sectors = 16;
}
else
{
/* 1 MB 2HD */
DiskDrive->Geometry.Cylinders = 77;
DiskDrive->Geometry.Heads = 2;
DiskDrive->Geometry.Sectors = 26;
}
break;
case 512:
if (DeviceAddress == 0x30 || DeviceAddress == 0xB0)
{
/* 1.44 MB 2HD */
DiskDrive->Geometry.Cylinders = 80;
DiskDrive->Geometry.Heads = 2;
DiskDrive->Geometry.Sectors = 18;
}
else if (DeviceAddress == 0x70 || DeviceAddress == 0xF0)
{
/* 720/640 kB 2DD */
DiskDrive->Geometry.Cylinders = 80;
DiskDrive->Geometry.Heads = 2;
DiskDrive->Geometry.Sectors = 8;
}
else
{
/* 1.2 MB 2HC */
DiskDrive->Geometry.Cylinders = 80;
DiskDrive->Geometry.Heads = 2;
DiskDrive->Geometry.Sectors = 15;
}
break;
case 1024:
/* 1.25 MB 2HD */
DiskDrive->Geometry.Cylinders = 77;
DiskDrive->Geometry.Heads = 2;
DiskDrive->Geometry.Sectors = 8;
break;
default:
DiskDrive->Initialized = FALSE;
return FALSE;
}
DiskDrive->Geometry.BytesPerSector = BytesPerSector;
DiskDrive->DaUa = DaUa;
DiskDrive->Type = DRIVE_FDD;
DiskDrive->LBASupported = FALSE;
DiskDrive->IsRemovable = TRUE;
DiskDrive->Initialized = TRUE;
TRACE("InitFloppyDrive(0x%x) returned:\n"
"Cylinders : 0x%x\n"
"Heads : 0x%x\n"
"Sects/Track: 0x%x\n"
"Bytes/Sect : 0x%x\n",
DaUa,
DiskDrive->Geometry.Cylinders,
DiskDrive->Geometry.Heads,
DiskDrive->Geometry.Sectors,
DiskDrive->Geometry.BytesPerSector);
return TRUE;
}
/* We emulate PC BIOS drive numbers here */
BOOLEAN
Pc98InitializeBootDevices(VOID)
{
PPC98_DISK_DRIVE DiskDrive;
UCHAR FakeFloppyDriveNumber = 0x30;
UCHAR FakeHardDriveDriveNumber = 0x80;
UCHAR FakeCdRomDriveNumber = 0xE0;
USHORT DiskEquipment = *(PUSHORT)MEM_DISK_EQUIP & ~(*(PUCHAR)MEM_RDISK_EQUIP);
UCHAR IdeDetectedCount;
UCHAR i;
TRACE("Pc98InitializeBootDevices()\n");
RtlZeroMemory(&Pc98DiskDrive, sizeof(Pc98DiskDrive));
/*
* Map DA/UA to drive number, i.e.
* 0x90 -> 0x30
* 0x80 -> 0x80
* 0xA0 -> 0x81, etc.
*/
/* Map floppies */
for (i = 0; i < 4; i++)
{
DiskDrive = &Pc98DiskDrive[FakeFloppyDriveNumber];
if (FIRSTBYTE(DiskEquipment) & (1 << i))
{
if (InitFloppyDrive(0x30 + i, DiskDrive) || InitFloppyDrive(0xB0 + i, DiskDrive) ||
InitFloppyDrive(0x90 + i, DiskDrive) || InitFloppyDrive(0x10 + i, DiskDrive))
++FakeFloppyDriveNumber;
}
}
for (i = 0; i < 4; i++)
{
DiskDrive = &Pc98DiskDrive[FakeFloppyDriveNumber];
if (FIRSTBYTE(DiskEquipment) & (16 << i))
{
if (InitFloppyDrive(0x50 + i, DiskDrive))
++FakeFloppyDriveNumber;
}
}
for (i = 0; i < 4; i++)
{
DiskDrive = &Pc98DiskDrive[FakeFloppyDriveNumber];
if (SECONDBYTE(DiskEquipment) & (16 << i))
{
if (InitFloppyDrive(0x70 + i, DiskDrive) || InitFloppyDrive(0xF0 + i, DiskDrive))
++FakeFloppyDriveNumber;
}
}
/* Map IDE/SASI drives */
for (i = 0; i < 4; i++)
{
DiskDrive = &Pc98DiskDrive[FakeHardDriveDriveNumber];
if (InitHardDrive(0x80 + i, DiskDrive) || InitHardDrive(0x00 + i, DiskDrive))
++FakeHardDriveDriveNumber;
}
AtaInit(&IdeDetectedCount);
for (i = 0; i <= IdeDetectedCount; i++)
{
DiskDrive = &Pc98DiskDrive[FakeCdRomDriveNumber];
if (InitIdeDrive(i, DiskDrive))
++FakeCdRomDriveNumber;
}
/* Map SCSI drives */
for (i = 0; i < 7; i++)
{
DiskDrive = &Pc98DiskDrive[FakeHardDriveDriveNumber];
if (InitScsiDrive(0xA0 + i, DiskDrive) || InitScsiDrive(0x20 + i, DiskDrive))
{
if (DiskDrive->Type & DRIVE_CDROM || DiskDrive->Type & DRIVE_MO)
{
/* Move to CD-ROM area */
Pc98DiskDrive[FakeCdRomDriveNumber] = *DiskDrive;
RtlZeroMemory(DiskDrive, sizeof(PC98_DISK_DRIVE));
++FakeCdRomDriveNumber;
}
else
{
++FakeHardDriveDriveNumber;
}
}
}
#if 1
// Ugly HACK: Force ISO boot
// FIXME: Fill ARC disk blocks completely
// to allow usage of CD-ROM root path (See floppy_pc98.ini).
FrldrBootDrive = 0xE0;
FrldrBootPartition = 0xFF;
#else
/* Reassign boot drive */
for (i = 0; i < MAX_DRIVES - 1; i++)
{
DiskDrive = &Pc98DiskDrive[i];
if (DiskDrive->Initialized && DiskDrive->DaUa == FrldrBootDrive)
{
TRACE("Boot drive: old 0x%x, new 0x%x\n", FrldrBootDrive, i);
FrldrBootDrive = i;
break;
}
}
#endif
/* Call PC version */
return PcInitializeBootDevices();
}
BOOLEAN
Pc98DiskReadLogicalSectors(
IN UCHAR DriveNumber,
IN ULONGLONG SectorNumber,
IN ULONG SectorCount,
OUT PVOID Buffer)
{
PPC98_DISK_DRIVE DiskDrive;
TRACE("Pc98DiskReadLogicalSectors() DriveNumber: 0x%x SectorNumber: %I64d SectorCount: %d Buffer: 0x%x\n",
DriveNumber, SectorNumber, SectorCount, Buffer);
/* 16-bit BIOS addressing limitation */
ASSERT(((ULONG_PTR)Buffer) <= 0xFFFFF);
DiskDrive = Pc98DiskDriveNumberToDrive(DriveNumber);
if (!DiskDrive)
return FALSE;
if (DiskDrive->LBASupported)
{
/* LBA is easy, nothing to calculate. Just do the read. */
TRACE("--> Using LBA\n");
return Pc98DiskReadLogicalSectorsLBA(DiskDrive, SectorNumber, SectorCount, Buffer);
}
else
{
/* LBA is not supported, default to CHS */
TRACE("--> Using CHS\n");
return Pc98DiskReadLogicalSectorsCHS(DiskDrive, SectorNumber, SectorCount, Buffer);
}
}
BOOLEAN
Pc98DiskGetDriveGeometry(UCHAR DriveNumber, PGEOMETRY Geometry)
{
PPC98_DISK_DRIVE DiskDrive;
TRACE("Pc98DiskGetDriveGeometry(0x%x)\n", DriveNumber);
DiskDrive = Pc98DiskDriveNumberToDrive(DriveNumber);
if (!DiskDrive)
return FALSE;
*Geometry = DiskDrive->Geometry;
return TRUE;
}
ULONG
Pc98DiskGetCacheableBlockCount(UCHAR DriveNumber)
{
PPC98_DISK_DRIVE DiskDrive;
DiskDrive = Pc98DiskDriveNumberToDrive(DriveNumber);
if (!DiskDrive)
return 1; // Unknown count.
/*
* If LBA is supported then the block size will be 64 sectors (32k).
* If not then the block size is the size of one track.
*/
if (DiskDrive->LBASupported)
return 64;
else
return DiskDrive->Geometry.Sectors;
}

File diff suppressed because it is too large Load diff

View file

@ -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 <freeldr.h>
#include <debug.h>
DBG_DEFAULT_CHANNEL(MEMORY);
/* pcmem.c */
extern VOID
SetMemory(
PFREELDR_MEMORY_DESCRIPTOR MemoryMap,
ULONG_PTR BaseAddress,
SIZE_T Size,
TYPE_OF_MEMORY MemoryType);
/* pcmem.c */
extern VOID
ReserveMemory(
PFREELDR_MEMORY_DESCRIPTOR MemoryMap,
ULONG_PTR BaseAddress,
SIZE_T Size,
TYPE_OF_MEMORY MemoryType,
PCHAR Usage);
/* pcmem.c */
extern ULONG
PcMemFinalizeMemoryMap(PFREELDR_MEMORY_DESCRIPTOR MemoryMap);
extern BOOLEAN HiResoMachine;
/* GLOBALS ********************************************************************/
#define KB 1024
#define MB (KB * KB)
static FREELDR_MEMORY_DESCRIPTOR Pc98MemoryMap[MAX_BIOS_DESCRIPTORS + 1];
/* FUNCTIONS ******************************************************************/
PFREELDR_MEMORY_DESCRIPTOR
Pc98MemGetMemoryMap(ULONG *MemoryMapSize)
{
USHORT ConventionalMemory, ExtendedMemory;
ULONG ExtendedMemory16;
TRACE("Pc98MemGetMemoryMap()\n");
RtlZeroMemory(&PcBiosMemoryMap, sizeof(BIOS_MEMORY_MAP) * MAX_BIOS_DESCRIPTORS);
PcBiosMapCount = 0;
ConventionalMemory = ((*(PUCHAR)MEM_BIOS_FLAG1 & CONVENTIONAL_MEMORY_SIZE) + 1) * 128;
ExtendedMemory = *(PUCHAR)MEM_EXPMMSZ * 128;
ExtendedMemory16 = (*(PUCHAR)MEM_EXPMMSZ16M_LOW + (*(PUCHAR)MEM_EXPMMSZ16M_HIGH << 8)) * 1024;
if (ConventionalMemory > 640 && !HiResoMachine)
ConventionalMemory = 640;
TRACE("Total conventional memory %d kB available.\n", ConventionalMemory);
TRACE("Total extended memory %d kB available.\n", ExtendedMemory);
TRACE("Total extended high memory %d kB available.\n", ExtendedMemory16);
TRACE("Installed physical memory %d kB.\n", ConventionalMemory + ExtendedMemory + ExtendedMemory16);
/* First, setup allowed ranges */
SetMemory(Pc98MemoryMap, 0x0000600, ConventionalMemory * KB, LoaderFree);
SetMemory(Pc98MemoryMap, 0x0100000, ExtendedMemory * KB, LoaderFree);
SetMemory(Pc98MemoryMap, 0x1000000, ExtendedMemory16 * KB, LoaderFree);
/* Next, setup some protected ranges */
if (HiResoMachine)
{
SetMemory(Pc98MemoryMap, 0x000000, 1 * KB, LoaderFirmwarePermanent); /* Real mode IVT */
SetMemory(Pc98MemoryMap, 0x000400, 512, LoaderFirmwarePermanent); /* Real mode BDA */
SetMemory(Pc98MemoryMap, 0x080000, 256 * KB, LoaderFirmwarePermanent);/* Memory Window */
SetMemory(Pc98MemoryMap, 0x0C0000, 128 * KB, LoaderFirmwarePermanent);/* VRAM */
SetMemory(Pc98MemoryMap, 0x0E0000, 16 * KB, LoaderFirmwarePermanent); /* Text VRAM */
SetMemory(Pc98MemoryMap, 0x0E4000, 4 * KB, LoaderFirmwarePermanent); /* CG Window */
SetMemory(Pc98MemoryMap, 0x0E5000, 103 * KB, LoaderSpecialMemory); /* BIOS ROM */
SetMemory(Pc98MemoryMap, 0xF00000, 640 * KB, LoaderSpecialMemory); /* Reserved */
}
else
{
SetMemory(Pc98MemoryMap, 0x000000, 1 * KB, LoaderFirmwarePermanent); /* Real mode IVT */
SetMemory(Pc98MemoryMap, 0x000400, 512, LoaderFirmwarePermanent); /* Real mode BDA */
SetMemory(Pc98MemoryMap, 0x000600 + ConventionalMemory * KB,
(640 - ConventionalMemory) * KB, LoaderSpecialMemory); /* External bus */
SetMemory(Pc98MemoryMap, 0x0A0000, 16 * KB, LoaderFirmwarePermanent); /* Text VRAM */
SetMemory(Pc98MemoryMap, 0x0A4000, 4 * KB, LoaderFirmwarePermanent); /* CG Window */
SetMemory(Pc98MemoryMap, 0x0A5000, 12 * KB, LoaderFirmwarePermanent); /* Reserved */
SetMemory(Pc98MemoryMap, 0x0A8000, 96 * KB, LoaderFirmwarePermanent); /* VRAM (Plane B, R, G) */
SetMemory(Pc98MemoryMap, 0x0C0000, 128 * KB, LoaderSpecialMemory); /* BIOS ROM (Peripherals) */
SetMemory(Pc98MemoryMap, 0x0E0000, 32 * KB, LoaderFirmwarePermanent); /* VRAM (Plane I) */
SetMemory(Pc98MemoryMap, 0x0E8000, 96 * KB, LoaderSpecialMemory); /* BIOS ROM */
SetMemory(Pc98MemoryMap, 0xF00000, 640 * KB, LoaderSpecialMemory); /* Reserved */
}
*MemoryMapSize = PcMemFinalizeMemoryMap(Pc98MemoryMap);
return Pc98MemoryMap;
}

View file

@ -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 <freeldr.h>
#define BCD_INT(bcd) (((bcd & 0xF0) >> 4) * 10 + (bcd & 0x0F))
TIMEINFO*
Pc98GetTime(VOID)
{
static TIMEINFO TimeInfo;
REGS Regs;
UCHAR SysTime[6];
/* Int 1Ch AH=00h
* TIMER BIOS - Read system time
*
* Call with:
* ES:BX -> data buffer
*/
Regs.b.ah = 0x00;
Regs.w.es = ((ULONG_PTR)SysTime) >> 4;
Regs.w.bx = ((ULONG_PTR)SysTime) & 0x0F;
Int386(0x1C, &Regs, &Regs);
TimeInfo.Year = BCD_INT(SysTime[0]);
TimeInfo.Month = BCD_INT(SysTime[1] >> 4);
TimeInfo.Day = BCD_INT(SysTime[2]);
TimeInfo.Hour = BCD_INT(SysTime[3]);
TimeInfo.Minute = BCD_INT(SysTime[4]);
TimeInfo.Second = BCD_INT(SysTime[5]);
if (TimeInfo.Year >= 80)
TimeInfo.Year += 1900;
else
TimeInfo.Year += 2000;
return &TimeInfo;
}

View file

@ -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 <freeldr.h>
#include <drivers/pc98/video.h>
extern UCHAR XboxFont8x16[];
extern BOOLEAN HiResoMachine;
/* GLOBALS ********************************************************************/
#define VGA_CHAR_SIZE 2
#define TEXT_CHAR_SIZE 2
UCHAR TextCols;
UCHAR TextLines;
#define CHAR_WIDTH 8
#define CHAR_HEIGHT 16
#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 400
#define BYTES_PER_SCANLINE (SCREEN_WIDTH / 8)
ULONG VramText;
static ULONG VramPlaneB;
static ULONG VramPlaneG;
static ULONG VramPlaneR;
static ULONG VramPlaneI;
static const PALETTE_ENTRY CgaPalette[] =
{
{0x00, 0x00, 0x00},
{0x00, 0x00, 0x0A},
{0x00, 0x0A, 0x00},
{0x00, 0x0A, 0x0A},
{0x0A, 0x00, 0x00},
{0x0A, 0x00, 0x0A},
{0x0A, 0x05, 0x00},
{0x0A, 0x0A, 0x0A},
{0x05, 0x05, 0x05},
{0x05, 0x05, 0x0F},
{0x05, 0x0F, 0x05},
{0x05, 0x0F, 0x0F},
{0x0F, 0x05, 0x05},
{0x0F, 0x05, 0x0F},
{0x0F, 0x0F, 0x05},
{0x0F, 0x0F, 0x0F}
};
/* FUNCTIONS ******************************************************************/
VOID
Pc98VideoInit(VOID)
{
REGS Regs;
USHORT i;
if (HiResoMachine)
{
VramPlaneB = VRAM_HI_RESO_PLANE_B;
VramPlaneG = VRAM_HI_RESO_PLANE_G;
VramPlaneR = VRAM_HI_RESO_PLANE_R;
VramPlaneI = VRAM_HI_RESO_PLANE_I;
VramText = VRAM_HI_RESO_TEXT;
TextCols = 80;
TextLines = 31;
}
else
{
VramPlaneB = VRAM_NORMAL_PLANE_B;
VramPlaneG = VRAM_NORMAL_PLANE_G;
VramPlaneR = VRAM_NORMAL_PLANE_R;
VramPlaneI = VRAM_NORMAL_PLANE_I;
VramText = VRAM_NORMAL_TEXT;
TextCols = 80;
TextLines = 25;
}
for (i = 0; i < VRAM_ATTR_SIZE; i += TEXT_CHAR_SIZE)
*(PUCHAR)(VramText + VRAM_TEXT_ATTR_OFFSET + i) = GDC_ATTR_WHITE | GDC_ATTR_VISIBLE;
/* Int 18h AH=41h
* CRT BIOS - Stop displaying graphics
*/
Regs.b.ah = 0x41;
Int386(0x18, &Regs, &Regs);
/* Int 18h AH=42h
* CRT BIOS - Set display area
*
* CH0-CH3 - always zero
* CH4 - video page
* CH5 - CRT display mode
* 0 - color
* 1 - monochrome
* CH6-CH7 - VRAM area
* 01 - Upper-half (16-32 kB), 640x200
* 10 - Lower-half (0-16 kB), 640x200
* 11 - All (0-32 kB), 640x400
*/
Regs.b.ah = 0x42;
Regs.b.ch = 0xC0;
Int386(0x18, &Regs, &Regs); /* 640x400 */
WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_MODE_FLIPFLOP2, GDC2_MODE_COLORS_16);
WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_VIDEO_PAGE, 0);
WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_VIDEO_PAGE_ACCESS, 0);
Pc98VideoSync();
for (i = 0; i < RTL_NUMBER_OF(CgaPalette); i++)
Pc98VideoSetPaletteColor(i, CgaPalette[i].Red, CgaPalette[i].Green, CgaPalette[i].Blue);
/* Int 18h AH=A0h
* CRT BIOS - Set text screen mode
*
* AL0 - text rows
* 0 - 25
* 1 - 20
* AL1 - text cols
* 0 - 80
* 1 - 40
* AL2 - text attribute
* 0 - with vertical line
* 1 - normal
* AL3 - KCG access mode
* 0 - code
* 1 - bitmap
* AL4-AL7 - always zero
*
* High-resolution machine:
* AL4 - text rows, AL3 - KCG access mode
*/
Regs.b.ah = 0xA0;
Regs.b.al = HiResoMachine ? 0x10 : 0x00;
Int386(0x18, &Regs, &Regs); /* 80x25(31) */
/* Int 18h AH=0Ch
* CRT BIOS - Start displaying text
*/
Regs.b.ah = 0x0C;
Int386(0x18, &Regs, &Regs);
/* Int 18h AH=40h
* CRT BIOS - Start displaying graphics
*/
Regs.b.ah = 0x40;
Int386(0x18, &Regs, &Regs);
}
VOID
Pc98VideoClearScreen(UCHAR Attr)
{
USHORT i;
USHORT B = (Attr & 0x10) ? 0xFFFF : 0;
USHORT G = (Attr & 0x20) ? 0xFFFF : 0;
USHORT R = (Attr & 0x40) ? 0xFFFF : 0;
USHORT I = (Attr & 0x80) ? 0xFFFF : 0;
for (i = 0; i < VRAM_TEXT_SIZE; i += TEXT_CHAR_SIZE)
*(PUSHORT)(VramText + i) = ' ';
for (i = 0; i < BYTES_PER_SCANLINE * SCREEN_HEIGHT; i += sizeof(USHORT))
{
*(PUSHORT)(VramPlaneB + i) = B;
*(PUSHORT)(VramPlaneG + i) = G;
*(PUSHORT)(VramPlaneR + i) = R;
*(PUSHORT)(VramPlaneI + i) = I;
}
}
VIDEODISPLAYMODE
Pc98VideoSetDisplayMode(char *DisplayModeName, BOOLEAN Init)
{
/* Not supported by hardware */
return VideoTextMode;
}
VOID
Pc98VideoGetDisplaySize(PULONG Width, PULONG Height, PULONG Depth)
{
*Width = SCREEN_WIDTH / CHAR_WIDTH;
*Height = SCREEN_HEIGHT / CHAR_HEIGHT;
*Depth = 0;
}
ULONG
Pc98VideoGetBufferSize(VOID)
{
return (SCREEN_WIDTH / CHAR_WIDTH) * (SCREEN_HEIGHT / CHAR_HEIGHT) * VGA_CHAR_SIZE;
}
VOID
Pc98VideoGetFontsFromFirmware(PULONG RomFontPointers)
{
*RomFontPointers = VramText + 0x4000;
}
VOID
Pc98VideoSetTextCursorPosition(UCHAR X, UCHAR Y)
{
CSRWPARAM CursorParameters;
RtlZeroMemory(&CursorParameters, sizeof(CSRWPARAM));
CursorParameters.CursorAdress = X + Y * TextCols;
CursorParameters.DotAddress = 0;
WRITE_PORT_UCHAR((PUCHAR)GDC1_IO_o_COMMAND, GDC_COMMAND_CSRW);
WRITE_GDC_CSRW((PUCHAR)GDC1_IO_o_PARAM, &CursorParameters);
}
VOID
Pc98VideoHideShowTextCursor(BOOLEAN Show)
{
CSRFORMPARAM CursorParameters;
RtlZeroMemory(&CursorParameters, sizeof(CSRFORMPARAM));
CursorParameters.Show = Show;
CursorParameters.Blink = TRUE;
CursorParameters.BlinkRate = 12;
CursorParameters.LinesPerRow = 16;
CursorParameters.StartScanLine = 12;
CursorParameters.EndScanLine = 15;
WRITE_PORT_UCHAR((PUCHAR)GDC1_IO_o_COMMAND, GDC_COMMAND_CSRFORM);
WRITE_GDC_CSRFORM((PUCHAR)GDC1_IO_o_PARAM, &CursorParameters);
}
VOID
Pc98VideoPutChar(int Ch, UCHAR Attr, unsigned X, unsigned Y)
{
UCHAR Line;
UCHAR B = (Attr & 0x10) ? 0xFF : 0;
UCHAR G = (Attr & 0x20) ? 0xFF : 0;
UCHAR R = (Attr & 0x40) ? 0xFF : 0;
UCHAR I = (Attr & 0x80) ? 0xFF : 0;
ULONG VramOffset = X + (Y * CHAR_HEIGHT) * BYTES_PER_SCANLINE;
PUCHAR FontPtr = XboxFont8x16 + Ch * 16;
for (Line = 0; Line < CHAR_HEIGHT; Line++)
{
if (Attr & 0x0F)
{
*(PUCHAR)(VramPlaneB + VramOffset + Line * BYTES_PER_SCANLINE) = B | ((Attr & 0x01) ? FontPtr[Line] : 0);
*(PUCHAR)(VramPlaneG + VramOffset + Line * BYTES_PER_SCANLINE) = G | ((Attr & 0x02) ? FontPtr[Line] : 0);
*(PUCHAR)(VramPlaneR + VramOffset + Line * BYTES_PER_SCANLINE) = R | ((Attr & 0x04) ? FontPtr[Line] : 0);
*(PUCHAR)(VramPlaneI + VramOffset + Line * BYTES_PER_SCANLINE) = I | ((Attr & 0x08) ? FontPtr[Line] : 0);
}
else
{
*(PUCHAR)(VramPlaneB + VramOffset + Line * BYTES_PER_SCANLINE) = B & ~FontPtr[Line];
*(PUCHAR)(VramPlaneG + VramOffset + Line * BYTES_PER_SCANLINE) = G & ~FontPtr[Line];
*(PUCHAR)(VramPlaneR + VramOffset + Line * BYTES_PER_SCANLINE) = R & ~FontPtr[Line];
*(PUCHAR)(VramPlaneI + VramOffset + Line * BYTES_PER_SCANLINE) = I & ~FontPtr[Line];
}
}
}
VOID
Pc98VideoCopyOffScreenBufferToVRAM(PVOID Buffer)
{
PUCHAR OffScreenBuffer = (PUCHAR)Buffer;
USHORT X, Y;
for (Y = 0; Y < SCREEN_HEIGHT / CHAR_HEIGHT; Y++)
{
for (X = 0; X < SCREEN_WIDTH / CHAR_WIDTH; X++)
{
Pc98VideoPutChar(OffScreenBuffer[0], OffScreenBuffer[1], X, Y);
OffScreenBuffer += VGA_CHAR_SIZE;
}
}
}
BOOLEAN
Pc98VideoIsPaletteFixed(VOID)
{
return FALSE;
}
VOID
Pc98VideoSetPaletteColor(UCHAR Color, UCHAR Red, UCHAR Green, UCHAR Blue)
{
if (Color < 16)
{
WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_PALETTE_INDEX, Color);
WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_RED, Red);
WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_GREEN, Green);
WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_BLUE, Blue);
}
}
VOID
Pc98VideoGetPaletteColor(UCHAR Color, UCHAR* Red, UCHAR* Green, UCHAR* Blue)
{
if (Color < 16)
{
WRITE_PORT_UCHAR((PUCHAR)GDC2_IO_o_PALETTE_INDEX, Color);
*Red = READ_PORT_UCHAR((PUCHAR)GDC2_IO_i_RED);
*Green = READ_PORT_UCHAR((PUCHAR)GDC2_IO_i_GREEN);
*Blue = READ_PORT_UCHAR((PUCHAR)GDC2_IO_i_BLUE);
}
else
{
*Red = 0;
*Green = 0;
*Blue = 0;
}
}
VOID
Pc98VideoSync(VOID)
{
while (READ_PORT_UCHAR((PUCHAR)GDC2_IO_i_STATUS) & GDC_STATUS_VSYNC)
NOTHING;
while (!(READ_PORT_UCHAR((PUCHAR)GDC2_IO_i_STATUS) & GDC_STATUS_VSYNC))
NOTHING;
}
VOID
Pc98VideoPrepareForReactOS(VOID)
{
REGS Regs;
/* Int 18h AH=41h
* CRT BIOS - Stop displaying graphics
*/
Regs.b.ah = 0x41;
Int386(0x18, &Regs, &Regs);
Pc98VideoHideShowTextCursor(FALSE);
}

View file

@ -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

View file

@ -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"

View file

@ -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*/

View file

@ -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);

View file

@ -104,16 +104,23 @@
#if defined(_M_IX86) || defined(_M_AMD64)
#include <arch/pc/hardware.h>
#include <arch/pc/pcbios.h>
#include <arch/pc/machpc.h>
#include <arch/pc/x86common.h>
#include <arch/pc/pxe.h>
#include <arch/i386/drivemap.h>
#endif
#if defined(_M_IX86)
#include <arch/i386/i386.h>
#if defined(SARCH_PC98)
#include <arch/i386/machpc98.h>
#elif defined(SARCH_XBOX)
#include <arch/pc/machpc.h>
#include <arch/i386/machxbox.h>
#else
#include <arch/pc/machpc.h>
#endif
#include <arch/i386/i386.h>
#include <internal/i386/intrin_i.h>
#elif defined(_M_AMD64)
#include <arch/pc/machpc.h>
#include <arch/amd64/amd64.h>
#include <internal/amd64/intrin_i.h>
#elif defined(_M_PPC)

View file

@ -1,25 +1,40 @@
/*
* FreeLoader
* Copyright (C) 1998-2003 Brian Palmer <brianp@sginet.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* PROJECT: FreeLoader
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: Key codes header file
* COPYRIGHT: Copyright 1998-2003 Brian Palmer (brianp@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

View file

@ -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;

View file

@ -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

View file

@ -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