% EtherBoot.mc
Last edit by HGM September 4, 1980 2:30 PM, Repair source filter, free up some space, use Junk rather than CSLoc
Last edit by Jim Frandeen Aug 26, 1980 11:30 AM Early detection bad packets
Modified by Ev Neely Aug 14, 1980 9:58 AM use new ID# for Initial.eb
edit by HGM March 7, 1980 3:56 PM
Shift out old RDC task asignment,
lengthen (short) initial Dally,
Make MP cleaner if it doesn’t work,
Remove initial Notify from task 4, Jump to EtherBoot in task 0 now
Put our number in MP before we start.
Last edit by Jim Frandeen March 2, 1980 1:53 PM Set everything to Font 0
Last modified by HGM on February 15, 1980 1:06 AM,
Pause before starting (to avoid flooding net).
Last modified by HGM on February 13, 1980 12:17 PM,
Blink lights, don’t check net number
Last modified by Jim Frandeen on February 5, 1980 11:34 PM, initialize EtherMPCode
Last modified by HGM on February 5, 1980 12:32 AM, timings cleanup
Last modified by Jim Frandeen on February 4, 1980 10:46 AM, change Input to Input@
Last modified by HGM on February 3, 1980 4:16 PM
Modified by Jim Frandeen on February 2, 1980 1:21 AM

Written by Hal Murray
%

INSERT[PromDefs];
TITLE[EtherBoot];


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


Set[InStartLoc, OR[LSHIFT[InPage,10], 2]];
Set[OutStartLoc, OR[LSHIFT[OutPage,10], 2]];

* 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];
MC[DisableInputOutput, 300];

* 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[ID48Initial, 400]; * Used in PupID
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)
%

ONPAGE[EmPage];
SETTASK[0];

* SetPanel smashes 3 and 13
RV[R0,3];
RV[Temp,27];

RV[Count,33];
RV[TimerReg,37];
RV[OldMPCode,43];
RV[EtherMPCode,47];
MC[EtherMPCodeRegister,IP[EtherMPCode]];
RV[DeviceAddress,53];
RV[ShiftCount,57];


%
Get here is Task 0 with previous error code in T and in MP and all old devices already turned off.

If EtherBoting doesn’t work, we will blink the MP between the old number and our number.
%

EtherBoot:
OldMPCode ← T, AT[EtherBootLoc];

%
Check to see if we were booted by the fault handler (Boot instruction in Boot.mc) or booted because we got another fault while the fault handler (task 15) was already running. If so, delay a while to avoid flooding the net. Note that we will get an H4 pairity error right away if the pairity bit on the host number switches is set wrong.

We would like to test for a programmed boot (BootF = 4) or a parity boot (1), but the hardware doesn’t latch BootF. So for now, we will take the short delay if we recognize why we are getting booted.
%
* BEWARE: The documentation (Feb 80) doesn’t mention that the bits are read in complemented.
T ← (GetRSpec[157]) XOR (77C);
R0 ← T;
*LU ← (R0) AND (5C); * programed Boot or Parity Boot
*
SKIP[ALU=0],TimerReg ← 400C;
LU ← (R0) AND (62C); * PowerOn, Tester, or PushButton
SKIP[ALU#0],TimerReg ← 400C;
TimerReg ← 20000C;
NOP;* Allocation
EtherDally:
R0 ← 60000C;
R0 ← (R0)-1, GOTO[.,R>=0];
TimerReg ← (TimerReg)-1, GOTO[EtherDally,R>=0];

* Announce that we are starting....
T ← EtherMPCode ← EtherMPCodeEtherbooting;
LoadPage[SetPanelPage], CALL[GoSetMP];

* Set all device address to zero so we don’t get confused when some other device gets the old RDC task shifted into it and we read/write to two devices at once.
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
FindBoard:
T ← DeviceAddress ← EtherAddresses;
ShiftCount ← 10C, CALL[Shift];

* Find the EtherNet board
Count ← 40C;
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:
EtherMPCode ← EtherMPCodeNoEthernet, GOTO[EmTrouble];

StartInput:
Count ← 15C; * This works out to 15 decimal

* Poke sending process to send the request again
SendAgain:

R0 ← AND[0377, InStartLoc]C;
R0 ← (R0) OR (OR[InNotifyTask,AND[007400, InStartLoc]]C);
APC&APCTask ← R0, CALL[EmRet];
R0 ← AND[0377, OutStartLoc]C;
R0 ← (R0) OR (OR[OutNotifyTask,AND[007400, OutStartLoc]]C);
APC&APCTask ← R0, CALL[EmRet];
* This double loop takes about 10 seconds
TimerReg ← 1000C;
More:
R0 ← 60000C;
* I can’t quite get rid of the extra instructions in this tangle
NOP;
CALL[EmRet];
R0 ← (R0)-1, GOTO[.-2,R>=0];
LU ← (TimerReg) AND (177C);
GOTO[Again,ALU#0], LU ← (TimerReg) AND (200C);
SKIP[ALU#0], T ← OldMPCode;
T ← EtherMPCode;
LoadPage[SetPanelPage], CALL[GoSetMP];
NOP;
Again:
TimerReg ← (TimerReg)-1, GOTO[More,R>=0];
Count ← (Count)-1, GOTO[SendAgain,R>=0];
EtherMPCode ← EtherMPTimeout;

EmTrouble:
R0 ← 77400C;
R0 ← (R0)-1, GOTO[.,R>=0];
LU ← (Count) AND (377C);
LU ← (Count) AND (2000C), GOTO[EmTroubleLoop,ALU=0];
SKIP[ALU#0], T ← OldMPCode;
T ← EtherMPCode;
LoadPage[SetPanelPage], CALL[GoSetMP];
EmTroubleLoop:
Count←(Count)+1, GOTO[EmTrouble];

* 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];
EmRet:
RETURN;

GoSetMP:
GOTOP[SetPanel];


ONPAGE[OutPage];
SETTASK[OutTask];

RV[OutTemp, 3];
RV[OutHost, 7];
RV[OutMask, 13];

* 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];
INPUT@[OutHost, ReadHostNumber];
T ← RHMASK[OutHost];
OutTemp ← T, CALL[OutData];
OutTemp ← EtherPup, CALL[OutData];
OutTemp ← PupOverhead, CALL[OutData];
OutTemp ← D0MicrocodeRequest, CALL[OutData];
OutTemp ← OurVersionNumber, CALL[OutData]; * PupID
OutTemp ← ID48Initial, CALL[OutData]; * PupID2
OutTemp ← 0C, CALL[OutTwice]; * dest PupAddress
OutTemp ← MiscServSocket, CALL[OutData];
T ← RHMASK[OutHost];
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[OutRet];
T ← (OutTemp)-1, GOTO[.-2,R<0];
GOTO[OutAgain];

OutTwice:
OUTPUT@ [OutTemp, WriteData];
OutData:
OUTPUT@ [OutTemp, WriteData], GOTO[OutRet];
OutState:
OUTPUT@ [OutTemp, WriteState];
OutRet:
OutTemp ← OutTemp, RETURN;

ONPAGE[InPage];
SETTASK[InTask];


RV[Word0,3];
*Used by LoadCS
RV[Word1,7];
*Used by LoadCS
RV[Word2,13];
*Used by LoadCS
RV[Checksum,17];
*Used by LoadCS
RV[CSAddress,23];
*Used by LoadCS
RV[Temp,27];
*Used by LoadCS

RV[InTemp, 33];
RV[InHost, 37];
RV[BytesLeft, 43];
RV[Junk, 47];

RV[Source, 53];
RV[SourceTemp, 57];
RV[Packet, 63];

InStart:
INPUT@[InHost, ReadHostNumber], AT [InStartLoc];
Source ← 0C;
Packet ← 0C;
Checksum ← 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), Call[PurgeIfNonZero]; * Jam or BadAlignment
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[InHost]) XOR (T), Call[PurgeIfNonZero];
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 could skip this if all the boot servers really sent the same version of the microcode.
HostCheck:
LU ← (Source);
T ← (SourceTemp), Skip[ALU#0];
Source ← T; * Latch on to this machine
LU ← (Source) XOR (T), Call[PurgeIfNonZero];
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);
PurgeIfNonZero:
GOTO[InPurge,ALU#0];
InRet:
InTemp ← InTemp, RETURN; * Interlock for outputs

InState:OUTPUT@ [InTemp, WriteState], GOTO[InRet];

InGo:
BytesLeft ← (BytesLeft)-(PupOverhead)-1, CALL[InRet];
InLoop:
NOP;
CALL[InRet];
BytesLeft ← (BytesLeft)-(6C), GOTO[InDone,R<0];
InShort:
GOTO[InStart, IOAtten]; * Packet ended early

* Again, we only get a wakeup if there are 4 words in the buffer
TakeOne:
INPUT@[Word0,ReadData];
INPUT@[Word1,ReadData];
INPUT@[Word2,ReadData], GOTO[StuffCS];

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


* Stuff three words of data into Control Store
StuffCS:

LoadPage[DiskBootPage];
CallP[LoadCS];

* LoadCS uses the data in Word0, Word1, and Word2. It also uses Checksum, CSAddress, and Temp. The condition code on return indicates the result. If we get end of file and the checksum is correct, we will not return.

GoTo[InLoop,ALU=0], FreezeResult;
T ← EtherMPCodeChecksumError, Skip[ALU<0];
T ← EtherMPCodeBadCSAddress;

PostEtherMPCode:
InTemp←EtherMPCodeRegister;
StkP←InTemp;
Stack←T,GoTo[InStart];
END;