{File name: EtherBootDLion.mc Description: Ethernet booting microcode Author: Amy Fasnacht Created: August 7, 1981 Amy Fasnacht, January 28, 1982 4:42 PM: Change socket numbers Amy Fasnacht, January 21, 1982 1:14 PM: Change boot file number to a 3-word quantity Amy Fasnacht, January 13, 1982 10:25 AM: Change file checksum to the standard rotate-and-add routine instead of a simple sum Amy Fasnacht, December 8, 1981 10:25 AM: Comment changes Amy Fasnacht, December 2, 1981 10:10 AM: Modify to run on Trident machine Amy Fasnacht, November 24, 1981 9:51 AM: Change boot file type Amy Fasnacht, November 20, 1981 1:48 PM: Remove checking packet checksums Amy Fasnacht, November 16, 1981 2:15 PM: Register name changes Amy Fasnacht, November 13, 1981 5:44 PM: Clear uTimeout before going to BadFinish Dennis Grundler: 2-Sep-84 15:46:14, add copyright notice. } { Copyright (C) 1981, 1982 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]; SetTask[2], StartAddress[Dispatch]; {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. } {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; rM ¬ 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 in Protected.mc. 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 Protected. This causes Task0 (Phase0) 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, {clock low} uTimeout ¬ 0, BRANCH[$, TenRetries], c2; rG ¬ 5, {clock high} c3; {Set up U register for packet type.} rY ¬ 6, c1; rY ¬ rY LRot8, c2; uPacketType ¬ rY, c3; {Turn the transmitter on.} EOCtl ¬ EnableTransmit, c1; {Turn the receiver on.} EICtl ¬ EnableReceive, c2; {Increment the retry count.} rEtherBootRetries ¬ rEtherBootRetries + 1, c3; {Set the sequence number expected to 1.} Q ¬ 1, c1; {Clear the backoff mask and return to Dispatch.} uBackoffMask ¬ 0, GOTO[GoToDispatch], c2; {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 broadcast a packet requesting the Ethernet booting initial microcode. If this is the first time we are attempting this 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 3-word host number, the source socket number, the boot packet type, and the 3-word 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. In order to transmit the data one word per click, we set up the packet in memory first, and then transmit the entire packet in a loop. The form of the packet we transmit is: / | FFFF | destination | | FFFF | host Ethernet | | FFFF | address header | | ------ | source | | ------ | host | | ------ | address \ | 0600 | packet type | ------ | checksum | 0026 | length | 0009 | control | 0000 | destination | 0000 | network | FFFF | destination | FFFF | host | FFFF | address | 000A | socket number | 0000 | source | 0000 | network | ------ | source | ------ | host | ------ | address | 0040 | 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 } {Set up the precalculated checksum of AA50.} Transmit: rY ¬ 0AA, c1, at[0, 4, Operation]; rP ¬ rY LRot8, c2; rP ¬ rP or 50, c3; {Add the 3-word host address to the checksum.} rWord ¬ uEHost0, CALL[AddChecksum], c1; rWord ¬ uEHost1, CALL[AddChecksum], c1, at[1, 10, ChksumReturn]; rWord ¬ uEHost2, CALL[AddChecksum], c1, at[2, 10, ChksumReturn]; {Add the source socket number to the checksum, 40 hex.} rWord ¬ 40, CALL[AddChecksum], c1, at[3, 10, ChksumReturn]; {Add the boot packet type to the checksum.} rWord ¬ 1, CALL[AddChecksum], c1, at[4, 10, ChksumReturn]; {Add the boot file number to the checksum. Note that I am using the fact that rY contains AA above. If the precalculated checksum changes, this code will fail.} rWord ¬ 0, CALL[AddChecksum], c1, at[5, 10, ChksumReturn]; rWord ¬ rY LRot8, CALL[AddChecksum], c1, at[6, 10, ChksumReturn]; rWord ¬ uFileNumber2, CALL[AddChecksum], c1, at[7, 10, ChksumReturn]; {If the calculated checksum is all 1s, then change it to 0.} ChksumTest: Ybus ¬ rP xor ~0, ZeroBr, c1, at[8, 10, ChksumReturn]; rWord ¬ 55, BRANCH[ChksumOK, ChksumZero], c2; ChksumZero: rP ¬ 0, GOTO[ChksumTest], c3; ChksumOK: rS ¬ 0, c3; rM ¬ 0, L6 ¬ 0, c1; Srh ¬ 1, c2; {Set up the packet we are to transmit in memory at the beginning of bank 1. First set up the preamble.} rWord ¬ rWord LRot8 or rWord, CALL[MakePacket], c3; GoToPacket: Yrh ¬ 0, CALL[MakePacket], c3, at[1, 10, PacketReturn]; CALL[MakePacket], c3, at[2, 10, PacketReturn]; rWord ¬ rWord or 0C0, CALL[MakePacket], c3, at[3, 10, PacketReturn]; {Set up the 3-word destination host address, all 1s for broadcast.} 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]; {Set up the source host address, provided by the IOP.} 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]; {Set up the packet type, 600 hex.} rWord ¬ uPacketType, CALL[MakePacket], c3, at[0A, 10, PacketReturn]; {Set up the checksum.} rWord ¬ rP, CALL[MakePacket], c3, at[0B, 10, PacketReturn]; {Set up the length, 26 hex.} rWord ¬ 26, CALL[MakePacket], c3, at[0C, 10, PacketReturn]; {Set up the control word, 9.} rWord ¬ 9, CALL[MakePacket], c3, at[0D, 10, PacketReturn]; {Set up the destination network number, two words of all 0s.} rWord ¬ 0, CALL[MakePacket], c3, at[0E, 10, PacketReturn]; CALL[MakePacket], c3, at[0F, 10, PacketReturn]; {Set up the 3-word destination host address, all 1s for broadcast.} 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]; {Set up the socket number, A hex.} rWord ¬ 0A, CALL[MakePacket], c3, at[3, 10, PacketReturn2]; {Set up the source network number, two words of all 0s.} rWord ¬ 0, CALL[MakePacket], c3, at[4, 10, PacketReturn2]; rCount ¬ 4, CALL[MakePacket], c3, at[5, 10, PacketReturn2]; {Set up the source host address, provided by the IOP.} 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]; {Set up the source socket number.} rWord ¬ 40, CALL[MakePacket], c3, at[9, 10, PacketReturn2]; {Set up the boot packet type, 1 for request.} rWord ¬ 1, CALL[MakePacket], c3, at[0A, 10, PacketReturn2]; {Set up the 3-word boot file number. Note that I am using the fact that rY contains AA as set above. If the precalculated checksum changes, this code will fail.} rWord ¬ 0, CALL[MakePacket], c3, at[0B, 10, PacketReturn2]; rWord ¬ rY LRot8, CALL[MakePacket], c3, at[0C, 10, PacketReturn2]; rWord ¬ uFileNumber2, CALL[MakePacket], c3, at[0D, 10, PacketReturn2]; 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 which we just constructed in memory. If Attention is set, we quit transmitting and turn the transmitter off. If the Attention is not due to UnderRun or Collision, restore the retransmission mask.} StartTransmit: rS ¬ 0, c3; EOCtl ¬ EnableTransmit, c1; Noop, 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.} rM ¬ 0, Mrh ¬ 0, c1; Xbus ¬ EStatus, XwdDisp, c2; DISP2[FinalStatus], c3; {The status is good. Turn the transmitter off and return to Dispatch.} rS ¬ 0FF + 1, GOTO[FullMask], c1, at[0, 4, FinalStatus]; {We have collided. Reset the transmitter and return to Dispatch.} EOCtl ¬ Off, GOTO[Collision], c1, at[2, 4, FinalStatus]; {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[FullMask], c1, at[1, 4, FinalStatus]; GOTO[FullMask], c1, at[3, 4, FinalStatus]; { 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. rM 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 ¬ rM ¬ rM + 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 initial 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 | | ------ | address header | | ------ | source | | ------ | host | | ------ | address \ | 0600 | packet type | ------ | checksum | ------ | length | 0009 | control | ------ | destination | ------ | network | ------ | destination | ------ | host | ------ | address | 0040 | socket number | ------ | source | ------ | network | ------ | source | ------ | host | ------ | address | 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 address 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 address 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.} rP ¬ 0, {clear checksum} 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.} rM ¬ 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 ¬ rM 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]; {Input the sequence number. If this is not the sequence number expected, then throw out the packet.} rY ¬ 0FF + 1, L6Disp, BRANCH[InputWord, PurgePacket], c1; Ybus ¬ Q xor rWord, NZeroBr, c3, at[L6.Sequence, 10, InputReturn]; {If the length of the data in this packet is 0, it is the last packet. Jump out of this loop.} Ybus ¬ rCount, ZeroBr, BRANCH[$, SequenceWrong], c1; rWord ¬ EIData, BRANCH[$, ZeroLength], c2; Srh ¬ 0, 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 ¬ [Srh, rS + 0], c1; MDR ¬ rWord, GOTO[Decrement], c2; InputLoop: MAR ¬ rS ¬ [Srh, rS + 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]; rS ¬ rS + 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[IncSequence], c3; FixPageCross: rS ¬ rS + 0FF + 1, GOTO[IncSequence], c3; {We seem to have received this packet successfully. Increment the sequence number.} IncSequence: Q ¬ Q + 1, c1; {Set up the clock to timeout in two seconds and return to Dispatch to wait for the next packet.} rG ¬ 1, {clock high} c2; rJ ¬ 0 {clock low}, GOTO[Dispatch], c3; { 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: EStrobe, CANCELBR[GoToDispatch, 1], c2; 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 send in our 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, rS contains the next available address in memory, so that when we subtract the starting address of the file from it (-1) we obtain the length of the file.} ZeroLength: rS ¬ rS - 0FF - 1, 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: rM ¬ 8, CALL[AddChecksum], c1; {The file checksum is good, so we are done. Turn the receiver off and inform Task0 that we have finished} CheckChksum: Ybus ¬ ~rP, NZeroBr, GOTO[GoodFinish], c1; CheckChksum2: Ybus ¬ ~rP, NZeroBr, c1; GoodFinish: rY ¬ 1, BRANCH[$, ChksumWrong], c2; uEtherBootDone ¬ rY, c3; EICtl ¬ Off, GOTO[GoodFinish], c1; {The file checksum is bad. Go back to Dispatch to try again.} ChksumWrong: GOTO[Dispatch], c3; { 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.} AddChecksum: rWord ¬ LRot1 rWord, c2; rP ¬ LRot1 rP, c3; rP ¬ rP + rWord, CarryBr, c1; CarryBr: Ybus ¬ rM ¬ rM + 1, YDisp, BRANCH[$, Carry], c2; DISP4[ChksumReturn], c3; Carry: rP ¬ rP + 1, CANCELBR[$, 0F], c3; rM ¬ rM - 1, GOTO[CarryBr], c1;