*---------------------------------------------------------------------------- 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;