% File Boot.mc
Last edit by HGM March 5, 1980 11:32 AM,
GOTO[.] on Boot,
ZapDevices when clearing timers,
Set Traps for strange Wakeups,
Change Hang to Dally and reBoot, lengthen Dally a bit.
Last edit by Jim Frandeen March 2, 1980 1:53 PM Set everything to Font 0
Last edit by Jim Frandeen February 22, 1980 12:16 PM H4 parity errors for Model 31
Last edit by HGM February 14, 1980 11:35 PM,
lights and dally into fault handler
Last edit by Jim Frandeen February 14, 1980 8:27 AM
Reorganized by Jim Frandeen January 26, 1980 11:11 AM. Unscramble Processor ID and store in CS location 7777. Handle H4 parity errors caused by the Model 31. General cleanup for SA4000 booting.
Created by Chuck Thacker, 1978
%****************************************************************
Insert[PromDefs];
Title[Boot];
Set Task[0];
OnPage[InitialBootPage];
*Definitions that depend on this page address. This will be page 0, but we leave it variable for test purposes.
SET[InitialBootOffset,LSHIFT[InitialBootPage,10]];
:IF[NotMidasTesting];*Don’t use locations 0 and 1 if testing with Midas.
StartBoot:
*We must be careful not to cause an R parity error. This means that we cannot read any Registers until they are loaded. Also, we must avoid a StackOverflow trap by loading Stkp with a known value.
T←StkP←GetRSpec[147],*Load T with 377 from NCIA.
GoTo[StartTest],
AT[0];*Boot starts at location 0.
*Control comes to location 1 if a parity error occurs.
:IF[Model31];***Begin Code for Model 31****************************
*The following code determines whether the H4 parity error was expected. If so, we continue at the location specified by btParityReturn.
ParityError:
LoadPage[0],
AT[1];
ResetErrors;
btParityTemp←10C;*Address for notify
APCTask&APC←btParityTemp;
ResetMemErrs,
RETURN;*Continue below
LU←btParityReturn,*Test for H4 parity error expected
AT[10];
GoTo[ResumeAfterParity,ALU#0];
Boot, GOTO[.];*Reboot
ResumeAfterParity:
*Come here if the H4 parity error was expected. Continue at the location specified by btParityReturn.
APCTask&APC←btParityReturn;
btParityReturn←0C,*Not expecting H4 parity now
RETURN;
:ELSE;**********End Code for Model 31******************************
**********Begin Code For SA4000*************************************
ParityError:* We are in Task 17B now
LoadPage[0],
AT[1];
T ← MPCodeFaultHappened;
T ← (Parity)+(T);* 20 thru 35
ResetErrors, GOTO[SetPanelAndDally];* UnFreeze Result and such
:ENDIF;*********End Code for SA4000********************************
:ELSE;*****Alternate code if MidasTesting so as not to wipe out 0
StartBoot:
GoTo[StartTest];
IMRESERVE[0,0,2];
:ENDIF; * Of MidasTesting
SetPanelAndDally:
Call[SetPanel];
* Dally a short while so somebody can read the lights
Dally:
btTemp1 ← 400C;
btTemp ← 60000C;
btTemp ← (btTemp)-1, GOTO[.,R>=0];
btTemp1 ← (btTemp1)-1, GOTO[.-2,R>=0];
Boot, GOTO[.];
* Reboot to reload ControlStore
*This routine puts the number in T in the Maintenance Panel.
* It does not TASK when it returns.
SetPanel:
ClearMPanel;
IncrementPanel:
btTemp←T;*btTemp=number to put in panel
SetPanelLoop:
btTemp1←4C;*Delay count
btTemp1←(btTemp1)-1,
GoTo[., ALU>=0];*Delay
btTemp←(btTemp)-1;
UseCTask,
GoTo[SetPanelReturn,ALU<0];*If finished
IncMPanel,
GoTo[SetPanelLoop];
SetPanelReturn:
RETURN;
*This section of code performs rudimentary tests to assure that the basic functions of the CPU are working. If an error is detected, put a number in the Maintenance Panel and hang.
StartTest:
*We must be careful not to cause an R parity error. This means that we cannot read a register until we load R3.
LoadPage[TestPage1],
btTemp←T,AT[2];
LU←T,
ClearMPanel,
GoToP[tsStartTest],AT[3];
OnPage[TestPage1];
tsStartTest:
LU←0C;
ResetMemErrs,
GoTo[tsTestNonZero,ALU=0];
tsFail1:
GoTo[NastyError];
tsTestNonZero:
LU←377C;
GoTo[tsLoadR0,ALU#0];
tsFail2:
GoTo[NastyError];
tsLoadR0:
btTemp←T←0C;
LU←T;*Allow load to happen (Don’t test bypass yet).
IncMPanel;*Increment to 1 to indicate that we have gotten started.
LU←(btTemp) XOR (0C);
GoTo[tsLoadT0,ALU=0];
tsFail4:
T←4C,GoTo[SetPanelAndDally1];
tsLoadT0:
LU←(btTemp) XOR (T);
T←btTemp←125C,
GoTo[tsLoadR125,ALU=0];
tsFail5:
T←5C,GoTo[SetPanelAndDally1];
tsLoadR125:
LU←(btTemp) XOR (125C);
GoTo[tsLoadT125,ALU=0];
tsFail6:
T←6C,GoTo[SetPanelAndDally1];
tsLoadT125:
LU←(btTemp) XOR T;
T←btTemp←252C,
GoTo[tsLoadR252,ALU=0];
tsFail7:
T←7C,GoTo[SetPanelAndDally1];
tsLoadR252:
LU←(btTemp) XOR (252C);
GoTo[tsLoadT252,ALU=0];
tsFail8:
T←10C,GoTo[SetPanelAndDally1];
tsLoadT252:
LU←(btTemp) XOR T;
T←btTemp←125000C,
GoTo[tsLoadR125000,ALU=0];
tsFail9:
T←11C,GoTo[SetPanelAndDally1];
tsLoadR125000:
LU←(btTemp) XOR (125000C);
GoTo[tsLoadT125000,ALU=0];
tsFail10:
T←12C,GoTo[SetPanelAndDally1];
tsLoadT125000:
LU←(btTemp) XOR T;
T←btTemp←52400C,
GoTo[tsLoadR52400,ALU=0];
tsFail11:
T←13C,GoTo[SetPanelAndDally1];
tsLoadR52400:
LU←(btTemp) XOR (52400C);
GoTo[tsLoadT52400,ALU=0];
tsFail12:
T←14C,GoTo[SetPanelAndDally1];
tsLoadT52400:
LU←(btTemp) XOR T;
btTemp←177400C,
GoTo[tsRByPass,ALU=0];
tsFail13:
T←15C,GoTo[SetPanelAndDally1];
tsRByPass:
LU←(btTemp) XOR (177400C);
GoTo[tsTByPass,ALU=0];
tsFail14:
T←16C,GoTo[SetPanelAndDally1];
tsTByPass:
T←177400C;
LU←(btTemp) XOR T;
GoTo[tsAluOR,ALU=0];
tsFail15:
T←17C,GoTo[SetPanelAndDally1];
SET[TestRegLoc,LSHIFT[TestPage2,10]];
MC[TestRegNotify,TestRegLoc];
tsAluOR:
btTemp←TestRegNotify;*Notify to insure task 0
APC&APCTask←btTemp,
GoTo[tsReturn];*Continue at tsRegTest
*This completes the tests of the basic CPU functions.
NastyError:
LoadPage[SetPanelPage];
GOTOP[Dally];
SetPanelAndDally1:
LoadPage[SetPanelPage];
GOTOP[SetPanelAndDally];
tsReturn:
RETURN;
*Come here via Notify from tsAluOR when the basic CPU functions have been tested. Now test the register functions. If all goes well, clear the registers except for the registers 0-17, which are not so easy to access with EPROM code.
OnPage[TestPage2];
tsTestReg:
T←btTemp←20C,*Start past Mesa stack to avoid overflow trap.
AT[TestRegLoc];
tsStkWriteLoop:
StkP←btTemp;
Stack←T;*Register n ← n
NOP;*Careful not to cause R parity error
LU←(Stack) XOR T;*Did write work?
T←btTemp←(btTemp)+1,*Increment n
GoTo[tsStkWriteIncr,ALU=0];
tsFail16:
T←20C,GoTo[SetPanelAndDally2];
tsStkWriteIncr:
LU←(btTemp) XOR (400C);*Test for last register
GoTo[tsStkWriteLoop,ALU#0];
T←btTemp←20C;
tsStkReadLoop:
StkP←btTemp;
LU←(Stack) XOR T;
Stack←0C,*Clear all registers
GoTo[tsStkReadIncr,ALU=0];
tsFail17:
T←21C,GoTo[SetPanelAndDally2];
tsStkReadIncr:
T←btTemp←(btTemp)+1;*Next register
LU←(btTemp) XOR (400C);*Test for last register
GoTo[tsStkReadLoop,ALU#0];
%Continue when registers 20-377 have been cleared.
The following code sets the Mesa Stack registers (registers 0-17) to zero in order to prevent a parity error. This won’t be easy because we can’t address all these registers directly (in EPROM code, we can only address registers with 11 in the low order two bits). To make things worse, we can’t address them with the stack pointer either; this would cause a trap. So all this code creates an instruction to set R0 to zero and then increments the RSEL field by one until 17.
The following definitions are for the three parts of a microinstruction that correspond to:
R0←0C,RETURN;
MEMINST=0, RMOD=0, RSEL=110000 (because RSEL.0 and RSEL.1 must be complemented), ALUF=0, BSEL=0, F1=0, F2=0, LR=1, LT=0, JC=110(return), FA=0, Parity=0
%
MC[ZeroReg00To15,30000];*Word 0
MC[ZeroReg16To31,101400];
*Word 1
MC[ZeroReg32To35,0];
*Word 2
*Definitions that depend on this page address:
MC[ClearStackPage,LSHIFT[TestPage2,10]];
MC[ClearStackOffset,50];*Arbitrary page offset
SET[ClearStackLoc,ADD[ClearStackPage!,ClearStackOffset!]];*Arbitrary location
SET[ClearStackEvenLoc1,ADD[ClearStackPage!,42]];*Arbitrary even location for CSop
SET[ClearStackEvenLoc2,ADD[ClearStackPage!,44]];*Arbitrary even location for CSop
ClearRegs0To17:
btCount←0C;*Start with register 0.
btBits32To35←ZeroReg32To35;*Initialize bits 32 to 35
btBits00To15←ZeroReg00To15;*Initialize bits 0 to 15
btBits16To31←ZeroReg16To31;*Initialize bits 16 to 31
btZeroRegAddr←ClearStackPage;*Initialize ClearStack address
btZeroRegAddr←(btZeroRegAddr)+(ClearStackOffset);
ZeroNextRegister:
*Create a microinstruction to zero the next register and store it in ClearStack.
T←btBits32To35;
LU←btBits00To15;
APC&APCTask←btZeroRegAddr;
WriteCS0&2;*Write bits 0 to 15, 32 to 35
LU←btBits16To31,
AT[ClearStackEvenLoc1];*Must be even location for CSop
APC&APCTask←btZeroRegAddr;
WriteCS1;*Write bits 16 to 31
*Now execute the instruction to clear a register.
Call[ClearStack],AT[ClearStackEvenLoc2];*Must be even location for CSop
*Test for last instruction.
LU←(btCount) XOR (17C);
btCount←(btCount)+1,*Increment register number
GoTo[RegistersInitialized,ALU=0];
*Continue if we have another register to initialize. Increment the RSEL field of the microinstruction. Bits 32-33 of the microinstruction are the low order 2 bits of RSEL. They will come from the low order 2 bits of Count.
T←(btCount) AND (3C);
btBits32To35←T;
btBits32To35←LSH[btBits32To35,2];
*Bits 4 and 5 of the microinstruction are bits 2 and 3 of RSEL. These come from the high order 2 bits of Count.
T←(btCount) AND (14C);
btTemp←T;
T←btTemp←LSH[btTemp,10];
btBits00To15←(btBits00To15) AND NOT (6000C);
btBits00To15←(btBits00To15) OR T;
*The parity bit is bit 31. This depends on the number of bits in Count. If the number of bits in Count is odd, the parity is odd.
T←btCount;
btTemp←T;
btBits16To31←(btBits16To31) AND NOT (1C);*Turn off parity bit.
NextParityBit:
LU←btTemp, Skip[R Even];
btBits16To31←(btBits16To31) XOR (1C);
btTemp←RSH[btTemp,1],
GoTo[NextParityBit, ALU#0];
GoTo[ZeroNextRegister];
ClearStack:
UseCTask;
ZeroReg:
btTemp←0C,RETURN,*This is the instruction that gets modified.
AT[ClearStackLoc];
RegistersInitialized:
*Now clear control store.
LoadPage[TestPage3];
btCount←20C,*Set page count
GoToP[ClearCS];
SetPanelAndDally2:
LoadPage[SetPanelPage];
GOTOP[SetPanelAndDally];
OnPage[TestPage3];
SET[ClearCSEvenLoc3,ADD[LSHIFT[TestPage3,10],46]];*Arbitrary even location for CSop
SET[ClearCSEvenLoc4,ADD[LSHIFT[TestPage3,10],52]];*Arbitrary even location for CSop
ClearCS:
btCSAddr←100C;*Don’t clear EPROM area
:IF[NotMidasTesting];*Don’t clear Control Store if testing with Midas.
ClearNextWord:
T←0C;
APC&APCTask←btCSAddr;
WriteCS0&2;
LU←1C,*Put in 1 for good parity
AT[ClearCSEvenLoc3];*Must be even location for CSop
APC&APCTask←btCSAddr;
WriteCS1;
btCSAddr←(btCSAddr)+1,
AT[ClearCSEvenLoc4];
T←LDF[btCSAddr,10,10];*Test for page boundary
GoTo[ClearNextWord,ALU#0];*if not at end of page boundary
btCount←(btCount)-1;*Decrement page count.
GoTo[CSCleared,ALU=0];*If done
btCSAddr←(btCSAddr)+(100C),*Step to next page
GoTo[ClearNextWord];
:ENDIF;*End of clearing ControlStore
CSCleared:
*Continue here when Control Store has been cleared.
*This routine unscrambles the Processor ID and stores in in CS location 7777.
SET[ProcessorIDPage,TestPage3];
SET[ProcessorIDdisp,ADD[LSHIFT[ProcessorIDPage,10],20]];*Arbitrary even location for dispatch
SET[ProcessorIDEvenLoc1,ADD[LSHIFT[ProcessorIDPage,10],30]];*Arbitrary even location for CSop
SET[ProcessorIDEvenLoc2,ADD[LSHIFT[ProcessorIDPage,10],32]];*Arbitrary even location for CSop
SET[ProcessorIDEvenLoc3,ADD[LSHIFT[ProcessorIDPage,10],34]];*Arbitrary even location for CSop
btCSAddr←(AND[377, ScrambledProcessorIDAddress]C);*Set CSAddr to point to the CS location where the Processor ID was loaded from the Prom.
btCSAddr←(btCSAddr) OR (AND[177400, ScrambledProcessorIDAddress]C);
T←btPIDstate←0C;*Start with word 0
ProcessorIDLoop:
APC&APCTask←btCSAddr;
ReadCS;*Read word 0 or word 1 from CS
Dispatch[btPIDstate,16,2],*Dispatch on state.
AT[ProcessorIDEvenLoc1];*Must be even location for CS OP
T←RHMask[CSData],*T contains next byte.
Disp[ProcessorIDByte0];
ProcessorIDByte0:
btTemp←T,*first byte
AT[ProcessorIDdisp,0];
ProcessorIDNextState:
btPIDstate←(btPIDstate)+1;*Next state
T←(btPIDstate) AND (1C),*Get word 0 on even states and word 1 on odd states.
GoTo[ProcessorIDLoop];
ProcessorIDByte1:
btTemp←(LSH[btTemp,10]) OR T,*Merge second byte
AT[ProcessorIDdisp,1];
btCSAddr←(btCSAddr)+1,*only 2 bytes per word
GoTo[ProcessorIDNextState];
ProcessorIDByte2:
btTemp1←T,*Third byte
GoTo[ProcessorIDNextState],
AT[ProcessorIDdisp,2];
ProcessorIDByte3:
btTemp1←(LSH[btTemp1,10]) OR (T),*Fourth (last) byte
AT[ProcessorIDdisp,3];
*This is the state of affairs:
* btTemp left byte = CS location 7475 bits 8-15
* btTemp right byte = CS location 7475 bits 24-31
* btTemp1 left byte = CS location 7476 bits 8-15
* btTemp1 right byte = CS location 7476 bits 24-31
*Now write the Processor ID in the last CS location.
btCSAddr←(AND[377, ProcessorIDAddress]C);*Set CSAddr to point to the CS location where the Processor ID will be saved.
btCSAddr←(btCSAddr) OR (AND[177400, ProcessorIDAddress]C);
T←0C;*Data for bits 32-35 is zero.
LU←btTemp;*Data to be written in word 0
APC&APCTask←btCSAddr;*Address to write Processor ID
WriteCS0&2,*Write word 0
LU←btTemp1;*Load CSIn with next word
APC&APCTask←btCSAddr,*Address to write Processor ID
AT[ProcessorIDEvenLoc2];*Must be even location
WriteCS1;*Write word 1
IncMPanel,*Increment panel to 2 to indicate that all tests are done.
AT[ProcessorIDEvenLoc3];*Must be even location
%
Look to see why we were booted: Midas or Panel are the expected cases.

Note that we will go the wrong way if we get (re)Booted because of a CS or R mem error after a Midas Boot. (Reloading the BootReason register forgets the Midas boot flag.) But that’s ok because Midas will notice that we are not talking to it and yank on it’s wire again.
%
:IF[NotMidasTesting];*If Midas Testing, we will always go DiskBoot.
LU←GetRSpec[157] AND (40C);*Test for tester boot (it is complemented)
GoTo[GoMidas,ALU=0];*Midas boot
:ENDIF;
GoDiskBoot:
LoadPage[ZapPage];
GoToP[ZapDevices];
GoMidas:
LoadPage[MidasBootPage];
GoToP[MidasBoot];

ONPAGE[ZapPage];
%
Clear all the devices just in case. This shouldn’t do anything because they were just reset by the Boot sequence that got us this far, but it can’t hurt. We have not done any TASKing RETURNs yet, so random wakeup requests have not had a chance to do anything horrible.

Note that this doesn’t do anything to old Ethernet boards. (Tough.)
%
ZapDevices:
T ← btTemp1 ← 177400C;
btTemp ← 0c;
ZapLoop:
OUTPUT@[btTemp];*send 0 to register 0
T ← btTemp1 ← (btTemp1) + (20c);
goto[ZapLoop,ALU<0];
*Clear all the timers; otherwise a timer will go off and kill us.
btTemp←100000C;
ClearTimers:
LoadTimer[btTemp];
ResetMemerrs;
btTemp←(btTemp)+1;
LU←(btTemp) AND (17C);
GoTo[ClearTimers,ALU#0];
LU←Timer;* Gobble up any timer that was ready
* Capture all TPCs in case we get a strange wakeup from broken hardware.
SET[TrapSetupLoc,ADD[LSHIFT[TrapPage,10],40]];
SET[SetNextTrapLoc,ADD[LSHIFT[ZapPage,10],42]];
SetTraps:
btTemp ← 60C;* Random scratch register
Stkp ← btTemp;* that won’t cause StackOvfl
Stack ← AND[377,SetNextTrapLoc]C;
Stack ← (Stack) OR (AND[177400,SetNextTrapLoc]C);
btTemp ← AND[377,TrapSetupLoc]C;
btTemp ← (btTemp) OR (AND[177400,OR[TrapSetupLoc,LSHIFT[17, 14]]]C);
Smash:
APC&APCTask ← btTemp;* Notify to TrapSetup
RETURN;
SetNextTrap:
LU ← LDF[btTemp,0,3], AT[SetNextTrapLoc];
btTemp ← (btTemp) - (10000C), GOTO [Smash, ALU#0];
LoadPage[DiskBootPage];
GoToP[DiskBoot];
* These 6 instructions should not get overlayed by initial.
TrapSetup:
CALL [TrapSetupExit], AT[TrapSetupLoc];*Leave TPC here
StrangeTaskRan:
T ← CTask;
btTemp ← T, LOADPAGE[SetPanelPage];
T ← (btTemp)+(MPCodeUnexpectedWakeup), GOTOP[SetPanelAndDally];
TrapSetupExit:
APC&APCTask←Stack;* Notify back to SetNextTrap
RETURN;
OnPage[MidasBootPage];
*Definitions that depend on this page address:
SET[MidasBootEvenLoc1,ADD[LSHIFT[MidasBootPage,10],2]];*Arbitrary even location
SET[MidasBootEvenLoc2,ADD[LSHIFT[MidasBootPage,10],4]];*Arbitrary even location
MidasBoot:
*This is the Midas loader. We come here if the boot reason is Midas Boot. We load the Kernel from the Alto Via the Printer interface and jump to it.
btTemp←(400C);*Set bus for receive
Printer←btTemp;
*Now receive address/data pairs from the Alto. The Address comes first (2 bytes) followed by data (5 bytes). If the address has bit 0 set, jump to that address.
MidasLoad:
btByte←0C,*Get 2 bytes of address
Call[MidasReceive],
AT[MidasBootEvenLoc1];*Must be even location
btCSAddr←T,
GoTo[MidasJump,ALU<0];*If address<0, jump to code
NOP;*Placement constraint
btByte←0C,
Call[MidasReceive];*Get data word 0
btWord0←T;
btByte←0C,*Get data word 1
Call[MidasReceive];
btWord1←T;
btData←0C;*Last byte, clear upper bits
btByte←1C,
Call[MidasReceive];*Get data word 1 (1 byte only)
LU←btWord0;*LoadCSIn, T has word 2
APCTask&APC←btCSAddr;*Address
WriteCS0&2;
LU←btWord1,*Load CSin
AT[MidasBootEvenLoc2];*Must be even location
APCTask&APC←btCSAddr;*Address
GoTo[MidasLoad],
WriteCS1;
*Come here when the address has bit 0 set.
MidasJump:
btCSAddr←(btCSAddr) AND NOT (170000C);*Clear upper bits
T←APCTask&APC←btCSAddr;*Get address to jump to
RETURN;*Jump to address
MidasReceive:
T←Printer,
GoTo[MidasReceive,R>=0];*Look for send strobe
LU←(Printer)-T;*Tell-me-thrice here to increase noise immunity
Skip[ALU=0];
GoTo[MidasReceive];
LU←(Printer)-T;*Tell-me-thrice here to increase noise immunity
Skip[ALU=0];
GoTo[MidasReceive];
btTemp←T;*Printer data and control
T←RHMask[btTemp];*Mask off data bits
T←btData←(LSH[btData,10]) OR T;*Shift and merge
btTemp←2400C;
Printer←btTemp;
WaitForACK:
LU←Printer,
GoTo[WaitForACK,R<0];*Look for bit 0=0
btTemp←400C;*Got it, turn ACK off
Printer←btTemp;
LU←btByte,
UseCTask,
Skip[R Odd];*one or two bytes (least significant byte if 1)
btByte←1C,
GoTo[MidasReceive];*Can’t go direct to MidasReceive
LU←T,
RETURN;*Set condition codes with data, no Task return
:END[Boot];