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