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