language-Modula2-0.1: examples/Modula-2_Libraries/PMOS/sources/general/harddisk.mod
IMPLEMENTATION MODULE HardDisk;
(********************************************************)
(* *)
(* Device driver for hard disk *)
(* *)
(* Programmer: P. Moylan *)
(* Last edited: 26 February 1995 *)
(* Status: Working *)
(* This module has been implicated in a number of *)
(* mysterious bugs, but every time I've tried to *)
(* track down problems they've turned out to be *)
(* artefacts of VID or unfortunate interactions *)
(* with SmartDrive. Although it's hard to be *)
(* completely sure that the module is bug-free, *)
(* I'm reasonably certain that it is working as *)
(* intended. *)
(* *)
(* Note however that the safety of this module is *)
(* suspect in an environment where the Windows *)
(* SmartDrive program is running. *)
(* *)
(* Note also that OS/2 will prevent the *)
(* initialisation phase of this module from *)
(* reading the partition table; this will cause *)
(* the module to conclude that there are no *)
(* logical drives, which of course means that *)
(* the module does not do anything useful *)
(* under OS/2. *)
(* *)
(********************************************************)
(************************************************************************)
(* *)
(* The hardware interface is an array of 8 ports, starting at port 1F0H,*)
(* as follows: *)
(* 0 read or write data *)
(* 1 set precompensation cylinder, when written to *)
(* error register, when read *)
(* 2 sector count *)
(* 3 sector number *)
(* 4 cylinder number (bottom 8 bits) *)
(* 5 cylinder number (top 2 bits) *)
(* 6 ECC/size/drive/head, stored as: *)
(* bits 3-0 head *)
(* bit 4 drive *)
(* bits 6, 5 sector size code: 00 for 256 *)
(* bytes, 01 for 512, 10 for 1024, *)
(* 11 for 128-byte sectors *)
(* bit 7 extend length to include ECC *)
(* 7 command register, when written to *)
(* status register, when read *)
(* *)
(* The status register bits have the meaning: *)
(* bit 7 controller busy *)
(* bit 6 drive ready *)
(* bit 5 write fault *)
(* bit 4 seek complete *)
(* bit 3 data request *)
(* bit 2 data error corrected from ECC *)
(* bit 1 command in progress *)
(* bit 0 status error, see error register *)
(* *)
(* The bit definitions for the error register are shown in procedure *)
(* CheckStatus. *)
(* *)
(* There is also a control byte at port 3F6H, for which we set bit 3 *)
(* to specify >8 heads; in addition setting bit 6 or bit 7 of this *)
(* byte will disable retries. *)
(* *)
(* Interrupts are INT 76H, which is IRQ level 14, i.e. it is through *)
(* the slave interrupt controller. *)
(* *)
(************************************************************************)
FROM SYSTEM IMPORT
(* type *) BYTE, ADDRESS;
FROM Storage1 IMPORT
(* proc *) ALLOCATE, DEALLOCATE;
FROM IOErrorCodes IMPORT
(* type *) ErrorCode;
FROM LowLevel IMPORT
(* proc *) InByte, OutByte, InStringWord, OutStringWord,
IANDB, IOR, LowByte, HighByte,
MakePointer, AddOffset;
FROM MiscPMOS IMPORT
(* type *) RegisterPacket,
(* proc *) BIOS, EnterCriticalSection, LeaveCriticalSection;
FROM Semaphores IMPORT
(* type *) Semaphore,
(* proc *) CreateSemaphore, Signal;
FROM Timer IMPORT
(* proc *) TimedWait;
FROM TaskControl IMPORT
(* proc *) CreateTask, WaitForInterrupt, CreateInterruptTask;
FROM Devices IMPORT
(* type *) BlockNumberType, Device, RequestBlock, RequestBlockPointer,
OperationType,
(* proc *) InstallDeviceDriver, DeviceName, AcceptRequest;
FROM DMA IMPORT
(* proc *) CheckDMAAddress;
FROM Windows IMPORT
(* type *) Window, Colour, FrameType, DividerType,
(* proc *) OpenWindow, WriteString, ChangeScrollingRegion;
FROM MaintenancePages IMPORT
(* type *) MaintenancePage,
(* proc *) CreateMaintenancePage, Associate;
(************************************************************************)
CONST
testing = TRUE; (* controls output to maintenance page *)
DataPort = 01F0H;
StatusPort = DataPort+7;
DiskInterrupt = 76H;
BlockSize = 512; (* bytes per sector *)
TYPE
(* The following declarations reflect the fact that a hard disk *)
(* may be partitioned into several logical drives, each of which *)
(* looks like a separate drive to the programmer. *)
PhysicalDriveNumber = [0..1];
LogicalDriveNumber = [0..7];
CommandArray = ARRAY [1..7] OF BYTE;
(* Because we have to allow for a variety of disk types, there is a *)
(* fixed disk parameter table where each entry describes one *)
(* supported disk type. Interrupt vector 41H (address 104H) is a *)
(* pointer to the table entry for drive 0, and vector 46H (address *)
(* 118H) points to the table entry for drive 1. The table entry is *)
(* 16 bytes long: *)
(* max # of cylinders : CARDINAL; *)
(* max # of heads : BYTE; *)
(* unused : WORD; *)
(* starting write precompensation cylinder: WORD; *)
(* max ECC data burst length: BYTE; *)
(* control byte : BYTE; *)
(* bit 7 disables retries -or- *)
(* bit 6 disables retries *)
(* bit 3 means more than 8 heads *)
(* unused: : ARRAY [1..3] OF BYTE; *)
(* landing zone: : WORD; *)
(* sectors per track: : BYTE; *)
(* reserved : BYTE *)
(* The relevant parts of this table are copied by the module *)
(* initialisation code into PhysicalDriveData. Note that a lot of *)
(* this information is never used by this module. *)
DiskParameterTable = RECORD
maxcylinders: CARDINAL;
maxheads: BYTE;
dummy1: CARDINAL; (* unused *)
precomp: CARDINAL;
maxECCburstlength: BYTE;
optionbyte: BYTE;
dummy2: ARRAY [1..3] OF BYTE; (* unused *)
landingzone: CARDINAL;
sectorspertrack: BYTE;
dummy3: BYTE; (* unused *)
END (*RECORD*);
VAR
log: Window; (* for maintenance page output *)
harddisk: Device;
DataRequest: Semaphore;
NumberOfPhysicalDrives: [0..MAX(PhysicalDriveNumber)+1];
NumberOfLogicalDrives: [0..MAX(LogicalDriveNumber)+1];
PhysicalDriveData: ARRAY PhysicalDriveNumber OF
RECORD
SectorsPerTrack: CARDINAL;
NumberOfHeads: CARDINAL;
PrecompensationCylinder: CARDINAL;
ExtraHeadOption: BYTE;
END (*RECORD*);
LogicalUnit: ARRAY LogicalDriveNumber OF
RECORD
physicaldrive: PhysicalDriveNumber;
bootblock: BlockNumberType;
numberofsectors: BlockNumberType;
END (*RECORD*);
(************************************************************************)
(* HARDWARE STATUS CHECKS *)
(************************************************************************)
PROCEDURE TestReady (VAR (*IN*) CommandBlock: CommandArray): ErrorCode;
(* Checks that both the controller and the drive are ready to *)
(* accept a command. *)
CONST lots = 32767;
VAR patience: CARDINAL;
BEGIN
(* First test the controller status byte, looping until the *)
(* busy bit is clear or until we run out of patience. *)
patience := lots;
WHILE ORD(IANDB (InByte (StatusPort), 80H)) <> 0 DO
DEC (patience);
IF patience = 0 THEN
RETURN ControllerNotListening;
END(*IF*);
END (*WHILE*);
(* Put out the ECC/size/drive/head code, to check whether the *)
(* drive is ready. *)
OutByte (DataPort+6, CommandBlock[6]);
IF ORD(IANDB (InByte(StatusPort), 40H)) = 0 THEN
RETURN DriveNotReady;
END (*IF*);
RETURN OK;
END TestReady;
(************************************************************************)
PROCEDURE CheckStatus (): ErrorCode;
(* Returns the current status of the disk controller. *)
(* N.B. Reports OK if the controller is still busy. *)
VAR StatusByte: BYTE;
BEGIN
StatusByte := InByte (StatusPort);
IF ORD(IANDB (StatusByte, 80H)) <> 0 THEN RETURN OK;
ELSIF ORD(IANDB (StatusByte, 20H)) <> 0 THEN RETURN WriteFault;
ELSIF ORD(IANDB (StatusByte, 40H)) = 0 THEN RETURN DriveNotReady;
ELSIF ORD(IANDB (StatusByte, 10H)) = 0 THEN RETURN SeekFailure;
(* If the low-order bit of the status register is set, we need *)
(* to look at the error register. *)
ELSIF ODD (StatusByte) THEN
StatusByte := InByte (DataPort+1);
IF ORD(IANDB(StatusByte,80H)) <> 0 THEN RETURN BadBlock
ELSIF ORD(IANDB(StatusByte,40H)) <> 0 THEN RETURN BadData
ELSIF ORD(IANDB(StatusByte,20H)) <> 0 THEN RETURN UndiagnosedFailure
ELSIF ORD(IANDB(StatusByte,10H)) <> 0 THEN RETURN SectorNotFound
ELSIF ORD(IANDB(StatusByte,08H)) <> 0 THEN RETURN UndiagnosedFailure
ELSIF ORD(IANDB(StatusByte,04H)) <> 0 THEN RETURN BadCommand
ELSIF ORD(IANDB(StatusByte,02H)) <> 0 THEN RETURN CalibrationFailure
ELSIF ORD(IANDB(StatusByte,01H)) <> 0 THEN RETURN BadData
ELSE RETURN OK;
END (*IF*);
ELSE RETURN OK;
END (*IF*);
END CheckStatus;
(************************************************************************)
(* SENDING A COMMAND TO THE DISK CONTROLLER *)
(************************************************************************)
PROCEDURE Command (VAR (*IN*) CommandBlock: CommandArray): ErrorCode;
(* Outputs the command block. *)
VAR patience: CARDINAL;
status: ErrorCode;
j: [1..7];
BEGIN
patience := 1000;
LOOP
status := TestReady (CommandBlock);
IF status = OK THEN EXIT (*LOOP*) END (*IF*);
IF status = ControllerNotListening THEN
RETURN status;
END (*IF*);
DEC (patience);
IF patience = 0 THEN RETURN status; END (*IF*);
END (*LOOP*);
(* Send the command block to the device interface. *)
FOR j := 1 TO 7 DO
OutByte (DataPort+j, CommandBlock[j]);
END (*FOR*);
RETURN OK;
END Command;
(************************************************************************)
(* THE INTERRUPT TASK *)
(************************************************************************)
PROCEDURE DRQInterruptHandler;
(* Performs a Signal(DataRequest) on each hardware interrupt. *)
BEGIN
LOOP (*FOREVER*)
WaitForInterrupt;
Signal (DataRequest);
END (*LOOP*);
END DRQInterruptHandler;
(************************************************************************)
(* CHECK FOR DISK READY *)
(************************************************************************)
PROCEDURE WaitDRQ (VAR (*OUT*) TimedOut: BOOLEAN);
(* Busy waits for a data request. *)
VAR patience: CARDINAL;
BEGIN
patience := 100H; TimedOut := FALSE;
LOOP
IF ORD(IANDB (InByte(StatusPort), 8)) <> 0 THEN
EXIT(*LOOP*);
END(*IF*);
DEC(patience);
IF patience = 0 THEN
TimedOut := TRUE;
EXIT (*LOOP*);
END (*IF*);
END (*LOOP*);
END WaitDRQ;
(************************************************************************)
(* THE ACTUAL READ AND WRITE OPERATIONS *)
(************************************************************************)
PROCEDURE ReadOperation (VAR (*INOUT*) CommandBlock: CommandArray;
BufferAddress: ADDRESS): ErrorCode;
(* Data transfer from disk to DataBuffer, where CommandBlock *)
(* specifies the precise operation desired. *)
CONST largenumber = 1000;
VAR status: ErrorCode; TimedOut: BOOLEAN;
sectors: SHORTCARD;
BEGIN
(* Check for 64K boundary error. *)
IF NOT CheckDMAAddress (BufferAddress,
BlockSize*VAL(CARDINAL,CommandBlock[2])) THEN
RETURN BadDMAAddress;
END (*IF*);
status := Command (CommandBlock);
IF status <> OK THEN
RETURN status;
END (*IF*);
sectors := CommandBlock[2];
REPEAT
TimedWait (DataRequest, largenumber, TimedOut);
IF TimedOut THEN
RETURN TimeoutError;
END (*IF*);
(* Get the sector, via block input *)
InStringWord (DataPort, BufferAddress, 256 (*words*) );
BufferAddress := AddOffset (BufferAddress, BlockSize);
status := CheckStatus();
IF status <> OK THEN
RETURN status;
END (*IF*);
DEC (sectors);
UNTIL sectors = 0;
RETURN OK;
END ReadOperation;
(************************************************************************)
PROCEDURE WriteOperation (VAR (*INOUT*) CommandBlock: CommandArray;
BufferAddress: ADDRESS): ErrorCode;
(* Writes a block of data from BufferAddress^. CommandBlock *)
(* specifies the disk block, amount of data to transfer, etc. *)
CONST largenumber = 1000;
VAR status: ErrorCode; TimedOut: BOOLEAN;
BEGIN
(* Check for 64K boundary error. *)
IF NOT CheckDMAAddress (BufferAddress,
BlockSize*VAL(CARDINAL,CommandBlock[2])) THEN
RETURN BadDMAAddress;
END (*IF*);
status := Command (CommandBlock);
IF status <> OK THEN
RETURN status;
END (*IF*);
WaitDRQ (TimedOut);
IF TimedOut THEN RETURN TimeoutError END (*IF*);
REPEAT
(* Put the sector, via block output *)
OutStringWord (DataPort, BufferAddress, 256 (*words*) );
BufferAddress := AddOffset (BufferAddress, BlockSize);
TimedWait (DataRequest, largenumber, TimedOut);
IF TimedOut THEN RETURN TimeoutError END (*IF*);
IF CheckStatus() <> OK THEN RETURN status END (*IF*);
(* Remark: the termination test below is a check on the *)
(* data request bit of the status register. When the data *)
(* request bit becomes 0, we know that there are no further *)
(* sectors to transfer. *)
UNTIL ORD(IANDB(InByte (StatusPort), 8)) = 0;
RETURN OK;
END WriteOperation;
(************************************************************************)
(* SETTING UP THE COMMAND BLOCK *)
(************************************************************************)
PROCEDURE Decompose (BlockNumber: BlockNumberType; drive: PhysicalDriveNumber;
VAR (*OUT*) head, cylinder, sector: CARDINAL);
(* Translates a block number into head/cylinder/sector coordinates. *)
(* The relationship is *)
(* BlockNumber = SectorsPerTrack*(NumberOfHeads*cylinder+head) *)
(* + sector - 1 *)
(* Note that sector number starts from 1, while the numbering *)
(* of blocks, heads, and cylinders all start from 0. *)
VAR H: CARDINAL;
BEGIN
WITH PhysicalDriveData[drive] DO
sector := CARDINAL(BlockNumber MOD LONGCARD(SectorsPerTrack)) + 1;
cylinder := CARDINAL(BlockNumber DIV LONGCARD(SectorsPerTrack));
H := NumberOfHeads;
END (*WITH*);
head := cylinder MOD H;
cylinder := cylinder DIV H;
END Decompose;
(************************************************************************)
PROCEDURE Encode (VAR (*OUT*) CommandBlock: CommandArray; opcode: BYTE;
drive: PhysicalDriveNumber;
BlockNumber: BlockNumberType; count: CARDINAL);
(* Given the drive number, block number, and byte count of a *)
(* disk operation, fills in the appropriate CommandBlock fields. *)
VAR sector, cylinder, head: CARDINAL;
BEGIN
(* Deal with the write precompensation cylinder, the "extra *)
(* head" option, and retry suppression. *)
WITH PhysicalDriveData[drive] DO
CommandBlock[1] := SHORTCARD(PrecompensationCylinder DIV 4);
OutByte (3F6H, ExtraHeadOption);
IF ORD(IANDB (ExtraHeadOption, 0C0H)) <> 0 THEN
INC (opcode); (* to disable retries *)
END (*IF*);
END (*WITH*);
(* CommandBlock[2] specifies the number of sectors. *)
CommandBlock[2] := SHORTCARD(count DIV BlockSize);
(* Turn the block number into head/cylinder/sector form. *)
Decompose (BlockNumber, drive, head, cylinder, sector);
CommandBlock[3] := SHORTCARD(sector);
CommandBlock[4] := LowByte (cylinder);
CommandBlock[5] := HighByte (cylinder); (* only 2 bits *)
(* The 0A0H below is the code for ECC and 512-byte sectors. *)
CommandBlock[6] := BYTE(16*drive + head + 0A0H);
CommandBlock[7] := opcode;
END Encode;
(************************************************************************)
(* THE USER-CALLABLE READ OPERATIONS *)
(************************************************************************)
PROCEDURE ReadPhysicalBlock (drive: PhysicalDriveNumber;
BlockNumber: BlockNumberType; count: CARDINAL;
BufferAddress: ADDRESS): ErrorCode;
(* Reads "count" bytes, starting at the beginning of disk block *)
(* "BlockNumber" on the specified drive, into a memory buffer. *)
VAR CommandBlock: CommandArray; result: ErrorCode;
BEGIN
IF drive >= NumberOfPhysicalDrives THEN
result := NoSuchUnit;
ELSE
Encode (CommandBlock, 20H, drive, BlockNumber, count);
result := ReadOperation (CommandBlock, BufferAddress);
END (*IF*);
RETURN result;
END ReadPhysicalBlock;
(************************************************************************)
PROCEDURE ReadLogicalBlock (unit: LogicalDriveNumber;
RelativeBlock: BlockNumberType; count: CARDINAL;
BufferAddress: ADDRESS): ErrorCode;
(* Reads "count" bytes, starting at the beginning of disk block *)
(* "RelativeBlock" on the specified logical unit, into memory *)
(* starting at "BufferAddress". *)
VAR drive: PhysicalDriveNumber; BlockNo: BlockNumberType;
BEGIN
WITH LogicalUnit[unit] DO
IF RelativeBlock >= numberofsectors THEN
RETURN IllegalBlockNumber;
END (*IF*);
drive := physicaldrive;
BlockNo := bootblock + RelativeBlock;
END (*WITH*);
RETURN ReadPhysicalBlock (drive, BlockNo, count, BufferAddress);
END ReadLogicalBlock;
(************************************************************************)
(* THE USER-CALLABLE WRITE OPERATIONS *)
(************************************************************************)
PROCEDURE WritePhysicalBlock (drive: PhysicalDriveNumber;
BlockNumber: BlockNumberType; count: CARDINAL;
BufferAddress: ADDRESS): ErrorCode;
(* Writes "count" bytes, starting at the beginning of disk block *)
(* "BlockNumber" on the specified drive, from a memory buffer. *)
VAR CommandBlock: CommandArray; result: ErrorCode;
BEGIN
IF drive >= NumberOfPhysicalDrives THEN
result := NoSuchUnit;
ELSE
Encode (CommandBlock, 30H, drive, BlockNumber, count);
result := WriteOperation (CommandBlock, BufferAddress);
END (*IF*);
RETURN result;
END WritePhysicalBlock;
(************************************************************************)
PROCEDURE WriteLogicalBlock (unit: LogicalDriveNumber;
RelativeBlock: BlockNumberType; count: CARDINAL;
BufferAddress: ADDRESS): ErrorCode;
(* Writes "count" bytes, starting at the beginning of disk block *)
(* "RelativeBlock" on the specified logical unit, from memory *)
(* starting at "BufferAddress". *)
VAR drive: PhysicalDriveNumber; BlockNo: BlockNumberType;
BEGIN
WITH LogicalUnit[unit] DO
IF RelativeBlock >= numberofsectors THEN
RETURN IllegalBlockNumber;
END (*IF*);
drive := physicaldrive;
BlockNo := bootblock + RelativeBlock;
END (*WITH*);
RETURN WritePhysicalBlock (drive, BlockNo, count, BufferAddress);
END WriteLogicalBlock;
(************************************************************************)
(* INTERFACE TO "Devices" MODULE *)
(************************************************************************)
PROCEDURE DiskOperation (VAR (*INOUT*) details: RequestBlock);
(* Performs a disk transfer as specified in the request block. *)
BEGIN
WITH details DO
IF operation = read THEN
Status := ReadLogicalBlock (unit, BlockNumber,
ByteCount, BufferAddress);
ELSIF operation = write THEN
Status := WriteLogicalBlock (unit, BlockNumber,
ByteCount, BufferAddress);
ELSIF operation = physicalread THEN
Status := ReadPhysicalBlock (LogicalUnit[unit].physicaldrive,
BlockNumber, BlockSize, BufferAddress);
ELSIF operation = physicalwrite THEN
Status := WritePhysicalBlock (LogicalUnit[unit].physicaldrive,
BlockNumber, BlockSize, BufferAddress);
ELSE
Status := FeatureNotImplemented;
END (*IF*);
END (*WITH*);
END DiskOperation;
(************************************************************************)
PROCEDURE DiskRequestHandler;
(* This procedure runs as an independent task. Each time around *)
(* its main loop, it picks up one enqueued I/O request and executes *)
(* the request. The requests are placed on the queue by calls to *)
(* module Devices. On completion of an operation, we inform the *)
(* caller by performing a Signal on a user-specified semaphore. *)
CONST MaxNumberOfRetries = 3;
VAR reply: ErrorCode; retries: CARDINAL;
RequestPointer: RequestBlockPointer;
BEGIN
LOOP
RequestPointer := AcceptRequest (harddisk);
WITH RequestPointer^ DO
IF operation = shutdown THEN
Signal (DoneSemaphorePointer^);
EXIT (*LOOP*);
END (*IF*);
END (*WITH*);
retries := 0;
REPEAT
DiskOperation (RequestPointer^);
reply := RequestPointer^.Status;
INC (retries);
UNTIL (reply = OK) OR (retries>MaxNumberOfRetries);
Signal (RequestPointer^.DoneSemaphorePointer^);
END (*LOOP*);
END DiskRequestHandler;
(************************************************************************)
(* INITIALISATION *)
(************************************************************************)
PROCEDURE SetUpDrive (d: PhysicalDriveNumber; pplace: CARDINAL);
(* Sets up PhysicalDriveData[d] by copying the appropriate *)
(* information from a table which already exists and whose address *)
(* is held at absolute location pplace in low memory. *)
(*# save, data(near_ptr=>off) *)
TYPE TblPtr = POINTER TO DiskParameterTable;
VAR TablePointer: TblPtr;
PointerPointer: POINTER TO TblPtr;
(*# restore *)
BEGIN
PointerPointer := MakePointer (0, pplace);
TablePointer := PointerPointer^;
IF TablePointer = FarNIL THEN
RETURN;
END (*IF*);
INC (NumberOfPhysicalDrives);
WITH PhysicalDriveData[d] DO
SectorsPerTrack := CARDINAL(TablePointer^.sectorspertrack);
NumberOfHeads := CARDINAL(TablePointer^.maxheads);
PrecompensationCylinder := TablePointer^.precomp;
ExtraHeadOption := TablePointer^.optionbyte;
END (*WITH*);
END SetUpDrive;
(************************************************************************)
PROCEDURE DecodePartitionTable;
(* On entry to this procedure, we have already initialised all data *)
(* pertaining to the physical drives, but do not yet have a logical *)
(* drive structure. This procedure reads the partition table on *)
(* physical block 0 of each physical drive, sets up the logical *)
(* drive information, and installs us as a device driver known to *)
(* module Devices. *)
VAR bufptr: POINTER TO ARRAY [0..BlockSize-1] OF BYTE;
drive: PhysicalDriveNumber; unit: CARDINAL;
bootblockptr, sizeptr: POINTER TO LONGCARD;
subscript: [0..BlockSize-1];
Registers: RegisterPacket;
(*# save, data(near_ptr=>off) *)
StringAddress: POINTER TO ARRAY [0..63] OF CHAR;
(*# restore *)
BEGIN
FOR unit := 0 TO MAX(LogicalDriveNumber) DO
WITH LogicalUnit[unit] DO
physicaldrive := 0; bootblock := 0;
numberofsectors := 0;
END (*WITH*);
END (*FOR*);
unit := 0;
ALLOCATE (bufptr, BlockSize);
FOR drive := 0 TO NumberOfPhysicalDrives-1 DO
IF ReadPhysicalBlock (drive, 0, BlockSize, bufptr) = OK
THEN
FOR subscript := 01C0H TO 01F0H BY 10H DO
IF bufptr^[subscript] <> BYTE(0) THEN
bootblockptr := AddOffset (bufptr, subscript+6);
sizeptr := AddOffset (bufptr, subscript+10);
WITH LogicalUnit[unit] DO
physicaldrive := drive;
bootblock := bootblockptr^;
numberofsectors := sizeptr^;
END (*WITH*);
INC (unit);
END (*IF*);
END (*FOR*);
END (*IF*);
END (*FOR*);
DEALLOCATE (bufptr, BlockSize);
(* Having collected all necessary information from the *)
(* partition tables, set up the drive names. *)
NumberOfLogicalDrives := unit;
IF NumberOfLogicalDrives > 0 THEN
harddisk := InstallDeviceDriver (NumberOfLogicalDrives);
FOR unit := 0 TO NumberOfLogicalDrives-1 DO
(* Read the current directory for the this logical drive. *)
WITH Registers DO
DL := SHORTCARD(unit) + 3;
AH := 71;
BIOS (33, Registers);
StringAddress := MakePointer (DS, SI);
END (*WITH*);
(* Pass the information to module Devices. *)
DeviceName (harddisk, unit, CHR(ORD("C")+unit),
LogicalUnit[unit].numberofsectors, StringAddress^);
END (*FOR*);
END (*IF*);
END DecodePartitionTable;
(************************************************************************)
(* MAIN PROGRAM *)
(************************************************************************)
VAR Mpage: MaintenancePage;
BEGIN
IF testing THEN
CreateMaintenancePage (Mpage);
OpenWindow (log, black, green, 0,23, 0,60, simpleframe, doubledivider);
Associate (log, Mpage);
WriteString (log," Diagnostic output from hard disk driver");
ChangeScrollingRegion (log, 3, 22);
END (*IF*);
CreateSemaphore (DataRequest, 0);
NumberOfPhysicalDrives := 0;
SetUpDrive (0, 104H);
SetUpDrive (1, 118H);
IF NumberOfPhysicalDrives = 0 THEN
NumberOfLogicalDrives := 0;
ELSE
CreateInterruptTask (DiskInterrupt, DRQInterruptHandler,
"Hard disk int");
DecodePartitionTable;
END (*IF*);
IF NumberOfLogicalDrives > 0 THEN
CreateTask (DiskRequestHandler, 13, "Harddisk queue");
END (*IF*);
END HardDisk.