-- SA800Impl.mesa (last edited by: Forrest on: March 20, 1981 10:57 AM)
-- THINGS TO DO:
-- 1) Allocate ScheduleObjects dynamically
-- 2) Handle multiple drives
-- Pilot Floppy Disk driver. This handles Pilot volumes on the floppy.
-- Non-Pilot-volume access is accomplished via the interface FloppyChannel.
-- The two forms of access are mutually interlocked.
DIRECTORY
DeviceTypes USING [sa800],
DiskChannel USING [
Address, CompletionStatus, DiskPageNumber, Drive, GetNextDrive,
IORequestHandle, Label, nullDrive, PLabel, PVHandle],
DiskChannelBackend USING [
ChangeStateProc, DriveHandle, GetDrive, GetPAProc, GetPNProc,
NotifyIOComplete, RequestIOProc],
Environment USING [wordsPerPage],
FloppyChannel USING [Context, Handle, SetContext, Status],
FloppyChannelInternal USING [DoRequest, maxDrives, OpBlock],
Inline USING [DIVMOD, LongDivMod, LowHalf],
PhysicalVolume USING [],
PilotDisk USING [Label, MatchLabels, NextLabel],
PilotFloppyFormat USING [
firstRealCylinder, FloppyPageCount, FloppyPageNumber, logicalTrack0,track0Context,
wordsPerSector],
PilotSwitches USING [switches],
Process USING [Detach, InitializeCondition, SecondsToTicks, SetPriority],
ProcessPriorities USING [diskInterruptPriority],
RuntimeInternal USING [WorryCallDebugger],
SA800Face USING [DiskAddress, Function, GetDeviceAttributes],
SimpleSpace USING [Create, LongPointer, Map, Unmap],
Space USING [defaultWindow, Handle, PageCount, PageNumber],
SwapperDriverStartChain USING [Start],
Utilities USING [LongPointerFromPage, PageFromLongPointer];
SA800Impl: MONITOR
IMPORTS
DiskChannel, DiskChannelBackend, FloppyChannel, FloppyChannelInternal, Inline,
PilotDisk, PilotSwitches, Process, RuntimeInternal, SA800Face, SimpleSpace,
RemainingDrivers: SwapperDriverStartChain, Utilities
EXPORTS PhysicalVolume, SwapperDriverStartChain
SHARES DiskChannel =
BEGIN
DriverStorage: TYPE = RECORD[contextSet: BOOLEAN];
driverStorage: ARRAY[0..FloppyChannelInternal.maxDrives) OF DriverStorage ←
ALL[[FALSE]];
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- LOOPHOLES
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--PhysicalVolume.-- Handle: PUBLIC TYPE = DiskChannel.PVHandle;
MakeHandle: PROC [dH: DiskChannelBackend.DriveHandle] RETURNS [Handle] =
INLINE {RETURN[[LOOPHOLE[dH], dH.changeCount]]};
GetDriveHandle: PROCEDURE [d: DiskChannel.Drive]
RETURNS [DiskChannelBackend.DriveHandle] =
INLINE {RETURN[LOOPHOLE[d]]};
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- Monitor EXTERNAL PROCEDURES (label simulation in own section)
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DecodeStatus: PROCEDURE [s: FloppyChannel.Status]
RETURNS [DiskChannel.CompletionStatus] =
BEGIN
RETURN[SELECT TRUE FROM
s.notReady => notReady,
s.recordNotFound, s.wrongSizeBuffer => noSuchPage,
s.crcError => dataError,
s.recalibrateError => seekFailed,
s.goodCompletion => goodCompletion
ENDCASE => hardwareError];
END;
ErrorHalt: PROCEDURE RETURNS [f: SA800Face.Function] =
{RuntimeInternal.WorryCallDebugger["Error in SA800Impl"]};
GetLabelAddress: PROCEDURE [
dH: DiskChannelBackend.DriveHandle, page: PilotFloppyFormat.FloppyPageNumber]
RETURNS [addr: SA800Face.DiskAddress] =
INLINE BEGIN
temp: CARDINAL;
[quotient: temp, remainder: addr.sector]
← Inline.LongDivMod[num: page, den: dH.sectorsPerTrack];
addr.sector ← addr.sector + 1;
[quotient: addr.cylinder, remainder: addr.head]
← Inline.DIVMOD[num: temp, den: dH.movingHeads];
addr.cylinder ← addr.cylinder + PilotFloppyFormat.firstRealCylinder;
RETURN[LOOPHOLE[addr]]
END;
GetPageAddress: DiskChannelBackend.GetPAProc =
-- [dH: DiskChannelBackend.DriveHandle, page: DiskChannel.DiskPageNumber]
-- RETURNS [DiskChannel.Address] =
BEGIN
addr: SA800Face.DiskAddress;
temp: CARDINAL;
[quotient: temp, remainder: addr.sector]
← Inline.LongDivMod[num: page, den: dH.sectorsPerTrack];
addr.sector ← addr.sector + 1;
[quotient: addr.cylinder, remainder: addr.head]
← Inline.DIVMOD[num: temp, den: dH.movingHeads];
addr.cylinder ← addr.cylinder
+ PilotFloppyFormat.firstRealCylinder + PilotFloppyFormat.logicalTrack0;
RETURN[LOOPHOLE[addr]]
END;
GetPageNumber: DiskChannelBackend.GetPNProc =
-- [dH: DiskChannelBackend.DriveHandle, page: DiskChannel.Address]
-- RETURNS [DiskChannel.DiskPageNumber] =
BEGIN OPEN addr: LOOPHOLE[page, SA800Face.DiskAddress];
offset: CARDINAL =
PilotFloppyFormat.firstRealCylinder + PilotFloppyFormat.logicalTrack0;
RETURN[
(addr.sector - 1)
+ dH.sectorsPerTrack*(addr.head + dH.movingHeads*(addr.cylinder - offset))]
END;
-- Pilot-device device-dependent setup/cleanup when drive changes type of access
-- from dH.state to state. possible state changes: inactive -> pilot OR
-- channel (or inactive) and vice versa
RequestQueueNotEmpty: ERROR = CODE;
PilotChangeState: DiskChannelBackend.ChangeStateProc =
BEGIN
IF dH.state = pilot THEN
BEGIN
IF ~QueueEmpty[dH] THEN ERROR RequestQueueNotEmpty; -- Pilot bug!
CountLabelClients[deleting];
END;
SELECT --new--state FROM
pilot =>
BEGIN
context: FloppyChannel.Context ←
[protect: FALSE, format: IBM, density: double, sectorLength: 256];
status: FloppyChannel.Status;
b: PACKED ARRAY [0..128) OF [0..400B);
handle: Handle = MakeHandle[dH];
count: CARDINAL;
op: FloppyChannelInternal.OpBlock;
E: ARRAY CHARACTER [’ ..’Z] OF [0..400B) = [
-- !"#$%&’-- 64, 126, 90, 123, 91, 108, 124, 125,
--()*+,-./-- 77, 93, 92, 78, 107, 96, 75, 97,
--01234567-- 240, 241, 242, 243, 244, 245, 246, 247,
--89:;<=>?-- 248, 249, 122, 94, 76, 126, 110, 111,
--@ABCDEFG-- 124, 193, 194, 195, 196, 196, 198, 199,
--HIJKLMNO-- 200, 201, 209, 210, 211, 212, 213, 214,
--PQRSTUVW-- 215, 216, 217, 226, 227, 228, 229, 230,
--XYZ -- 231, 232, 233];
SetDefaultContext: PROCEDURE =
BEGIN
-- First try to treat the diskette as double density. If this fails, try and
-- treat it as single density. If this fails, punt!
context.density ← double; dH.sectorsPerTrack ← 15;
op ← [
device: handle, function: nop,
address: [cylinder: 0, head: 0, sector: 7],
buffer: [@b, 64], incrementDataPtr: FALSE, count: 1];
[status, count] ← FloppyChannelInternal.DoRequest[@op];
dH.movingHeads ← IF status.twoSided THEN 2 ELSE 1;
LOOPHOLE[dH.driverStorage, LONG POINTER TO DriverStorage].contextSet ←
TRUE; -- incase we succeed at the next statement
IF FloppyChannel.SetContext[handle, context] THEN RETURN;
context.density ← single; dH.sectorsPerTrack ← 8;
LOOPHOLE[dH.driverStorage, LONG POINTER TO DriverStorage].contextSet ←
FloppyChannel.SetContext[handle, context];
END;
CountLabelClients[adding];
IF ~FloppyChannel.SetContext[handle, PilotFloppyFormat.track0Context] THEN
SetDefaultContext[] -- non-standard track 0 so use defaults
ELSE
BEGIN
op ← [
device: handle, function: readSector,
address: [cylinder: 0, head: 0, sector: 7],
buffer: [@b, 64], incrementDataPtr: FALSE, count: 1];
[status, count] ← FloppyChannelInternal.DoRequest[@op];
IF count ~= 1 THEN SetDefaultContext[] -- couldn’t read sector 7
ELSE
BEGIN
SELECT b[71] FROM
E[’M] =>
BEGIN
context.density ← double; dH.sectorsPerTrack ← 15;
dH.movingHeads ← IF status.twoSided THEN 2 ELSE 1;
END;
E[’2] =>
BEGIN
context.density ← single; dH.sectorsPerTrack ← 8;
dH.movingHeads ← 2;
END;
E[’ ] =>
BEGIN
context.density ← single; dH.sectorsPerTrack ← 8;
dH.movingHeads ← 1;
END;
ENDCASE => SetDefaultContext[]; -- garbage found!
LOOPHOLE[dH.driverStorage, LONG POINTER TO DriverStorage].contextSet ←
FloppyChannel.SetContext[handle, context];
END;
END;
END;
channel => [numberOfHeads: dH.movingHeads, numberOfCylinders: dH.cylinders]
← SA800Face.GetDeviceAttributes[dH.driveID.handle].attributes;
inactive => NULL;
ENDCASE => ERROR;
END;
-- Root of IO process
RequestProcess: PROCEDURE =
BEGIN
Process.SetPriority[ProcessPriorities.diskInterruptPriority];
DO
status: FloppyChannel.Status;
countDone: CARDINAL;
req: DiskChannel.IORequestHandle = GetNextRequest[];
drive: DiskChannelBackend.DriveHandle = DiskChannelBackend.GetDrive[req.channel];
labCountDone: CARDINAL;
labelIoError: BOOLEAN; -- TRUE if an I/O error while accessing the labels
labelStatus: DiskChannel.CompletionStatus;
op: FloppyChannelInternal.OpBlock;
IF ~LOOPHOLE[drive.driverStorage, LONG POINTER TO DriverStorage].contextSet
THEN
BEGIN
-- Couldn’t set context correctly at initialization time so complain
req.status ← notPilotVolume; -- TEMPORARY. In long term, complain when onlined
DiskChannelBackend.NotifyIOComplete[req];
LOOP;
END;
[labCountDone, labelIoError] ← CheckLabels[req, drive];
labelStatus ← req.status;
op ←
[device: MakeHandle[drive],
function: SELECT req.command FROM
vvr, vrr => readSector,
vvw, vww => writeSector,
ENDCASE => ErrorHalt[],
address: LOOPHOLE[req.address],
buffer:
[Utilities.LongPointerFromPage[req.memoryPage],
PilotFloppyFormat.wordsPerSector*labCountDone],
incrementDataPtr: ~req.dontIncrement,
count: labCountDone];
[status, countDone] ← FloppyChannelInternal.DoRequest[@op];
req.status ←
IF ((labCountDone < req.count) AND (countDone = labCountDone))
THEN (IF labelIoError THEN labelStatus ELSE labelDoesNotMatch)
ELSE DecodeStatus[status];
req.countDone ← countDone;
DiskChannelBackend.NotifyIOComplete[req]
ENDLOOP;
END;
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- Label simulation
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- Changes need to be made when DiskChannel is revised to allow an error return
-- when trying to initialize the label image.
-- This code has some assumptions build in about floppyPageSize=Environemnt.pageSize
-- Label simulation machinery
-- This need not execute within the monitor as it is protected by the serialization
-- imposed by Pilot’s page fault machinery and the sequentiality of RequestProcess.
CheckLabels: PROCEDURE [
req: DiskChannel.IORequestHandle, drive: DiskChannelBackend.DriveHandle]
RETURNS [valid: PilotFloppyFormat.FloppyPageCount, ioError: BOOLEAN] =
BEGIN
firstPage: PilotFloppyFormat.FloppyPageNumber = Inline.LowHalf[req.diskPage];
labelP: DiskChannel.PLabel;
runCount: PilotFloppyFormat.FloppyPageCount = Inline.LowHalf[req.count];
SetLabelClient[DiskChannelBackend.GetDrive[req.channel]];
req.status ← goodCompletion;
FOR page: PilotFloppyFormat.FloppyPageNumber IN [firstPage..firstPage+runCount) DO
SELECT req.command FROM
vrr =>
BEGIN
[labelP, req.status] ← ReadLabel[page];
IF req.status = dataError THEN req.status ← labelError; -- simulate label crc error
IF req.status = goodCompletion THEN req.label↑ ← labelP↑
ELSE RETURN[page-firstPage, TRUE];
END;
vww =>
BEGIN
lastValid: PilotFloppyFormat.FloppyPageNumber;
[req.status, lastValid] ← WriteLabel[page, req.label];
IF req.status = dataError THEN req.status ← labelError; -- simulate label crc error
IF req.status = goodCompletion THEN PilotDisk.NextLabel[req.label]
ELSE -- a read/write to the floppy failed. Report the count of correct labels.
IF lastValid = LAST[PilotFloppyFormat.FloppyPageNumber] -- "page" failed
THEN RETURN[page-firstPage, TRUE]
ELSE RETURN[lastValid-firstPage, TRUE];
END;
vvr, vvw =>
BEGIN
[labelP, req.status] ← ReadLabel[page];
IF req.status = dataError THEN req.status ← labelError; -- simulate label crc error
IF req.status ~= goodCompletion THEN RETURN[page-firstPage, TRUE];
IF ~PilotDisk.MatchLabels[req.label, labelP]
THEN {req.status ← labelDoesNotMatch; RETURN[page-firstPage, FALSE]};
PilotDisk.NextLabel[req.label];
END;
ENDCASE => ERROR;
ENDLOOP;
IF req.command = vww AND runCount > 0 THEN
BEGIN
lastValid: PilotFloppyFormat.FloppyPageNumber;
[req.status, lastValid] ← UpdateLabelImage[];
IF req.status = dataError THEN req.status ← labelError; -- simulate label crc error
IF req.status = goodCompletion THEN RETURN[runCount, FALSE]
ELSE RETURN[LOOPHOLE[lastValid-firstPage+1], TRUE];
END
ELSE RETURN[runCount, FALSE];
END;
-- Label I/O machinery. Does sector level I/O and attempts to keep a
-- "reasonable" collection of labels in memory at any one time.
-- At the moment, "reasonable" is defined as a run of labelSpaceSize sectors
-- from the diskette containing the last referenced label. This simulation assumes
-- that every sequence of writes is followed by an UpdateLabelImage[] before
-- any reading is done on labels or another SetLabelClient[] is done. If these
-- restrictions are not imposed, ReadLabel must call UpdateLabelImage before
-- reading new labels from the diskette and it is hard for it to reasonably handle
-- errors while writing out someone else’s modified labels.
PLabels: TYPE = LONG POINTER TO ARRAY [0..0) OF PilotDisk.Label;
labelSpace: Space.Handle;
labelSpaceSize: Space.PageCount;
labelSpacePtr: LONG POINTER;
labels: PLabels;
currentLabelClient: DiskChannelBackend.DriveHandle;
firstLabel: PilotFloppyFormat.FloppyPageNumber;
countLabels: PilotFloppyFormat.FloppyPageCount;
firstModifiedLabel: PilotFloppyFormat.FloppyPageNumber;
lastModifiedLabel: PilotFloppyFormat.FloppyPageNumber;
labelClients: CARDINAL;
labelsModified: BOOLEAN;
CountLabelClients: PROCEDURE [type: {adding, deleting}] =
BEGIN
SELECT type FROM
adding =>
IF (labelClients ← labelClients+1) = 1 THEN
BEGIN -- grab some RESIDENT memory for the labels
SimpleSpace.Map[labelSpace, Space.defaultWindow, TRUE];
currentLabelClient ← NIL;
END;
deleting =>
IF (labelClients ← labelClients-1) = 0 THEN
SimpleSpace.Unmap[labelSpace];
ENDCASE => ERROR;
END;
SetLabelClient: PROCEDURE [dH: DiskChannelBackend.DriveHandle] =
BEGIN
IF dH = currentLabelClient THEN RETURN;
currentLabelClient ← dH;
firstLabel ← 0; -- make it look as if there are no labels in memory
countLabels ← 0;
labelsModified ← FALSE;
END;
UpdateLabelImage: PROCEDURE
RETURNS [
status: DiskChannel.CompletionStatus,
lastValid: PilotFloppyFormat.FloppyPageNumber] =
BEGIN
firstPage: Space.PageNumber =
Utilities.PageFromLongPointer[@labels[firstModifiedLabel-firstLabel]];
lastPage: Space.PageNumber = -- take into account the size of a label!
Utilities.PageFromLongPointer[(@labels[lastModifiedLabel-firstLabel+1])-1];
pageCount: CARDINAL = lastPage-firstPage+1;
op: FloppyChannelInternal.OpBlock;
floppyStatus: FloppyChannel.Status;
countDone: PilotFloppyFormat.FloppyPageCount;
labelAddress: PilotFloppyFormat.FloppyPageNumber;
IF ~labelsModified THEN
RETURN[goodCompletion, LAST[PilotFloppyFormat.FloppyPageNumber]];
labelAddress ← GetLabelLocation[firstModifiedLabel].pageWithLabel;
op ←
[device: MakeHandle[currentLabelClient], function: writeSector,
address: GetLabelAddress[currentLabelClient, labelAddress],
buffer: [
Utilities.LongPointerFromPage[firstPage],
Environment.wordsPerPage*pageCount],
incrementDataPtr: TRUE, count: pageCount];
[floppyStatus, countDone] ← FloppyChannelInternal.DoRequest[@op];
IF ~floppyStatus.goodCompletion THEN
RETURN[DecodeStatus[floppyStatus], LastLabel[labelAddress+countDone-1]];
labelsModified ← FALSE;
firstModifiedLabel ← LAST[PilotFloppyFormat.FloppyPageNumber];
lastModifiedLabel ← FIRST[PilotFloppyFormat.FloppyPageNumber];
RETURN[goodCompletion, LAST[PilotFloppyFormat.FloppyPageNumber]];
END;
ReadLabel: PROCEDURE [page: PilotFloppyFormat.FloppyPageNumber]
RETURNS[labelP: DiskChannel.PLabel, status: DiskChannel.CompletionStatus] =
BEGIN
op: FloppyChannelInternal.OpBlock;
floppyStatus: FloppyChannel.Status;
countDone: PilotFloppyFormat.FloppyPageCount;
labelLocation: PilotFloppyFormat.FloppyPageNumber;
offset: CARDINAL;
countNeeded: PilotFloppyFormat.FloppyPageCount;
IF page IN [firstLabel..firstLabel+countLabels) THEN
RETURN[@labels[page-firstLabel], goodCompletion];
[labelLocation, firstLabel, offset, countNeeded] ← GetLabelLocation[page];
op ← [
device: MakeHandle[currentLabelClient], function: readSector,
address: GetLabelAddress[currentLabelClient, labelLocation],
buffer: [labelSpacePtr, Environment.wordsPerPage*labelSpaceSize],
incrementDataPtr: TRUE,
count: MIN[labelSpaceSize, LastLabelPage[currentLabelClient]-labelLocation+1]];
[floppyStatus, countDone] ← FloppyChannelInternal.DoRequest[@op];
labels ← SimpleSpace.LongPointer[labelSpace]+offset;
IF ~floppyStatus.goodCompletion AND countDone < countNeeded THEN
RETURN [NIL, DecodeStatus[floppyStatus]];
-- We are happy so long as we got the page with the label we need
countLabels ← (countDone*Environment.wordsPerPage-offset)/SIZE[PilotDisk.Label];
firstModifiedLabel ← LAST[PilotFloppyFormat.FloppyPageNumber];
lastModifiedLabel ← FIRST[PilotFloppyFormat.FloppyPageNumber];
labelsModified ← FALSE;
RETURN[@labels[page-firstLabel], goodCompletion];
END;
WriteLabel: PROCEDURE [
page: PilotFloppyFormat.FloppyPageNumber, label: DiskChannel.PLabel]
RETURNS [
status: DiskChannel.CompletionStatus,
lastValid: PilotFloppyFormat.FloppyPageNumber] =
-- Because this procedure may have to unilaterally write previously written
-- labels to the Floppy, it must be able to declare that a previous WriteLabel
-- failed. It does so by returning status ~= goodCompletion and
-- lastValid as the last correctly written label (~= LAST[FloppyPageNumber])
BEGIN
op: FloppyChannelInternal.OpBlock;
floppyStatus: FloppyChannel.Status;
countDone: PilotFloppyFormat.FloppyPageCount;
labelLocation: PilotFloppyFormat.FloppyPageNumber;
offset: CARDINAL;
countNeeded: PilotFloppyFormat.FloppyPageCount;
IF page IN [firstLabel..firstLabel+countLabels) THEN
BEGIN
labels[page-firstLabel] ← label↑;
labelsModified ← TRUE;
firstModifiedLabel ← MIN[firstModifiedLabel, page];
lastModifiedLabel ← MAX[lastModifiedLabel, page];
RETURN[goodCompletion, LAST[PilotFloppyFormat.FloppyPageNumber]];
END;
-- Read in a group of labels containing the one of interest after saving
-- any modified labes on the floppy
[status, lastValid] ← UpdateLabelImage[];
IF status ~= goodCompletion THEN RETURN;
[labelLocation, firstLabel, offset, countNeeded] ← GetLabelLocation[page];
op ← [
device: MakeHandle[currentLabelClient], function: readSector,
address: GetLabelAddress[currentLabelClient, labelLocation],
buffer: [labelSpacePtr, Environment.wordsPerPage*labelSpaceSize],
incrementDataPtr: TRUE,
count: MIN[labelSpaceSize, LastLabelPage[currentLabelClient]-labelLocation+1]];
[floppyStatus, countDone] ← FloppyChannelInternal.DoRequest[@op];
labels ← SimpleSpace.LongPointer[labelSpace]+offset;
IF ~floppyStatus.goodCompletion AND countDone < countNeeded THEN
RETURN[
DecodeStatus[floppyStatus], LAST[PilotFloppyFormat.FloppyPageNumber]];
-- We are happy so long as we got the page with the label we need
countLabels ← (countDone*Environment.wordsPerPage-offset)/SIZE[PilotDisk.Label];
labels[page-firstLabel] ← label↑;
labelsModified ← TRUE;
firstModifiedLabel ← lastModifiedLabel ← page;
RETURN[goodCompletion, LAST[PilotFloppyFormat.FloppyPageNumber]];
END;
GetLabelLocation: PROCEDURE [dataPage: PilotFloppyFormat.FloppyPageNumber]
RETURNS [
pageWithLabel: PilotFloppyFormat.FloppyPageNumber, -- sector with label
firstLabel: PilotFloppyFormat.FloppyPageNumber, -- first label in that sector
offset: CARDINAL, -- offset of first label in that sector
countNeeded: PilotFloppyFormat.FloppyPageCount] = -- for full label for dataPage
BEGIN
-- It is important that the following be LONG so that LONG arithmetic is performed
-- when doing word address calculations as they produce results which are:
-- <= PilotFloppyFormat.wordsPerSector*LAST[PilotFloppyFormat.PageNumber]
sectorSize: LONG CARDINAL = LONG[PilotFloppyFormat.wordsPerSector];
labelSize: LONG CARDINAL = LONG[SIZE[PilotDisk.Label]];
pageWithLabel ← Inline.LowHalf[(dataPage*labelSize)/sectorSize];
countNeeded ← -- check to see if the last word of the label is on the next sector
IF dataPage*labelSize+labelSize-1 < (1+pageWithLabel)*sectorSize
THEN 1 ELSE 2;
firstLabel ← Inline.LowHalf[(pageWithLabel*sectorSize+labelSize-1)/labelSize];
offset ← Inline.LowHalf[firstLabel*labelSize-pageWithLabel*sectorSize];
END;
LastLabel: PROCEDURE [diskPage: PilotFloppyFormat.FloppyPageNumber]
RETURNS [lastLabel: PilotFloppyFormat.FloppyPageNumber] =
BEGIN -- Get identity of the last valid label on disk page diskPage
-- It is important that the following be LONG so that LONG arithmetic is performed
-- when doing word address calculations as they produce results which are:
-- <= PilotFloppyFormat.wordsPerSector*LAST[PilotFloppyFormat.PageNumber]
sectorSize: LONG CARDINAL = LONG[PilotFloppyFormat.wordsPerSector];
labelSize: LONG CARDINAL = LONG[SIZE[PilotDisk.Label]];
lastLabel ← Inline.LowHalf[((1+diskPage)*sectorSize+labelSize-1)/labelSize-1];
IF GetLabelLocation[lastLabel].countNeeded >1 THEN lastLabel ← lastLabel-1;
END;
LastLabelPage: PROCEDURE[dH: DiskChannelBackend.DriveHandle]
RETURNS [page: PilotFloppyFormat.FloppyPageNumber] =
BEGIN -- Get the FloppyPageNumber of the last page devoted to label simulation
-- logicalTrack0 is also the count of tracks devoted to the label image
page ← PilotFloppyFormat.logicalTrack0*dH.sectorsPerTrack*dH.movingHeads;
END;
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- ENTRY PROCEDURES
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- Request buffering machinery. All of the following procedures MUST be resident as
-- RequestIO is called from within the page fault handling path and it executes within
-- the monitor.
first, last: DiskChannel.IORequestHandle ← NIL;
requestSubmitted: CONDITION;
RequestIO: ENTRY DiskChannelBackend.RequestIOProc =
BEGIN
-- Link request into IORequest list using unused link field
req.address ← GetPageAddress[
DiskChannelBackend.GetDrive[req.channel], req.diskPage];
req.retryCount ← 0; req.next ← NIL;
IF first # NIL THEN last.next ← req ELSE first ← req;
last ← req;
NOTIFY requestSubmitted;
END;
GetNextRequest: ENTRY PROCEDURE RETURNS [req: DiskChannel.IORequestHandle] =
BEGIN
WHILE first = NIL DO WAIT requestSubmitted ENDLOOP;
req ← first; first ← first.next;
END;
QueueEmpty: ENTRY PROCEDURE [dH: DiskChannelBackend.DriveHandle]
RETURNS [empty: BOOLEAN] =
BEGIN
req: DiskChannel.IORequestHandle;
FOR req ← first, req.next WHILE req ~= NIL DO
IF DiskChannelBackend.GetDrive[req.channel] = dH THEN RETURN[FALSE]
ENDLOOP;
RETURN[TRUE];
END;
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- INITIALIZATION
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
RegisterDriverProcs: PROCEDURE RETURNS [drivesFound: BOOLEAN] =
BEGIN
device: DiskChannel.Drive ← DiskChannel.nullDrive;
nDrives: CARDINAL ← 0;
drivesFound ← FALSE;
WHILE nDrives < FloppyChannelInternal.maxDrives DO
drive: DiskChannelBackend.DriveHandle;
IF (device ← DiskChannel.GetNextDrive[device])=DiskChannel.nullDrive THEN EXIT;
drive ← GetDriveHandle[device];
IF drive.driveID.type = DeviceTypes.sa800 THEN
BEGIN
drivesFound ← TRUE;
drive.requestIO ← RequestIO;
drive.getPageAddress ← GetPageAddress;
drive.getPageNumber ← GetPageNumber;
drive.changeState ← PilotChangeState;
drive.driverStorage ← @driverStorage[nDrives];
nDrives ← nDrives + 1;
END;
ENDLOOP;
END;
Start: PUBLIC PROCEDURE = {RemainingDrivers.Start[]};
IF RegisterDriverProcs[].drivesFound THEN
BEGIN
Process.InitializeCondition[@requestSubmitted, Process.SecondsToTicks[60]];
Process.Detach[FORK RequestProcess];
labelSpaceSize ←
IF PilotSwitches.switches.u = down -- If we are in UtilityPilot
THEN 2 -- minimize space usage at the expense of performance
ELSE 5; -- empirically determined to give reasonable performance
labelSpace ← SimpleSpace.Create[labelSpaceSize, hyperspace];
labelSpacePtr ← SimpleSpace.LongPointer[labelSpace];
END;
END.
LOG
Time: June 20, 1980 6:35 PMBy: Jose
Action: Create file
Time: September 15, 1980 2:55 PMBy: Jose
Action: Separate startup- and new volume-initialization; pin code only if drive present
Time: October 10, 1980 3:23 PMBy: Jose
Action: Changes for runs of pages in head: divide label image into subspaces, pin when updating.
Time: October 21, 1980 2:15 PMBy: Jose
Action: Remove wrongSizeBuffer from impossible errors.
Time: December 17, 1980 3:41 PMBy: Luniewski
Action: Don’t pin code dynamically - let the packager do what is needed. Only create subspaces of the label image in regular Pilot.
Time: January 26, 1981 2:15 PMBy: Luniewski
Action: Merge in changes from Mokelumne maintenance release. Modify to use a small SimpleSpace as a buffer for the label simulation instead of mapping a large Space to hold the labels. Place on SwapperDriverStartChain.
Time: February 3, 1981 9:14 AMBy: Fay/Luniewski
Action: Differentiate between dataError and labelError (previously merged into checksumError). Handle the density/sidedness info stored on track zero.
Time: February 26, 1981 6:21 PMBy: Yokota
Action: Remove LOOPHOLE for dataError and labelError. The inactive case in PilotChangeState is changed to NULL.
Time: March 20, 1981 11:00 AMBy: Forrest
Change [1..400B) to [0..400B) in the probe floppy flavor procedures..