{
 File name:  EtherInitial.mc
 Description:  Alternate Ethernet Initial microcode (6 boot)
 Last Edited: bj, 12-Apr-86 21:17:46
 Fiala 25-Jul-86 12:35:02
}

{
	Copyright (C) 1981, 1982, 1983 by Xerox Corporation.  All rights reserved.
}


Reserve[ProtectStart, ProtectFence], Reserve[0FE0, 0FFF];	{save room for boot kernel}


{
	Phase0Protected (Protected.mc, IOPBoot.mc) resides in 0 - 00FF
	Phase0 (Phase0.mc, DiskBootDLion.mc, EtherBootDLion.mc) resides in 0100 - 0FDF
	The BootKernel resides in 0FE0 - 0FFF
	Part of the BootKernel that can be overlaid resides in 0FD8 - 0FDF
}

{
	Link register values used in 16-way dispatch InputReturn
}

Set[L6.Host1, 0];
Set[L6.Host0, 1];
Set[L6.Host2, 2];
Set[L6.Sequence, 3];
Set[L6.File1, 4];
Set[L6.File2, 5];
Set[L6.Source2, 6];
Set[L6.Source1, 7];
Set[L6.DontUseThisValue, 8];
Set[L6.Checksum, 0A];
Set[L6.Length, 0C];
Set[L6.PacketType, 9];
Set[L6.Source0, 0E];

{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	The Laws of the Ethernet Hardware:

	EICtl ← and EOCtl ← can occur in any cycle.
	They must occur in c1 or c2  when turning off wakeups.
	
	← EIData must occur in c2.
	When read in c3, it retrieves the previous input word.

	EOData ← can occur in any cycle.
	EStrobe for throwing out input packet must occur in c2.
	EStrobe for writing the data from EOData into the FIFO must occur in c1 or c3.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}

SetTask[0];

{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	This code is entered as a subroutine of CoreInitial for Ethernet booting.
	
	CoreInitial passes control to InitDLion, which in turn passes control here.
	This code runs concurrently with EtherInitial task 2 and Protected and
	they communicate through the uEtherBootDone, uEtherBootStatus,
	and uTimeout registers and EICtl.
	
	This module basically sets up some things for EtherInitial task 2,
	and then when task 2 has retrieved Mesa.db and the Germ,
	it moves the Germ into its proper location.
	Upon completion, we branch back to CoreInitial.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}
{
	Initialize registers for EtherInitial
}

DoneOnceOnlyInit:
		uEtherBootDone ← 0						,c1;
		uEtherBootStatus ←  0						,c2;
		rEtherBootRetries ← 0, uGotFirstCode ← 0			,c3;

		rG ← 0FF							,c1; {clock high} {so we don't timeout right away}
		uTimeout ← 0							,c2;
		Yrh ← 0								,c3;
		
WakeUpEther:	EICtl ← 2, CANCELBR[$, 1]					,c1; {Wake up the Initial code}

{
	Wait for the timeout flag to get set by Initial.
	Then if the uEtherBootDone flag has not been set,
	we go wake up the Initial code again.
}

TestTimeout:	Ybus ← uTimeout, NZeroBr, CANCELBR[$, 1]			,c2;
		Ybus ← uEtherBootDone, ZeroBr, BRANCH[$, WakeUpEther]		,c3;

		Ybus ← uEtherBootStatus, NZeroBr, BRANCH[$, TestTimeout]	,c1;
		rB ← germStart, BRANCH[MoveGerm, BootFailure]			,c2;
{
	The uEtherBootDone flag has been set.
	If the uEtherBootStatus register indicates unsuccessful completion,
	then report an error.
}

BootFailure:	acR ← bootDeviceError, GOTOABS[Maintenance1Loc]			,c3;

{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    MoveGerm is *not* device specific and lives in CoreInitial (for simplicity)
    Entry is with rB=germStart, nextPage=the page after the germ
    The return is via GOTO[SetRequest] at c3.
    
    SetRequest is responsible for settting SD[request] to indicate the method
    that was used to Boot, then continue w/exitToEmulator
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}
{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	Here are the appropriate entries from the Cedar6.0 release:
	
	BootFile.Location: TYPE = MACHINE DEPENDENT RECORD[
	    deviceType(0): DiskFace.Type,
	    deviceOrdinal(1): CARDINAL,
	    vp(2): SELECT OVERLAID * FROM
		disk => [diskFileID(2): DiskFileID],
		ethernet => [bootFileNumber(2): CARDINAL, net(3), host(4): CARDINAL ← 0],
		any => [a(2), b(3), c(4), d(5), e(6), f(7), g(10B), h(11B): WORD],
		ENDCASE
	    ];
	
	DiskFace.Type: TYPE = MACHINE DEPENDENT {
		null(0), sa800(1), sa1000(2), sa4000(3), cdc9730(4), ethernet(5),(LAST[CARDINAL])
	    };
	
	-- Identification of a boot file for loading it.
	
	BootFile.DiskFileID: TYPE = MACHINE DEPENDENT RECORD[
	    fID: DiskFace.FileID, -- for disk label
	    firstPage: INT, -- for disk label
	    firstLink: DiskFace.DontCare -- initial boot chain link
	    ];
	
	DiskFace.FileID: TYPE = MACHINE DEPENDENT RECORD[
	    id(0): SELECT OVERLAID * FROM
		rel => [relID(0): RelID, fill4(4): CARDINAL ← 0],
		abs => [absID(0): AbsID],
		ENDCASE
	    ];
	
	DiskFace.RelID: TYPE[4];
	
	DiskFace.AbsID: TYPE[5];
	
	-- Ignored in label verification.
	-- Typically used for boot chain links, in which case it's actually a DiskAddress.
	
	DiskFace.DontCare: TYPE[2];
	
	Corresponding values from Pilot12.0:
	
	Device.Type: TYPE = PRIVATE RECORD [CARDINAL];
	Ethernet:  TYPE = CARDINAL [5..16);
	PilotDisk: TYPE = CARDINAL [64..1024);
	nullType:  Type = [0];
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}
{
	Set up an Ethernet InLoad request for the Germ.
	rE has @SD[] - 1 relative to the MDS value
	(=1FF for PrincOps4.0, =23F for PrincOps3.0)
	rFrh has germPageHigh (or cedarGermPageHigh, which is the Germ's MDS!)
}
{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	This uses Howard's Jackpot subBoot mechanism (SubCalc)
	Here we're constructing the bootfile request for the germ

		subBoot = 20, number = 05200'b
		subBoot = 21, number = 05201'b,
		...
		subBoot = 27, number = 05207'b
		
	The communciation from PreEtherInitial is via uESubBoot which contains
	values in the range 0120 => 0127 because we're doing 20 sub-boots,
	the user selected the low order digit (octit?)
	
	What we want is to have #'s in the range 05200'b => 05207'b,
	we get these by noting that: 05060'b + 120'b = 05200'b,
	for TriDLion's the values are: 05070'b + 120'b = 05210'b
	then we create the interesting values appropriately
	
	rC ← 05000'b (12 LRot8) + 060'b (or 70'b) + uESubBoot (12x'b)
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}

SetRequest:
CedarRequest:	

{
	offset in first page of germ,
	POINTER TO Request= LOOPHOLE[1360'b]
	remember POINTER = [3E,0000]
	and Germ is offset [00,0200] 1000'b
}

		{Noop}								,c1;
		rE ← cedarGermPageLow						,c2;
		rE ← rE LRot8							,c3;
		
		Map ← rF ← [rFrh, rE + 0]					,c1; {.mv cedarGerm}
		rC ← germRequest						,c2;
		rBrh ← rB ← MD							,c3; {.mr cedarGerm}

		MAR ← [rBrh, cedar.Request.Action+0]				,c1;
		MDR ← inLoad							,c2;
		{Noop}								,c3;	
		
		MAR ← [rBrh, cedar.Request.deviceType+0]			,c1;
		MDR ← cedarGermEthernet						,c2;
		rD ← CPBootDevice						,c3;
		
		MAR ← [rBrh, cedar.Request.devOrd+0]				,c1;
		MDR ← ethernetDeviceOrdinal					,c2;
		Ybus ← rD, YDisp						,c3;

		rC ← 0A, BRANCH[ShugartNo, TridentNo, 7]			,c1; {12'b}
ShugartNo:	rD ← 30, GOTO[SubCalc]						,c2; {060'b}
TridentNo:	rD ← 38								,c2; {070'b}
SubCalc:	rC ← rC LRot8							,c3;

		rC ← rC + rD							,c1;
		rD ← uESubBoot							,c2;
		rC ← rC + rD							,c3;
		
		MAR ← [rBrh, cedar.Request.BootFileNumber+0]			,c1;
		MDR ← rC							,c2; {bottom 16 bits of boot file #}
		{Noop}								,c3;
		
		MAR ← [rBrh, cedar.Request.NetworkNumber+0]			,c1;
		MDR ← 0								,c2; {pup broadcast net}
		{Noop}								,c3;
		
		MAR ← [rBrh, cedar.Request.HostNumber+0]			,c1;
		MDR ← 0, GOTO[Exit]						,c2; {pup broadcast host}
{**

MesaRequest:	rC ← sFirstGermRequestHigh					,c1;
		rC ← rC LRot8							,c2; {Constants use an 8-bit data path.}
		rE ← rE + rC + 1						,c3; {correct page}
	
		Map ← rF ← [rFrh, rE + 0]					,c1;
		rC ← sFirstGermRequestLow					,c2;
		rBrh ← rB ← MD							,c3;

ZeroReq:	MAR ← [rBrh, rC+0]						,c1;
		MDR ← rD, rC ← rC + 1, PgCarryBr				,c2; {rD zero from above}
		BRANCH[ZeroReq, $]						,c3;
			
		MAR ← [rBrh, Request.action + 0]				,c1;
		MDR ← inLoad							,c2;
		rD ← RequestVersionHigh						,c3;
	
		MAR ← [rBrh, Request.location.deviceType + 0]			,c1;
		MDR ← germEthernet						,c2;
		rD ← rD LRot8							,c3;
		
		MAR ← [rBrh, Request.location.devOrd + 0]			,c1;
		MDR ← ethernetDeviceOrdinal					,c2;
		rD ← rD or RequestVersionLow					,c3;
	
		MAR ← [rBrh, Request.version + 0]				,c1;
		MDR ← rD							,c2;
		rD ← ethernetBootFileNumberMiddle				,c3;
	
		MAR ← [rBrh, Request.location.ethernetBootFileNumber0 + 0]	,c1;
		MDR ← ethernetBootFileNumberHigh				,c2;
		rD ← rD LRot8							,c3;
		
		MAR ← [rBrh, Request.location.ethernetBootFileNumber1 + 0]	,c1;
		MDR ← rD							,c2;
		rD ← CPBootDevice						,c3;
	
		MAR ← [rBrh, Request.location.ethernetNetworkNumber0 + 0]	,c1;
		MDR ← ethernetBootNetworkNumberHigh				,c2;
		Ybus ← rD, YDisp						,c3;

		rC ← 0A, BRANCH[ShugartNo, TridentNo, 7]			,c1; {12'b}
ShugartNo:	rD ← 30, GOTO[SubCalc]						,c2; {060'b}
TridentNo:	rD ← 38								,c2; {070'b}
SubCalc:	rC ← rC LRot8							,c3;

		rC ← rC + rD							,c1;
		rD ← uESubBoot							,c2;
		rC ← rC + rD							,c3;
		
		MAR ← [rBrh, Request.location.ethernetBootFileNumber2 + 0] 	,c1;
		MDR ← rC							,c2;
		Noop								,c3;

		MAR ← [rBrh, Request.location.ethernetNetworkNumber1 + 0]	,c1;
		MDR ← ethernetBootNetworkNumberLow				,c2;
		Noop								,c3;
	
		MAR ← [rBrh, Request.location.ethernetHostNumber0 + 0]		,c1;
		MDR ← rD xor ~rD						,c2;
		rDrh ← 0							,c3;
	
		MAR ← [rBrh, Request.location.ethernetHostNumber1 + 0]		,c1;
		MDR ← rD xor ~rD						,c2;
		Noop								,c3;
	
		MAR ← [rBrh, Request.location.ethernetHostNumber2 + 0]		,c1;
		MDR ← rD xor ~rD						,c2;
		Noop								,c3;
	
		MAR ← [rBrh, Request.location.ethernetSocket + 0]		,c1;
		MDR ← ethernetBootSocket					,c2;
		rC ← uDiagnostic, ZeroBr					,c3;
	
		rD ← 1, BRANCH[$, MesaBoot]					,c1;
		rD ← rD LRot8							,c2;
		rC ← rC + 1							,c3;

		MAR ← [rDrh, rC + 0]						,c1;
		Noop								,c2;
		rC ← MD								,c3;

		rC ← rC + rD							,c1;
		uBootStart ← rC, GOTO[Exit]					,c2;
**}

MesaBoot:	Noop								,c2;
Exit:		dY ← 1, GOTO[exitToEmulator]					,c3;

{
	Return to CoreInitial
}

SetTask[2], StartAddress[Dispatch];

{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	This code runs concurrently with EtherInitial task 0 and Protected
	and is awakened by EtherInitial task 0.
	EtherInitial has been brought into memory by EtherBootDLion
	during the first stage of booting and set into execution by the IOP.
	This module implements the core of the second stage of booting,
	consisting of bringing the Mesa.db or MoonBoot.db code and the Germ into memory.
	The modules communicate through the uEtherBootDone, uEtherBootStatus, uTimeout
	and clock (dX, dY) registers and EICtl.

	This Dispatch is the center of control for the entire module.
	Based on the value of the TurnOff and RcvMode bits in EStatus,
	it branches to one of the large sections of code
	named Preparing to Transmit, Transmitting a Packet, or Receiving a Packet.
	The setting of either of these bits by another task causes an Attention,
	which we eventually notice and branch back here.
	This code gets wakeups only when the transmitter is on,
	the receiver is on and there is something to receive on the Ethernet,
	or the TurnOff bit has been set in EStatus.
	The transmitter is turned on here and is left on until we have finished booting
	successfully (GoodFinish) or have attempted to boot ten times and have given up (BadFinish).
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}

Dispatch:	rY ← 9, CANCELBR[$, 0F]						,c1;
Dispatch2:	Xbus ← EStatus, XLDisp, L6 ← L6.Host0				,c2;
		rMM ← 0, DISP2[Operation]					,c3;
GoToDispatch:	CANCELBR[Dispatch, 0F]						,c3;

{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	Preparing to Transmit

	The registers rJ (low) and rG (high) hold a 32-bit count
	which is decremented every 28.8 microseconds by the refresh task.
	We initialize the counter in this code so that it will reach zero in 10 seconds.
	If the count reaches zero before we have successfully received a packet,
	uTimeout gets set by refresh.
	This causes Task0 to set bit 15 of EStatus and causes an Attention (EtherDisp).
	When this happens, we end up here through Dispatch.
	Thus, this code gets executed before we transmit for the first time
	and whenever we timeout and have to transmit the request packet again.
	After 10 retries with no success, we give up attempting to boot.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}
{
	If we have already transmitted the packet 10 times without success, then give up.
}
		Ybus ← rY - rEtherBootRetries, NegBr, GOTO[ResetClock]		,c1, at[1, 4, Operation];
		Ybus ← rY - rEtherBootRetries, NegBr, GOTO[ResetClock]		,c1, at[3, 4, Operation];
{
	Initialize the counter to time out in approximately 10 seconds.
	Reset the timeout flag.
}

ResetClock:	rJ ← 0, uTimeout ← 0, BRANCH[$, TenRetries]			,c2; {clock low}
		rG ← 5								,c3; {clock high}
		EOCtl ← EnableTransmit						,c1; {Turn the transmitter on.}
		EICtl ← EnableReceive						,c2; {Turn the receiver on.}
		rEtherBootRetries ← rEtherBootRetries + 1			,c3; {Increment the retry count.}

		Q ← 1								,c1; {Set the sequence number expected to 1.}
		rMM ← 0, uBackoffMask ← 0, GOTO[GoToDispatch]			,c2; {Clear the R link register and the backoff mask and return to Dispatch.}
{
	We have transmitted ten times with no success.
	Notify task0 that we have finished unsuccessfully
}

TenRetries:	uEtherBootStatus ← rY						,c3;

BadFinish:	EOCtl ← Off							,c1;
		EICtl ← Off							,c2;
		uEtherBootDone ← rY, GOTO[BadFinish]				,c3;

{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	Transmitting a Packet

	We come here from Dispatch whenever the transmitter is turned on,
	we are not timed out, and there is nothing on the Ethernet to receive.
	We wish to first broadcast a packet requesting the Mesa.db code,
	then repeat all this code again to request the Germ.
	If this is the first time we are attempting to transmit a specific packet
	or if we have transmitted previously but have not had any collisions,
	we wait 1 ms (specified by InitialCountdown) before attempting to send the packet.
	With each collision on the Ethernet, we shift a 1 into the BackoffMask
	so that the length of time before retransmission
	becomes an exponentially increasing random number.
	When the mask becomes full due to too many collisions,
	we turn the transmitter off and wait to timeout
	and try again with a clear mask.

	The checksum includes all words in the packet
	except the Ethernet header and trailer and the checksum word itself.
	It has been precalculated as much as possible.
	In this code we add to the checksum
	the words source host number through boot file number.
	As no packet can be less than 30 decimal words long,
	we add 4 words of zeros to the end of the packet.
	These are not included in the length or the checksum.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	The form of the packet we transmit is:
					
		/	|	FFFF	|	destination
		|	|	FFFF	|	  host
	Ethernet|	|	FFFF	|	  number
	header	|	|	----	|	source
		|	|	----	|	  host
		|	|	----	|	  number
		\	|	0600	|	packet type
			|	----	|	checksum
			|	0026	|	length
			|	0009	|	control
			|	0000	|	destination
			|	0000	|	  network
			|	FFFF	|	destination
			|	FFFF	|	  host
			|	FFFF	|	  number
			|	000A	|	socket number
			|	0000	|	source
			|	0000	|	  network
			|	----	|	source
			|	----	|	  host
			|	----	|	  number
			|	0041	|	source socket
			|	0001	|	boot packet type (request)
			|	0000	|	boot
			|	AA00	|	  file
			|	~~~~	|	  number
			|	0000	|	
			|	0000	|	
			|	0000	|	
			|	0000	|	
	Ethernet/	|	----	|	hardware
	trailer	\	|	----	|	  CRC

	----	varies with the particular machine
	~~~~	determined by boot buttons

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

	Determine which file we are requesting
	
	This is Howard's Jackpot subBoot mechanism
	Here we're constructing the microcode/germ file number

		subBoot = 20, uCode = 0130'b, germ = 0140'b
		subBoot = 21, uCode = 0131'b, germ = 0141'b
		...
		subBoot = 27, uCode = 0137'b, germ = 0147'b
	
	For TriDLion's the values are:
	
		subBoot = 20, uCode = 0230'b, germ = 0240'b
		subBoot = 21, uCode = 0231'b, germ = 0241'b
		...
		subBoot = 27, uCode = 0237'b, germ = 0247'b
	
	So, what we end up with is uESubBoot + (10'b, 20'b, 110'b, 120'b)
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}
		Ybus ← uGotFirstCode, NZeroBr					,c1, at[0, 4, Operation];
		rY ← 0FF + 1, BRANCH[FirstTime, SecondTime]			,c2;
FirstTime:	Ybus ← uDiagnostic, NZeroBr					,c3;

		uNextAddress ← rY, BRANCH[NoDiagBoot, DiagBoot]			,c1;
NoDiagBoot:	rY ← 8, GOTO[WhichDisk]						,c2; {10'b}
DiagBoot:	rY ← 8, GOTO[WhichDisk]						,c2; {10'b}

SecondTime:	rY ← 10								,c3; {20'b}
		Noop								,c1;
		Noop								,c2;
WhichDisk:	rWord ← CPBootDevice						,c3;

		Ybus ← rWord, YDisp						,c1;
		rP ← 0AA, BRANCH[SAx000Disk, TridentDisk, 7]			,c2; {252'b}
SAx000Disk:	GOTO[SetFileNo]							,c3;
TridentDisk:	rY ← rY + 40							,c3; {100'b}

{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	Set up the precalculated checksum of AA50
	in rP: the running checksum
	
	Add to the checksum:
	(0-2) 3-word host number
	(3) source socket number #41
	(4) boot packet type #1=request
	(5-7) boot file number 0000-AA00-xxxx = 25200000000B
	
	When the checksum calculation is complete,
	(8) launder the all 1's back to all 0's.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}

SetFileNo:	rWord ← uESubBoot						,c1; {0120 => 0127}
		rY ← rY + rWord							,c2;
		Noop								,c3;

		uFileNumber2 ← rY						,c1;
		rY ← rP LRot8							,c2; {252000'b}
		rP ← rY or 50							,c3;

		rWord ← uEHost0, CALL[AddChecksum]				,c1;
		rWord ← uEHost1, CALL[AddChecksum]				,c1, at[1, 10, ChksumReturn];
		rWord ← uEHost2, CALL[AddChecksum]				,c1, at[2, 10, ChksumReturn];
		rWord ← 41, CALL[AddChecksum]					,c1, at[3, 10, ChksumReturn];
		rWord ← 1, CALL[AddChecksum]					,c1, at[4, 10, ChksumReturn];
		rWord ← 0, CALL[AddChecksum]					,c1, at[5, 10, ChksumReturn];
		rWord ← rY, CALL[AddChecksum]					,c1, at[6, 10, ChksumReturn];
		rWord ← uFileNumber2, CALL[AddChecksum]				,c1, at[7, 10, ChksumReturn];
ChksumTest:	Ybus ← rP xor ~0, ZeroBr					,c1, at[8, 10, ChksumReturn];

		rWord ← 55, BRANCH[ChksumGood, ChksumZero]			,c2; {preamble pattern}
ChksumZero:	rP ← 0, GOTO[ChksumTest]					,c3;
ChksumGood:	Srh ← 2, rS ← 2							,c3;

		rMM ← 0, L6 ← 0							,c1;
		rS ← rS LRot8							,c2;

{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	Assemble the packet we are to transmit in memory, at address 20200.
	
	This address is just an arbitrary pick.
	We could make the packet anywhere in memory
	except the first two banks of memory (display bank and memory map),
	and the first page of the third bank (IOPage).

	(0-3) preamble
	(4-6) 3-word destination host number, all 1s for broadcast
	(7-9) 3-word source host number, provided by the IOP
	(0A) packet type = 600'h
	(0B) checksum
	(0C) length = 26'h
	(0D) control word = 9
	(0E-0F) destination network number, two words of all 0s
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}
		rWord ← rWord LRot8 or rWord, CALL[MakePacket]			,c3;
GoToPacket:	CALL[MakePacket]						,c3, at[1, 10, PacketReturn];
		CALL[MakePacket]						,c3, at[2, 10, PacketReturn];
		rWord ← rWord or 080, CALL[MakePacket]				,c3, at[3, 10, PacketReturn]; {last preamble bit on}
		rWord ← ~rWord xor rWord, CALL[MakePacket]			,c3, at[4, 10, PacketReturn];
		CALL[MakePacket]						,c3, at[5, 10, PacketReturn];
		CALL[MakePacket]						,c3, at[6, 10, PacketReturn];
		rWord ← uEHost0, CALL[MakePacket]				,c3, at[7, 10, PacketReturn];
		rWord ← uEHost1, CALL[MakePacket]				,c3, at[8, 10, PacketReturn];
		rWord ← uEHost2, CALL[MakePacket]				,c3, at[9, 10, PacketReturn];
		rWord ← uPacketType, CALL[MakePacket]				,c3, at[0A, 10, PacketReturn];
		rWord ← rP, CALL[MakePacket]					,c3, at[0B, 10, PacketReturn];
		rWord ← 26, CALL[MakePacket]					,c3, at[0C, 10, PacketReturn];
		rWord ← 9, CALL[MakePacket]					,c3, at[0D, 10, PacketReturn];
		rWord ← 0, CALL[MakePacket]					,c3, at[0E, 10, PacketReturn];
		CALL[MakePacket]						,c3, at[0F, 10, PacketReturn];
{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	Now we construck the *real* request
	
	(0-2) 3-word destination host number, all 1s for broadcast
	(3) socket number, 0A'h
	(4-5) source network number, two words of all 0s
	(6-8) 3-word source host number, provided by the IOP
	(9) source socket number.
		This number starts at 2500 decimal
		and is incremented in diskboot.ureg
		each time the initial microcode changes.
	(0A) boot packet type, 1 = request
	(0B-0D) 3-word boot file number (constructed above)
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}
		rWord ← ~rWord xor rWord, L6 ← 1, CALL[MakePacket]		,c3, at[0, 10, PacketReturn];
		CALL[MakePacket]						,c3, at[1, 10, PacketReturn2];
		CALL[MakePacket]						,c3, at[2, 10, PacketReturn2];
		rWord ← 0A, CALL[MakePacket]					,c3, at[3, 10, PacketReturn2];
		rWord ← 0, CALL[MakePacket]					,c3, at[4, 10, PacketReturn2];
		rCount ← 4, CALL[MakePacket]					,c3, at[5, 10, PacketReturn2];
		rWord ← uEHost0, CALL[MakePacket]				,c3, at[6, 10, PacketReturn2];
		rWord ← uEHost1, CALL[MakePacket]				,c3, at[7, 10, PacketReturn2];
		rWord ← uEHost2, CALL[MakePacket]				,c3, at[8, 10, PacketReturn2];
		rWord ← 41, CALL[MakePacket]					,c3, at[9, 10, PacketReturn2];
		rWord ← 1, CALL[MakePacket]					,c3, at[0A, 10, PacketReturn2];
		rWord ← 0, CALL[MakePacket]					,c3, at[0B, 10, PacketReturn2]; {0000'b}
		rWord ← rY, CALL[MakePacket]					,c3, at[0C, 10, PacketReturn2]; {0252'b}
		rWord ← uFileNumber2, CALL[MakePacket]				,c3, at[0D, 10, PacketReturn2]; {offset}
		rS ← rS - 1							,c3, at[0E, 10, PacketReturn2];
{
	Add four words of zeros to the end of the packet.
	We must do this so that the total length of the packet in words
	including the Ethernet header is not less than 30 decimal words.
}

ZeroLoop:	MAR ← rS ← [Srh, rS + 1], BRANCH[$, EndLoop]			,c1;
		MDR ← 0, CANCELBR[$, 0]						,c2;
		rCount ← rCount - 1, ZeroBr, GOTO[ZeroLoop]			,c3;





{
	Determine the length of time we should wait before retransmitting.
}

EndLoop:	rCount ← InitialCountdown, CANCELBR[$, 0]			,c2;
		Xbus ← rY ← uBackoffMask, XHDisp				,c3;

		rY ← rY LShift1, SE ← 1, ZeroBr, uOldMask ← rY, BRANCH[$, FullMask, 1]	,c1;
		uBackoffMask ← rY, BRANCH[OldMask, NewMask]			,c2;
OldMask:	rCount ← rJ, {clock low} GOTO[Retransmit]			,c3;
NewMask:	rY ← rY xor ~rY, GOTO[Retransmit]				,c3;

FullMask:	EOCtl ← Off, CANCELBR[GoToDispatch, 1]				,c2;
Retransmit:	rCount ← rCount and rY, GOTO[WaitLoop2]				,c1;

{
	Loop to count down the time to wait before transmitting.
}

WaitLoop:	rCount ← rCount - 1, NegBr, BRANCH[$, GoToAttention, 0E]	,c1;
WaitLoop2:	EOCtl ← EnableTransmitDefer, BRANCH[$, StartTransmit]		,c2;
		rY ← uOldMask, EtherDisp, GOTO[WaitLoop]			,c3;

GoToAttention:	 CANCELBR[AttentionSet, 0F]					,c2;

{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	Transmit the packet we have just constructed in memory.
	If Attention is set, we quit transmitting and turn the transmitter off.
	If the Attemtion is not due to UnderRun or Collision,
	then restore the retransmission mask.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}

StartTransmit:	rS ← 2								,c3;

		EOCtl  ← EnableTransmit						,c1;
		rS ← rS LRot8							,c2;
		rCount ← 22							,c3;

		MAR ← [Srh, rS + 0], EtherDisp, GOTO[OutputLoop2]		,c1;
OutputLoop:	MAR ← rS ← [Srh, rS + 1], EtherDisp, EStrobe			,c1;
OutputLoop2:	rCount ← rCount - 1, ZeroBr, BRANCH[$, AttentionSet, 0E]	,c2;
		EOData ← MD, BRANCH[OutputLoop, LastWord]			,c3;

AttentionSet:	Xbus ← EStatus, XwdDisp, CANCELBR[$, 1]				,c3;

		EOCtl ← Off, DISP2[Attention]					,c1;
		EOCtl ← EnableTransmit, GOTO[PutBackMask]			,c2, at[Other, 4, Attention];
		GOTO[GoToDispatch]						,c2, at[UnderRun, 4, Attention];
Collision:	EOCtl ← EnableTransmit, GOTO[GoToDispatch]			,c2, at[Collision, 4, Attention];
		GOTO[GoToDispatch]						,c2, at[CollisionUnderRun, 4, Attention];

PutBackMask:	uBackoffMask ← rY, GOTO[Dispatch]				,c3;

{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	The last word to transmit has been placed into the FIFO.
	EOCtl ← EnableTransmitLastWord tells the hardware
	that no more words will be placed into the FIFO,
	and must occur in c1 or c2.
	Wakeups will not occur again
	until the FIFO has been emptied onto the Ethernet.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}

LastWord:	EStrobe								,c1;
		EOCtl ← EnableTransmitLastWord					,c2;
		EStrobe								,c3;
{
	Check the final status.
}
		Xbus ← EStatus, XwdDisp						,c1;
		DISP2[Status]							,c2;
{
	The status is good.
	Turn the transmitter off and return to Dispatch.
}

StatusOK:	rMM ← 0, MMrh ← 0						,c3, at[0, 4, Status];
GoToFullMask:	GOTO[FullMask]							,c1;

{
	We have collided.
	Reset the transmitter and return to Dispatch.
}
		Noop								,c3, at[2, 4, Status];
		EOCtl ← Off, GOTO[Collision]					,c1;
{
	We have UnderRun.
	Turn the transmitter off and return to Dispatch.
	(Due to a hardware bug, this condition can never occur,
	but we insert this code in case the bug gets fixed.)
}
		GOTO[GoToFullMask]						,c3, at[1, 4, Status];
		GOTO[GoToFullMask]						,c3, at[3, 4, Status];

{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	Common Code for Transmitting

	This code places a word of the packet into the next memory location
	in the process of constructing a packet to transmit.
	On entry, Srh = 1, rS = the next available location in memory,
	and rWord = the next word to be added to the packet.
	rMM is the link register used in determining the return address.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}

MakePacket:	MAR ← [Srh, rS + 0]						,c1;
		MDR ← rWord							,c2;
		rS ← rS + 1, L6Disp						,c3;

		Ybus ← rMM ← rMM + 1, YDisp, BRANCH[$, Ending2, 0E]		,c1;
		DISP4[PacketReturn]						,c2;
Ending2:	DISP4[PacketReturn2]						,c2;

{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	Receiving a Packet

	We come here from Dispatch
	whenever TurnOff is not set and
	there is something on the Ethernet to be received.
	Thus receiving has precedence over transmitting.
	We receive all packets which appear on the Ethernet
	and must filter out the ones we do not want.
	The microcode we intend to receive comes in several packets
	with increasing sequence numbers, starting with 1.
	Whenever we receive one complete packet successfully,
	we reset the clock to timeout in two seconds.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	The form of each of the packets is:

					
		/	|	----	|	destination
		|	|	----	|	   host
	Ethernet|	|	----	|	   number
	header	|	|	----	|	source
		|	|	----	|	  host
		|	|	----	|	  number
		\	|	0600	|	packet type
			|	----	|	checksum
			|	----	|	length
			|	0009	|	control
			|	----	|	destination
			|	----	|	  network
			|	----	|	destination
			|	----	|	  host
			|	----	|	  number
			|	0041	|	socket number
			|	----	|	source
			|	----	|	  network
			|	----	|	source
			|	----	|	  host
			|	----	|	  number
			|	000A	|	source socket
			|	0002	|	boot packet type (reply)
			|	0000	|	boot
			|	AA00	|	  file
			|	~~~~	|	  number
			|	----	|	sequence number (1, ...)
			|	----	|	boot file data
				. . .		  . . .	
				. . .		  . . .	
				. . .		  . . .	
	Ethernet/	|	----	|	hardware
	trailer	\	|	----	|	  CRC


	----	varies
	~~~~	determined by boot buttons

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

	Much of the packet we input and throw out without verifying its correctness.
	For the first packet of the sequence,
	if the host number matches our host number,
	the sequence number is 1,
	the boot file number is correct,
	and the packet type is correct,
	then we assume the packet is the correct packet.
	For subsequent packets,
	we check that the host number is correct,
	it has the next consecutive sequence number,
	the packet type and boot file number are correct,
	and that the source number matches the source number of packet 1 in the sequence.
	Due to space limitations, packet checksums are not verified.

	A packet having a length of 40 decimal indicates
	that there is no boot file data present
	and implies the last packet of the sequence.
	As no packet can be less than 30 decimal words long,
	there may be extra words added on the end which
	are not included in the length and which we throw out.

	Throughout the receive code,
	Q contains the sequence number
	which we expect the packet we are receiving to have.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}
{
	Input destination number and see if it matches our host number.
	If not, throw out the packet.
}

Receive:	rY ← uEHost0, L6Disp, CALL[InputWord]				,c1, at[2, 4, Operation];
		Ybus ← rY xor rWord, NZeroBr, L6 ← L6.Host1			,c3, at[L6.Host0, 10, InputReturn];

		rY ← uEHost1, L6Disp, BRANCH[InputWord, PurgePacket]		,c1;
		Ybus ← rY xor rWord, NZeroBr, L6 ← L6.Host2			,c3, at[L6.Host1, 10, InputReturn];

		rY ← uEHost2, L6Disp, BRANCH[InputWord, PurgePacket]		,c1;
		Ybus ← rY xor rWord, NZeroBr, L6 ← L6.Source0			,c3, at[L6.Host2, 10, InputReturn];
{
	Input the 3-word source number.
	If this is the first packet of the sequence,
	then store the source number away in U registers 7E, 7F, and 70.
}
		EtherDisp, BRANCH[$, Host2Wrong]				,c1;
		Ybus ← Q - 1, ZeroBr, L6 ← L6.Source0, BRANCH[$, HostAttn, 0E]	,c2;
		rCount ← 7C, L0 ← 0, BRANCH[NotFirstPacket, FirstPacket]	,c3;

FirstPacket:	rCount ← rCount + 1, AltUaddr, NibCarryBr			,c1;
		rY ← EIData, uSourceBlock ← rY, BRANCH[$, PacketType]		,c2;
		rWord ← uPacketType, GOTO[FirstPacket]				,c3;
{
	If this is not the first packet of the sequence,
	then see if the source number of this packet
	matches the source of packet 1.
	If not, throw out the packet.
}

NotFirstPacket:	rY ← uSource0, L6Disp, CALL[InputWord]				,c1;
		Ybus ← rY xor rWord, NZeroBr, L6 ← L6.Source1			,c3, at[L6.Source0, 10, InputReturn];

		rY ← uSource1, L6Disp, BRANCH[InputWord, PurgePacket]		,c1;
		Ybus ← rY xor rWord, NZeroBr, L6 ← L6.Source2			,c3, at[L6.Source1, 10, InputReturn];

		rY ← uSource2, L6Disp, BRANCH[InputWord, PurgePacket]		,c1;
		Ybus ← rY xor rWord, NZeroBr, L6 ← L6.PacketType		,c3, at[L6.Source2, 10, InputReturn];
		rY ← uPacketType, L6Disp, BRANCH[InputWord, PurgePacket]	,c1;
{
	Input packet type.
	If it is not 0600 hex,
	then throw out the packet.
}

PacketType:	Ybus ← rY xor rWord, NZeroBr, L6 ← L6.Checksum			,c3, at[L6.PacketType, 10, InputReturn];

{
	Input checksum and throw out.
}
		rP ← 0, L6Disp, BRANCH[InputWord, PurgePacket]			,c1;
{
	Input length and calculate the actual length of the data in the packet in words,
	which is the total length in bytes minus 28 bytes for the header,
	rounded up and divided by 2.
}
		rMM ← 0AA, L6 ← L6.Length					,c3, at[L6.Checksum, 10, InputReturn];

		rY ← 0E, L6Disp, CALL[InputWord]				,c1;
		rWord ← rWord - 27						,c3, at[L6.Length, 10, InputReturn];
{
	Input the next 14 words of the header and throw out
}

EatGarbage:	rCount ← RShift1 rWord, SE ← 0, BRANCH[$, CheckFile]		,c1;
		rCount ← EIData, EtherDisp					,c2;
		rY ← rY - 1, ZeroBr, BRANCH[EatGarbage, GarbageAttn, 0E]	,c3;
{
	Input the 3-word boot file number
	and see if it matches the file number
	we requested in our request packet.
	If not, throw out the packet.
}

CheckFile:	rWord ← EIData							,c2;
		Ybus ← rWord, NZeroBr, L6 ← L6.File1				,c3;

		rY ← rMM LRot8, L6Disp, BRANCH[InputWord, PurgePacket]		,c1;
		Ybus ← rWord xor rY, NZeroBr, L6 ← L6.File2			,c3, at[L6.File1, 10, InputReturn];

		rY ← uFileNumber2, L6Disp, BRANCH[InputWord, PurgePacket]	,c1;
		Ybus ← rWord xor rY, NZeroBr, L6 ← L6.Sequence			,c3, at[L6.File2, 10, InputReturn];
{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	If we are fetching the Germ,
	then set the starting address to germStart.
	This catches the case where it is not our first try
	to receive the Germ successfully.
	Input the sequence number.
	If this is not the sequence number expected,
	then throw out the packet.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}
		L6Disp, BRANCH[InputWord, PurgePacket]				,c1;
		
ChkSequence:	Ybus ← Q xor 1, ZeroBr						,c3, at[L6.Sequence, 10, InputReturn];

		Ybus ← uGotFirstCode, ZeroBr, BRANCH[NotPacket1, Packet1]	,c1;
Packet1:	Ybus ← Q xor rWord, NZeroBr, BRANCH[SetGermAddress, SetStartAddress]	,c2;
NotPacket1:	Ybus ← Q xor rWord, NZeroBr, CANCELBR[SetStartAddress, 1]	,c2;
SetStartAddress:
		rY ← uNextAddress, BRANCH[SequenceOK, SequenceWrong]		,c3;
SetGermAddress:	rY ← germStart, BRANCH[SequenceOK, SequenceWrong]		,c3;

{
	If the length of the data in this packet is 0,
	it is the last packet.
	Jump out of this loop.
}

SequenceOK:	Ybus ← rCount, ZeroBr						,c1;
		rWord ← EIData, BRANCH[$, ZeroLength]				,c2;
		rY ← uNextAddress						,c3;
{
	Loop to input the next data word and store it into memory.
	If Attention is set, throw out the packet.
	We exit the loop normally
	when the length in words has counted down to zero.
}

FirstWord:	MAR ← [Yrh, rY + 0]						,c1;
		MDR ← rWord, GOTO[Decrement]					,c2;

InputLoop:	MAR ← rY ← [Yrh, rY + 1], EtherDisp, BRANCH[$, ExtraWords]	,c1;
InputLoop2:	MDR ← rWord ← EIData, DISP4[InputState, 0C]			,c2;
Decrement:	rCount ← rCount - 1, ZeroBr, GOTO[InputLoop]			,c3, at[0C, 10, InputState];
		GOTO[GoToPurge]							,c3, at[0D, 10, InputState];
		rY ← rY + 0FF + 1, GOTO[FirstWord]				,c3, at[0E, 10, InputState];
		GOTO[GoToPurge]							,c3, at[0F, 10, InputState];
{
	Throw out the rest of the packet,
	including the hardware CRC and any extraneous words
	which may be on the end of the packet to make the length at least 30 words.
}

ExtraWords:	EStrobe, BRANCH[$, FixPageCross, 0D]				,c2;
		GOTO[NextAddress]						,c3;
FixPageCross:	rY ← rY + 0FF + 1, GOTO[NextAddress]				,c3;

NextAddress:	uNextAddress ← rY						,c1;

{
	We seem to have received this packet successfully.
	Increment the sequence number.
}
		Q ← Q + 1							,c2;
{
	Set up the clock to timeout in two seconds
	and return to Dispatch to wait for the next packet.
}
		rG ← 1								,c3; {clock high}
		rJ ← 0								,c1; {clock low}
		GOTO[GoToDispatch]						,c2;

{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	Common Code for Receiving

	As each word of the packet is inputted,
	we check the Attention bit.
	Attention in this case indicates timeout
	or the end of the packet has been received.
	Since the normal exit to the receive loop
	is for the length to count down to zero,
	an Attention is an abnormal occurrence.
	If it has been set,
	we throw out the rest of the packet by doing an EStrobe.
	This EStrobe must occur in c2.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}

InputWord:	rWord ← EIData, DISP4[InputReturn]				,c2;

HostAttn:	CANCELBR[GoToPurge, 1]						,c3;
Host2Wrong:	EStrobe, CANCELBR[GoToDispatch, 1]				,c2;
SequenceWrong:	CANCELBR[PurgePacket, 1]					,c1;
GarbageAttn:	CANCELBR[PurgePacket, 1]					,c1;
GoToPurge:	CANCELBR[PurgePacket, 1]					,c1;
PurgePacket:	EStrobe, CANCELBR[GoToDispatch, 0F]				,c2;

{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	Verifying the File Checksum

	Read the code out of memory to check the file checksum,
	which is the last word in the file.
	This checksum is calculated in the same manner
	as the checksum which we transmitted in the request packet.
	When the checksum is computed on all words of the file
	including the checksum word itself,
	the result should be all 1s.
	On entry to this code, rP = 0.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}

ZeroLength:	Noop								,c3;

		Ybus ← uGotFirstCode, NZeroBr					,c1;
		BRANCH[Mesa, Germ]						,c2;
Mesa:		rY ← 0FF + 1, GOTO[SetAddress]					,c3;
Germ:		rY ← germStart, GOTO[SetAddress]				,c3;

SetAddress:	rS ← uNextAddress						,c1;
		rS ← rS - rY							,c2;
		Noop								,c3;

FileCheck:	MAR ← [Yrh, rY + 0]						,c1;
		GOTO[NoPageCross]						,c2;

FileChksum:	MAR ← rY ← [Yrh, rY + 1]					,c1, at[9, 10, ChksumReturn];
		rS ← rS - 1, ZeroBr, BRANCH[$, PageCross2, 1]			,c2;
NoPageCross:	rWord ← MD, BRANCH[AddWord, CheckChksum]			,c3;
PageCross2:	rY ← rY + 0FF + 1, BRANCH[FileCheck, CheckChksum2]		,c3;

AddWord:	rMM ← 8, CALL[AddChecksum]					,c1;

{
	If the file checksum is wrong,
	then return to Dispatch and wait to timeout and retransmit.
}

CheckChksum:	Ybus ← ~rP, NZeroBr, GOTO[CheckBranch]				,c1;
CheckChksum2:	Ybus ← ~rP, NZeroBr						,c1;
CheckBranch:	Ybus ← uGotFirstCode, NZeroBr, BRANCH[ChksumOK, ChksumWrong]	,c2;
ChksumWrong:	CANCELBR[Dispatch, 1]						,c3;

{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	The file checksum is good.
	If we have just received Mesa.db,
	then set up the starting address for the Germ,
	turn the transmitter back on,
	and return to Dispatch.
	If we have just received MoonBoot.db,
	then we are done.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}

ChksumOK:	Q ← 1, BRANCH[$, GotGerm]					,c3;

		uGotFirstCode ← Q						,c1;
		rY ← rY and ~0FF						,c2;
		Ybus ← uDiagnostic, NZeroBr					,c3;
	
		rY ← rY + 0FF + 1, BRANCH[$, GoodFinish2]			,c1;
		uNextAddress ← rY						,c2;
		germStart ← rY							,c3;

		EICtl ← 2							,c1; {set Attention}
		rEtherBootRetries ← 0, GOTO[GoToDispatch]			,c2;
{
	If we have just received the Germ,
	then we are finished.
	Notify Task0 of our successful completion.
}

GotGerm:	rMM ← rY and ~0FF						,c1;
		rMM ← rMM + 0FF + 1						,c2;
		rMM ← rMM LRot8							,c3;

GoodFinish:	nextPage ← rMM							,c1;
GoodFinish2:	EICtl ← Off							,c2;
		uEtherBootDone ← Q, GOTO[GoodFinish]				,c3; {uEtherBootStatus already zero}

{
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	Checksum Calculation

	The checksum is initialized to 0.
	It is computed by rotating both the checksum and the data word left one bit
	and adding the data to the checksum using ones-complement addition.
	All words in the packet are used in calculating the checksum
	except the Ethernet header and the checksum word itself.
	A checksum of all 1s means that no checksum has been computed,
	so that if a checksum is computed to be all 1s,
	it is changed to 0 by convention.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
}

GoToAdd:	Noop								,c1;
AddChecksum:	rWord ← LRot1 rWord						,c2;
		rP ← LRot1 rP							,c3;
		rP ← rP + rWord, CarryBr					,c1;

CarryBr:	Ybus ← rMM ← rMM + 1, YDisp, BRANCH[$, Carry]			,c2;
		DISP4[ChksumReturn]						,c3;

Carry:		rP ← rP + 1, CANCELBR[$, 0F]					,c3;
		rMM ← rMM - 1, GOTO[CarryBr]					,c1;

{eof...}