: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];