* EtherLoad.mc
* Edit by HGM September 4, 1980 12:14 PM Avoid H4PE, more switches fixes, interlock outputs, use Junk rather than (R)CSLoc
* Edit by Ev Neely Aug 15, 1980 10:57 AM Fix swithches/bootaddress bug
*Edit by Jim Frandeen July 15, 1980 12:15 PM RCSLoc conflicts with def in GlobalDefs. Change to RRCSLoc
* Edit by Jim Frandeen March 2, 1980 11:29 AM Delete Includes for Integration Procedure
* Edit by HGM on February 4, 1980 11:59 PM

TITLE[EtherBoot];


* Tasks
Set[OutTask, 2];
Set[InTask, 3];
Set[OutNotifyTask, LSHIFT[OutTask,14]];
Set[InNotifyTask, LSHIFT[InTask,14]];

* Locations
Set[EtherPage, 4];

Set[EmStartLoc, OR[LSHIFT[EtherPage,10], 102]];
Set[OutStartLoc, OR[LSHIFT[EtherPage,10], 130]];
Set[InStartLoc, OR[LSHIFT[EtherPage,10], 110]];

* I/O Address Registers for use by the IO Task
Set[ReadDeviceID, 0];
Set[ReadHostNumber, 1];
Set[ReadStatus, 2];
Set[ReadData, 3];
Set[WriteState, 0];
Set[WriteData, 1];

* for access from Emulator Task
MC[ReadInputDeviceID, OR[LSHIFT[InTask,4],ReadDeviceID]];
MC[ReadOutputDeviceID, OR[LSHIFT[OutTask,4],ReadDeviceID]];
MC[ReadInputHostNumber, OR[LSHIFT[InTask,4],ReadHostNumber]];
MC[WriteInputState, OR[LSHIFT[InTask,4],WriteState]];
MC[WriteOutputState, OR[LSHIFT[OutTask,4],WriteState]];

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

* New Ethernet device IDs (This won’t work with old boards - fix them)
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];

* 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)
%

% Macros for generating Notify values
Sample usage is:
R ← RHNV[task#,loc];
R ← (R) OR (LHNV[task#,loc]);
APC&APCTask ← R;
RETURN;
%
MACRO[RHNV,AND[0377,#2]C];
MACRO[LHNV,OR[LSHIFT[#1,14],AND[007400,#2]]C];

* Registers:

SETTASK[0];
RV[R0, 0];
RV[Temp, 2];
RV[Count, 3];
RV[DeviceAddress, 4];
RV[ShiftCount, 5];

SETTASK[OutTask];
RV[OutTemp, 41];
RV[OutMask, 42];
RV[BootFileNumber, 43];

SETTASK[InTask];
RV[InTemp, 61];
RV[BytesLeft, 62];
RV[Junk, 63];
RV[Source, 64];
RV[SourceTemp, 65];
RV[Packet, 66];
RV[Checksum, 67];
RV[Word, 70];
RV[Base, 72];
RV[BaseHi, 73];

* Task 3 registers referenced by other tasks
RV[TimerReg, 76];
RV[Host, 77];

ONPAGE[EtherPage];
SETTASK[0];

* Jump here in Task 0 with error code already in MP.
* MP will be bumped once if we can’t load things.
* BootFile number is in T

EtherLoad:
BootFileNumber ← T, AT [EmStartLoc];

* I’m not sure if this is really needed
* set all device address to zero so stray wakeups won’t kill us
T ← DeviceAddress ← (ZERO)-1;
ShiftCount ← 400C, CALL[Shift];

MC[EtherAddresses, AND[NOT[OR[LSHIFT[OutTask,4],InTask]],377]];
* Load CAddr Register of the first 2 devices
T ← DeviceAddress ← EtherAddresses;
ShiftCount ← 10C, CALL[Shift];

* Find the EtherNet board
Count ← 20C;
NextBoard:
T ← ReadInputDeviceID;
Input[Temp];
LU ← (Temp) XOR (EtherInputDeviceID);
GOTO[StartInput,ALU=0], T ← DeviceAddress ← (ZERO)-1;
ShiftCount ← 4C, CALL[Shift];
GOTO[NextBoard,R>=0], Count ← (Count) - 1;
NoEthernet:
GOTO[EmTrouble];

StartInput:
Count ← 15C; * This works out to 15 decimal
T ← ReadInputHostNumber;
Input[Host];
SendAgain:
R0 ← AND[0377, InStartLoc]C;
R0 ← (R0) OR (OR[InNotifyTask,AND[007400, InStartLoc]]C);
APC&APCTask ← R0, CALL[Ret];
* Poke sending process to send the request again
R0 ← AND[0377, OutStartLoc]C;
R0 ← (R0) OR (OR[OutNotifyTask,AND[007400, OutStartLoc]]C);
APC&APCTask ← R0, CALL[Ret];
* This double loop should take about 10 seconds
* I used trial and error to get it right
TimerReg ← 1000C;
More:
R0 ← 60000C;
* I can’t quite get rid of the extra instructions in this tangle
NOP;
CALL[Ret];
R0 ← (R0)-1, GOTO[.-2,R>=0];
TimerReg ← (TimerReg)-1, GOTO[More,R>=0];
Count ← (Count)-1, GOTO[SendAgain,R>=0];
* Timeout

EmTrouble:
IncMPanel;
GOTO[.];

* Shift the device address registers another step
* Args in T, DeviceAddress, and Count
Shift:
ShiftCount ← (ShiftCount) - 1, GENSRCLOCK;
GOTO [.-1,ALU#0], T ← DeviceAddress ← RCY[DeviceAddress, 1];
RETURN;


SETTASK[OutTask];

* 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

OutStart:
OutMask ← 0C, AT [OutStartLoc];
OutAgain:
OutTemp ← DisableOutput;
OUTPUT[OutTemp, WriteState];
OutTemp ← EnableOutput, CALL[OutState];
T ← RHMASK[Host];
OutTemp ← T, CALL[OutData];
OutTemp ← EtherPup, CALL[OutData];
OutTemp ← PupOverhead, CALL[OutData];
OutTemp ← D0MicrocodeRequest, CALL[OutData];
OutTemp ← OurVersionNumber, CALL[OutData]; * PupID
OUTPUT[BootFileNumber, WriteData]; * PupID2
OutTemp ← 0C, CALL[OutTwice]; * dest PupAddress
OutTemp ← MiscServSocket, CALL[OutData];
T ← RHMASK[Host];
OutTemp ← T, CALL[OutData]; * our PupAddress
OutTemp ← OurVersionNumber, CALL[OutTwice];
OutTemp ← (ZERO)-1, CALL[OutData]; * checksum
OutTemp ← SendThisPacket, CALL[OutState];
INPUT[OutTemp, ReadStatus];
LU ← (OutTemp) AND (CollisionMask);
GOTO[OutColl,ALU#0];
OutDone:
OutTemp ← DisableOutput, GOTO[OutState];

* Rats, got a collision
OutColl:
T ← OutMask ← (LSH[OutMask,1])+1, SKIP[R>=0];
GOTO[OutDone]; * Too many collisions, give up
NOP; * ARG
NOP;
OutTemp ← T, CALL[Ret];
T ← (OutTemp)-1, GOTO[.-2,R<0];
GOTO[OutAgain];

OutTwice:
OUTPUT [OutTemp, WriteData];
OutData:
OUTPUT [OutTemp, WriteData], GOTO[Ret];
OutState:
OUTPUT [OutTemp, WriteState];
Ret:
OutTemp ← OutTemp, RETURN; * Interlock

SETTASK[InTask];

InStart:
Source ← 0C, AT [InStartLoc];
Packet ← 0C;
Checksum ← 0C;
Base ← MicrocodeAddress;
Base ← (Base) + 1;* eb files don’t have version word
BaseHi ← 0C;
* skip over first packet
InPurge:
InTemp ← PurgeThisPacket, CALL[InState];

* wait here for next packet to arrive
InFilter:
INPUT[InTemp,ReadStatus]; * Beware of H4PE
LU ← (InTemp) AND (100400C); * Jam or BadAlignment
Skip[ALU=0];
GOTO[InPurge]; * Looks bad - don’t touch this packet
NOP; * Alignment
T ← EtherPup, Call[InSkip1Check];
INPUT[BytesLeft, ReadData]; * PupLength
T ← D0MicrocodeAnswer, CALL[InCheck];
T ← OurVersionNumber, CALL[InCheck]; * PupID
T ← Packet, CALL[InCheck]; * PupID2
INPUT[InTemp, ReadData]; * our PupAddress
T ← RHMASK[InTemp];
LU ← (RHMASK[Host]) XOR (T);
SKIP[ALU=0];
GOTO[InPurge]; * Not for us
NOP; * Alignment
T ← OurVersionNumber, CALL[InCheck];
T ← OurVersionNumber, CALL[InCheck];
INPUT[SourceTemp,ReadData];
T ← MiscServSocket, CALL[InSkip1Check]; * his PupAddress

* 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

* Check that this packet is comming from the right machine.
* We can skip this if all the boot servers really send the same version of the microcode.
HostCheck:
T ← (Source);
T ← SourceTemp, SKIP[ALU#0];
Source ← T;
LU ← (Source) XOR (T);
SKIP[ALU=0];
GOTO[InPurge];
Packet ← (Packet)+1, GOTO[InGo];

* 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
InSkip1Check:
INPUT[Junk, ReadData];
* 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
InCheck:
INPUT[InTemp, ReadData];
NOP; * Beware of bypass kludge
LU ← (InTemp) XOR (T);
GOTO[Ret,ALU=0];
GOTO[InPurge];

InState:OUTPUT [InTemp, WriteState], GOTO[Ret];

InGo:
BytesLeft ← (BytesLeft)-(PupOverhead);
GOTO[EndMarker,ALU=0];
BytesLeft ← (BytesLeft)-1, CALL[Ret];
InLoop:
BytesLeft ← (BytesLeft)-(2C), GOTO[InDone,R<0];
SKIP[NOAtten];
InShort:
GOTO[InStart]; * Packet ended early

TakeOne:
INPUT[Word,ReadData];
T ← Word;
Checksum ← (Checksum)+T;
PSTORE1[Base,Word,0];
Base ← (Base)+1, RETURN;


InDone:
INPUT[Junk,ReadData]; * Pup Checksum
INPUT[Junk,ReadData]; * CRC
INPUT[InTemp,ReadStatus];
LU ← (InTemp) AND (InputStatusMask);
SKIP[ALU=0];
BadStatus:
GOTO[InStart]; * Packet got smashed
* Reset timer to 2 sec whenever we get a good packet
TimerReg ← 140C, GOTO [InPurge];


EndMarker:
LU ← Checksum;
InTemp ← and[MicrocodeLoadedLoc, 7400]c, SKIP[ALU=0];
GOTO[InStart]; * Checksum didn’t match

InTemp ← (InTemp) or (and[MicrocodeLoadedLoc, 377]c);
APC&APCTask ← InTemp;
return;

END;