*---------------------------------------------------------------------------- Title[Ether.Mc...November 23, 1982 10:51 AM...Taft]; *** Pilot-only version *** *---------------------------------------------------------------------------- *---------------------------------------------------------------------------- NewOp; ESCEntry[RESETH]; * Reset Ethernet * Resets Ethernet hardware and tasks. *---------------------------------------------------------------------------- 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]; Call[EOTInitPC]; LdTPC_ T, Wakeup[EOT]; T_ A0, TaskingOn; TIOA_ T, NextOpcode; *---------------------------------------------------------------------------- * Initialization code for Input Task *---------------------------------------------------------------------------- Set[XTask, IP[EIT]]; Subroutine; EITInitPC: T_ EIT, CoReturn; TopLevel; T_ EControl; TIOA_ T, Block, Branch[EIIdle]; * full TIOA for initialization *---------------------------------------------------------------------------- * Initialization code for Output Task *---------------------------------------------------------------------------- Set[XTask, IP[EOT]]; Subroutine; EOTInitPC: T_ EOT, CoReturn; TopLevel; T_ EControl; TIOA_ T, Block, Branch[EOIdle]; * full TIOA for initialization *---------------------------------------------------------------------------- * Input Task *---------------------------------------------------------------------------- Set[XTask, IP[EIT]]; * Non-emulator mode * Idle state of the Ethernet input task. * Wake up here when first word of a new packet arrives. EIIdle: MemBase_ IOBR; T_ ICSB.next; Fetch_ T, T_ ICSB.host; PD_ MD, RBase_ RBase[EIRegs]; * Is there an IOCB? EICB_ MD, TIOA[EData], Branch[EINoCB, ALU=0]; * Branch if no IOCB Fetch_ T, EITemp2_ T_ Input, * Fetch ICSB.host, save first word of packet Branch[EIPLZ, IOAtten]; * Branch if zero-length packet * Address filtering. PD_ NOT MD, TIOA[EControl]; * (Done upside-down for placement) T_ A0, Branch[MultiTest, ALU>=0]; * Use multicast method if ICSB.host is negative * Traditional method: test for single host, broadcast, or promiscuous. T_ RCY[T, EITemp2, 10], Call[EAdrCk]; * T_ destination host, test for zero PD_ T-MD, Call[EAdrCk]; * Test for destination host = me PD_ MD, Call[EAdrCk]; * Test for me = 0 (promiscuous) * Packet not accepted by filter. * Tell hardware to ignore the rest of this packet. EIIgn: T_ WaitForBOP; * Wakeup at start of next packet EILast: Output_ T, Block, Branch[EIIdle]; Subroutine; EAdrCk: Branch[Accept, ALU=0]; Return; TopLevel; * Multicast method: use destination as an index into the ICSB.hosts array. MultiTest: T_ RSH[EITemp2, 14]; * T _ Word offset T_ T+(ICSB.hosts); Fetch_ T, EITemp1_ EITemp2; T_ MD, EIPtr_ ShC; EITemp1_ T, ShC_ EITemp1; * Shift count _ low 4 bits of destination host * (EITemp1[4:7]); R/T select bits get garbage PD_ ShiftNoMask[EITemp1]; * Left-justify selected bit (from EITemp1 or T) ShC_ EIPtr, Branch[EIIgn, ALU>=0]; * Reject if selected bit is zero Nop; * Packet accepted by filter. Set up buffer pointer and count. Accept: EIPtr_ A0, Call[EIOCBSetup]; T_ EITemp2, TIOA[EData], Block; * Recover first data word, await next * Input task (cont'd) * Input main loop. * Each iteration stores the word previously input from the interface * and inputs the next word. This facilitates end-of-packet handling. * EIPtr has negative of number of words remaining in the buffer. * IOAtten branches if the data word being read is the end-of-packet status. EIPtr_ (Store_ EIPtr)+1, DBuf_ T, Branch[EIEnd, IOAtten]; T_ Input, Block, Branch[.-1, ALU#0]; * Get here when buffer is exactly full (EIPtr=0). * We know that the word in T is not EOP status since we would have taken * the IOAtten branch if it were. If IOAtten is now true, then the word in T * is the CRC (which we must discard) and the next input is the status word. * If IOAtten is false, the buffer has overflowed. EIPtr_ EITemp1; * Set packet length = buffer length T_ Input, Branch[EIEnd1, IOAtten]; * Branch if end-of-packet T_ CS.bufferOverflow, Branch[EIPost]; * Normal end-of-packet exit from main loop. * One extra word (the CRC) has been stored in the buffer and must not be * included in the count. The next input is the status word. EIEnd: T_ (EITemp1)-1; * Initial buffer length EIPtr_ (EIPtr)+T; * Actual packet length T_ Input; * Read status word EIEnd1: T_ T OR (CS.done); * Done completion status * Post input completion status now in T. EIPost: EITemp1_ ICSB.next; Call[EPost]; Block, Branch[EIIdle]; * Await next packet * Here if packet length zero, i.e., first word input was end-of-packet status. * This should happen only if receiver-detected collision reporting is turned * on, since runt packets are ordinarily filtered out by the phase decoder. EIPLZ: EIPtr_ A0, Branch[EIEnd1]; * Length_ 0, post done status * Discard packet because IOCB queue is empty. EINoCB: T_ T+1, TIOA[EControl]; * Increment ICSB.missed Fetch_ T; EITemp1_ MD+1; Store_ T, DBuf_ EITemp1, Branch[EIIgn]; * Ignore rest of packet *---------------------------------------------------------------------------- * Output Task *---------------------------------------------------------------------------- Set[XTask, IP[EOT]]; * Idle state of the Ethernet output task. TIOA=EControl. * Wake up here at end of previous packet or when poked by software. EOIdle: MemBase_ IOBR; T_ OCSB.next; * Fetch OCSB.next T_ (Fetch_ T)+(Sub[OCSB.minPacketSpacing!, OCSB.next!]C); RBase_ RBase[EORegs]; Fetch_ T, EOCB_ PD_ MD; * Fetch OCSB.minPacketSpacing T_ (EOCB)+(IOCB.load), Branch[EONoCB, ALU=0]; * Go shut off transmitter if no IOCB * Test and update load Fetch_ T, EOTemp2_ MD; * Fetch IOCB.load EOTemp1_ MD+MD+1; * Load lsh 1 +1 Store_ T, DBuf_ EOTemp1, EOTemp1_ MD, * Store new load, save current Call[Random]; * T_ random number T_ RSH[T, 10]; * Use leftmost 8 bits * 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_ ((EOTemp1) AND T) LSH 1, Branch[ELodOv, R<0]; PD_ T-T, Branch[EWaitC, ALU#0]; * Carry_ 1; branch if wait required * On first transmission attempt, enforce minimum inter-packet spacing. * Note: ITLo counts 32-usec intervals. EOTemp2 = OCSB.minPacketSpacing T_ EOTime, RBase_ RBase[ITLo]; * Time last transmission ended T_ (ITLo)-T, RBase_ RBase[EORegs]; * Inter-packet spacing T_ ((EOTemp2)-T-1) LSH 1; * Compare with min permitted EWaitC: EOTemp1_ T-1, Branch[EOGo, Carry']; * Start now if exceeded min * Must wait before retransmitting. EOTemp1 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; EOTemp1_ (EOTemp1)-1, Output_ T, Block, Branch[.-1, R>=0]; * Now time to transmit packet. Set up buffer pointer and count. EOGo: EOPtr_ T-T-1, Call[EIOCBSetup]; EOPtr_ (Fetch_ EOPtr)+1; * Pre-fetch the first word TIOA[EData], Branch[EOEnd, ALU=0]; * Output task (cont'd) * Output main loop. * Data transfer is pipelined: each iteration outputs a data word * fetched in the previous iteration and starts the fetch of the data for * the next iteration. This way, cache misses generally occur while our * task is blocked, and lower-priority tasks are permitted to run while * the cache is being loaded. * 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, T_ MD, Branch[EOAbrt, IOAtten]; Output_ T, Block, Branch[.-1, ALU#0]; * Next-to-last word has been output and last word has been fetched EOEnd: T_ EOTemp1, Output_ MD; * Output last word EOPtr_ T, TIOA[EControl]; * Set words used = buffer length T_ SendEOP; * Declare end of packet Output_ T, Block; * Wake up here only when packet completely sent or collision has occurred. EOTemp1_ Input, Branch[EOAbr1, IOAtten]; * Branch if aborted, get status RBase_ RBase[ITLo]; * Remember current time T_ ITLo, RBase_ RBase[EORegs]; EOTime_ T; EODone: T_ (EOTemp1) AND (377C); * Just the status bits T_ T OR (CS.done); * Done completion status * Post ending status and reset interface for next packet. EOPost: EOTemp1_ OCSB.next; Call[EPost]; * Post status in T T_ TurnOffTx, Branch[EORest]; * Turn transmitter off and on again * Here if collision or other error occurred. * If a collision, just try again. If not, post output done. EOAbrt: T_ (EOTemp1)-1; * Initial buffer length EOPtr_ (EOPtr)+T, TIOA[EControl]; * Words actually sent EOTemp1_ Input; * Read status * If not a collision, post output done with whatever status bits we got. EOAbr1: PD_ (EOTemp1) AND (TxCollision); * Collision? T_ TurnOffTx, Branch[EODone, ALU=0]; * Branch if not EORest: Output_ T; * Turn off to reset collision bit T_ TurnOnTx, Branch[EOLast]; * Turn on and try again * Load overflow, post error status ELodOv: T_ CS.loadOverflow, Branch[EOPost]; * Load overflow post code * Shut off hardware because IOCB queue is empty. EONoCB: T_ TurnOffTx, Branch[EOLast]; EOLast: Output_ T, Block, Branch[EOIdle]; *---------------------------------------------------------------------------- * Task-independent Subroutines *---------------------------------------------------------------------------- *---------------------------------------------------------------------------- EIOCBSetup: * Set up IOCB pointers and counts * Enter: ExCB = IOCB being worked on * ExPtr = 0 for input, -1 for output * MemBase = IOBR * Exit: ExPtr = negative word count * MemBase = ExBR * ExBR = base register properly set up for indexing by ExPtr * Points the base register beyond the end of the buffer -2^16, and * addresses the buffer with a negative index in ExPtr. * Clobbers ExTemp1, T *---------------------------------------------------------------------------- Subroutine; T_ (ExCB)+(IOCB.length); T_ (Fetch_ T)+1; * Fetch IOCB.length T_ (Fetch_ T)+1, ExTemp1_ MD; * Fetch low buffer pointer Fetch_ T, T_ MD, * Fetch high buffer pointer ExPtr, Branch[.+2, R<0]; * Which base register? T_ T+(ExTemp1), MemBase_ EIBR, Branch[.+2]; * Low pointer + length T_ T+(ExTemp1), MemBase_ EOBR; * Low pointer + length T_ MD, BRLo_ T; T_ T-1, XorSavedCarry; * Adjust high for negative indexing T_ ExTemp1, BRHi_ T; ExPtr_ (0S)-T, Return; * ExPtr_ negative count *---------------------------------------------------------------------------- EPost: * Post command completion. * Enter: T = completion status word * ExPtr = actual packet length * ExCB = IOCB being worked on * ExTemp1 = ICSB or OCSB pointer (IOBR relative) * Exit: MemBase = IOBR * Clobbers T, ExCB, ExTemp1, RBase *---------------------------------------------------------------------------- Subroutine; MemBase_ IOBR; ExCB_ (Fetch_ ExCB)+1; * Fetch IOCB.next T_ (Store_ ExCB)+1, DBuf_ T; * Store IOCB.completion Store_ T, DBuf_ ExPtr, T_ MD; * Store IOCB.wordsUsed ExTemp1_ (Store_ ExTemp1)+1, DBuf_ T; * Store xCSB.next Fetch_ ExTemp1; * Fetch xCSB.interruptMask RBase_ RBase[WP]; * Initiate interrupts WP_ (WP) OR MD, Reschedule, Return; *---------------------------------------------------------------------------- Random: * Generate random number and return it in T. * Must have RBase[RNum] (=EORegs). * Uses algorithm: * R_ (2^11 + 2^2 + 2^0)* R + 13849 *---------------------------------------------------------------------------- Subroutine; KnowRBase[RNum]; T_ LSH[RNum, 11]; * T_ 2^9 * R T_ T+(RNum); * (2^9 + 2^0)* R T_ LSH[T, 2]; * (2^11 + 2^2)* R T_ T+(RNum); * (2^11 + 2^2 + 2^0)* R T_ RNum_ T+(RConst), Return; * +13849 TopLevel;