*-----------------------------------------------------------
Title[InitialMain.mc...June 27, 1982  11:26 AM...Taft];
*-----------------------------------------------------------

%
Initial is a microprogram that executes after the first stage of Dorado's
bootstrap process is complete.  During stage 1, the BaseBoard forced the
Bootstrap program into IM, which in turn loaded the Initial microprogram from
the BaseBoard.  Stage two completes the machine initialization. Then it loads
emulator microcode from disk or Ethernet into main memory, and loads and
starts it by means of LoadRam.  The particulars of communication between the
Baseboard and the processor occur in the documentation for Bootstrap.mc

Second Stage Initialization
---------------------------

Second stage initialization performs the following activities:

It reads one last word from the baseboard.  Reading this word should cause
the checksum computed by the bootstrap microcode to go to zero.

It initializes ALUFM.

It sets MCR to disallow hold and wakeup.

It writes RM, STACK, T, and IFUM with valid data -- to prevent parity errors.

It initializes the memory system enough to prevent unexpected errors and to
permit using VM in a simpleminded way.

It sets a bit in the Baseboard, using the manifold system, which notifies the
baseboard that bootstrapping proceeded ok.

It reads the keyboard to decide what emulator to load, and loads that
emulator from disk or Ethernet into main memory.  It then calls LoadRam to
load that emulator into IM/IFUM and start it.
%

* Force Initial to be compacted into the high part of IM, and lock out
* the page containing Bootstrap.
IMReserve[0, 0, LShift[FirstInitialPage, 6]];
IMReserve[BootstrapPage, 0, 100];

TopLevel;
Set[XTask, IP[EMU]];

*-----------------------------------------------------------
* Start here to debug:
*-----------------------------------------------------------
InitialDebug:
	ChkSum← T XOR T, TaskingOff, Branch[Initial1], At[InitialLoc, 3];

*-----------------------------------------------------------
* Enter here from Bootstrap.  Tasking is off.
* Check that the checksum is correct by reading one last word from the
* baseboard (that should force chkSum to zero -- ignoring tag bit).
*-----------------------------------------------------------
Initial: At[InitialLoc],
	CallExternal[ReadBBLoc];	* Read checksum word --
					* that should make computed sum zero

* Must do IFUReset before LoadMCR, because the IFU may have powered up in a state
* such that it drives the MAR bus continuously.
Initial1:
	T← LSH[ChkSum, 1];		* Ignore tag bit
	PD← T, IFUReset;
	T← Add[mcr.noWake!, mcr.disHold!]C, Branch[.+2, ALU=0];

*-----------------------------------------------------------
BootstrapChecksumError:		* Error in microcode loaded from BaseBoard
*-----------------------------------------------------------
	Branch[.], Breakpoint;

*-----------------------------------------------------------
* At this point, the only defined ALU functions are A XOR B, A+1, NOT A, and B.
* Initialize all of ALUFM.  We presume these correspondences:
* "AFMi" where i IN [0..17B] are symbols whose value is the contents of ALUFM
* for each i.
*-----------------------------------------------------------
	RX1← T XOR T, LoadMCR[T, T];	* RX1← 0; disable faults and holds
	RX0← AFM0;			* Value for ALUFM[0]

* RX1 = ALUFM address, RX0 = value to be stored there.
WriteALUF:
	T← DPF[RX1, 4, 11];		* Construct LH of instruction:
	T← T XOR (104C), Call[.+2];	* RSTK=0, ALUF=[RX1], BSEL=1, LC=0, ASEL=4

	ALUFMRW← RX0, ALUF[0], Branch[WriteALUFTable]; * This instruction is modified

	IMLHR0'POK← T;			* Modify the LH of the instruction
	RX1← (RX1)+1, BigBDispatch← RX1, Branch[.-2]; * Execute inst and then dispatch

WriteALUFTable: DispTable[20];
	RX0← AFM1, Branch[WriteALUF];
	RX0← AFM2, Branch[WriteALUF];
	RX0← AFM3, Branch[WriteALUF];
	RX0← AFM4, Branch[WriteALUF];
	RX0← AFM5, Branch[WriteALUF];
	RX0← AFM6, Branch[WriteALUF];
	RX0← AFM7, Branch[WriteALUF];
	RX0← AFM10, Branch[WriteALUF];
	RX0← AFM11, Branch[WriteALUF];
	RX0← AFM12, Branch[WriteALUF];
	RX0← AFM13, Branch[WriteALUF];
	RX0← AFM14, Branch[WriteALUF];
	RX0← AFM15, Branch[WriteALUF];
	RX0← AFM16, Branch[WriteALUF];
	RX0← AFM17, Branch[WriteALUF];

*-----------------------------------------------------------
* Now we can use all the "default" ALUF functions.
* Initialize RM. and all four stacks.
* The Notify instructions are logically part of the task initialization code below;
* it doesn't hurt to do them earlier or multiple times, and it saves microcode.
*-----------------------------------------------------------

* Write into all words of RM to set correct parity.
	T← (RX1)-1;			* T← 17 -- last inst of the dispatch table
RMInitL:
	RX17← RBase← T;
	RX0← T, IFUReset;
	RX1← T, Notify[1];
	RX2← T, Notify[2];
	RX3← T, Notify[3];
	RX4← T, Notify[4];
	RX5← T, Notify[5];
	RX6← T, Notify[6];
	RX7← T, Notify[7];
	RX10← T, Notify[10];
	RX11← T, Notify[11];
	RX12← T, Notify[12];
	RX13← T, Notify[13];
	RX14← T, Notify[14];
	RX15← T← T-1, Notify[15];
	RX16← T, Branch[RMInitL, ALU>=0];

* Correct parity in all words of STK (but don't change the data, in case
* STK holds a boot parameter).  Leave StkP=0 (value assumed below).
	RX17← T← 377C;			* Init StkP value
STKInitL:
	T← T-1, StkP← T;
	StackNoUfl← StackNoUfl, Branch[STKInitL, ALU>=0];

*-----------------------------------------------------------
* Initialize all of IFUM with correct parity.
* This is not strictly necessary, but it prevents Midas's PEScan operation
* from turning up zillions of bogus errors if the emulator that gets loaded
* doesn't provide data for all of IFUM (as none do).
*-----------------------------------------------------------

	RScr← 101777C;			* Start with InsSet=3, BrkIns=377
IFUMInitL:
	InsSetOrEvent← RScr;		* B[0]=1 loads InsSet from B[6..7]
	T← LSH[RScr, 10];		* BrkIns loads from B[0..7]
	RScr← (RScr)-1, BrkIns← T;
	RM0← T← A0;			* 1-cycle delay required
	IFUMLH← T;			* Load both halves of IFUM with zero
	IFUMRH← T;
	T← A0, B← T, RScr, Branch[IFUMInitL, R<0]; * Let it settle

*-----------------------------------------------------------
* Bootstrap the memory system:
* Set ProcSRN to zero and reset the IFU.
* Set CFlags to vacant (puts proper parity into A-mem, too).
* Kick-start the automata
* Initialize the map w/ identity (virtual page i = physical page i).
* Set TSyn to perform error correction
* The task initialization code below completes the memory system
* initialization by placing proper parity in MD* and getting the tags
* initialized.
*-----------------------------------------------------------

BootMem:
	ProcSRN← T, StkP+3;		* StkP← 6 to protect boot parameter
	NoReschedule, StkP+3, Call[ClearCacheFlags];
	Call[PresetMap];
	Notify[16], Call[ClearCacheFlags];
	RScr2← 200C, Call[SetTestSyn];
	Notify[17], Call[ClearCacheFlags];
	Call[WaitForMapBuf];

	RScr← A0, Cnt← 17S;		* do numerous mem ops
	B← FaultInfo';			* Reset error indications
	T← 40C, Call[LongWait];		* wait for memory
	RScr← (Store← RScr)+1, DBuf← RScr, Branch[.-2, Cnt#0&-1];

*-----------------------------------------------------------
* For each task in [1..17] this code sets TPC to TaskInit and wakes up the task.
*-----------------------------------------------------------

* Make sure IOReset is on before enabling tasking.  This is not necessary
* during normal booting, but it makes debugging this code a lot easier.
	T← 2000C;
	T← T OR (251C), Call[WriteManifold];

	T← (RM0)+1;			* Start with task 1
TaskInitLoop:
	RScr← -17C, Call[GetTaskInitPC]; * Link← TaskInit
	PD← (RScr)+(LdTPC← T);
	T← T+1, Branch[TaskInitLoop, ALU<0];

* Now allow it all to happen.
* Note that all of the "Notify"s have been executed already, so as soon as
* tasking is turned on, all the tasks will wake up.
	TaskingOn, Branch[BlessBaseBoard];

*-----------------------------------------------------------
* This code is executed by each task and initializes all task-specific
* registers: T, RBase, MemBase, TIOA, MD.
*-----------------------------------------------------------

Subroutine;
GetTaskInitPC:
	TIOA← RM0, CoReturn;		* Zero emulator's TIOA
TopLevel;
Set[XTask, 1];
TaskInit:
	T← A0;
	Pointers← T;			* RBase and MemBase
	RScr← BRHi← T;
	BRLo← T, Call[ResetTags];	* will fix MD parity, too.
	TIOA← RScr, Block, Branch[.];	* TIOA← 0

Set[XTask, 0];

*-----------------------------------------------------------
* Tell the baseboard that we believe we've successfully initialized
* everything.  Do this by placing 2261 in the DMux address register and
* performing UseDMD function.  Then we must reinitialize the various errors
* (Initial runs with errors like RM and STACK parity errors disabled).
*-----------------------------------------------------------

BlessBaseBoard:
	T← mcr.noWake, Call[SetMCR];	* Now allow holds, etc., but
					*  no fault task wakeups
	RScr← T← 2000C;
	T← T OR (261C), Call[WriteManifold]; * manifold 2261 => boot ok
	T← (RScr) OR (253C), Call[WriteManifold]; * 2253 => turn off IOReset.
	T← 77C, Call[WriteManifold];	* 0077 => enable all errors
	Branch[BootEmulator];		* Now go find and boot an emulator

*-----------------------------------------------------------
WriteManifold:
* Enter with T = address to strobe using UseDMD.
* Uses Cnt, T.
*-----------------------------------------------------------
Subroutine;

	Cnt← 13S;
WriteManLp:
	PD← A0, MidasStrobe← T;
	PD← T-T-1, Branch[., ALU=0];	* Delay 2 cycles
	T← T LSH 1, Branch[WriteManLp, Cnt#0&-1];
	UseDMD, Return;

TopLevel;

*-----------------------------------------------------------
* Now time to find and bootstrap an emulator from disk or Ethernet.
* First, initialize I/O tasks we will need, and do other emulator-related
* initialization required to operate the I/O tasks.
*-----------------------------------------------------------

BootEmulator:
	RBase← RBase[AEmRegs];
	R400← 400C;

* Zero out first 64K, and use it for all memory accesses:
	T← A0, MemBase← IOBR;
	BRLo← T;
	PD← (BRHi← T)+1;		* ALU#0
	T← (Store← T)+1, DBuf← 0C, Branch[., ALU#0];

* Note: Ethernet tasks are initialized later if needed
	TaskingOff, Call[DisplayInitConfig]; * Figure out how terminal is hooked up
	Call[THTInitPC];		* Init DHT or AHT to read keyboard
	LdTPC← T, RBase← RBase[RTClock];
	Call[DSKInitPC];
	LdTPC← T, Wakeup[DSK];
	Wakeup[JNK], Call[JNKInitPC];	* Init Junk for RTC maintenance
	LdTPC← T, TaskingOn;

*-----------------------------------------------------------
* Microcode booting conventions:
* If a boot file parameter has been supplied (see below) then boot according to it.
* If one of the microcode boot keys (see below) is pressed down, then
* boot the selected microcode from the Ethernet (halt if fail).
* If none of the keys is down, then attempt to boot microcode from the
* Initial region of the disk; if that fails, boot Alto/Mesa microcode
* from the Ethernet.
*-----------------------------------------------------------

* Wait at least 100 ms to allow terminal to update all keyboard words.
* There are 5 of these, sent at 17 ms intervals.
	T← (RTClock)+(6000C);		* Now + 6000B 32-microsecond ticks
	PD← T-T-1, Cnt← 1S, StkP-3;	* ALU<0, StkP← 3
	PD← (RTClock)-T, Branch[., ALU<0];

* Check to see whether we have a valid boot parameter.
* If STK[2] = BootParameterSeal and STK[1]+STK[2]+STK[3] = 0, then use STK[1]
* as the microcode boot file number, and boot that file from the Ethernet.
	T← Stack&-1, RBase← RBase[AEmRegs]; * T← STK[3]
	T← (Stack&-1)+T, Branch[., Cnt#0&-1]; * STK[3]+STK[2]+STK[1]
	T← HighByte[BootParameterSeal], StkP+2, Branch[ReadBootKeys, ALU#0];
	T← T OR (LowByte[BootParameterSeal]); * STK[2] = BootParameterSeal?
	PD← (Stack&-1) XOR T, Stack&-1← MD; * Smash the seal after checking
	T← Stack, Branch[DoEtherMicrocodeBoot, ALU=0];
	Nop;

ReadBootKeys:
	T← (R400) XOR (177434C);	* VM 177034 = first keyboard word
	T← (Fetch← T)+1;
	T← (Fetch← T)+1, ETemp0← MD;	* Remember the keys are complemented
	T← (Fetch← T)+1, ETemp1← MD;
	T← (Fetch← T)+1, ETemp2← MD;
	T← A0, ETemp3← MD, ETemp0, Branch[EtherBooting, R even]; * BS down?

* Look at these keys only if BS is NOT down, since some of them conflict
* with the boot file number for booting Alto programs from the Ethernet.
	PD← LSH[ETemp3, 7], Call[CheckKey];	* [0] M Alto/Mesa
	PD← LSH[ETemp1, 4], Call[CheckKey];	* [1] S Smalltalk
	PD← LSH[ETemp1, 12], Call[CheckKey];	* [2] L Lisp
	PD← LSH[ETemp2, 5], Call[CheckKey];	* [3] C Cedar
	T← A0;

* Look at alternate keys, regardless of whether or not BS is down.
EtherBooting:
	BootDataPtr← 1000C;		* Here for placement
	PD← LSH[ETemp2, 14], Call[CheckKey];	* [0] RETURN Alto/Mesa
	PD← LSH[ETemp3, 14], Call[CheckKey];	* [1] right-SHIFT Smalltalk
	PD← LSH[ETemp3, 12], Call[CheckKey];	* [2] [ Lisp
	PD← LSH[ETemp3, 13], Call[CheckKey];	* [3] = Cedar
	PD← LSH[ETemp3, 1], Call[CheckKey];	* [4] T Test

* BootEmulator (cont'd)

* No key down.  First attempt disk microcode boot.
	SCall[DiskHardMicrocodeBoot];
	 T← A0, Branch[.+2];		* Failed, give up on the disk
	ETemp0← A0, Call[CheckChecksumAndLoad]; * Load if checksum is OK (returns T=0)

* Boot microcode from the Ethernet.  Desired boot file number is T+110B.
GotBootKey:
	Stack← T← T+(110C);		* The desired boot file number
DoEtherMicrocodeBoot:
	BootDataPtr← 1000C, SCall[EtherMicrocodeBoot]; * Boot microcode from Ethernet
	 Branch[MicrocodeBootFailed];	* Failed, give up
	ETemp0← A0, Call[CheckChecksumAndLoad]; * Load if checksum is OK
	T← Stack;			* Bad checksum; try loading it once more
	BootDataPtr← 1000C, SCall[EtherMicrocodeBoot];
	 Branch[MicrocodeBootFailed];	* Failed, give up
	ETemp0← A0, Call[CheckChecksumAndLoad]; * Load if checksum is OK

MicrocodeBootFailed:
	Breakpoint, Branch[BootEmulator];


*-----------------------------------------------------------
CheckKey:
* Subroutine to assist in boot key detection.
* Increments T and returns if ALU<0 (selected key is up).
* Uses T+110 as the boot file number if ALU>=0 (selected key is down).
*-----------------------------------------------------------
Subroutine;

	Branch[GotBootKey, ALU>=0];
	T← T+1, Return;


*-----------------------------------------------------------
CheckChecksumAndLoad:
* Checks the checksum of a loaded microcode image, and if it is OK then
* call LoadRam on it, never to return.  Returns only if the checksum is not OK.
* Enter: BootDataPtr = address of last word loaded +1
*	ETemp0 = 0
* Returns with T=0, if it returns at all.
* Clobbers T, BootDataPtr, ETemp0
*-----------------------------------------------------------
Subroutine;

	T← (BootDataPtr)-(1000C)-1;	* T← (# words loaded)-1
	BootDataPtr← (BootDataPtr)-(Cnt← T)-1; * BootDataPtr← 1000, Cnt← (# words loaded)-1

* Not the world's fastest checksum loop, but who cares?
	BootDataPtr← (Fetch← BootDataPtr)+1;
	ETemp0← (ETemp0)+MD, Branch[.-1, Cnt#0&-1];

	T← 1000C, Branch[.+2, ALU=0];	* Done checksumming; OK?
	T← A0, Return;			* No
TopLevel;
	LRFlag← A0, BRLo← T, Branch[LoadRam]; * Yes, dive into it!