:TITLE[SA4000Loader];

%Ed Fiala 7 June 1983: Eliminate def of SDOffset and absorb its value (now
conflicts with value defined in Trinity GlobalDefs.mc.
Ed Fiala 11 January 1982: Remove insert of D0MPCOdes.
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 boot file into memory. When end of file is found, we transfer control to LoadRAM&Jump.
%****************************************************************
* 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[sFirstFree,142];*Index of sFirstFree (From SDDefs.Mesa)
Set[GermSwitchesOffset,ADD[1100,sFirstFree]];*Offset of Germ Switches (1100 is SDOffset for Rubicon) within its MDS: LOOPHOLE [@SDDefs.SD[SDDDefs.sFirstFree]]

*The definitions below for the offset to the germ Request, the three offsets within the germ request
* and the constant for booting the physical volume are provided by Boot.mesa as follows:
*
-- Basic request to germ
* pRequest:
POINTER TO Request = LOOPHOLE[1360B]; -- relative to germ’s MDS
* Request:
TYPE = MACHINE DEPENDENT RECORD [
* action: Action,
* location: Location];
* Action:
TYPE = RECORD [CARDINAL];
* ...
* bootPhysicalVolume: Action = [2]; -- do inLoad using Location specified indirectly in pilot entry of PVBootFiles
* ...
* Location: TYPE = MACHINE DEPENDENT RECORD [
* deviceType: Device.Type, -- e.g. diablo31, sa4000, ethernet
* deviceOrdinal: CARDINAL, -- position of device within all those of same type
* ... (we don’t use the rest of the Location) ...
Set[GermRequestOffset,1360];*Offset of Germ Request within its MDS
Set[ReqActionOffset,0];*Offset of Action within the Request
Set[ReqDevTypeOffset,1];*Offset of firstPage within the Request
Set[ReqDevOrdinalOffset,2];*Offset of cylinder within the Request
MC[bootPhysicalVolume,2];*The Request Action we use
*
MC[SA4000DeviceType,3];*From Device.mesa

*The following four definitions provide relative offsets within a DiskFileID.
*They are determined from Boot.mesa as follows:
*
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 two definitions provide the relative offsets within PVBootFiles
*of the
SoftDiskFileID and GermDiskFileID. They are provided by Boot.mesa as follows:
* 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
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];
MC[SalVfirstRead,2];
MC[SalVnotFirstRead,3];
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[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[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[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[SalMap1,15];*Used for XMap instructions.
RV[SalFileID1,15];*Used to do Fetch4 to get FileID from Root Page bootingInfo.
RV[SalMap2,16];*Used for XMap instructions.
RV[SalFileID2,16];*Used to do Fetch4 to get FileID from Root Page bootingInfo.
RV[SalBootChainLink,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[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:
LoadPage[0];
T← SalTemp ← (SalTemp)+(MPOffset),
Call[PNIP];
*Instead of crashing, go to EtherBoot with the crash code in T.
LoadPageExternal[RSHift[EtherBootLoc,10]];
T←(SalTemp),
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)+(RdcIOCBdataPtr1);
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)+(RdcIOCBdataPtr);
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)+(RdcIOCBoperationClientHeader);
OddPStore2[SalZero,SalCylinder];*Changes to the IOCB to support 48bit processor-id’s created two copies of 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)+(RdcIOCBcontrollerCommand);
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!,7]C),*T points to bootChainLink in 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. Look in the last word of the label to see if this is the end of file.
OddPFetch1[SalZero,SalHeadSector];*Fetch packedDiskAddress from IOCBdiskLabel. -1 signals eof.
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.
LoadPage[SalLoaderPage2];
CallP[SalUnpackDiskAddress];*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.
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);
OddPFetch1[SalZero,SalCylinder];
T←(SalTemp)+(ADD[bootingInfoOffset!, SoftDiskFileIDOffset!, headSectorOffset!]C);
OddPFetch1[SalZero,SalHeadSector];
T←SalCylinder;
LU←(SalHeadSector) OR T;
T←(SalIOCB)+(RdcIOCBclientHeader),
GoTo[SalNoSoftBootCode,ALU=0];
OddPStore2[SalZero,SalCylinder];
T←(SalIOCB)+(RdcIOCBoperationClientHeader);
OddPStore2[SalZero,SalCylinder];*Changes to the IOCB to support 48bit processor-id’s created two copies of header.
*Set up IOCBclientLabel for reading of the SoftDiskFile.
*In the good-old-days (before 48bit 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 SoftDiskFile bootingInfo fileID and firstPage and stuff them into IOCBclientLabel. And then zero IOCBclientLabel’s diskChainAddress.
*Sigh. Now it’s not so nice. BootingInfo fileIDs are five words long which makes the FileID-data-structure 9 words long which prevents nice alignment. FileIDs are also in a very different format than that contained in the disk label. Someday it might be nice to write a conversion subroutine and stuff a converted bootingInfo fileID into the IOCBclientLabel.
*For now we put junk in IOCBclientLabel except for getting filePage from bootingInfo firstPage and setting the bootChainAddress to zero (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.
T←(SalIOCB)+(RdcIOCBclientLabel);
OddPStore4[SalZero,SalFileID];
SalTemp←RootPageAddress;
T←(SalTemp)+(ADD[bootingInfoOffset!, SoftDiskFileIDOffset!, firstPageOffset!]C);
OddPFetch1[SalZero,SalFilePage],
Call[SalTask];
SalBootChainLink←0C;
T←(SalIOCB)+(ADD[RdcIOCBclientLabel!,4]C);
OddPStore4[SalZero,SalLabel4];
*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];
SalState←SalVfirstRead;*Set to fixup clientLabel from first diskLabel.
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)+(RdcIOCBdataPtr);
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);
OddPFetch1[SalZero,SalCylinder];
T←(SalTemp)+(ADD[bootingInfoOffset!, GermDiskFileIDOffset!, headSectorOffset!]C);
OddPFetch1[SalZero,SalHeadSector];
T←SalCylinder,
Call[SalTask];
LU←(SalHeadSector) OR T;
T←(SalIOCB)+(RdcIOCBclientHeader),
GoTo[SalNoGerm,ALU=0];
OddPStore2[SalZero,SalCylinder];
T←(SalIOCB)+(RdcIOCBoperationClientHeader);
OddPStore2[SalZero,SalCylinder];*Changes to the IOCB to support 48bit processor-id’s created two copies of header.
*Set up IOCBclientLabel for reading of the GermDiskFile.
*Someday it might be nice to write a conversion subroutine and stuff a converted bootingInfo fileID into the IOCBclientLabel.
*For now we put junk in IOCBclientLabel except for getting filePage from bootingInfo firstPage and setting the bootChainAddress to zero (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.
T←(SalIOCB)+(RdcIOCBclientLabel);
OddPStore4[SalZero,SalFileID];
SalTemp←RootPageAddress;
T←(SalTemp)+(ADD[bootingInfoOffset!, GermDiskFileIDOffset!, firstPageOffset!]C);
OddPFetch1[SalZero,SalFilePage];
SalBootChainLink←0C;
T←(SalIOCB)+(ADD[RdcIOCBclientLabel!,4]C);
OddPStore4[SalZero,SalLabel4];
*Initialize the rest of the Germ IOCB.
Call[SalInitIOCB];
SalState←SalVfirstRead;*Set to fixup clientLabel from first diskLabel.
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)+(RdcIOCBdataPtr);
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 insert a bootPhysicalVolume request into Germ’s MDS.
T←GermMDSPage;
Call[SalBaseRegisterFromPage];*On return, SalBaseLo and Hi point to Germ MDS
SalBaseLo←(SalBaseLo)+(AND[LoByteMask,GermRequestOffset]C);
SalBaseLo←(SalBaseLo)+(AND[HiByteMask,GermRequestOffset]C);
SalTemp←bootPhysicalVolume;* Action requested is bootPhysicalVolume.
PStore1[SalBaseLo,SalTemp,ReqActionOffset];
SalTemp←SA4000DeviceType;
PStore1[SalBaseLo,SalTemp,ReqDevTypeOffset];
SalTemp←0C;
PStore1[SalBaseLo,SalTemp,ReqDevOrdinalOffset];
GermRequestCopied:
*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;
NOP;
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.
xfTemp ← 0C;*Start loading at word 0.
LU←(SalState) XOR (ReadInitial);
LP←CSDataAddress,*Set low base pointer to point to first data word.
Skip[ALU=0];*If Alto
LP ← (LP) + (400C);*If Pilot, skip first .eb format page.
LoadPageExternal[LRJPage];
RTemp1←0C,*RTemp is even iff start address is to be believed.
GoToExternal[AND[LRJContinue,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)+(RdcIOCBoperationCommand);
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];
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.
*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.
T←(SalIOCB)+(RdcIOCBclientHeader);
OddPFetch2[SalZero,SalCylinder];
T←(SalIOCB)+(RdcIOCBoperationClientHeader);
OddPStore2[SalZero,SalCylinder];
*
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)+(RdcIOCBcontrollerCommand);
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.
LU←(SalState) XOR (SalVfirstRead);*Check for first read of this file.
GoTo[SalFirstReadFixup,ALU=0];*If first read of this file.
SalTemp←0C;
SalCheckNextLabelWord:
*Check and see if the first seven words of the ClientLabel match the first seven words of the DiskLabel..
T←(SalIOCB)+(RdcIOCBclientLabel);
T←(SalTemp) + T;
*Word 0 through 6
OddPFetch1[SalZero,SalClientLabelWord];
T←(SalIOCB)+(RdcIOCBdiskLabel);
T←(SalTemp) + T;
*Word 0 through 6
OddPFetch1[SalZero,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
SalLabelFixup:
*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.
T←(SalIOCB)+(ADD[RdcIOCBdiskLabel!,7]C);
OddPFetch1[SalZero,SalBootChainLink];
T←(SalIOCB)+(ADD[RdcIOCBclientLabel!,7]C);
OddPStore1[SalZero,SalBootChainLink];
SalTemp←1C;
T←(SalIOCB)+(RdcIOCBpageCount);
OddPStore1[SalZero,SalTemp],*Set to read one page.
GoTo[SalRetryRun];
SalFirstReadFixup:
*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.
T←(SalIOCB)+(RdcIOCBdiskLabel);
OddPFetch4[SalZero,SalLabel4];
T←(SalIOCB)+(RdcIOCBclientLabel);
OddPStore4[SalZero,SalLabel4];
T←(SalIOCB)+(ADD[RdcIOCBdiskLabel!,4]C);
OddPFetch1[SalZero,SalLabel4];
T←(SalIOCB)+(ADD[RdcIOCBclientLabel!,4]C);
OddPStore1[SalZero,SalLabel4];
T←(SalIOCB)+(ADD[RdcIOCBdiskLabel!,6]C);
OddPFetch1[SalZero,SalLabel4];
T←(SalIOCB)+(ADD[RdcIOCBclientLabel!,6]C);
OddPStore1[SalZero,SalLabel4];
*Continue reading the run of pages starting with retrying this page.
SalState←SalVnotFirstRead;
GoTo[SalRetryRun];
SalEndRunLabelFixup:
*We are at the end of the file, or the run of pages has an interruption. We know this because we came here on goodCompletion. GoodCompletion 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.
T←(SalIOCB)+(ADD[RdcIOCBdiskLabel!,7]C);
OddPFetch1[SalZero,SAlHeadSector];*Fetch packedDiskAddress from IOCBdiskLabel. -1 signals eof.
T←(zero)-1;
LU←(SAlHeadSector) XOR T;*Test for -1 in last word
GoTo[SalEndOfRun,ALU=0];*If end of file
*Continue if not end of file. Working with an interrupted run of pages.
Call[SalUnpackDiskAddress];*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.
T←(SalIOCB)+(RdcIOCBpageCount);
SalTemp←10000C;
OddPStore1[SalZero,SalTemp];*Set to read a lot of pages.
T←(SalIOCB)+(RdcIOCBclientHeader);
OddPStore2[SalZero,SalCylinder];
T←(SalIOCB)+(RdcIOCBoperationClientHeader);
OddPStore2[SalZero,SalCylinder];*Changes to the IOCB to support 48bit processor-id’s created two copies of header.
*Set BootChainLink backto zero.
SalBootChainLink←0C;
T←(SalIOCB)+(ADD[RdcIOCBclientLabel!,7]C);
OddPStore1[SalZero,SalBootChainLink];
GoTo[SalRetryRun];
SalUnpackDiskAddress:
*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.
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,*Repack HeadSector.
RETURN;
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];