% File SA4000Boot.mc
Last edit by Ev Neely August 26, 1980 11:49 AM Check for end of boot chain so InitialPilot will not need dummy file after it.
Last edit by Ev Neely Aug 15, 1980 3:57 PM New ".sb" format
Last edit by Ev Neely Aug 13, 1980 9:35 AM 48PID
Last edit by HGM March 8, 1980 1:55 PM
Bold down FatalRdcError.
Last edit by HGM March 6, 1980 6:51 PM
Initial MP, touch up find device, retry some errors, emulator timeout,
Fixup interface to EtherBoot
Last edit by Jim Frandeen March 2, 1980 2:02 PM assure Font 0
Last edit by Jim Frandeen February 13, 1980 12:50 PM
%****************************************************************
Insert[PromDefs];
Title[SA4000Boot];
*RDC CONTROLLER REGISTER DEFINITIONS
Set[RdcAddr,LSHIFT[RdcTask,4]];*Device address of RDC
Set[RdcGeneralReset,0];*General Reset
Set[RdcStatus,1];*Controller and drive status
Set[RdcInput,17];*Presents Buffer[RdcMemBuffAdr]
MC[RdcInputAddress,ADD[RdcAddr,RdcInput]];
Set[RdcDrive/Head,1];*Drive is in bits 12-13
*Head is in bits 14-17
Set[RdcErrorReset,2];*Error reset
Set[RdcDevOp,3];*Disk commands and allow wake
Set[RdcOutput,4];*write to buffer[RdcMemBufAddr]
Set[RdcMemBuffAdr,6];*current pointer into the Rdc buffer
Set[RdcPrimeIData,7];*Must be accessed before reading from the RdcBuffer
*CONSTANTS
MC[RdcMaxHead, 7];*max head address for boot chain address.
MC[RdcMaxSector, 33];*max head address for boot chain address.
MC[RdcDevSelOK, 20];*Device is selected and ready.
MC[RdcIDHigh,1400];*Upper half of Rdc ID
MC[RdcAllowWake,4000];
MC[RdcSeek-D,6000];*Seek in negative direction plus allow wake
MC[RdcReadLabelAndData,122];
MC[RdcAllowWakeAndSeekDirection,5000];
MC[RdcSeekComplete,40];
MC[RdcTrack0,100];
MC[RdcSeekTimeOutWakeUps, 1000];
MC[RdcHeadSettlingTimeWakeUps,37];
MC[RdcSectorTimeOutWakeUps,50];
*Microcode begins at [0,1,0] and continues on sequential sectors. Bad pages are simply skipped.
MC[FirstCylinder, 0];
MC[FirstHeadSector, 400];
Set Task[BootTask];
OnPage[DiskBootPage];
SET[InitRdcLoc,ADD[LSHIFT[InitPage,10],0]];*Must be first word of a page in order for APC&APCTask to fit in 8 bits.
MC[InitRdcAPC&APCTask,ADD[LSHIFT[RdcTask,14],InitRdcLoc]];
*Come here from Boot
DiskBoot:
ResetMemErrs;
btTemp←InitRdcAPC&APCTask;
APC&APCTask←btTemp,
Call[DiskPageTaskSwitch];
%
A full control store is 3*4096/256 = 48 pages, or 2 revolutions. Thus we can retry quite a few times without slowint anything down much. (The main delay is probably waiting for the arm to get back to cyl 0.)
%
btTemp1 ← 100C;* Adjusted to be a second or two
DiskEmulatorLoop:
btTemp ← 60000C;
NOP;
Call[DiskPageTaskSwitch];
btTemp ← (btTemp)-1, GOTO[.-2,R>=0];
btTemp1 ← (btTemp1)-1, GOTO[DiskEmulatorLoop,R>=0];
* Timeout, go try Ether
LoadPage[DiskBootPage3];
GoToP[DiskTimedOut];
DiskPageTaskSwitch:
RETURN;
Set Task[RdcTask];
OnPage[InitPage];
*Definitions that depend on this page address:
*SET[InitRdcLoc,ADD[LSHIFT[DiskBootPage1,10],0]];*Defined above
*MC[InitRdcAPC&APCTask,ADD[LSHIFT[RdcTask,14],InitRdcLoc]];*Defined above
InitRdc:
LoadPage[SetPanelPage],* Show the world that we got this far
AT[InitRDCLoc];
T←MPCodeStartRdcBoot,
CallP[SetPanel];
*Find the Rdc and initialize its task to RdcTask. 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
RdcCount←(77C);*16=64/4 decimal max controllers
RdcCount←(RdcCount)-1,
GenSRClock,*Shift out of bit 17 of T
GoTo[.,ALU>=0];*Do this 64 times.
* Note that this is testing after shifting out each bit rather than every 4 bits.
* It seems to work ok. /HGM
RdcCount←(100C);
T←RdcRdcTask←(RdcTaskNumberComplement);
InputControllerID:
*RdcTask will be shifted out. The value in the Controller task register is in complement form.
GenSRClock;*Shift next bit from T[17]
Input@[RdcControllerID,0];*Read ID from controller
RdcControllerID←(LHMask[RdcControllerID]);
LU←(RdcControllerID) XOR (RdcIDHigh);
RdcCount←(RdcCount)-1,
GoTo[RdcFound,ALU=0];*If RdcControllerID=RdcID
T←RdcRdcTask←RSH[RdcRdcTask,1],
GoTo[InputControllerID,ALU#0];*If not last controller
NoRdcController:
*Continue if the Rdc controller cannot be found.
LoadPage[DiskBootPage3];
RdcMPCode←MPCodeNoRdcController,
GoToP[FatalRdcError];
RdcFound:
*We have found the Rdc Controller and initialized its task register so that we can talk to it. Initialize the Controller and all registers that need to be initialized. Initialize HeadSector to the first disk address to read. The first cylinder will be zero.
RdcZero←0C;
LoadPage[DiskBootPage2];
Output@[RdcZero,RdcGeneralReset],*Reset the Controller
GoToP[StartSA4000Boot];
OnPage[DiskBootPage2];
StartSA4000Boot:
*Check to be sure the device is ready. We must wait for two sector pulses to go by before we check. Also come back here to retry various errors.
Output@[RdcZero,RdcDrive/Head];
RdcTemp←(Zero)+1;
RdcTemp←(RdcTemp)+1,
GoTo[.,ALU#0];
Input@[RdcDiskStatus,RdcStatus];*Get status from Controller
LU←(RdcDiskStatus) AND (RdcDevSelOK);
GoTo[DiskReady,ALU#0];
*Continue if the disk is not ready.
LoadPage[DiskBootPage3];
RdcMPCode←MPCodeDiskNotReady,
GoToP[FatalRdcError];
DiskReady:
RdcWhichWord←(-400C);*Set to skip first 256 words
RdcHeadSector←FirstHeadSector;
RdcChecksum←0C;*Initialize checksum
RdcHeadSettleCount←RdcHeadSettlingTimeWakeUps;
Output@[RdcZero,RdcGeneralReset];*Reset the Controller
RdcCommand←RdcAllowWake;
SendAllowWake:
Output@[RdcCommand,RdcDevOp];*Send allow wake
RdcSectorTimeOutCount←RdcSeekTimeOutWakeUps;
Recalibrate:
RdcTemp←20C,*For seek delay
Call[DiskBootPage2Strobe];*Clear wakeup
*Continue here after the next sector wakeup. See if we are at track 0 yet.
Input@[RdcDiskStatus,RdcStatus];*Get status from Controller
LU←(RdcDiskStatus) AND (RdcTrack0);
RdcCommand←RdcSeek-D,*Seek in negative direction
GoTo[RecalibrateComplete,ALU#0];
*Continue if we are not at track zero yet. See if the seek has completed.
LU←(RdcDiskStatus) AND (RdcSeekComplete);
RdcSectorTimeOutCount←(RdcSectorTimeOutCount)-1,
GoTo[Seek,ALU#0];*If seek has completed
*The seek has not yet completed. Check to see if the seek has timed out.
GoTo[Recalibrate,ALU#0];*If seek has not timed out
*Continue if the seek has timed out.
LoadPage[DiskBootPage3];
RdcMPCode←MPCodeSeekTimeOut,
GoToP[FatalRdcError];
Seek:
Output@[RdcCommand,RdcDevOp];*Send seek command
*Now we need to delay at least one microsecond before issuing another Output command.
RdcTemp←(RdcTemp)-1,*Delay loop;
GoTo[.,R>=0];
*Turn off the seek bit in the command, but leave AllowWake and Direction bits on.
RdcCommand←(RdcCommand) AND (RdcAllowWakeAndSeekDirection),
GoTo[SendAllowWake];
RecalibrateComplete:
RdcHeadSettleCount←(RdcHeadSettleCount)-1;
RdcSectorTimeOutCount←RdcSectorTimeOutWakeUps,*We try every sector until we find the right one.
Skip[ALU=0];
GoTo[Recalibrate];
LoadPage[DiskBootPage1];
GoToP[ReadSector];
DiskBootPage2Strobe:
IOStrobe,
GoTo[DiskPage2TaskSwitch];
OnPage[DiskBootPage1];
ReadSectorAgain:
*Come here from HeaderIOAtten to read the sector again.
Call[RdStrobe];*Wait for the next sector wakeup.
ReadSector:
*Continue here at the next sector wakeup when it is time to read a sector. RdcHeadsector has the disk address of the next sector to read. We try every sector until we find the right one. First, select the drive and head we are about to access.
T←(LDF[RdcHeadSector,0,10]);*Head is in bits 14-17
RdcTemp←T;*Drive is in bits 12-13
*Assume drive 0
Output@[RdcTemp,RdcDrive/Head];*send drive and head
RdcCommand←RdcReadLabelAndData;
RdcCommand←(RdcCommand) OR (RdcAllowWake);
Output@[RdcCommand,RdcDevOp];*Send read command
Output@[RdcZero,RdcMemBuffAdr];*Set MemBufAdr to zero
RdcZero←RdcZero;*Interlock Output. NOTE: it does not work without this interlock!
Output@[RdcZero,RdcOutput];*Send cylinder 0
Output@[RdcHeadSector,RdcOutput];*Send head sector
*Now fill the label area with zero. We must fill the label area of the controller buffer with something; otherwise we will get IOAtten.
RdcTemp←15C;*Loop 14 Decimal times
FillLabelArea:
*This NOP is to allow the write of Temp to complete; otherwise the bypassed value written is the result of the base register addition of the Output instruction. This is D0Gotcha number 11, which got me again!
NOP;
RdcTemp←(RdcTemp)-1;
Output@[RdcZero,RdcOutput],
GoTo[FillLabelArea,ALU>=0];
Call[RdStrobe];*Turn off wakeup
*Continue here when the Controller has read the header field from the disk into its buffer. Wait one instruction before testing IOAtten.
NOP;
RdcSectorTimeOutCount←(RdcSectorTimeOutCount)-1,
GoTo[HeaderAtten,IOAtten];
*Come here when the header has been successfully read from the disk. Now we will get 2 label field field wakeups.
T←17C,*Initialize data wakeup count.
Call[RdStrobe];*Turn off wakeup
*Continue after the first label field wakeup. Ignore this wakeup. Initialize wakeup count to -17 to count up to zero.
RdcWakeupCount←(zero)-T,*WakeupCount= -17
Call[RdStrobe];*Turn off wakeup.
*Continue after the second label field wakeup. The last word of the label contains the packed address of the next boot sector. Assume cylinder zero. Read the last word into HeadSector.
T←15C,
Call[PrimeIData];*Start the controller
Input@[RdcHeadSector,RdcInput];
*Convert packed HeadSector to form needed by the controller.
*Also Test boot chain address to see if it is valid.
T←(zero)-1;*Begin test for eof
LU←(RdcHeadSector) XOR T;
RdcTemp←RdcMaxSector,
Skip[ALU#0];
GoTo[WaitForData];*EOF is valid bootchain
T←(LDF[RdcHeadSector,13,5]);*Unpack sector
LU←(RdcTemp)-(T);*(max sector address) - (sector address)
RdcTemp←RdcMaxHead,
GoTo[InvalidBootAddress,ALU<0];*If sector is too big
RdcHeadSector←(LDF[RdcHeadSector,10,3]);*Unpack head
RdcHeadSector←(LSH[RdcHeadSector,10]) OR T; *Format HeadSector
T←LDF[RdcHeadSector,0,10];*T=boot chain head address
LU←(RdcTemp)-(T);*(max head address) - (head address)
Skip[ALU<0];*If head is too big
GoTo[WaitForData];*Boot chain link is OK
GoTo[InvalidBootAddress];
InvalidBootAddress:
RdcMPCode←MPCodeBadBootFile;*Boot chain link is bad
GoTo[RetryableRDCError];
WaitForData:
Call[RdStrobe];*Turn off wakeup
*Continue after the first data field wakeup. Set MemBufAdr to 21B. Do PrimeIData to start the Controller.
T←21C,
Call[PrimeIData];
RdcWordCount←17C,*Initialize word count.
GoTo[NextDataWord];
*Now we will get 15 more data wakeups and a sector wakeup. After each wakeup, process 16 words of data.
NextDataWakeup:
RdcWordCount←17C,*Initialize word count.
Call[RdStrobe];
*Continue after the next data wakeup. Test for the sector wakeup following the last data wakeup.
RdcWakeupCount←(RdcWakeupCount)+1,
GoTo[NextDataWord, R<0];
NOP;*Placement for below
Call[RdStrobe];*One more, just to see if it works
*Continue after the 17th wakeup.
GoTo[DataAtten,IOAtten];
*Continue if the page has been successfully read and loaded. Go read the next page.
RdcSectorTimeOutCount←RdcSectorTimeOutWakeUps,
GoTo[ReadSector];
NextDataWord:
LoadPage[DiskBootPage2];
GoToP[GetNextDataWord];
OnPage[DiskBootPage2];
*Definitions that depend on this page address:
SET[RdcWordDisp,ADD[LSHIFT[DiskBootPage2,10],20]];*Arbitrary location for dispatch table
GetNextDataWord:
RdcWordCount←(RdcWordCount)-1,
GoTo[InputNextDataWord,R>=0];*If not last word this wakeup
LoadPage[DiskBootPage1];*If last word this wakeup
GoToP[NextDataWakeup];
InputNextDataWord:
Input@[RdcWord,RdcInput];*RdcWord contains the next word read from the disk.
RdcWhichWord←(RdcWhichWord)+1,
GoTo[RealData,R>=0];*After Skipping the 1st 256 words
GoTo[GetNextDataWord];
RealData:
Dispatch[RdcWhichWord,16,2];
T←RdcWord,*T=word just read
Disp[saWord1];
saWord1:
* Bits 0-13 are the CS address. 7777 means end of file.
* Bits 14-17 are bits 32-35 of the CS word.
RdcWord0←T,
GoTo[GetNextDataWord],
AT[RdcWordDisp,1];
saWord2:
* Bits 0-15 of the CS word
RdcWord1←T,
GoTo[GetNextDataWord],
AT[RdcWordDisp,2];
Word3:
*Bits 16-31 of the CS word.
RdcWhichWord←0c,
AT[RdcWordDisp,3];
LoadPage[DiskBootPage];
RdcWord2←T,
CallP[LoadCS];
*On return, ALUA indicates the return code.
GoTo[GetNextDataWord,ALU=0],
FreezeResult;
GoTo[ChecksumError,ALU<0];
*Come here if the CSAddress is bad, i.e., it would cause a store in the EPROM area..
LoadPage[DiskBootPage1];
RdcMPCode←MPCodeBadCSAddress,
GoToP[RetryableRdcError];
ChecksumError:
*Come here if the computed checksum does not match the checksum from the boot file.
LoadPage[DiskBootPage1];
RdcMPCode←MPCodeChecksumError,
GoToP[RetryableRdcError];
DiskPage2TaskSwitch:
RETURN;
OnPage[DiskBootPage1];
PrimeIData:
*This subroutine sets MemBuffAdr to the value in T and primes the Controller by Output to PrimeIdata. We then interlock to insure that the Output has been completed.
RdcTemp←T;*Value to write to MembuffAdr
Output@[RdcTemp,RdcMemBuffAdr];*Set MemBuffAdr
Output@[RdcTemp,RdcPrimeIdata];*Start the Controller.
RdcTemp←RdcTemp,*Wait for Output
UseCTask,
GoTo[DiskPage1TaskSwitch];
*Terminate the wakeup and go to sleep until the next sector wakeup. Note: This does not work if IOStrobe and RETURN are on the same statement. This is a mystery to me.
RdStrobe:
IOStrobe,
GoTo[DiskPage1TaskSwitch];
DiskPage1TaskSwitch:
RETURN;
*ERRORS COME THROUGH HERE
HeaderAtten:
*Come here if we get header IOAtten. This is usually because we are not at the right sector yet.
GoTo[HeaderRetry,ALU=0];*If sector time out
Output@[RdcTemp,RdcErrorReset],
GoTo[ReadSectorAgain];*Try again after the next sector wakeup.
HeaderRetry:
NOP;
DataAtten:
RdcMPCode←MPCodeReadError;
RetryableRdcError:
*Come here if a reasonable error occurs. That includes things like bad CS addr since we have not had a chance to check the CRC on this page yet. RdcMPCode contains the value to display in the maintenance panel. Display the error code in the Maintenance Panel and then try again. This gives us a chance to recover from a transient read error. If the error persists, the retry count will run out or the emulator task will timeout.
Output@[RdcZero,RdcGeneralReset];
LoadPage[SetPanelPage];
T←RdcMPCode,
CallP[SetPanel];
LoadPage[DiskBootPage2];
GoToP[StartSA4000Boot];
OnPage[DiskBootPage3];
Set[DiskTimedOut2Loc,ADD[LSHIFT[DiskBootPage3,10],70]];
Set[DiskTimedOutAPC&APCTask,ADD[LSHIFT[RdcTask,14],DiskTimedOut2Loc]];
Set[GetToEtherLoc,ADD[LSHIFT[DiskBootPage3,10],72]];
SetTask[0];
DiskTimedOut:
btTemp←AND[377,DiskTimedOutAPC&APCTask]C;
btTemp←(btTemp) OR (AND[177400,DiskTimedOutAPC&APCTask]C);
APC&APCTask←btTemp,
GOTO[DiskPage3Return];
SetTask[RdcTask];
DiskTimedOut2:
RdcMPCode←MPCodeRdcTimeout,
GoTo[FatalRdcError],
AT[DiskTimedOut2Loc];
FatalRdcError:
*Come here (in RdcTask) if a fatal error occurs. RdcMPCode contains the value to display in the maintenance panel. Display the error code in the Maintenance Panel. Turn off the RDC Controller. Then transfer (in Task 0) to EtherBoot.
Output@[RdcZero,RdcGeneralReset],
AT[FatalRdcErrorLoc];
LoadPage[SetPanelPage];
T←RdcMPCode,
CallP[SetPanel];
RdcTemp←AND[377,GetToEtherLoc]C;
RdcTemp←(RdcTemp) OR (AND[177400,GetToEtherLoc]C);
APC&APCTask←RdcTemp,
GOTO[DiskPage3Return];
GetToEther:
RdcTemp←IP[RdcMPCode]C,* Need a pointer to RdcMPCode
AT[GetToEtherLoc];
Stkp ← RdcTemp;
Loadpage[EMPage];
T ← Stack,
GOTOP[EtherBoot];
DiskPage3Return:
RETURN;
%
This subroutine loads one word of Control Store. On Entry:
Word0 Bits 0-13 are the CS address. 7777 means end of file.
Word0 Bits 14-17 are bits 32-35 of the CS word
Word1 Bits 0-15 of the CS word
Word2 Bits 16-31 of the CS word
Checksum is the running checksum.
The condition code of ALUA indicates the return code:
ALUA = 0 for a normal return.
ALUA < 0 If the CSAddress is the last address and the checksum is wrong.
ALUA > 0 If the CSAddress is a bad address (i.e., it would destroy the EPROM area).
This subroutine is also used by EtherBoot. EtherBoot must have the same registers allocated. Registers are as follows:
R3:Word0
R7:Word1
R13:Word2
R17:running checksum
R23:temporary used for CSAddress
R27:temporary
%
OnPage[DiskBootPage];
SET[RdcEvenLoc,ADD[LSHIFT[DiskBootPage,10],40]];*Arbitrary even location for CSop
SET[RdcEvenLoc1,ADD[LSHIFT[DiskBootPage,10],42]];*Arbitrary even location for CSop
LoadCS:
T←RdcWord0;
RdcChecksum←(RdcChecksum)+T;
T←RdcWord1;
RdcChecksum←(RdcChecksum)+T;
T←RdcWord2;
RdcChecksum←(RdcChecksum)+T;
T←LDF[RdcWord0,0,14];
RdcCSAddress←T;
LU←(RdcCSAddress) XNOR (170000C);*Test for CSAddress = 7777
GoTo[LoadEOF,ALU=0],*If last address
LU←(RdcCSAddress) AND (300C);*Test for page displacement < 100
GoTo[LoadCSWord,ALU#0],*If not EPROM area
T←(RdcCSAddress) AND (7400C);*T is page number.
*Continue if CSAddress is in a page displacement less than 100. Test to see if the page is one of the EPROM pages.
RdcTemp←LSHIFT[LastEPROMpage,10]C;
LU←(RdcTemp)-T;*(Last EPROM page) - (page to store in)
T←RdcWord0,*T is data for bits 32-35
GoTo[LoadCSWord1,ALU<0];*If address is not in EPROM area
UseCTask;
LU←1C,*ALUA > 0 for error return: CSAddress is in EPROM area
RETURN;
*Continue if the CS address is OK.
LoadCSWord:
T←RdcWord0;*T is data for bits 32-35
LoadCSWord1:
LU←RdcWord1;*ALUA is data for bits 00-15
APC&APCTask←RdcCSAddress;*APC is CS address
WriteCS0&2;*Write bits 32-35, bits 00-15
LU←RdcWord2,*ALUA is data for bits 16-1
AT[RdcEvenLoc];*Must be even location
APC&APCTask←RdcCSAddress;*APC is CS address
WriteCS1;*Write bits 16-31
UseCTask,
AT[RdcEvenLoc1];*Must be even location
LoadCSJump:
LU←0C,*ALUA = 0 for normal return
RETURN;
*Come here when 7777 has been found in the CSAddress.
LoadEOF:
RdcTemp←0C;
Output@[RdcTemp,RdcGeneralReset];*Turn off the controller. NOTE: this also resets the Ethernet controller when this routine is called by Etherboot.
LU←RdcChecksum;*Checksum should be zero
APCTask&APC←RdcWord2,
GoTo[LoadCSJump,ALU=0];*Jump to code if checksum is OK
UseCTask;
LU←(zero)-1,*Set ALU <0 to indicate checksum error
RETURN;
:END[SA4000Boot];