:TITLE[SA4000Load]; INSERT[RdcDefs]; %Ed Fiala 9 January 1984: Move some definitions to InitialDefs.mc; change SalEOF code to exit through MicrocodeLoaded1 in Initial.mc. Reabsorb the Rubicon code which moved the germ into high VM as a conditional assembly but move it to Initial.mc and generalize it slightly for use when etherbooting; for Rubicon, the germ is XMapped into the correct virtual pages rather than copying it; for Trinity and later, copying the germ is eliminated by reading it directly to the correct location; eliminate LoByteMask, HiByteMask, SalNotifyTask; eliminate SalWordOffset, SalPage, SalBaseFromLo/Hi, SalGermWords, and SalBaseToLo/Hi registers; extend SalIOCB into the SalIOCBLo/Hi base register saving many mi; eliminate SalLoaderPage2; combine the large blocks of identical code for reading the Pilot germ and microcode into the SalReadRunOfPages subroutine. Fix bug in not initializing SalRetryCount for Alto emulator boot. Ed Fiala 11 January 1982: Eliminate Insert[D0MPCodes]. Jim Frandeen 9 December 1981: Change to new Germ in MDS 0 for Trinity. Jim Frandeen September 4, 1980: Fix to load Pilot.eb. Put code in MP for no germ or no microcode. Ev Neely Aug 22, 1980: Send LoadRam adj. LP to load new InitialAlto files. Ev Neely July 21, 1980: Fix bug in SalRetryRun. Ev Neely July 17, 1980: 48bit processor-ID. Jim Frandeen June 12, 1980: Fix MPCodes 809 - 811. Jim Frandeen March 7, 1980: Fix for labelCheck that causes MP 725. Jim Frandeen March 5, 1980: Pilot under the red boot button. Jim Frandeen February 4, 1980: change Breakpoints to Call[PNIP]. Johnsson February 3, 1980: Jim Frandeen February 1, 1980: This module uses the standard RDC microcode to read a microcode file (PilotD0.eb) and (for Pilot booting) a germ boot file (D0.eg) into memory. The microcode is then transferred into the microstore and started using LoadRAM.mc. The following four definitions from Boot.mesa provide relative offsets within a DiskFileID: DiskFileID: TYPE = MACHINE DEPENDENT RECORD [ fID(0B): File.ID, firstPage(5B): File.PageNumber, da(7B): DiskAddress]; DiskAddress: TYPE = PRIVATE RECORD [UNSPECIFIED, UNSPECIFIED]; % MC[FileIDOffset,0]; *Offset of FileID from start of DiskFileID MC[firstPageOffset,5]; *Offset of firstPage from start of DiskFileID MC[cylinderOffset,7]; *Offset of cylinder from start of DiskFileID MC[headSectorOffset,10]; *Offset of HeadSector from start of DiskFileID %The following definitions from Boot.mesa provide the relative offsets within PVBootFiles of the SoftDiskFileID and GermDiskFileID: BootFileType: TYPE = {hardMicrocode, softMicrocode, germ, pilot, debugger, debuggee} PVBootFiles: TYPE = ARRAY BootFileType[hardMicrocode..pilot] OF DiskFileID; As shown in the previous set of comments DiskFileID is 9 words long. % MC[SoftDiskFileIDOffset,11]; *Offset of Soft DiskFileID from start of bootingInfo MC[GermDiskFileIDOffset,22]; *Offset of Germ DiskFileID from start of bootingInfo %The definition of bootingInfoOffset is provided by PhysicalVolumeFormat.mesa Descriptor: TYPE = MACHINE DEPENDENT RECORD [ ... bootingInfo (10B): Boot.PVBootFiles _ nullPVBootFiles, ... % MC[bootingInfoOffset,10]; *Offset in PVR to bootingInfo *REGISTER DEFINITIONS SetTask[0]; *IniBaseLo/Hi, RTemp, and RTemp1 are also used after loading the germ, *just before exiting to MicrocodeLoaded. RV[SalReturn,0]; *Used to save the return address for nested subroutines. RV[SalRetryCount,1]; *Error retry count is decremented. RV[SalCompletionCode,2]; *This register contains the completion code *when the command has completed. RV[SalCSB,3]; *This register points to the CSB in memory. This is how we communicate with the RDC task. RV[SalState,4]; *What we are doing: MC[ReadRootPage,0]; MC[ReadInitial,1]; MC[SalVfirstRead,2]; MC[SalVnotFirstRead,3]; RV[SalFileIDOffset,4]; *Used to hold RootPageAddress+displacement *to the disk file address for either the germ *or the microcode file id. RV[SalZero,5]; *Contains zero. Must be odd RV[SalTemp,6]; *Temporary. RV2[SalCylinder,SalHeadSector,10]; *Cylinder and HeadSector of next disk address. RV2[SalIOCBLo,SalIOCBHi,12]; *Base reg pointing at IOCB in memory *used to issue commands to the RDC task. *Used to do Fetch4 to get FileID from Root Page bootingInfo. RV4[SalFileID,SalFileID1,SalFileID2,SalFileID3,14]; RV[SalLabel4,14]; *First of four regs used to contain 2nd 4 words for clientLabel. RV[SalClientLabelWord,14]; *Used to fetch one of the first six words from the client label. RV[SalFilePage,15]; *Used to fetch FilePage from Root Page bootingInfo. RV[SalDiskLabelWord,15]; *Used to fetch one of the first six words from the disk label. RV[SalBootChainLink,17]; *Used to Initialize boot chain link in the client label. *Following registers not needed after initialization. RV[SalCount,13]; RV[SalRdcTask,14]; RV[SalControllerID,15]; *CONSTANTS *The address of the next HeadSector is left in register 77 of the RDC Boot task. MC[NextHeadSectorReg,Add[LShift[RdcBootTask,4],77]]; MC[ReadLabelAndData,122]; MC[VerifyLabelReadData,112]; MC[RdcIDHigh,1400]; *Upper half of Rdc ID OnPage[SalLoaderPage]; %Begin at this location to continue loading from cylinder 0 of the initial microcode file. The next HeadSector address to load from is in register 177 (register 77 of the RDC boot task, which is task 4). We load until we get to the end of the boot file, signalled by 177777b in the last word of the label field. % SA4000Load: SalTemp _ NextHeadSectorReg; StkP _ SalTemp; *Point StkP at register 13 in the RDC Boot Task. T _ Stack; SalHeadSector _ T; *HeadSector _ address of next HeadSector in boot chain. SalState _ ReadInitial, GoTo[SalInitialize]; *Don't read Physical Volume Root Page. *Begin here to load Pilot microcode and germ. Read the Physical Volume *Root Page to determine the starting address of the files. SA4000LoadPilot: SalState _ ReadRootPage; *Set to read root page SalHeadSector _ 0C; *Set HeadSector address of root page. *Find the RDC and initialize its task to SalTask. Initial.mc has assigned *all devices to task 0. Shift the (complement of the) RdcTask through the *devices and Input from the device ID register until a Controller answers *with the correct device ID. SalInitialize: SalCount _ 60C; T _ SalRdcTask _ Xor[RdcTask,17]C; InputControllerID: GenSRClock; *Shift next bit from T[17] SalTemp _ T _ LShift[RdcTask,4]C; Input[SalControllerID], Call[SalTask]; *Read controller ID SalControllerID _ LHMask[SalControllerID]; LU _ (SalControllerID) xor (RdcIDHigh); SalCount _ (SalCount) - 1, GoTo[SalRdcFound,ALU=0]; T _ SalRdcTask _ RSh[SalRdcTask,1], GoTo[InputControllerID,ALU#0]; @SalNoRdcController: SalTemp _ NoRDC; *Instead of crashing, go to EtherBoot with the crash code in T. SalCrashPlus256: SalTemp _ (SalTemp) + (MPOffset256); SalCrash: LoadPage[opPage2]; T_ SalTemp _ (SalTemp) + (MPOffset), CallP[imMPDelay]; *Instead of crashing, go to EtherBoot with the crash code in T. *This code in Boot.mb from the EPROM will still exist because Initial is *not allowed to overwrite it. LoadPageExternal[RShift[EtherBootLoc,10]]; T _ SalTemp, GoToExternal[EtherBootLoc]; *The SA4000 Controller has been found and its task register initialized so *we can talk to it. Point R0 of the RDC task at the CSB; initialize other *task 0 registers; then call the RDC initialization task. SalRdcFound: StkP _ SalTemp; *Point StkP at R0 of RDC task. Stack _ CSBAddress; *R0 of RDC Task _ pointer to CSB. SalCSB _ CSBAddress; *Initialize pointer to CSB. SalCylinder _ 0C; *Set to read Cylinder zero SalZero _ 0C; T _ (SalCSB) + (IOCBoffset); SalIOCBLo _ T; *Initialize IOCB base register. SalIOCBHi _ 0C; SalTemp _ LoA[RdcInitLoc]; SalTemp _ (SalTemp) or (HiA[RdcInitLoc,RdcTask]); APCTask&APC _ SalTemp, Call[SalTask]; Call[SalInitIOCB]; *Initialize the IOCB. SalInitDataPtr: PStore1[SalIOCBLo,SalZero,RdcIOCBdataPtr1!]; LU _ (SalState) xor (ReadInitial); SalTemp _ MicrocodeAddress, Skip[ALU=0]; *Initialize pointer to memory where CS data will be loaded. *If reading the RootPage, set the memory address of the RootPage SalTemp _ RootPageAddress; *Point IOCB.data at the first word of memory where data will be read. PStore1[SalIOCBLo,SalTemp,RdcIOCBdataPtr!]; *Send an IOCB to the RDC task to read the next page in the boot chain or *the RootPage. Store Next disk address in IOCB header; IOCB changes in *support of 48-bit processor id's created two header copies. SalReadNextPage: SalRetryCount _ 12C, Call[SalInitHeaders]; SalTemp _ 1C; *Set to read one page. PStore1[SalIOCBLo,SalTemp,RdcIOCBpageCount!]; T _ (SalCSB) + (RdcCSBnext); OddPStore1[SalZero,SalIOCBLo]; *Store pointer to IOCB in CSB.next *Come here to retry the command after an error. SalRetry: PStore1[SalIOCBLo,SalZero,RdcIOCBdeviceStatus!]; *Initialize completion code. SalWaitForCompletion: *Wait for the command to be executed. SalTemp _ ReadLabelAndData; SalTemp _ (SalTemp) or (RdcAllowWake); *Set command to read label and data. PStore1[SalIOCBLo,SalTemp,RdcIOCBcontrollerCommand!]; Task; PFetch1[SalIOCBLo,SalCompletionCode,RdcIOCBdeviceStatus!]; SalCompletionCode _ LdF[SalCompletionCode,0,4]; LU _ (SalCompletionCode) xor (RdcGoodCompletion), GoTo[SalWaitForCompletion,ALU=0]; *The RDC has completed execution. The completion code is in the low *order four bits of CompletionCode. *Point T at bootChainLink in disk label. T _ Add[RdcIOCBdiskLabel!,7]C, GoTo[SalGoodCompletion,ALU=0]; *Continue if error occurred. @SalReadError: SalRetryCount _ (SalRetryCount) - 1; *Decrement retry count T _ (SalCSB) + (RdcCSBdeferring), Skip[ALU#0]; SalTemp _ RDCReadError, GoTo[SalCrash]; *Hard read error *Reset CSB.deferring to start the Controller again. OddPStore1[SalZero,SalZero], GoTo[SalRetry]; *The last command completed successfully. Test to see if we are reading *the Physical Volume Root Page. SalGoodCompletion: LU _ (SalState) xor (ReadRootPage); *Fetch packedDiskAddress from IOCBdiskLabel; -1 signals eof; this is used *only if not reading root page. PFetch1[SalIOCBLo,SalHeadSector], GoTo[SalRootPageRead,ALU=0]; *Continue if not reading root page. Look in the last word of the label to *see if this is the end of file. LU _ (SalHeadSector) xnor (0C); *Test for -1 in last word GoTo[SalEOF,ALU=0]; *Continue if not end of file. *Builds a two word diskAddress in SalCylinder and SalHeadSector from a *packedDiskAddress in SalHeadSector. SalCylinder and SalHeadSector now *contain the disk address of the next page to read. The data pointer in the *IOCB was incremented by the RDC task. Call[SalUnpackDiskAddress]; GoTo[SalReadNextPage]; *Come here when the Physical Volume Root Page has been read. Set up the *IOCB to read the Pilot microcode installed on the disk (PilotD0.eb). *Put the starting disk address in the IOCB. SalRootPageRead: SalFileIDOffset _ Add[bootingInfoOffset!,SoftDiskFileIDOffset!]C; SalTemp _ MPCodeNoSoftBootCode, Call[SalReadRunOfPages]; *Now set up the IOCB to load the Germ from the disk; for Trinity and later *system releases, the germ is loaded directly into its running location in *bank 0; for Rubicon, it is loaded into bank 0 and then XMaped into high *virtual memory. SalTemp _ HiA[GermAddress]; PStore1[SalIOCBLo,SalTemp,RdcIOCBdataPtr!]; SalFileIDOffset _ Add[bootingInfoOffset!,GermDiskFileIDOffset!]C; SalTemp _ MPCodeNoGerm, Call[SalReadRunOfPages]; %Now initialize most of the germ request and (Rubicon only) switches. For Rubicon, FixGerm does a three-way page exchange to move the germ from its read-in location in low VM to its running location and to fill the hole in VM left by this move, it then moves the real storage underneath the highest VM pages into the hole vacated by the germ. Then FixGerm zeroes the germ request and (Rubicon only) switches; then it initializes parts of the request common to various kinds of boot. Determine the number of germ pages by fetching the data pointer from the IOCB used to load the Germ. This has been updated to point to the next free word of memory (which is on a page boundary). % :IF[Rubicon]; ************************************************ PFetch1[SalIOCBLo,IniPageCount,RdcIOCBdataPtr!]; :ENDIF; ****************************************************** @SalFixGerm: LoadPage[InitialPage]; UseCTask, CallP[FixGerm]; *In Initial.mc *Now specialize the request to a bootPhysicalVolume request. RTemp _ bootPhysicalVolume; *Action requested PStore1[IniBaseLo,RTemp,ReqActionOffset]; RTemp _ PilotDiskDeviceType; PStore1[IniBaseLo,RTemp,ReqDevTypeOffset]; *Come here when all data pages have been read into memory. *Jump to LoadRam to load the microcode image into the CS. SalEOF: T _ LShift[RdcTask,4]C; Output[SalZero]; *Turn off the RDC. *Resume LoadRAM through entry sequence in Initial.mc. LU _ (SalState) xor (ReadInitial); LoadPage[InitialPage], Skip[ALU=0]; *If Alto *PilotD0.eb--skip .eb format page. LP _ Add[MicrocodeAddress!,400]C, GoToP[MicrocodeLoaded1]; *AltoD0.eb--just continue loading. LP _ MicrocodeAddress, GoToP[MicrocodeLoaded1]; SalTask: Return; *Initialize IOCB.disk, IOCB.command to increment data pointer, retry *counts, IOCB.next SalInitIOCB: Nop; ***Nop maybe unnecessary PStore1[SalIOCBLo,SalZero,RdcIOCBdisk!]; *Zero IOCB.disk *Initialize IOCB.command to increment data pointer. SalTemp _ (Zero) - 1; PStore1[SalIOCBLo,SalTemp,RdcIOCBoperationCommand!]; *Initialize IOCB.serviceLateRetryCount, rateErrorRetryCount. SalTemp _ 100C; PStore1[SalIOCBLo,SalTemp,RdcIOCBserviceLateRetryCount!]; T _ RdcIOCBrateErrorRetryCount; PStore1[SalIOCBLo,SalTemp]; *Zero IOCB.next PStore1[SalIOCBLo,SalZero,RdcIOCBnext!], Return; SalInitHeaders: T _ RdcIOCBclientHeader; PStore2[SalIOCBLo,SalCylinder]; PStore2[SalIOCBLo,SalCylinder,RdcIOCBoperationClientHeader!], Return; *Have the MP code for non-existent file in SalTemp and the displacement *within the RootPage of the FileID in SalFileIDOffset. This subroutine *is called once for the Pilot microcode and once for the germ. SalReadRunOfPages: UseCTask; T _ APCTask&APC; SalReturn _ T; *Save subroutine return SalFileIDOffset _ (SalFileIDOffset) + (RootPageAddress); T _ (SalFileIDOffset) + (cylinderOffset); OddPFetch1[SalZero,SalCylinder]; T _ (SalFileIDOffset) + (headSectorOffset); OddPFetch1[SalZero,SalHeadSector], Task; T _ SalCylinder; LU _ (SalHeadSector) or T; %IOCB changes to support 48-bit processor IDs created two copies of the header. Set up IOCBclientLabel for reading of the Pilot microcode or germ file. Before 48-bit processor IDs, bootingInfo FileIDs were 4 words long, which made the bootingInfo FileID-data-structure 8 words long with convenient alignments. Also bootingInfo fileIDs were in the same format as disk-label fileIDs. Therefore, at this point, we used to aquire the bootingInfo fileID and firstPage and stuff them into IOCBclientLabel, then zero IOCBclientLabel's diskChainAddress. Now BootingInfo fileIDs are five words long which makes the FileID-data-structure 9 words long, preventing nice alignment. FileIDs are also in a very different format than that contained in the disk label. Someday it might be nice to have a conversion subroutine stuff a converted bootingInfo fileID into the IOCBclientLabel. For now we put junk in IOCBclientLabel except for getting filePage from bootingInfo firstPage and zeroing bootChainAddress (it will be zero until the end of the file or a break in the run of pages). The junk will cause the first read on this file to fail the label verify. SalReadRunOfPages will detect this special case via register SalState = SalVfirstRead. The other 6 words of IOCBclientLabel will be set from IOCBdiskLabel. This label is then used to read the file starting with a retry of the first page. % *SalTemp setup by caller for SalCrashPlus256 T _ RdcIOCBclientLabel, GoTo[SalCrashPlus256,ALU=0]; PStore4[SalIOCBLo,SalFileID], Call[SalInitHeaders]; T _ (SalFileIDOffset) + (firstPageOffset); OddPFetch1[SalZero,SalFilePage], Call[SalTask]; SalBootChainLink _ 0C; T _ Add[RdcIOCBclientLabel!,4]C; PStore4[SalIOCBLo,SalLabel4], Call[SalInitIOCB]; SalState _ SalVfirstRead; *Set to fixup clientLabel from first diskLabel. SalTemp _ 10000C; *Set to read a lot of pages. *Read in the file. PStore1[SalIOCBLo,SalTemp,RdcIOCBpageCount!]; SalRetryCount _ 12C; *Come here to retry the command after an error. *When retrying after a partial run we have to remember that with the new *IOCB(the one for 48bit PID) RDC.mc expects RdcIOCBoperationClientHeader *and RdcIOCBclientHeader to be the same when it is called but that it *only updates RdcIOCBclientHeader. Therefore we have to update *RdcIOCBoperationClientHeader. SalRetryRun: T _ RdcIOCBclientHeader; PFetch2[SalIOCBLo,SalCylinder]; PStore2[SalIOCBLo,SalCylinder,RdcIOCBoperationClientHeader!]; T _ (SalCSB) + (RdcCSBnext); OddPStore1[SalZero,SalIOCBLo]; *Store pointer to IOCB in CSB.next PStore1[SalIOCBLo,SalZero,RdcIOCBdeviceStatus!]; *Initialize completion code. T _ (SalCSB) + (RdcCSBdeferring); OddPStore1[SalZero,SalZero]; *Reset CSB.deferring to start the Controller again. SalWaitForRunCompletion: *Wait for the command to be executed. SalTemp _ VerifyLabelReadData; SalTemp _ (SalTemp) or (RdcAllowWake); *Set command to read label and data. PStore1[SalIOCBLo,SalTemp,RdcIOCBcontrollerCommand!]; Task; PFetch1[SalIOCBLo,SalCompletionCode,RdcIOCBdeviceStatus!]; SalCompletionCode _ LdF[SalCompletionCode,0,4]; LU _ (SalCompletionCode) xor (RdcLabelCheck), GoTo[SalWaitForRunCompletion,ALU=0]; *The RDC has completed execution. The completion code is in the *low-order four bits of CompletionCode. GoTo[SalRunLabelCheck,ALU=0], LU _ (SalCompletionCode) xor (RdcGoodCompletion); GoTo[SalEndRunLabelFixup,ALU=0]; *Continue if error occurred. SalRunReadError: SalRetryCount _ (SalRetryCount) - 1; *Decrement retry count GoTo[SalRetryRun,ALU#0]; SalTemp _ RDCReadError, GoTo[SalCrash]; *Hard read error *We got a label check reading the run of pages. SalRunLabelCheck: LU _ (SalState) xor (SalVfirstRead); *Check for first read of this file. GoTo[SalFirstReadFixup,ALU=0]; *If first read of this file. SalTemp _ 0C; *Check and see if the first seven words of the ClientLabel match the first *seven words of the DiskLabel. SalCheckNextLabelWord: T _ (SalTemp) + (RdcIOCBclientLabel); *Word 0 through 6 PFetch1[SalIOCBLo,SalClientLabelWord]; T _ (SalTemp) + (RdcIOCBdiskLabel); PFetch1[SalIOCBLo,SalDiskLabelWord]; T _ SalClientLabelWord; LU _ (SalDiskLabelWord) xor T; GoTo[SalRunRealLabelCheck,ALU#0]; LU _ (SalTemp) xor (6C); *Check for last label word. SalTemp _ (SalTemp) + 1, GoTo[SalCheckNextLabelWord,ALU#0]; *If not last label word *Continue if the first seven words of the label match. This means there is *a break in the run of pages. We must set up to read the last page again. *We will replace the boot chain link in the client label with the boot *chain link read from the disk. This will let us read this page. SalLabelFixup: T _ Add[RdcIOCBdiskLabel!,7]C; PFetch1[SalIOCBLo,SalBootChainLink]; T _ Add[RdcIOCBclientLabel!,7]C; PStore1[SalIOCBLo,SalBootChainLink]; SalTemp _ 1C; *Set to read one page. PStore1[SalIOCBLo,SalTemp,RdcIOCBpageCount!], GoTo[SalRetryRun]; *Come here on first read of softMicrocode and Germ files. This fixup *moves 6 words from IOCBdiskLabel to IOCBclientLabel leaving filePage *and bootChainLink as they were. SalFirstReadFixup: T _ RdcIOCBdiskLabel; PFetch4[SalIOCBLo,SalLabel4]; T _ RdcIOCBclientLabel; PStore4[SalIOCBLo,SalLabel4]; T _ Add[RdcIOCBdiskLabel!,4]C; PFetch1[SalIOCBLo,SalLabel4]; T _ Add[RdcIOCBclientLabel!,4]C; PStore1[SalIOCBLo,SalLabel4]; T _ Add[RdcIOCBdiskLabel!,6]C; PFetch1[SalIOCBLo,SalLabel4]; T _ Add[RdcIOCBclientLabel!,6]C; PStore1[SalIOCBLo,SalLabel4]; *Continue reading the run of pages starting with retrying this page. SalState _ SalVnotFirstRead, GoTo[SalRetryRun]; *We are at EOF, or the run of pages has an interruption because we came *here on GoodCompletion, which only occurs on a succesful reread of a page *after a label fixup because all other reads have such large page counts *that they cannot end with GoodCompletion. SalEndRunLabelFixup: T _ Add[RdcIOCBdiskLabel!,7]C; *Fetch packedDiskAddress from IOCBdiskLabel. -1 signals eof. PFetch1[SalIOCBLo,SalHeadSector]; LU _ (SalHeadSector) xnor (0C); *Test for -1 in last word GoTo[SalEndOfRun,ALU=0]; *Continue if not end of file. Working with an interrupted run of pages. *Builds a two-word diskAddress in SalCylinder and SalHeadSector from a *packedDiskAddress in SalHeadSector. SalCylinder and SalHeadSector *contain the disk address of the next page to read. The data pointer in the *IOCB has been incremented by the RDC task. SalTemp _ 10000C, Call[SalUnpackDiskAddress]; *Set to read a lot of pages. PStore1[SalIOCBLo,SalTemp,RdcIOCBpageCount!], Call[SalInitHeaders]; SalBootChainLink _ 0C; T _ Add[RdcIOCBclientLabel!,7]C; PStore1[SalIOCBLo,SalBootChainLink], GoTo[SalRetryRun]; *This subroutine builds a two word diskAddress in SalCylinder and *SalHeadSector from a packedDiskAddress in SalHeadSector. A *packedDiskAddress has an 8 bit cylinder beginning at bit0, a 3 bit head *beginning at bit8 and a 5 bit sector beginning at bit11. SalUnpackDiskAddress: T _ (LdF[SalHeadSector,0,10]); *Unpack cylinder. SalCylinder _ T; T _ LdF[SalHeadSector,13,5]; *Unpack Sector. SalHeadSector _ (LdF[SalHeadSector,10,3]); *Unpack Head. SalHeadSector _ (LSh[SalHeadSector,10]) or T, Return; *Repack HeadSector. *Come here if we get a real label check. SalRunRealLabelCheck: SalTemp _ MPCodeRDCLabelCheck, GoTo[SalCrashPlus256]; *Come here When we have finished reading the run. SalEndOfRun: APCTask&APC _ SalReturn, GoTo[SalTask]; :END[SA4000Load];(2048)\f2