*----------------------------------------------------------------------------
Title[InitialEther.Mc...June 18, 1982  2:03 PM...Taft];
* Boot-loads microcode from Ethernet
*----------------------------------------------------------------------------

* Register usage -- Alto Emulator registers

SetRMRegion[AEmRegs];
	Reserve[Add[And[IP[BootDataPtr], 17], 1]]; * Skip over R400, BootDataPtr
	RVN[LHost];		* Local host address
	RVN[RHost];		* Remote host address
	RVN[BFN];		* Boot file number
	RVN[RTimer];		* Receive timeout time
	RVN[Retries];		* Number of retries remaining
	RVN[SeqNo];		* Next expected sequence number
	RVN[EMBLink];		* Return link

* Used by Ethernet tasks -- beware simultaneous use in the Emulator!
	RVN[ELoad];		* Current Ethernet load mask
	RVN[EIPtr];		* Input pointer
	RVN[EICnt];		* Input count
	RVN[EOPtr];		* Output pointer
	RVN[EOCnt];		* Output count


Set[XTask, IP[EMU]];
KnowRBase[AEmRegs];
TopLevel;

*----------------------------------------------------------------------------
EtherMicrocodeBoot:
* Broadcast a MicrocodeBootRequest Pup for the microcode file we want.
* A boot server will respond by transmitting the entire file in
* consecutively-numbered MicrocodeBootReply Pups, ending with a
* MicrocodeBootReply Pup containing zero bytes.
* Locations 0-416 are used as a packet buffer.
* Enter: BootDataPtr = address of base of block into which to load the microcode image
*	T = boot file number
*	RBase = AEmRegs
*	MemBase = IOBR
* Call by: SCall[EtherMicrocodeBoot]
* Returns +1: Failed
*	+2: Succeeded; BootDataPtr updated to point to last word loaded +1
* Clobbers T, Q, and all AEmRegs except R400
*----------------------------------------------------------------------------

Subroutine;
	EMBLink← Link;
TopLevel;
	BFN← T;
	Retries← -14C;			* Try 12 times (at 5-second intervals)

* EtherMicrocodeBoot (cont'd)

SetupEtherBootRequest:
* Reset receiver and get local host address
	ELoad← A0, Call[ResetEther];	* Also selects TIOA[EControl]
	LHost← Input;
	LHost← RSH[LHost, 10];		* LHost← host address

* Set up the MicrocodeBootRequest
	RHost← T← 177400C;		* Build packet at VM 177400;
					* remote host not established yet
	T← (Store← T)+1, DBuf← LHost;	* [ 0] 0 ,, local host
	T← (Store← T)+1, DBuf← 1000C;	* [ 1] typePup
	T← (Store← T)+1, DBuf← 26C;	* [ 2] Pup.length
	T← (Store← T)+1, DBuf← 264C;	* [ 3] Pup.type = MicrocodeBootRequest
	T← (Store← T)+1, DBuf← 1C;	* [ 4] Pup.id.high (server expects 1)
	T← SeqNo← (Store← T)+1, DBuf← BFN; * [ 5] Pup.id.low = boot file number
	T← SeqNo← (Store← T)+1, DBuf← 0C, * [ 6] Pup.dest.net = 0, host = 0
		Branch[., R even];	* [ 7] Pup.dest.socket = 4 = [0, 4]
	T← (Store← T)+1, DBuf← 4C;	* [10]
	T← (Store← T)+1, DBuf← LHost;	* [11] Pup.source.net = 0, host = me
	T← SeqNo← (Store← T)+1, DBuf← 1C, * [12] Pup.source.socket = 200001B = [1, 1]
		Branch[., R even];	* [13]
	Store← T, DBuf← -1C, SeqNo← A0;	* [14] Pup.checksum = nil;

* Turn on receiver and transmitter.  Transmitter will immediately start
* and send the MicrocodeBootRequest Pup.
	T← TurnOnRx, Call[OutputGetsT];
	T← TurnOnTx, Call[OutputGetsT];

* Wait until the microcode image has been received (signalled by SeqNo = -1)
* or we time out (SeqNo unchanging for a long time).
ResetEtherBootTimer:
	RBase← RBase[RTC430];
	T← (RTC430)+1, RBase← RBase[AEmRegs]; * High word of 32-bit clock
	T← T+1, Q← SeqNo;		* Now + 2*65536 32-us ticks = ~ 4 seconds

AwaitEtherBootReply:
	PD← (SeqNo)#Q, RBase← RBase[RTC430], Branch[EtherBootDone, R<0];
	PD← (RTC430)-T-1, RBase← RBase[AEmRegs], Branch[ResetEtherBootTimer, ALU#0];
	Branch[AwaitEtherBootReply, ALU<0];

* Timed out.  Start over from the beginning.
	Retries← (Retries)+1, Branch[SetupEtherBootRequest, R<0];
	PD← T-T, Branch[EtherBootReturn]; * Carry← 1

EtherBootDone:
	PD← A, RBase← RBase[AEmRegs];	* Carry← 0

* Here when done.  Carry=0 if completed successfully, 1 if unsuccessfully.
EtherBootReturn:
	Link← EMBLink;
Subroutine;
	Return[Carry'];

*----------------------------------------------------------------------------
ResetEther:
* Reset Ethernet hardware and tasks.  Called from Emulator only.
*----------------------------------------------------------------------------

Subroutine;
	Q← Link;
TopLevel;
	T← EControl;
	TIOA← T;
	TaskingOff;			* Smash the hardware off
	T← TurnOffRx, Call[OutputGetsT];
	T← TurnOffTx, Call[OutputGetsT];
	Call[EITInitPC];		* Reset the tasks
	LdTPC← T, Wakeup[EIT];
	Wakeup[EOT], Call[EOTInitPC];
	LdTPC← T, TaskingOn;
	Link← Q, Branch[EOTInitPC];	* Actually, just return


*----------------------------------------------------------------------------
* Initialization code for Input Task
*----------------------------------------------------------------------------
Set[XTask, IP[EIT]];

Subroutine;
EITInitPC: T← EIT, CoReturn;
TopLevel;
	
	Call[EInitCommon];


*----------------------------------------------------------------------------
* Ethernet Input Task.
* This microcode does all the necessary filtering and copying to receive
* a microcode image and store it in memory starting at BootDataPtr.
* When it receives the end packet, it sets SeqNo to -1, shuts off the
* receiver, and blocks.
* Input task uses EIPtr and EICnt for scratch.
* It also cooperates with the Emulator task in use of the following registers:
* LHost = local host number
* RHost = remote host number -- emulator should initialize to a negative value
* SeqNo = sequence number -- emulator should initialize to 0
* BootDataPtr = address to store microcode image -- emulator should initialize
*----------------------------------------------------------------------------

Set[XTask, IP[EIT]];

* Idle state of the Ethernet input task.
* Wake up here when first word of a new packet arrives.
* Don't bother filtering on destination as packet starts to arrive --
* just receive every packet and ignore the ones we don't want!
EILast:	EIPtr← A0, Block;
EIIdle:	T← (R400)+(20C);		* 258 data + 13 overhead + CRC = 420B
	EICnt← T, TIOA[EData];

* Input main loop.
* IOAtten branches if the data word being read is the end-of-packet status.
	T← Input, Branch[EIEnd, IOAtten];
	EICnt← (EICnt)-1;
	EIPtr← (Store← EIPtr)+1, DBuf← T, Block, Branch[.-2, ALU>=0];

* Get here when buffer overflows.
* Tell hardware to ignore the rest of this packet.
	TIOA[EControl];
	T← WaitForBOP;			* Wakeup at start of next packet
	EIPtr← A0, Output← T, Block, Branch[EIIdle];

* Input task (cont'd)

* Normal end-of-packet exit from main loop.
* One extra word (the CRC) has been stored in the buffer.
* T contains the status word.
EIEnd:	TIOA[EControl];
	EIPtr← T AND (377C), Call[BootFilter]; * Good status?
	EIPtr← (Fetch← EIPtr)+(3C);	* Fetch VM 0 = dest ,, source host
	T← LSH[LHost, 10];		* Local host to left byte
	EICnt← T XOR MD;		 * EICnt← (XORed dest) ,, source
	PD← (EICnt) AND (177400C), Call[BootFilter]; * dest = me?
	Fetch← EIPtr, T← 265C;		* Fetch Pup.type
	PD← T XOR MD, Call[BootFilter];	* type = MicrocodeBootReply?
	Fetch← 5S, T← EICnt;		* Fetch low Pup.id, T← source host
	PD← (SeqNo) XOR MD, Call[BootFilter]; * Sequence # = next expected?
	RHost, Branch[.+2, R>=0];	* Remote host already established?
	RHost← T;			* No, establish remote host address
	EIPtr← 2C;			* (here for placement)
	PD← (RHost) XOR T, Call[BootFilter]; * Correct remote host?

* It is the desired packet.
* Copy the data into the IM array we are building.
* When we encounter a packet with zero data words, that is the end.
* Note: OK to use Cnt here because we know the emulator isn't using it!
	Fetch← EIPtr, T← -30C;		* Fetch Pup.length, T← -(overhead+2)
	T← (T+MD) RSH 1;		* length-overhead-2, => words-1
	Cnt← T, Branch[EIDone, ALU<0];	* Branch if empty
	EIPtr← (Fetch← 14S)+1;		* Where Pup contents begin

	EIPtr← (Fetch← EIPtr)+1, T← MD;
	BootDataPtr← (Store← BootDataPtr)+1, DBuf← T, Branch[.-1, Cnt#0&-1];

* Advance sequence number and await next packet.
	SeqNo← (SeqNo)+1, Branch[EILast];

* Received last packet of data.  Turn off the receiver, set SeqNo to -1 to signal
* the emulator task that we are done, and block forever.
EIDone:	T← TurnOffRx;
	SeqNo← T-(Output← T)-1, Block, Branch[.];  * All OK


*-----------------------------------------------------------
BootFilter:
* Subroutine to assist in packet filtering after reception of full packet.
* Returns if ALU=0 upon entry.
* Discards current packet and awaits next if ALU#0.
*-----------------------------------------------------------
Subroutine;
	Branch[EILast, ALU#0];
	Return;
TopLevel;

*----------------------------------------------------------------------------
* Initialization code for Output Task
*----------------------------------------------------------------------------
Set[XTask, IP[EOT]];

Subroutine;
EOTInitPC: T← EOT, CoReturn;
TopLevel;

	Call[EInitCommon];

*----------------------------------------------------------------------------
* Output Task.
* This microcode sends one packet of length 15 starting at address 177400,
* then shuts off the transmitter and blocks.
* Input task uses EOPtr and EOCnt for scratch.
* It also cooperates with the Emulator task in use of the following register:
* ELoad -- emulator should initialize to 0
*----------------------------------------------------------------------------

* Idle state of the Ethernet output task.  TIOA=EControl.
* Wake up here when poked by Emulator task.
* Use fastest-changing bits of real time clock as random number.
EOIdle:	T← (R400)-1, RBase← RBase[RTClock];
	T← (RTClock) AND T, RBase← RBase[AEmRegs];

* Test for load overflow (old load has sign bit set).
* Then mask countdown with old load and test for nonzero result.
* Note that the countdown clock ticks every 16 us, but we want to count in
* units of 32 us, so we double the count before using it.
	T← ((ELoad) AND T) LSH 1, Branch[ELodOv, R<0];
	EOCnt← T-1, Branch[EOGo, ALU=0];

* Must wait before retransmitting.  EOCnt has wait time -1 in 16-usec units.
* CountDown turns off data wakeups and requests a wakeup at next
* tick of countdown clock.
EWait:	T← CountDown;
	EOCnt← (EOCnt)-1, Output← T, Block, Branch[.-1, R>=0];

* Now time to transmit packet.  Set up buffer pointer and count.
EOGo:	ELoad← (ELoad)+(ELoad)+1, TIOA[EData];
	EOPtr← 177400C;
	EOCnt← 13C;			* Packet length -2

* Output main loop.
* EOPtr is the negative of the number of words remaining to be fetched.
* IOAtten branches if a collision, data late, or Fifo PE has aborted output.
	EOPtr← (Fetch← EOPtr)+1, Branch[EOAbrt, IOAtten];
	EOCnt← (EOCnt)-1, Output← MD, Block, Branch[.-1, R>=0];

	TIOA[EControl];
	T← SendEOP;			* Declare end of packet
	Output← T, Block;

* Wake up here only when packet completely sent or collision has occurred.
	T← TurnOffTx, Branch[EOAbr1, IOAtten]; * Branch if aborted, get status

* Probably successful transmission.  Turn off transmitter and block.
EOStop:	Output← T, Block, Branch[.];

* Output task (cont'd)

* Here if collision or other error occurred.
* Treat all errors as if they were collisions.
EOAbrt:	TIOA[EControl];
	T← TurnOffTx;
EOAbr1:	Output← T;			* Turn off to reset collision bit
	T← TurnOnTx;			* Turn on and try again
	Output← T, Block, Branch[EOIdle];

* If a load overflow occurs then just give up.
ELodOv:	T← TurnOffTx, Branch[EOStop];


*----------------------------------------------------------------------------
EInitCommon:	* Common task initialization
*----------------------------------------------------------------------------
Subroutine;

	T← EControl;
	TIOA← T;			* full TIOA for initialization
	RBase← RBase[AEmRegs];
	MemBase← IOBR, Block, Return;

TopLevel;