DIRECTORY Basics USING [DivMod, LongDiv, LongDivMod, LongMult], BootFile USING [Location, nullLink], BootChannel USING [Create, Operation, Handle, transferCleanup, transferWait], DiskFace USING[ Label ], GermPrivate USING [ Error ], MPCodes USING [germDeviceError, germLabelCheck], PrincOps USING [PageCount, PageNumber], PrincOpsUtils USING [AddressForPageNumber, LowHalf, VERSION], ProcessorFace USING [GetClockPulses, microsecondsPerHundredPulses, SetMP], SA4000Face USING [Command, DeviceHandle, DiskAddress, GetDeviceAttributes, GetNextDevice, globalStateSize, Initialize, Initiate, nullDeviceHandle, Operation, operationSize, Poll, Recalibrate]; BootChannelDisk: PROGRAM IMPORTS Basics, RemainingChannels: BootChannel, GermPrivate, PrincOpsUtils, ProcessorFace, SA4000Face EXPORTS BootChannel = { label1, label2: DiskFace.Label; -- HOW SHOULD THIS BE ALIGNED FOR FUTURE DISKS? pAllocateNext: LONG POINTER; -- allocator for first 64K storage pLoc: POINTER TO BootFile.Location; tries: [0..triesMax]; triesMax: CARDINAL = 8; movingHeads, sectorsPerTrack: CARDINAL; doubleBuffer: BOOLEAN; currentDiskAddress: SA4000Face.DiskAddress; currentFilePage: LONG CARDINAL; Operation: TYPE = RECORD [ pOp: LONG POINTER TO SA4000Face.Operation, state: {idle, busy}]; current, alternate: Operation; Create: PUBLIC PROC [ pLocation: POINTER TO BootFile.Location, operation: BootChannel.Operation, dFirst64KStorage: LONG DESCRIPTOR FOR ARRAY OF WORD] RETURNS [BootChannel.Handle] = { pAllocateNext _ BASE[dFirst64KStorage]; -- reset first 64K allocator SELECT (pLoc _ pLocation).deviceType FROM = sa1000, sa4000 => { da: SA4000Face.DiskAddress = LOOPHOLE[pLocation.diskFileID.firstLink]; device: SA4000Face.DeviceHandle; doubleBuffer _ PrincOpsUtils.VERSION[].machineType # dandelion; current.pOp _ Allocate[SA4000Face.operationSize]; current.state _ idle; alternate.pOp _ Allocate[SA4000Face.operationSize]; alternate.state _ idle; SA4000Face.Initialize[0, LOOPHOLE[PrincOpsUtils.LowHalf[Allocate[SA4000Face.globalStateSize]]]]; device _ SA4000Face.nullDeviceHandle; THROUGH [0..pLocation.deviceOrdinal] DO device _ SA4000Face.GetNextDevice[device] ENDLOOP; [movingHeads: movingHeads, sectorsPerTrack: sectorsPerTrack] _ SA4000Face.GetDeviceAttributes[device]; currentDiskAddress _ da; current.pOp.labelPtr _ @label1; current.pOp.command _ vr; -- read label (and ignore data) current.pOp.device _ device; TransferSA4000[page: NULL, count: 1]; -- fetch type, attribute fields TransferSA4000[page: NULL, count: BootChannel.transferWait]; label1.fileID _ pLoc.diskFileID.fID; label1.filePage _ pLoc.diskFileID.firstPage; label1.dontCare _ BootFile.nullLink; --- so TransferSA4000 will use pOp.clientHeader label2 _ label1; currentFilePage _ pLoc.diskFileID.firstPage; currentDiskAddress _ da; -- since transfer above incremented it current.pOp.incrementDataPtr _ TRUE; current.pOp.command _ SELECT operation FROM read => vvr, write => vvw, ENDCASE --rawRead-- => vrr; alternate.pOp^ _ current.pOp^; alternate.pOp.labelPtr _ @label2; RETURN[TransferSA4000]; }; ENDCASE => -- not anything I implement. Pass it on. RETURN[RemainingChannels.Create[pLocation, operation, dFirst64KStorage]]; }; TransferSA4000: PROC [page: PrincOps.PageNumber, count: PrincOps.PageCount] = { IF current.state=busy THEN Cleanup[]; SELECT count FROM BootChannel.transferWait, BootChannel.transferCleanup => { Pause[100]; IF doubleBuffer THEN { ExchangeCurAlt[]; IF current.state=busy THEN {Cleanup[count = BootChannel.transferCleanup]}; }; }; ENDCASE => { current.pOp.dataPtr _ PrincOpsUtils.AddressForPageNumber[page]; current.pOp.pageCount _ count; Initiate[]; IF current.state=busy THEN Cleanup[];--wait for previous transfer to complete IF ~doubleBuffer THEN {ExchangeCurAlt[]; Cleanup[]}; }; }; Initiate: PROC = { current.pOp.clientHeader _ currentDiskAddress; current.pOp.labelPtr.filePage _ currentFilePage; current.pOp.labelPtr.dontCare _ BootFile.nullLink; IncrementDiskAddressAndPage[current.pOp.pageCount]; SA4000Face.Initiate[current.pOp]; current.state _ busy; ExchangeCurAlt[]; }; pulsesPerMilli: CARDINAL = Basics.LongDiv[100000, ProcessorFace.microsecondsPerHundredPulses]; Pause: PROC [msecs: CARDINAL _ 10] = { THROUGH [0..msecs) DO init: LONG CARDINAL = ProcessorFace.GetClockPulses[]; DO current: LONG CARDINAL = ProcessorFace.GetClockPulses[]; IF current-init > pulsesPerMilli THEN EXIT; ENDLOOP; ENDLOOP; }; Cleanup: PROC [ignoreCheck: BOOL _ FALSE] = { initialCount: CARDINAL _ LAST[CARDINAL]; extraPause: BOOL _ ignoreCheck; DO -- until count pages transferred DO -- until status~=inProgress extraPause _ ignoreCheck; SELECT SA4000Face.Poll[current.pOp] FROM inProgress => NULL; goodCompletion => GOTO TransferComplete; labelCheck => SELECT TRUE FROM current.pOp.labelPtr.dontCare~= BootFile.nullLink => { current.pOp.clientHeader _ LOOPHOLE[current.pOp.labelPtr.dontCare]; extraPause _ TRUE; EXIT; }; ignoreCheck => { current.state _ idle; ProcessorFace.SetMP[809]; Pause[500]; RETURN; }; ENDCASE => { initialCount _ current.pOp.pageCount; ProcessorFace.SetMP[100+initialCount]; Pause[10000]; GermPrivate.Error[MPCodes.germLabelCheck]; }; notReady => { IF (tries _ tries - 1) = 0 THEN GermPrivate.Error[MPCodes.germDeviceError]; Pause[10]; initialCount _ current.pOp.pageCount; }; ENDCASE -- other error -- => { IF current.pOp.pageCount ~= initialCount THEN tries _ triesMax - 1 ELSE SELECT tries _ tries - 1 FROM 0 => GermPrivate.Error[MPCodes.germDeviceError]; triesMax/2 => SA4000Face.Recalibrate[current.pOp.device]; ENDCASE; initialCount _ current.pOp.pageCount; EXIT; }; ENDLOOP; currentDiskAddress _ current.pOp.clientHeader; currentFilePage _ current.pOp.labelPtr.filePage; Initiate[]; IF extraPause THEN Pause[40]; IF current.state=busy THEN Initiate[] ELSE ExchangeCurAlt[]; REPEAT TransferComplete => { current.state _ idle; IF current.pOp.labelPtr.dontCare # BootFile.nullLink AND current.pOp.labelPtr.dontCare # LOOPHOLE[current.pOp.clientHeader] THEN { currentDiskAddress _ LOOPHOLE[current.pOp.labelPtr.dontCare]; currentFilePage _ current.pOp.labelPtr.filePage; IF alternate.state=busy THEN { ExchangeCurAlt[]; WHILE SA4000Face.Poll[current.pOp] = inProgress DO ENDLOOP; Initiate[]; }; }; }; ENDLOOP; }; IncrementDiskAddressAndPage: PROC [count: CARDINAL] = INLINE { virtualDA: LONG CARDINAL = count + currentDiskAddress.sector + Basics.LongMult[sectorsPerTrack, currentDiskAddress.head + movingHeads*currentDiskAddress.cylinder]; c: CARDINAL; [c, currentDiskAddress.sector] _ Basics.LongDivMod[virtualDA, sectorsPerTrack]; [currentDiskAddress.cylinder, currentDiskAddress.head] _ Basics.DivMod[c, movingHeads]; currentFilePage _ currentFilePage+count; }; ExchangeCurAlt: PROC = INLINE { t: Operation _ current; current _ alternate; alternate _ t; }; Allocate: PROC [size: CARDINAL] RETURNS [lp: LONG POINTER] = { pAllocateNext _ (lp _ pAllocateNext) + LONG[((size + 15)/16)*16]; }; }. (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 July 21, 1983 2:10 pm Taft Disable double-buffering of commands if running on Dandelion July 23, 1983 4:32 pm: Sturgis also handle sa1000 device type July 28, 1983 2:54 pm: Andrew Birrell wait for previous transfer after initiating second transfer October 31, 1983 2:45 pm: Andrew Birrell convert to 5.0 ^BootChannelDisk.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Taft, July 21, 1983 5:09 pm Sturgis, July 23, 1983 4:33 pm Andrew Birrell, October 31, 1983 3:33 pm Russ Atkinson, February 28, 1985 0:06:35 am PST Doug Wyatt, February 26, 1985 6:05:55 pm PST NOTE: If you change the imports, you will also have to change the Germ*.config files!! Implementation of BootChannel for Cedar volume on SA4000. A single, serially reusable, channel is supported. Disable double buffering of commands on the Dandelion, because there seems to be some bug in the head or microcode that prevents it from working. Initialize label dataPtr, incrementDataPtr irrelevant pageCount set in Transfer SetLabelFilePage[@label1, pLoc.diskFileID.firstPage]; Initialize remaining operation fields labelPtr set above dataPtr set in Transfer pageCount set in Transfer device set above SetLabelFilePage[current.pOp.labelPtr, currentFilePage]; A simple processor-independent pausing routine. end of run on disk; chain to next run and resume transfer bogus test here for debugging purposes Wait ~ 10 millisecond to see if disk becomes ready 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. currentFilePage _ GetLabelFilePage[current.pOp.labelPtr]; if we are chaining, then we pause more after this operation Restart the alternate operation if there is one, since the head has forgotten it. 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. currentFilePage _ GetLabelFilePage[current.pOp.labelPtr]; Allocator for 16-word aligned storage in first64K Κ ˜codešœ™Kšœ Οmœ1™K˜'—Kšœ™K˜K˜Kšœ$™$KšœŸ˜9Kšœ™K˜Kšœžœ Ÿ˜EKšœžœ#˜šœ žœžœ&˜>˜ K˜C——Kšœžœ˜ K˜OKšœW˜WK˜(Kšœ˜K˜—š œžœžœ˜K˜;Kšœ˜K˜—Kšœ1™1š  œžœžœžœžœžœ˜>Kšœ'žœ˜AKšœ˜K˜—Kšœ˜K˜—K˜8Kšœžœ7˜MKšœžœ?˜WKšœžœ<˜TKšœžœ6˜NKšœžœ<˜RK˜YK˜>K˜bK˜8—…—-p