*---------------------------------------------------------------------------- Title[AltoEther.Mc...December 4, 1980 9:53 AM...Taft]; * Main I/O task-level Dorado Ethernet microcode (Alto emulation) *---------------------------------------------------------------------------- *---------------------------------------------------------------------------- * Note on instruction placement: *---------------------------------------------------------------------------- * Emulator and input task code are in separate pages (though neither * one completely fills up its page, so could share with other code). * Output task and subroutine code fit together on one page. * This means that the emulator and input tasks cannot use FF in * instructions that call common subroutines. Also, calls to EResI and * EResO must leave FF free. *---------------------------------------------------------------------------- * Initialization code for Output Task *---------------------------------------------------------------------------- Set[XTask, IP[EOT]]; Subroutine; EOTInitPC: T_ EOT, CoReturn; TopLevel; RBase_ RBase[EORegs]; T_ 33000C; * Init random number generator RConst_ T+(31C); * Constant (33031B = 13849) T_ 1400C; * Minimum inter-packet spacing MinPktSpc_ T+(110C), Branch[ExInit]; * 1510B = 840 => ~500 usec *---------------------------------------------------------------------------- * Initialization code for Input Task *---------------------------------------------------------------------------- Set[XTask, IP[EIT]]; Subroutine; EITInitPC: T_ EIT, CoReturn; TopLevel; MemBase_ ECBR; T_ 300C; T_ T+T, RBase_ RBase[AEmRegs]; BRHi_ EmuBrHiReg; BRLo_ T; * ECBR_ {EmuBrHiVal,,600} ExInit: T_ EControl; TIOA_ T, Block; * full TIOA for initialization Branch[.], Breakpoint; * Should never get here *---------------------------------------------------------------------------- * Input Task *---------------------------------------------------------------------------- Set[XTask, IP[EIT]]; * Non-emulator mode KnowRBase[EIRegs]; * Subroutine called to reset input hardware and task PC to idle state. * Call at EResI. Assumes TIOA selects Ethernet control register. * Tasking must be turned off by caller. EResI1: LdTPC_ EIT; * Load input TPC from Link EResI2: Link_ T, Branch[EIRetn]; * Restore caller's PC and return Subroutine; EResI: T_ TurnOffRx; * Turn off input hardware Output_ T; TopLevel; T_ Link, Call[EResI1]; * Link_ EIIdle, save caller's PC * Idle state of the Ethernet input task. * Wake up here when first word of a new packet arrives. EIIdle: MemBase_ ECBR; * Control block base register RBase_ RBase[EIRegs]; TIOA[EData]; * Select data I/O address EITemp2_ Input, Branch[EIPLZ, IOAtten]; * Save first word of packet TIOA[EControl]; * Select control register * Address filtering. Fetch_ EHLoc, T_ A0; * Fetch local host address 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. T_ WaitForBOP, Branch[EILast]; * Wakeup at start of next packet * Subroutine called by address filtering logic. * Returns if ALU#0 but falls through (i.e., accepts the packet) if ALU=0. Subroutine; EAdrCk: Branch[.+2, ALU=0]; EIRetn: Return; TopLevel; * Packet accepted by filter. * Reset output task if it is on (probably in retransmission wait). TaskingOff; Call[EResO]; * Must leave FF free TaskingOn; * Set up pointer and count. T_ EIPLoc; * Set up EIPtr, return pointer Call[EGPCnt]; * Must leave FF free MemBase_ EIBR, Branch[EIWCZ, ALU=0]; * Test count for zero, select BR BRLo_ T, T_ EITemp2, Block; * Load BR, recover 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. T_ Input, Branch[EIEnd1, IOAtten]; * Branch if end-of-packet T_ InBufOverflow, Branch[EIPost]; * Input buffer full post code * 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: EIPtr_ (1S)-(EIPtr); * Make ending count positive, add 1 T_ Input; * Read status word EIEnd1: T_ T AND (EISMask); * Mask out uninteresting status bits T_ T XOR (InDone); * Post input done status * Turn off interface and go to idle state (awaiting next SIO). EIPost: Call[EPost]; * Post status in T (must not use FF) T_ TurnOffRx; EILast: Output_ T, Block, Branch[EIIdle]; * Word count zero when interface started, post error status EIWCZ: T_ CountZero, Branch[EIPost]; * Word count zero post code * 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: Fetch_ EICLoc, T_ EITemp2; * Fetch input word count EIPtr_ MD, Branch[EIEnd1]; * Use as ending count, go report status *---------------------------------------------------------------------------- * Output Task *---------------------------------------------------------------------------- Set[XTask, IP[EOT]]; KnowRBase[EORegs]; * Subroutine called to reset output hardware and task PC to idle state. * Call at EResO. Assumes TIOA selects Ethernet control register. * Tasking must be turned off by caller. EResO1: LdTPC_ EOT; * Load output TPC from Link Branch[EResI2]; * Restore Link from T and Return Subroutine; EResO: T_ TurnOffTx; * Turn off output hardware Output_ T; TopLevel; T_ Link, Call[EResO1]; * Link_ EOIdle, save caller's PC * Idle state of the Ethernet output task EOIdle: MemBase_ ECBR; * Set up MemBase, TIOA RBase_ RBase[EORegs]; TIOA[EControl]; * Test and update load EOTemp1_ (Fetch_ ELLoc); * Fetch current load T_ MD+MD+1; * Load lsh 1 +1 Store_ EOTemp1, DBuf_ T, 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. EOTemp1_ ((EOTemp1) AND T) LSH 1, Branch[ELodOv, R<0]; Branch[EWait, ALU#0]; * Branch if wait required * On first transmission attempt, enforce minimum inter-packet spacing. * Note: RTClock counts 38-usec intervals in bits [0:9]. T_ EOTime, RBase_ RBase[RTClock]; * Time last transmission ended T_ T-(RTClock), RBase_ RBase[EORegs]; * -(inter-packet spacing) T_ T+(MinPktSpc); * Compare with min permitted T_ RSH[T, 5], Branch[EOGo, Carry']; * Start now if exceeded min * Note: RTClock time is in units of 38 usec, but we pretend it is 32. * Round up to ensure wait interval is nonzero. EOTemp1_ T+1; * Wait for remainder of interval * Must wait before retransmitting. EOTemp1 has wait time in 16-usec units. * If the input word count is nonzero, enable the receiver while waiting. EWait: Fetch_ EICLoc; * Fetch input word count PD_ MD; T_ TurnOnRx, Branch[.+2, ALU=0]; * Branch if no count set up Output_ T; * Enable input wakeups * Retransmission countdown wait loop. * CountDown turns off data wakeups and requests a wakeup at next * tick of countdown clock. T_ CountDown; EOTemp1_ (EOTemp1)-1, Output_ T; Block, Branch[.-1, ALU#0]; * Done waiting. Shut off the receiver if it was turned on. TaskingOff; Call[EResI]; * Must leave FF free TaskingOn; * Output task (cont'd) * Set up count and pointer EOGo: T_ EOPLoc, Call[EGPCnt]; * Set up EOPtr, return pointer MemBase_ EOBR, Branch[EOWCZ, ALU=0]; * Test count for zero, select BR BRLo_ T; * Load low BR * Select data I/O address, then pre-fetch the first word. EOPtr_ (Fetch_ EOPtr)+1; Branch[EOEnd, ALU=0]; * 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: Output_ MD; * Output last word TIOA[EControl]; * Select control register T_ SendEOP; * Declare end of packet Output_ T, Block; * Wake up here only when packet completely sent or collision has occurred. * Note that EOPtr=0, the proper value to post for normal ending word count. EOTemp1_ Input, Branch[EOAbr1, IOAtten]; * Branch if aborted, get status RBase_ RBase[RTClock]; * Remember current time T_ RTClock, RBase_ RBase[EORegs]; EOTime_ T; EODone: T_ (EOTemp1) AND (EOSMask); * Mask out uninteresting status bits T_ T XOR (OutDone); * Post output done status * Turn off interface and go to idle state (awaiting next SIO). EOPost: Call[EPost]; * Post status in T T_ TurnOffTx; EOLast: Output_ T, Block, Branch[EOIdle]; * Here if collision or other error occurred. * If a collision, just try again. If not, post output done. EOAbrt: EOPtr_ (1S)-(EOPtr); * Make count positive, add 1 TIOA[EControl]; EOTemp1_ Input; * Read status * If not a collision, post output done with whatever status bits we got. * The status bits posted are not actually defined in the Alto, but the software * will notice that the hardware status is not good and will therefore retry. EOAbr1: PD_ (EOTemp1) AND (TxCollision); * Collision? T_ TurnOffTx, Branch[EODone, ALU=0]; * Branch if not Output_ T; * Turn off to reset collision bit T_ TurnOnTx, Branch[EOLast]; * Turn on and try again * Word count zero when interface started, post error status EOWCZ: T_ CountZero, Branch[EOPost]; * Word count zero post code * Load overflow, post error status ELodOv: T_ LoadOverflow, Branch[EOPost]; * Load overflow post code *---------------------------------------------------------------------------- * Task-independent Subroutines *---------------------------------------------------------------------------- Subroutine; *---------------------------------------------------------------------------- * Post command completion. * Expects post code and status in T and ending word count in EIPtr or EOPtr. * Assumes RBase is set up but makes no assumption about MemBase or TIOA. * Returns with standard MemBase and TIOA set up. * Clobbers T and RBase. *---------------------------------------------------------------------------- EPost: MemBase_ ECBR; * Select BR for page 1 I/O locations T_ (Store_ EPLoc)+1, DBuf_ T; * Store ending status in EPLoc T_ (Fetch_ T)+1; * Fetch bit mask (know EBLoc=EPLoc+1) Store_ T, DBuf_ ExPtr, T_ MD; * Store word count (know EELoc=EBLoc+1) RBase_ RBase[NWW]; * T has interrupt bit mask NWW_ (NWW) OR T, TIOA[EControl]; * Or bit(s) into NWW Reschedule, Return; * Force emulator to notice *---------------------------------------------------------------------------- * Get input or output pointer and count. * Expects EIPLoc or EOPLoc in T (knows that EICLoc=EIPLoc-1, EOCLoc=EOPLoc-1). * Sets up ExPtr (EIPtr or EOPtr) and returns memory base in T (for BRLo_). * Upon return, ALU branches are conditioned by -count (for zero test). * Also sets TIOA_ EData. *---------------------------------------------------------------------------- EGPCnt: T_ (Fetch_ T)-1; * Fetch pointer T_ MD, Fetch_ T, ExPtr_ A0; * T_ pointer, fetch count * Want to set base register such that adding 2^16-count (treating -count * as an unsigned number) will address the first word of the packet. * Note that (pointer-(2^16-count)) mod 2^16 = pointer+count T_ T+MD; * T_ (pointer-(2^16-count)) mod 2^16 ExPtr_ (ExPtr)-MD, TIOA[EData], Return; * ExPtr_ -count *---------------------------------------------------------------------------- * Generate random number and return it in T. * Must have RBase[RNum] (=EORegs). * Uses algorithm: * R_ (2^11 + 2^2 + 2^0)* R + 13849 *---------------------------------------------------------------------------- KnowRBase[RNum]; Random: 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; (1552)