:TITLE[E3Load];*Formerly called EtherLoad

%Ed Fiala 5 January 1984: Reformat file to current conventions; allow
ethernet input code to be called twice for Pilot booting (once for the
germ, once for microcode). Renamed Base/BaseHi base register to be
IniBaseLo/Hi (now setup by Initial); use a new register WordOffset so
that IniBaseLo/Hi doesn’t change, permitting restart; use a new register
IReturn to hold the return PC. Change the socket number from the
constant value OurVersionNumber to the BootFileNumber to avoid mistaking
mistaking a retransmission of the microcode for the germ. Temporarily
remove the checksum check at the end of transmission because the 10.0
germ does not have a checksum; this krock is fixed in Klamath. Moved
register definitions and some other stuff to InitialDefs.mc. Removed
the initialization assigning all devices to task 0 (now done by Initial).
Call the SrShift procedure in Initial rather than duplicating it here.
Add code to turn off ethernet controller interrupts before exit.
HGM September 4, 1980 12:14 PM Avoid H4PE, more switches fixes, interlock
outputs, use Junk rather than (R)CSLoc
Ev Neely Aug 15, 1980 10:57 AM Fix swithches/bootaddress bug
Jim Frandeen July 15, 1980 12:15 PM RCSLoc conflicts with def in
GlobalDefs. Change to RRCSLoc
Jim Frandeen March 2, 1980 11:29 AM Delete Includes for Integration
Procedure
HGM on February 4, 1980 11:59 PM
%

*I/O Address Registers for use by the IO Task
Set[E3ReadDeviceID,0];
Set[E3ReadHostNumber,1];
Set[E3ReadStatus,2];
Set[E3ReadData,3];
Set[E3WriteState,0];
Set[E3WriteData,1];

*for access from Emulator Task
MC[ReadInputDeviceID,OR[LShift[E3InTask,4],E3ReadDeviceID]];
MC[ReadOutputDeviceID,OR[LShift[E3OutTask,4],E3ReadDeviceID]];
MC[ReadInputHostNumber,OR[LShift[E3InTask,4],E3ReadHostNumber]];
MC[WriteInputState,OR[LShift[E3InTask,4],E3WriteState]];
MC[WriteOutputState,OR[LShift[E3OutTask,4],E3WriteState]];

*Status masks
MC[CollisionMask,10000];
MC[OutputStatusMask,53000];
MC[InputStatusMask,124400];

*Ethernet device IDs
MC[EtherInputDeviceID,3400];
MC[EtherOutputDeviceID,3000];

*State Register command words
MC[PurgeThisPacket,260];
*Enable Input, PurgeMode
MC[SendThisPacket,107];
*Enable Output, OutputEOP, JamEnable
MC[EnableOutput,103];
*Enable Output, JamEnable
MC[DisableOutput,100];
MC[DisableInput,200];
*Disable input

*Constants for things in a packet
MC[EtherPup,1000];
*in second word of packet on an Ethernet
MC[OurVersionNumber,1];
*Used in PupID and socket#
MC[MiscServSocket,4];
*Misc Services socket
MC[D0MicrocodeRequest,264];
*pupType
MC[D0MicrocodeAnswer,265];
MC[PupOverhead,26];
*pupLength of empty Pup

%
Encapsulation is:
0 dest,,source (dest is 0 for broadcast)
1 packet type (1000 for pup)
Pup body is:
2 length (bytes) (26B is min)
3 ctl,,pupType (ctl is normally 0)
4 first word of pupID
5 second word of pupID
6 dest net,,dest host (0 for broadcast)
7 first word of dest socket
8 second word of dest socket
9 source net,,source host
10 first word of source socket
11 second word of source socket
... body goes here
12 pup checksum (-1 to ignore)
%

SetTask[0];

%Call here in Task 0 with error code already in MP. All devices are
quiescent and assigned to task 0. The MP will be bumped once after about
2.5 minutes, if loading fails. The BFI (Boot File Index) is in T at call.
IniBaseLo/Hi point at the block of storage where the boot file should be
put.
%
OnPage[E3Page];

E3Load:
BootFileNumber ← T, UseCTask;
T ← APCTask&APC;
IReturn ← T, Task;

*Load CAddr Register of the first 2 devices
T ← RTemp1 ← Xor[LShift[E3OutTask,4],E3InTask,377]C;
LoadPage[InitialPage];
RTemp ← 10C, CallP[SrShift];

*Find the 3 mb EtherNet board
Count ← 20C;
E3NextBoard:
T ← ReadInputDeviceID;
Input[RTemp];
LU ← (RTemp) xor (EtherInputDeviceID);
T ← RTemp1 ← (Zero) - 1, GoTo[E3StartInput,ALU=0];
LoadPage[InitialPage];
RTemp ← 4C, CallP[SrShift];
Count ← (Count) - 1, GoTo[E3NextBoard,R>=0];
GoTo[E3Trouble];*No 3 mb Ethernet

E3StartInput:
Count ← 15C;*This works out to 15 decimal retries
*before giving up.
T ← ReadInputHostNumber;
Input[Host];
E3SendAgain:
RTemp ← LoA[E3InStartLoc];
RTemp ← (RTemp) or (HiA[E3InStartLoc,E3InTask]);
APCTask&APC ← RTemp, Call[E3Ret];
*Poke sending process to send the request again
RTemp ← LoA[E3OutStartLoc];
RTemp ← (RTemp) or (HiA[E3OutStartLoc,E3OutTask]);
*The double loop below should take about 10 seconds initially; after
*connecting to a boot server, each good packet resets the high-order
*count to 300b for a 1.91 sec timeout.
*(2000b+2)*[60000b*4 + 15d] * 100d ns = 102.6us*(16384*6 + 15) = 10.09 sec
RTemp1 ← 2000C;
APCTask&APC ← RTemp, Call[E3Ret];
E3More:
Nop;
RTemp ← 60000C, Call[.+1];
RTemp ← (RTemp) - 1, Skip[R<0];
Return;*Interlock
RTemp1 ← (RTemp1) - 1, GoTo[E3More,R>=0];
*Timeout. Try again.
Count ← (Count) - 1, GoTo[E3SendAgain,R>=0];
E3Trouble:
IncMPanel;
GoTo[.];

SetTask[E3OutTask];

*The Ethernet encapsulation is 2 words and a short Pup is 11 words
*That fits into the buffer so there is no need to TASK

E3OutStart:
OutMask ← 0C, At[E3OutStartLoc];
E3OutAgain:
OutTemp ← DisableOutput;
Output[OutTemp,E3WriteState];
OutTemp ← EnableOutput, Call[E3OutState];
T ← RHMask[Host];
OutTemp ← T, Call[E3OutData];
OutTemp ← EtherPup, Call[E3OutData];
OutTemp ← PupOverhead, Call[E3OutData];
*? The D0MicrocodeRequest communication convention puts 3xN words +
*Pup overhead in each packet; this results in total packet lengths
*alternating between 776b and 1004b when PupWatch is used. The
*BootFileNumber used here is really a BFI, and the BFN used by the
*boot servers is BFI+3000b.
OutTemp ← D0MicrocodeRequest, Call[E3OutData];
OutTemp ← OurVersionNumber, Call[E3OutData];*PupID
Output[BootFileNumber,E3WriteData];*PupID2
OutTemp ← 0C, Call[E3OutTwice];*dest PupAddress
OutTemp ← MiscServSocket, Call[E3OutData];
T ← RHMask[Host];
OutTemp ← T, Call[E3OutData];*our PupAddress
T ← BootFileNumber;
OutTemp ← T, Call[E3OutTwice];
OutTemp ← (Zero) - 1, Call[E3OutData];*checksum
OutTemp ← SendThisPacket, Call[E3OutState];
Input[OutTemp,E3ReadStatus];
LU ← (OutTemp) and (CollisionMask);
GoTo[E3OutColl,ALU#0];
E3OutDone:
OutTemp ← DisableOutput, GoTo[E3OutState];

*Rats, got a collision
E3OutColl:
T ← OutMask ← (LSh[OutMask,1]) + 1, Skip[R>=0];
GoTo[E3OutDone];*Too many collisions, give up
Nop;
Nop;
OutTemp ← T, Call[E3Ret];
T ← (OutTemp) - 1, GoTo[.-2,R<0];
GoTo[E3OutAgain];

E3OutTwice:
Output[OutTemp,E3WriteData];
E3OutData:
Output[OutTemp,E3WriteData], GoTo[E3Ret];
E3OutState:
Output[OutTemp,E3WriteState], GoTo[E3Ret];

*Because of the way the hardware OR’s the task number into the high-order
*bits of the RM address, this OutTemp←OutTemp will also interlock
*InTemp for the input task.
E3Ret:
OutTemp ← OutTemp, Return;*Interlock

SetTask[E3InTask];

E3InStart:
Source ← 0C, At[E3InStartLoc];
Packet ← 0C;
Checksum ← 0C;
*Caller has setup IniBaseLo/Hi to point at storage address to receive
*the microcode or germ.
WordOffset ← 0C;
*Skip first packet
E3InPurge:
InTemp ← PurgeThisPacket, Call[E3InState];

*wait here for next packet to arrive
E3InFilter:
Input[InTemp,E3ReadStatus];*Beware of H4PE
*Jam or BadAlignment?
LU ← (InTemp) and (100400C), Call[E3InEqChk];
T ← EtherPup, Call[E3InSkip1Check];
Input[BytesLeft,E3ReadData];*PupLength
T ← D0MicrocodeAnswer, Call[E3InCheck];
T ← OurVersionNumber, Call[E3InCheck];*PupID
T ← Packet, Call[E3InCheck];*PupID2
Input[InTemp,E3ReadData];*our PupAddress
T ← RHMask[InTemp];
LU ← (RHMask[Host]) xor T, Call[E3InEqChk];*For us?
T ← BootFileNumber, Call[E3InCheck];
T ← BootFileNumber, Call[E3InCheck];
Input[SourceTemp,E3ReadData];
T ← MiscServSocket, Call[E3InSkip1Check];*his PupAddress
*Check that this packet is coming from the right machine. This can be
*skipped if all boot servers really send the same version of the file.
LU ← Source;
T ← SourceTemp, Skip[ALU#0];
Source ← T, Skip;
LU ← (Source) xor T, Call[E3InEqChk];
*We have not checked to see if the packet has ended already
*(Probably with bad status because of a collision)
*The hope is that some data will mismatch by now
Packet ← (Packet) + 1, GoTo[E3InGo];

*We only get a wakeup if there are at least 4 words in the buffer
*so it is safe to skip up to 3 words before checking one
E3InSkip1Check:
Input[Junk,E3ReadData];
*T has data word to be compared with next input data word
*There is no check for short packet
*since it will get tested at InLoop before we do anything stupid
E3InCheck:
Input[InTemp,E3ReadData];
Nop;*Beware of bypass kludge
LU ← (InTemp) xor T;
E3InEqChk:
GoTo[E3Ret,ALU=0];
GoTo[E3InPurge];

E3InState:
Output[InTemp,E3WriteState], GoTo[E3Ret];

E3InGo:
BytesLeft ← (BytesLeft) - (PupOverhead);
LU ← Checksum, GoTo[E3EndMarker,ALU=0];
BytesLeft ← (BytesLeft) - 1, Call[E3Ret];
E3InLoop:
BytesLeft ← (BytesLeft) - (2C), GoTo[E3InDone,R<0];
Skip[IOAtten’];
GoTo[E3InStart]; *Packet ended early
Input[Word,E3ReadData];
T ← Word;
Checksum ← (Checksum) + T;
T ← WordOffset;
PStore1[IniBaseLo,Word];
WordOffset ← (WordOffset) + 1, Return;


E3InDone:
Input[Junk,E3ReadData];*Pup Checksum
Input[Junk,E3ReadData];*CRC
Input[InTemp,E3ReadStatus];
LU ← (InTemp) and (InputStatusMask);
Skip[ALU=0];
GoTo[E3InStart];*Packet had bad status (got smashed)
*Reset timer to 2 sec whenever we get a good packet
RTemp1 ← 300C, GoTo[E3InPurge];


E3EndMarker:
InTemp ← DisableInput, Skip[ALU=0];
***Temporarily patch out error check because germ doesn’t have checksum
***Fixed in Klamath.
*
GoTo[E3InStart];*Checksum didn’t match
Nop;
Output[InTemp,E3WriteState];
APCTask&APC ← IReturn, GoTo[E3Ret];

:END;