% File SA4000Loader.mc
Last edit by Jim June 12, 1980 7:09 PM, Fix MPCodes 809 - 811.
Last edit by Jim March 7, 1980 9:57 AM, Fix for labelCheck that causes MP 725.
Last edit by Jim March 5, 1980 11:19 AM, Pilot under the red boot button.
Last edit by Jim Frandeen February 4, 1980 11:26 AM, change Breakpoints to Call[PNIP].
Last edit by Johnsson February 3, 1980 2:00 PM
Last edit by Jim Frandeen February 1, 1980 11:54 PM

This module uses the standard RDC microcode to read a boot file into memory. When end of file is found, we transfer control to LoadRAM&Jump.
%****************************************************************
Title[SA4000Loader];
Insert[D0MPCodes];
* THE FOLLOWING ARE SOFTWARE CONSTANTS THAT WILL REQUIRE THIS MICROCODE TO BE REASSEMBLED.
MC[GermMDSPage,37000];
MC[GermStartOffset,2];*Offset of Germ from its MDS
Set[GermDataOffset,1360];*Offset of Germ Data from its MDS
Set[SDOffset,1100];*Address of SD (From SDDefs.Mesa)
Set[sFirstFree,142];*Index of sFirstFree (From SDDefs.Mesa)
Set[GermSwitchesOffset,ADD[SDOffset,sFirstFree]];*Offset of Germ Switches from its MDS: LOOPHOLE [@SDDefs.SD[SDDDefs.sFirstFree]]
MC[SA4000DeviceType,3];*From DeviceTypes.mesa
MC[InLoad,0];*From Boot.mesa
*Offsets in Physical Volume Root Page
MC[bootingInfoOffset,10];*Offset in PVR to bootingInfo
MC[SoftDiskFileIDOffset,10];*Offset of Soft DiskFileID from start of bootingInfo
MC[GermDiskFileIDOffset,20];*Offset of Germ DiskFileID from start of bootingInfo
MC[PilotDiskFileIDOffset,30];*Offset of Pilot DiskFileID from start of bootingInfo
MC[FileIDOffset,0];*Offset of FileID from start of DiskFileID
MC[firstPageOffset,4];*Offset of firstPage from start of DiskFileID
MC[cylinderOffset,6];*Offset of cylinder from start of DiskFileID
MC[headSectorOffset,7];*Offset of HeadSector from start of DiskFileID
Insert[RdcDefs];
*TASK DEFINITIONS
SET[SalLoaderTask,0];
SET[SalNotifyTask,LSHIFT[SalLoaderTask,14]];
*LOADER TASK REGISTER DEFINITIONS
Set Task[SalLoaderTask];
RV[SalReturn,0];*Used to save the return address for nested subroutines.
RV[SalWordOffset,0];*Used to copy the Germ.
RV[SalRetryCount,1];*Error retry count is decremented.
RV[SalRealPages,1];*Used to count the number of real pages of memory.
RV[SalCompletionCode,2];*This register points contains the completion code when the command has completed.
RV[SalGermPages,2];*Used to calculate the number of pages in the Germ.
RV[SalCSB,3];*This register points to the CSB in memory. This is how we communicate with the RDC task.
RV[SalPage,3];*Used by BaseRegisterFromPage.
RV[SalState,4];*What we are doing:
MC[ReadRootPage,0];
MC[ReadInitial,1];
RV[SalFrom,4];*Used to move the Germ
RV[SalZero,5];*Contains zero. Must be odd
RV[SalTemp,6];*Temporary.
RV[SalGerm,7];*This register points to the first word of the Pilot Germ.
RV[SalCylinder,10];*Cylinder of next disk address. Must be consecutive with HeadSector.
RV[SalBaseLo,10];*Base register for Map instructions.
RV[SalBaseFromLo,10];*Base register used to copy the Germ.
RV[SalHeadSector,11];*HeadSector of next disk address
RV[SalBaseHi,11];*Base register for Map instructions.
RV[SalBaseFromHi,11];*Base register used to copy the Germ.
RV[SalIOCB,12];*This register points to the IOCB in memory. We use this to issue commands to the RDC task.
RV[SalBaseToLo,12];*Base register used to copy the Germ.
RV[SalCount,13];*Used only to Initialize RDC task.
RV[SalTo,13];*Used to move the Germ.
RV[SalBaseToHi,13];*Base register used to copy.
RV[SalRdcTask,14];*Used only to Initialize RDC task.
RV[SalFileID,14];*Used to do Fetch4 to get FileID from Root Page bootingInfo.
RV[SalFilePageLo,14];*Used to fetch FilePage from Root Page bootingInfo.
RV[SalClientLabelWord,14];*Used to fetch one of the first six words from the client label.
RV[SalMap,14];*Used for XMap instructions.
RV[SalGermWords,14];*Used to copy the Germ four words at a time.
RV[SalControllerID,15];*Used only to Initialize RDC task.
RV[SalFilePageHi,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[SalMap1,15];*Used for XMap instructions.
RV[SalFileID1,15];*Used to do Fetch4 to get FileID from Root Page bootingInfo.
RV[SalBootChainLinkLo,16];*Used to Initialize boot chain link in the client label.
RV[SalMap2,16];*Used for XMap instructions.
RV[SalFileID2,16];*Used to do Fetch4 to get FileID from Root Page bootingInfo.
RV[SalBootChainLinkHi,17];*Used to Initialize boot chain link in the client label.
RV[SalMap3,17];*Used for XMap instructions.
RV[SalFileID3,17];*Used to do Fetch4 to get FileID from Root Page bootingInfo.
*CONSTANTS
Set[LoByteMask,377];
Set[HiByteMask,177400];
MC[Vacant,60000];*For Map Vacant bits
*The address of the next HeadSector is left in register 77 of the RDC Boot task.
SET[RdcBootTask,4];
SET[RdcNotifyTask,LSHIFT[RdcTask,14]];
MC[NextHeadSectorReg,ADD[LSHIFT[RdcBootTask,4],77]];
MC[ReadLabelAndData,122];
MC[VerifyLabelReadData,112];
MC[RdcTaskNumberComplement,AND[17,NOT[RdcTask]]];
MC[RdcIDHigh,1400];*Upper half of Rdc ID
MC[BootFile,20];*Bit in FilePageHi
MC[CSBAddress,1000];*Memory address of CSB
MC[IOCBOffset,100];*Offset of IOCB from the CSB
MC[CSDataAddress,2000];*Memory address of Control Store data
MC[RootPageAddress,1400];*Memory address of Physical Volume Root Page
OnPage[SalLoaderPage];
SA4000Load:
*We begin at this location to continue loading from cylinder 0 of the 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. This is signalled by 177777 in the last word of the label field.
SalTemp←NextHeadSectorReg;
StkP←SalTemp;*Set Stack Pointer to point to register 13 in the RDC Boot Task.
T←Stack;
SalHeadSector←T;*HeadSector ← address of next HeadSector in boot chain.
SalState←ReadInitial,*Don’t read Physical Volume Root Page.
GoTo[SalInitialize];
SA4000LoadPilot:
*We begin at this location to load the Pilot microcode. We must read the Physical Volume Root Page in order to determine the starting address of the Pilot boot file.
SalState←ReadRootPage;*Set to read root page
SalHeadSector←0C;*Set HeadSector address of root page.
SalInitialize:
*Initialize registers.
SalCylinder←0C;*Set to read Cylinder zero
SalZero←0C;
*Initialize pointers to memory addresses:
*CSB is at 1000
*IOCB is at 1100
*Physical Volume Root Page is at 1400
*Control Store data will be loaded into 2000 and following.
SalCSB←CSBAddress;*Initialize pointer to CSB.
T←(SalCSB)+(IOCBoffset);
SalIOCB←T;*Initialize pointer to IOCB.
*Find the RDC and initialize its task to SalTask. First we shift 0 into every controller task (there are 48 possible controllers). Then we do an Input of the device ID register until a Controller answers with the correct device ID.
T←0C;*Shift out zero
SalCount←(57C);*48 decimal max controllers
SalCount←(SalCount)-1,
GenSRClock,*Shift out of bit 17 of T
GoTo[.,ALU>=0];*Do this 48 times.
SalCount←(60C);
T←SalRdcTask←(RdcTaskNumberComplement);
InputControllerID:
*SalTask will be shifted out. The value in the Controller task register is in complement form.
GenSRClock;*Shift next bit from T[17]
T←LSHIFT[RdcTask,4]C;
Input[SalControllerID];*Read ID from controller
SalControllerID←(LHMask[SalControllerID]);
LU←(SalControllerID) XOR (RdcIDHigh);
SalCount←(SalCount)-1,
GoTo[SalRdcFound,ALU=0];*If SalControllerID=SalID
T←SalRdcTask←RSH[SalRdcTask,1],
GoTo[InputControllerID,ALU#0];*If not last controller
SalNoRdcController:
*Continue if the RDC controller cannot be found.
SalTemp←NoRDC;
SalCrashPlus256:
*Instead of crashing, go to EtherBoot with the crash code in T.
SalTemp←(SalTemp)+(MPOffset256);
SalCrash:
*Instead of crashing, go to EtherBoot with the crash code in T.
LoadPageExternal[RSHift[EtherBootLoc,10]];
T←(SalTemp)+(MPOffset),
GoToExternal[EtherBootLoc];
SalRdcFound:
*We have found the Sal Controller and initialized its task register so that we can talk to it. Set R0 of the RDC task to point to the CSB. Then call the RDC initialization task.
SalTemp←(LSHIFT[RdcTask,4]C);
StkP←SalTemp;*Set Stack Pointer to point to R0 of RDC task.
T←SalCSB;
Stack←T;*R0 of RDC Task ← pointer to CSB.
SalTemp←AND[LoByteMask,RdcInitLoc]C;
SalTemp←(SalTemp) OR (OR[RdcNotifyTask,AND[HiByteMask,RdcInitLoc]]C);
APC&APCTAsk←SalTemp,Call[SalTask];
*Initialize the IOCB.
Call[SalInitIOCB];
SalInitDataPtr:
T←(SalIOCB)+(RdcIOCBdata1);
OddPStore1[SalZero,SalZero];
LU←(SalState) XOR (ReadInitial);
SalTemp←CSDataAddress,*Initialize pointer to memory where CS data will be loaded.
Skip[ALU=0];*If reading initial microcode
*If reading the RootPage, set the memory address of the RootPage
SalTemp←RootPageAddress;
T←(SalIOCB)+(RdcIOCBdata);
OddPStore1[SalZero,SalTemp];*Initialize IOCB.data to point to the first word of memory where data will be read.
SalReadNextPage:
*Send an IOCB to the RDC task to read the next page in the boot chain or the RootPage.
T←(SalIOCB)+(RdcIOCBclientHeader);
OddPStore2[SalZero,SalCylinder];*Store Next disk address in IOCB header.
T←(SalIOCB)+(RdcIOCBpageCount);
SalTemp←1C;
OddPStore1[SalZero,SalTemp];*Set to read one page.
T←(SalCSB)+(RdcCSBnext);
OddPStore1[SalZero,SalIOCB];*Store pointer to IOCB in CSB.next
SalRetry:
*Come here to retry the command after an error.
T←(SalIOCB)+(RdcIOCBdeviceStatus);
OddPStore1[SalZero,SalZero];*Initialize completion code.
SalWaitForCompletion:
*Wait for the command to be executed.
SalTemp←ReadLabelAndData;
SalTemp←(SalTemp) OR (RdcAllowWake);
T←(SalIOCB)+(RdcIOCBrdcCommand);
OddPStore1[SalZero,SalTemp];*Set command to read label and data.
T←(SalIOCB)+(RdcIOCBdeviceStatus),*T= offset of IOCB.deviceStatus
TASK;
OddPFetch1[SalZero,SalCompletionCode];
SalCompletionCode←LDF[SalCompletionCode,0,4];
LU←(SalCompletionCode) XOR (RdcGoodCompletion),
GoTo[SalWaitForCompletion,ALU=0];*If still in progress
*The RDC has completed execution. The completion code is in the low order four bits of CompletionCode.
T←(SalIOCB)+(ADD[RdcIOCBdiskLabel!,6]C),*T is offset to point to last two words of disk label.
GoTo[SalGoodCompletion,ALU=0];
*Continue if error occurred.
SalReadError:
SalRetryCount←(SalRetryCount)-1;*Decrement retry count
T←(SalCSB)+(RdcCSBdeferring),
GoTo[SalHardReadError,ALU=0];*If retry count exceeded
OddPStore1[SalZero,SalZero],*Reset CSB.deferring to start the Controller again.
GoTo[SalRetry];
SalHardReadError:
*Come here if hard read error occurs.
SalTemp←RDCReadError,
GoTo[SalCrash];
SalGoodCompletion:
*The last command completed successfully. Test to see if we are reading the Physical Volume Root Page.
LU←(SalState) XOR (ReadRootPage);
GoTo[SalRootPageRead,ALU=0];
*Continue if not reading root page. Fetch the last two words of the label. Look in the last word of the label to see if this is the end of file.
OddPFetch2[SalZero,SalCylinder];*Fetch last two words of label.
T←(zero)-1;
LU←(SalHeadSector) XOR T;*Test for -1 in last word
GoTo[SalEOF,ALU=0];*If end of file
*Continue if not end of file. Cylinder and HeadSector contain the disk address of the next page to read. The data pointer in the IOCB has been incremented by the RDC task.
GoTo[SalReadNextPage];
SalRootPageRead:
*Come here when the Physical Volume Root Page has been read. Set up our IOCB to read the Soft microcode (Pilot.sb).
*Put the starting disk address in the IOCB.
SalTemp←RootPageAddress;
T←(SalTemp)+(ADD[bootingInfoOffset!, SoftDiskFileIDOffset!, cylinderOffset!]C);
OddPFetch2[SalZero,SalCylinder];
T←SalCylinder;
LU←(SalHeadSector) OR T;
T←(SalIOCB)+(RdcIOCBclientHeader),
GoTo[SalNoSoftBootCode,ALU=0];
OddPStore2[SalZero,SalCylinder];
*Put the FileID in the client label of the IOCB.
SalTemp←RootPageAddress;
T←(SalTemp)+(ADD[bootingInfoOffset!, SoftDiskFileIDOffset!, FileIDOffset!]C);
OddPFetch4[SalZero,SalFileID];
T←(SalIOCB)+(RdcIOCBclientLabel);
OddPStore4[SalZero,SalFileID];
*Put the starting page number in the client label of the IOCB. Set the bootFile bit in FilePageHi. Set the boot chain link to zero. It will be zero until the end of the file or a break in the run of pages.
SalTemp←RootPageAddress;
T←(SalTemp)+(ADD[bootingInfoOffset!, SoftDiskFileIDOffset!, firstPageOffset!]C);
OddPFetch2[SalZero,SalFilePageLo],
Call[SalTask];
SalFilePageHi←(SalFilePageHi) OR (BootFile);
SalBootChainLinkLo←0C;
SalBootChainLinkHi←0C;
T←(SalIOCB)+(RdcIOCBclientLabelFilePageLo);
OddPStore4[SalZero,SalFilePageLo];
*Initialize the rest of the IOCB to read the Soft microcode. Since we just read in the RootPage, the data address was updated to point to the next word in memory, which is the first word of Soft microcode.
Call[SalInitIOCB];
T←(SalIOCB)+(RdcIOCBpageCount);
SalTemp←10000C;
OddPStore1[SalZero,SalTemp];*Set to read a lot of pages.
*Read in the Pilot Microcode.
LoadPage[SalLoaderPage2];
CallP[SalReadRunOfPages];
*Now set up the IOCB to load the Germ. The data pointer from the IOCB used to load the Pilot microcode has been updated to point to the next free word of memory (which is on a page boundary).
T←(SalIOCB)+(RdcIOCBdata);
OddPFetch1[SalZero,SalGerm];*Save the starting address of the Germ.
*Put the starting disk address in the IOCB.
SalTemp←RootPageAddress;
T←(SalTemp)+(ADD[bootingInfoOffset!, GermDiskFileIDOffset!, cylinderOffset!]C);
OddPFetch2[SalZero,SalCylinder];
T←SalCylinder,
Call[SalTask];
LU←(SalHeadSector) OR T;
T←(SalIOCB)+(RdcIOCBclientHeader),
GoTo[SalNoGerm,ALU=0];
OddPStore2[SalZero,SalCylinder];
*Put the FileID in the client label of the IOCB.
SalTemp←RootPageAddress;
T←(SalTemp)+(ADD[bootingInfoOffset!, GermDiskFileIDOffset!, FileIDOffset!]C);
OddPFetch4[SalZero,SalFileID];
T←(SalIOCB)+(RdcIOCBclientLabel);
OddPStore4[SalZero,SalFileID];
*Put the starting page number in the client label of the IOCB. Set the bootFile bit in FilePageHi. Setthe boot chain link to zero. It will be zero until the end of the file or a break in the run of pages.
SalTemp←RootPageAddress;
T←(SalTemp)+(ADD[bootingInfoOffset!, GermDiskFileIDOffset!, firstPageOffset!]C);
OddPFetch2[SalZero,SalFilePageLo];
SalFilePageHi←(SalFilePageHi) OR (BootFile);
SalBootChainLinkLo←0C;
SalBootChainLinkHi←0C;
T←(SalIOCB)+(RdcIOCBclientLabelFilePageLo);
OddPStore4[SalZero,SalFilePageLo];
*Initialize the rest of the Germ IOCB.
Call[SalInitIOCB];
T←(SalIOCB)+(RdcIOCBpageCount);
SalTemp←10000C;
OddPStore1[SalZero,SalTemp];*Set to read a lot of pages.
*Read in the Pilot Germ.
LoadPage[SalLoaderPage2];
CallP[SalReadRunOfPages];
*Now we will count the number of real memory pages. We do this by looking at each MAP word until we find a vacant page.
SalRealPages←0C;
SalNextRealPage:
*Create a base register pair from the next page number.
T←SalRealPages,
Call[SalBaseRegisterFromPage];
*The following XMap instruction replaces the map entry with the contents of SalMap. It then writes the original contents of the map entry into Map1, Map2, and Map3.
XMap[SalBaseLo,SalMap,0];
T←LSH[SalMap3,10];*Put flags,,card,,blk0 in left byte
T←SalMap1←(RHMask[SalMap1]) OR T;*Put blk1,,rowaddr in low byte
T←(ZERO) OR NOT T;*T contains old flags and page
SalMap←T;
LU←(LDF[SalMap3,11,3])-1;*zero if map entry was vacant
XMap[SalBaseLo,SalMap,0],*Restore old flags
Skip[ALU=0];
SalRealPages←(SalRealPages)+1,
GoTo[SalNextRealPage];
SalRealPagesFound:
*Now SalRealPages contains the number of real pages. Calculate the numberof germ pages. Fetch 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).
T←(SalIOCB)+(RdcIOCBdata);
OddPFetch1[SalZero,SalGermPages];*GermPages points to the next page.
T←SalGerm;*T points to the first Germ page
SalGermPages←(SalGermPages)-T;
SalGermPages←RSH[SalGermPages,10];
SalGermPagesFound:
*Now we will Move pages from the end of real memory (from RealPages-GermPages) to the Germ MDS.
T←GermMDSPage;
SalTo←T;*Page to move to
SalTo←(SalTo)+(GermStartOffset);
T←SalRealPages;
SalFrom←T;*Page to move from
T←SalGermPages;
SalFrom←(SalFrom)-T;*RealPages-GermPages
SalTemp←T;*Number of pages to move is GermPages
SalMoveNextPage:
*Create a base register pair from the next page number.
T←SalFrom,
Call[SalBaseRegisterFromPage];
*The following XMap instruction replaces the map entry for FromPage with the contents of SalMap, which will be vacant in the flag bits and zero in the address bits. It then writes the original contents of the map entry into Map1, Map2, and Map3.
SalMap←Vacant;
XMap[SalBaseLo,SalMap,0];
T←LSH[SalMap3,10];*Put flags,,card,,blk0 in left byte
T←SalMap1←(RHMask[SalMap1]) OR T;*Put blk1,,rowaddr in low byte
T←(ZERO) OR NOT T;*T contains old flags and page
SalMap←T;
*Now set the Map entry for the next ToPage to the previous contents of the FromPage.
T←SalTo,
Call[SalBaseRegisterFromPage];
XMap[SalBaseLo,SalMap,0];*Restore old flags
SalTo←(SalTo)+1;
SalTemp←(SalTemp)-1;*Decrement count
SalFrom←(SalFrom)+1,
GoTo[SalMoveNextPage,ALU#0];
*Now we need to copy the Germ from where it is in low memory to its own MDS.
SalCopyGerm:
T←SalGerm;
SalBaseFromLo←T;
SalBaseFromHi←0C;*Address to move from
SalTemp←GermMDSPage;*Page to move to
SalTemp←(SalTemp)+(GermStartOffset);
T←LSH[SalTemp,10];
SalBaseToLo←T;
T←LHMask[SalTemp];
SalBaseToHi←T;*Address to move to
T←(LDF[SalTemp,0,10])+1;
SalBaseToHi←(SalBaseToHi) OR T;
T←LSH[SalGermPages,10];
SalTemp←T;*Number of words to move
T←SalWordOffset←0C;*T is displacement
SalCopyNext4Words:
NOP;
PFetch4[SalBaseFromLo,SalGermWords];
Call[SalTask];
PStore4[SalBaseToLo,SalGermWords];
SalTemp←(SalTemp)-(4C);
T←SalWordOffset←(SalWordOffset)+(4C),
GoTo[SalCopyNext4Words,ALU#0];
*Now we need to copy the Germ Data into its MDS. Germ Data is a Boot.Request as follows:
* Word 0 is 0 for inLoad.
* Word 1 is Device.Type = 3 for SA4000
* Word 2 is deviceOrdinal = 0
* Word 3 is FileID of Pilot BootFile
* Word 4 is FileID of Pilot BootFile
* Word 5 is FileID of Pilot BootFile
* Word 6 is FileID of Pilot BootFile
* Word 7 is PageNumber of Pilot BootFile
* Word 10 is PageNumber of Pilot BootFile
* Word 11 is DiskAddress of Pilot BootFile
* Word 12 is DiskAddress of Pilot BootFile
T←GermMDSPage;
Call[SalBaseRegisterFromPage];*On return, SalBaseLo and Hi point to Germ MDS
SalBaseLo←(SalBaseLo)+(AND[LoByteMask,GermDataOffset]C);
SalBaseLo←(SalBaseLo)+(AND[HiByteMask,GermDataOffset]C);
SalTemp←Inload;* Word 0 is 0 for inLoad.
PStore1[SalBaseLo,SalTemp,0];
SalTemp←SA4000DeviceType;* Word 1 is Device.Type = 3 for SA4000.
PStore1[SalBaseLo,SalTemp,1];
SalTemp←0C;* Word 2 is deviceOrdinal = 0.
PStore1[SalBaseLo,SalTemp,2];
*Put the FileID of the Pilot boot volume in the next four words.
SalTemp←RootPageAddress;
T←(SalTemp)+(ADD[bootingInfoOffset!, PilotDiskFileIDOffset!, FileIDOffset!]C);
OddPFetch4[SalZero,SalFileID],
Call[SalTask];
PStore1[SalBaseLo,SalFileID,3];
PStore1[SalBaseLo,SalFileID1,4];
PStore1[SalBaseLo,SalFileID2,5];
PStore1[SalBaseLo,SalFileID3,6];
*Put the starting page number and the disk address of the Pilot boot volume in the next four words.
SalTemp←RootPageAddress;
T←(SalTemp)+(ADD[bootingInfoOffset!, PilotDiskFileIDOffset!, firstPageOffset!]C);
OddPFetch4[SalZero,SalFileID];
PStore1[SalBaseLo,SalFileID,7];
PStore1[SalBaseLo,SalFileID1,10];
LU←SalFileID2;*Test cylinder
PStore1[SalBaseLo,SalFileID2,11],
GoTo[SalNoPilotBootVolume,ALU=0];
PStore1[SalBaseLo,SalFileID3,12];
GermDataCopied:
*Now we must move the Switches into the Germ MDS. Three words of zero will do.
T←GermMDSPage,
Call[SalBaseRegisterFromPage];*On return, SalBaseLo and Hi point to Germ MDS
SalBaseLo←(SalBaseLo)+(AND[LoByteMask,GermSwitchesOffset]C);
SalBaseLo←(SalBaseLo)+(AND[HiByteMask,GermSwitchesOffset]C);
SalTemp←0C;
PStore1[SalBaseLo,SalTemp,0];
PStore1[SalBaseLo,SalTemp,1];
PStore1[SalBaseLo,SalTemp,2];
SalEOF:
*Come here when all data pages have been read into memory. Transfer control to LoadRam to load it into ControlStore.
T←LSHIFT[RdcTask,4]C;
Output[SalTemp];*Turn off the RDC.
xfTemp1←0C;*xfTemp1=0 for inline refresh without tasks.
LPhi←0C;*High order of base pointer is zero.
LP←CSDataAddress;*Set low base pointer to point to first data word.
LoadPageExternal[LRJPage];
RTemp1←0C,*RTemp is even iff start address is to be believed.
GoToExternal[AND[LRJStart,377]];
SalTask:
RETURN;
SalInitIOCB:
*Initialize the following fields of the IOCB:
*IOCB.disk, IOCB.command to increment data pointer, retry counts, IOCB.next
T←(SalIOCB)+(RdcIOCBdisk);
OddPStore1[SalZero,SalZero];*Zero IOCB.disk
SalTemp←(zero)-1;
T←(SalIOCB)+(RdcIOCBcommand);
OddPStore1[SalZero,SalTemp];*Initialize IOCB.command to increment data pointer.
SalTemp←100C;
T←(SalIOCB)+(RdcIOCBserviceLateRetryCount);
OddPStore1[SalZero,SalTemp];*Initialize IOCB.serviceLateRetryCount.
T←(SalIOCB)+(RdcIOCBrateErrorRetryCount);
OddPStore1[SalZero,SalTemp];*Initialize IOCB.rateErrorRetryCount.
T←(SalIOCB)+(RdcIOCBnext);
OddPStore1[SalZero,SalZero],*Zero IOCB.next
RETURN;
SalBaseRegisterFromPage:
*Create a base register pair from the page number in T.
SalBaseLo←T;
SalBaseHi←T;
SalBaseLo←LSH[SalBaseLo,10];
SalBaseHi←LHMask[SalBaseHi];
T←(LDF[SalBaseHi,0,10])+1;
SalBaseHi←(SalBaseHi) OR T;
SalBaseRegisterFromPageRet:
NOP;*So I can breakpoint
RETURN;
SalNoSoftBootCode:
SalTemp←MPCodeNoSoftBootCode,
GoTo[SalCrashPlus256];
SalNoGerm:
SalTemp←MPCodeNoGerm,
GoTo[SalCrashPlus256];
SalNoPilotBootVolume:
SalTemp←MPCodeNoPilotBootVolume,
GoTo[SalCrashPlus256];
OnPage[SalLoaderPage2];
SalReadRunOfPages:
*Save the return address.
UseCTask;
T←APC&APCTask;
SalReturn←T;
SalRetryCount←12C;
SalRetryRun:
*Come here to retry the command after an error.
T←(SalCSB)+(RdcCSBnext);
OddPStore1[SalZero,SalIOCB];*Store pointer to IOCB in CSB.next
T←(SalIOCB)+(RdcIOCBdeviceStatus);
OddPStore1[SalZero,SalZero];*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);
T←(SalIOCB)+(RdcIOCBrdcCommand);
OddPStore1[SalZero,SalTemp];*Set command to read label and data.
T←(SalIOCB)+(RdcIOCBdeviceStatus),*T= offset of IOCB.deviceStatus
TASK;
OddPFetch1[SalZero,SalCompletionCode];
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[SalRunHardReadError,ALU=0];*If retry count exceeded
GoTo[SalRetryRun];
SalRunHardReadError:
*Come here if hard read error occurs.
LoadPage[SalLoaderPage];
SalTemp←RDCReadError,
GoToP[SalCrash];
SalRunLabelCheck:
*We got a label check reading the run of pages. Check and see if the first six words of the ClientLabel match the first six words of the DiskLabel..
SalTemp←0C;*Temp will count from 0 to 5
SalCheckNextLabelWord:
T←(SalIOCB)+(RdcIOCBclientLabel);
T←(SalTemp) + T;*Word 0 through 5
OddPFetch1[SalZero,SalClientLabelWord];
T←(SalIOCB)+(RdcIOCBdiskLabel);
T←(SalTemp) + T;*Word 0 through 5
OddPFetch1[SalZero,SalDiskLabelWord];
T←SalClientLabelWord;
LU←(SalDiskLabelWord) XOR T;
GoTo[SalRunRealLabelCheck,ALU#0];
LU←(SalTemp) XOR (5C);*Check for last label word.
SalTemp←(SalTemp)+1,
GoTo[SalCheckNextLabelWord,ALU#0];*If not last label word
SalLabelFixup:
*Continue if the first six 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.
T←(SalIOCB)+(ADD[RdcIOCBdiskLabel!,6]C);
OddPFetch2[SalZero,SalBootChainLinkLo];
T←(SalIOCB)+(ADD[RdcIOCBclientLabelFilePageLo!,2]C);
OddPStore2[SalZero,SalBootChainLinkLo];
T←(SalIOCB)+(RdcIOCBpageCount);
SalTemp←1C;
OddPStore1[SalZero,SalTemp],*Set to read one page.
GoTo[SalRetryRun];
SalEndRunLabelFixup:
*Come here when We have completed a label fixup. This is the only time we get a good completion code because we have read only one page. Every other time, we should get a label check. We are at the end of the file, or the run of pages has an interruption. If we are not at the end of the run, store the boot chain link in the client header and continue the run of pages.
T←(SalIOCB)+(ADD[RdcIOCBdiskLabel!,6]C);
OddPFetch2[SalZero,SalBootChainLinkLo];
LU←SalBootChainLinkHi,
GoTo[SalEndOfRun,R<0];*If end of run
T←(SalIOCB)+(RdcIOCBpageCount);
SalTemp←10000C;
OddPStore1[SalZero,SalTemp];*Set to read a lot of pages.
T←(SalIOCB)+(RdcIOCBclientHeader);
OddPStore2[SalZero,SalBootChainLinkLo];
*Set BootChainLink backto zero.
SalBootChainLinkLo←0C;
SalBootChainLinkHi←0C;
T←(SalIOCB)+(ADD[RdcIOCBclientLabel!,6]C);
OddPStore2[SalZero,SalBootChainLinkLo],
GoTo[SalRetryRun];
SalRunRealLabelCheck:
*Come here if we get a real label check.
LoadPage[SalLoaderPage];
SalTemp←MPCodeRDCLabelCheck,
GoToP[SalCrashPlus256];
SalEndOfRun:
*Come here When we have finished reading the run.
APC&APCTask←SalReturn;
RETURN;
:END[SA4000Loader];