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