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