-- BootChannelDisk.mesa (last edited by: Taft on: June 18, 1981 2:04 PM)
DIRECTORY
Boot USING [Location, LP],
BootChannel USING [Create, Operation, Handle, transferCleanup, transferWait],
DeviceTypes USING [sa4000],
Environment USING [PageCount, PageNumber],
Inline USING [DIVMOD, LongDivMod, LongMult, LowHalf],
PilotDisk USING [Address, GetLabelFilePage, Label, SetLabelFilePage],
PilotMP USING [cGermDeviceError, cGermLabelCheck, Code],
SA4000Face USING [
Command, DeviceHandle, DiskAddress, GetDeviceAttributes, GetNextDevice,
globalStateSize, Initialize, Initiate, nullDeviceHandle, Operation, operationSize,
Poll, Recalibrate];
BootChannelDisk: PROGRAM
IMPORTS Boot, RemainingChannels: BootChannel, Inline, PilotDisk, SA4000Face
EXPORTS BootChannel
SHARES PilotDisk =
-- Implementation of BootChannel for Pilot volume on SA4000.
-- A single, serially reusable, channel is supported.
BEGIN OPEN Boot, Environment;
daNull: PilotDisk.Address = [0, 0];
label1, label2: PilotDisk.Label; -- HOW SHOULD THIS BE ALIGNED FOR FUTURE DISKS?
pAllocateNext: LONG POINTER TO UNSPECIFIED; -- allocator for first 64K storage
pLoc: POINTER TO Location;
tries: [0..triesMax];
triesMax: CARDINAL = 8;
movingHeads, sectorsPerTrack: CARDINAL;
currentDiskAddress: SA4000Face.DiskAddress;
currentFilePage: LONG CARDINAL;
Operation: TYPE = RECORD [
pOp: LONG POINTER TO SA4000Face.Operation,
state: {idle, busy}];
current, alternate: Operation;
Create: PUBLIC PROCEDURE [
pLocation: POINTER TO Location, operation: BootChannel.Operation,
dFirst64KStorage: LONG DESCRIPTOR FOR ARRAY OF WORD]
RETURNS [BootChannel.Handle] =
BEGIN
pAllocateNext ← BASE[dFirst64KStorage]; -- reset first 64K allocator
SELECT (pLoc ← pLocation).deviceType FROM
= DeviceTypes.sa4000 =>
BEGIN
da: SA4000Face.DiskAddress = LOOPHOLE[pLocation.diskFileID.da];
device: SA4000Face.DeviceHandle;
current.pOp ← Allocate[SA4000Face.operationSize];
current.state ← idle;
alternate.pOp ← Allocate[SA4000Face.operationSize];
alternate.state ← idle;
SA4000Face.Initialize[0, Inline.LowHalf[Allocate[SA4000Face.globalStateSize]]];
device ← SA4000Face.nullDeviceHandle;
THROUGH [0..pLocation.deviceOrdinal] DO
device ← SA4000Face.GetNextDevice[device] ENDLOOP;
[movingHeads: movingHeads, sectorsPerTrack: sectorsPerTrack] ←
SA4000Face.GetDeviceAttributes[device];
-- Initialize label
currentDiskAddress ← da;
current.pOp.labelPtr ← @label1;
-- dataPtr, incrementDataPtr irrelevant
current.pOp.command ← vr; -- read label (and ignore data)
-- pageCount set in Transfer
current.pOp.device ← device;
TransferSA4000[page: NULL, count: 1]; -- fetch type, attribute fields
TransferSA4000[page: NULL, count: BootChannel.transferWait];
label1.fileID ← pLoc.diskFileID.fID;
PilotDisk.SetLabelFilePage[@label1, pLoc.diskFileID.firstPage];
label1.bootChainLink ← daNull; --- so TransferSA4000 will use pOp.clientHeader
label2 ← label1;
currentFilePage ← pLoc.diskFileID.firstPage;
-- Initialize remaining operation fields
currentDiskAddress ← da; -- since transfer above incremented it
-- labelPtr set above
-- dataPtr set in Transfer
current.pOp.incrementDataPtr ← TRUE;
current.pOp.command ← SELECT operation FROM
read => vvr,
write => vvw,
ENDCASE --rawRead-- => vrr;
-- pageCount set in Transfer
-- device set above
alternate.pOp↑ ← current.pOp↑;
alternate.pOp.labelPtr ← @label2;
RETURN[TransferSA4000];
END;
ENDCASE => -- not anything I implement. Pass it on.
RETURN[RemainingChannels.Create[pLocation, operation, dFirst64KStorage]];
END;
TransferSA4000: PROCEDURE [page: PageNumber, count: PageCount] =
BEGIN
IF current.state=busy THEN Cleanup[];
SELECT count FROM
BootChannel.transferWait, BootChannel.transferCleanup =>
BEGIN
ExchangeCurAlt[];
IF current.state=busy THEN Cleanup[];
END;
ENDCASE =>
BEGIN
current.pOp.dataPtr ← LPFromPage[page];
current.pOp.pageCount ← count;
Initiate[];
END;
END;
Initiate: PROCEDURE =
BEGIN
current.pOp.clientHeader ← currentDiskAddress;
PilotDisk.SetLabelFilePage[current.pOp.labelPtr, currentFilePage];
current.pOp.labelPtr.bootChainLink ← daNull;
IncrementDiskAddressAndPage[current.pOp.pageCount];
SA4000Face.Initiate[current.pOp];
current.state ← busy;
ExchangeCurAlt[];
END;
Cleanup: PROCEDURE =
BEGIN
initialCount: CARDINAL ← LAST[CARDINAL];
DO -- until count pages transferred
DO -- until status~=inProgress
SELECT SA4000Face.Poll[current.pOp] FROM
inProgress => NULL;
goodCompletion => GOTO TransferComplete;
labelCheck =>
IF current.pOp.labelPtr.bootChainLink~=daNull THEN
BEGIN -- end of run on disk; chain to next run and resume transfer
current.pOp.clientHeader ← LOOPHOLE[current.pOp.labelPtr.bootChainLink];
GOTO RetryOperation;
END
ELSE Error[PilotMP.cGermLabelCheck];
ENDCASE -- other error -- =>
BEGIN
IF current.pOp.pageCount ~= initialCount THEN tries ← triesMax - 1
ELSE
SELECT tries ← tries - 1 FROM
0 => Error[PilotMP.cGermDeviceError];
triesMax/2 => SA4000Face.Recalibrate[current.pOp.device];
ENDCASE;
initialCount ← current.pOp.pageCount;
GOTO RetryOperation
END;
REPEAT RetryOperation => NULL;
ENDLOOP;
-- At this point we know the disk controller is dormant, since errors cause
-- all pending transfers to be abandoned. Therefore it is safe to fool with
-- the label and the operation.
currentDiskAddress ← current.pOp.clientHeader;
currentFilePage ← PilotDisk.GetLabelFilePage[current.pOp.labelPtr];
Initiate[];
-- Restart the alternate operation if there is one, since the head has forgotten it.
IF current.state=busy THEN Initiate[] ELSE ExchangeCurAlt[];
REPEAT TransferComplete =>
BEGIN
current.state ← idle;
-- If this operation happened to finish at the end of a run of pages, we must
-- manually follow the bootChainLink. If the next operation has already been
-- initiated, it will surely suffer a check error; wait for it to terminate,
-- and then restart it at the correct place.
IF current.pOp.labelPtr.bootChainLink # daNull AND
current.pOp.labelPtr.bootChainLink # LOOPHOLE[current.pOp.clientHeader] THEN
BEGIN
currentDiskAddress ← LOOPHOLE[current.pOp.labelPtr.bootChainLink];
currentFilePage ← PilotDisk.GetLabelFilePage[current.pOp.labelPtr];
IF alternate.state=busy THEN
BEGIN
ExchangeCurAlt[];
WHILE SA4000Face.Poll[current.pOp] = inProgress DO ENDLOOP;
Initiate[];
END;
END;
END;
ENDLOOP;
END;
IncrementDiskAddressAndPage: PROCEDURE [count: CARDINAL] = INLINE
BEGIN
virtualDA: LONG CARDINAL = count + currentDiskAddress.sector +
Inline.LongMult[sectorsPerTrack,
currentDiskAddress.head + movingHeads*currentDiskAddress.cylinder];
c: CARDINAL;
[c, currentDiskAddress.sector] ← Inline.LongDivMod[virtualDA, sectorsPerTrack];
[currentDiskAddress.cylinder, currentDiskAddress.head] ← Inline.DIVMOD[c, movingHeads];
currentFilePage ← currentFilePage+count;
END;
ExchangeCurAlt: PROCEDURE = INLINE
BEGIN
t: Operation ← current; current ← alternate; alternate ← t;
END;
-- Allocator for 16-word aligned storage in first64K
Allocate: PROCEDURE [size: CARDINAL] RETURNS [lp: LONG POINTER TO UNSPECIFIED] =
BEGIN pAllocateNext ← (lp ← pAllocateNext) + LONG[((size + 15)/16)*16] END;
Error: SIGNAL [PilotMP.Code] = CODE;
LPFromPage: PROCEDURE [page: PageNumber] RETURNS [LONG POINTER] = INLINE
BEGIN RETURN[LP[highbits: page/256, lowbits: page*256 --MOD 2**16--]] END;
END.
(For earlier log entries see Pilot 4.0 archive version.)
June 23, 1980 2:31 PM McJones OISDisk,FilePageLabel=>PilotDisk
August 13, 1980 6:02 PM McJones Read first label to determine attributes
August 23, 1980 1:21 PM McJones Don't report labelCheck at end of run
January 4, 1981 3:03 PM Taft Queue up to 2 operations at a time
June 18, 1981 2:04 PM Taft Bug in pathological case of boot file chaining