:IF[With10MB]; ************************************** TITLE[ENXTask]; % Fiala 1 April 1982: eliminate OnPage[EtherInitPage], reformat; absorb ENXDefs; use macro to eliminate all the duplicate register defs; eliminate pENXNotifyReg/2/3; conditionally assemble if With10MB. HGM, November 29, 1981 10:57 PM, Patch for wakeup confusion HGM, November 28, 1981 8:02 PM, Make IOCB Chaining Atomic HGM, November 27, 1981 10:40 PM, Late Collision trap HGM, November 20, 1981 3:26 PM, Merge into Fiala's world HGM, October 27, 1981 4:22 PM, Use last few words in buffer HGM, October 5, 1981 1:09 AM, Fixup Input end test HGM, June 24, 1981 1:46 AM, Expand Runt filter to 4 data words HGM, June 24, 1981 12:22 AM, Patch for wakeup pipeline delay HGM, June 16, 1981 8:03 PM, Interlock xxIndex @ xxSetup HGM, May 27, 1981 12:51 PM, Merge dispatch tables HGM, May 25, 1981 6:49 PM, massive fiddling HGM, May 21, 1981 8:15 PM, IOATTEN vs Fault task fixes HGM, May 7, 1981 11:02 PM, subtract off extra byte (anti dribble hardware change) HGM, April 23, 1981 10:18 PM, Rubiconize and whatever HGM, From Tom Henning's version of January 8, 1981 2:52 PM % Set[ZeroModFour,And[enxTask,14]]; SetTask[ZeroModFour]; %RM definitions The ENXReg macro defines a register for task ZeroModFour which will be used in the program, and the addressing mechanism will automatically displace references to another group of 20b registers as appropriate for whatever task is running. In addition, ENXReg defines offset names for each of the enx tasks for use when referencing the registers from Midas. The names defined are: ENX#1, ENX1#1, ENX2#1, and ENX3#1, where one of the last three names is suppressed if coincident with ENX#1. % Macro[TSReg,IFE[And[#1,3],0,,RV[enx#2,Add[LShift[And[#1,3],4],#3]]]]; Macro[ENXReg,( TSReg[enxTask,1#1,#2], TSReg[enxTask2,2#1,#2], TSReg[enxTask3,3#1,#2], RV[enx#1,#2])]; ENXReg[Temp1,0]; *quad temporary registers, also used by NotifyInterrupt ENXReg[Temp2,1]; *quad temporary registers, also used by NotifyInterrupt ENXReg[Temp3,2]; *quad temporary registers ENXReg[Temp4,3]; *quad temporary registers ENXReg[RcvIndex,4]; *receive buffer displacement, points to next word in buffer ENXReg[RcvCount,5]; *receive buffer word counter, words left in buffer ENXReg[RcvPtr,6]; *low base register pointer to receive buffer ENXReg[RcvPtrHi,7]; *high base register pointer to receive buffer ENXReg[XmtIndex,10]; *transmit buffer displacement, points to next word in buffer ENXReg[XmtCount,11]; *transmit buffer word counter, words left in buffer ENXReg[XmtPtr,12]; *low base register pointer to transmit buffer ENXReg[XmtPtrHi,13]; *high base register pointer to transmit buffer ENXReg[CSBPtr,14]; *short pointer to CSB ENXReg[CSBPtrHi,15]; *high base pointer to CSB, set to zero ENXReg[Link,16]; *linking register for saving APC&APCTASK ENXReg[Temp,17]; *temporary register *Additional overlay R Register definitions ENXReg[InitialCSB,0]; *CSB pointer passed in this reg from initialize.mc ENXReg[ICBPtr,2]; *Short pointer to ICB ENXReg[OCBPtr,2]; *Short pointer to OCB ENXReg[DestID1,0]; *Input Packet DestinationID, first word ENXReg[DestID2,1]; *Input Packet DestinationID, second word ENXReg[DestID3,2]; *Input Packet DestinationID, third word ENXReg[DestID4,3]; *Input Packet, fourth word ENXReg[NextICB,4]; *Short pointer to next ICB ENXReg[HostID1,5]; *First word of HostID -- Sign bit on if promiscious ENXReg[HostID2,6]; *Second word of HostID ENXReg[HostID3,7]; *Third word of HostID ENXReg[XmtMask,10]; *Retransmission mask ENXReg[XmtIntLo,10]; *Retransmission interval, low bits ENXReg[XmtIntHi,11]; *Retransmission interval, high bits ENXReg[ZeroBase,15]; *used to access ICB's and OCB's, value is always zero ENXReg[Completion,17]; *Completion word *IOCB pointers MC[ENXIndexNext,0]; *Short Pointer to next ICB or OCB MC[ENXIndexXmtMask,1]; *Retransmission Mask MC[ENXIndexCompletion,3]; MC[ENXIndexBuffer,4]; *Long pointer and length (Quadword aligned) *CSB pointers MC[ENXIndexOCB,0]; *current OCB pointer MC[ENXIndexIWake,2]; *Wakeup Bitmask MC[ENXIndexOWake,1]; *Wakeup Bitmask MC[ENXIndexICB,4]; *current ICB pointer MC[ENXIndexNoICBCount,3]; *Count of times when no ICB chain (For test only) MC[ENXIndexScratch,10]; *Quadword scratch area *Controller Hardware Input Registers address definitions Set[ENXDevID,0]; *Device I.D. number Set[ENXStatus1,1]; *Status1 register Set[ENXXmtStatus,15]; *Status1 register, sets Mode Latch to Xmt Mode Set[ENXRcvStatus,11]; *Status1 register, resets Mode Latch to Rcv Mode Set[ENXStatus2,2]; *Status2 register Set[ENXReadState,2]; *read State register, via Status2 register Set[ENXExcessCount,2]; *Excess Count, via Status2 register Set[ENXInData,3]; *Input data for INPUT operation Set[ENXIData,Add[LShift[ZeroModFour,4],3]]; *Input data for IOSTORE16 operation *Controller Hardware Output Registers address definitions Set[ENXResetState,0]; *General Reset Set[ENXIState,1]; *InState register Set[ENXOState,2]; *OutState register Set[ENXOutData,4]; *Output data for OUTPUT operation Set[ENXOData,Add[LShift[ZeroModFour,4],4]]; *Output data for IOFETCH16 operation *Status bit definitions Set[ENXSIBA,4000]; *Input Bad Alignment Set[ENXSIORun,2000]; *Input overrun Set[ENXSIBadPkt,1000]; *Input Bad Packet Set[ENXSICRC,400]; *Input Bad CRC Set[ENXSOPAR,200]; *Output Bad Parity Set[ENXSOURun,100]; *Output Underrun Set[ENXSOCOLL,40]; *Transmitter-detected collision (Collision) Set[ENXSOFAULT,20]; *Output Data Fault MC[ENXISMask,OR[ENXSIBadPkt,ENXSIORun,ENXSICRC,ENXSIBA]]; *Status bits bits MC[ENXOSMask,OR[ENXSOURun,ENXSOCOLL,ENXSOFAULT,ENXSOPAR]]; *Output Status bits MC[ENXOCollisionMask,ENXSOCOLL]; *Completion codes MC[ENXGoodPacket,040000]; MC[ENXErrorZeroBuf,064000]; *On input (length<3) and output (length=0) MC[ENXPktBufOverrun,061000]; *Input only MC[ENXErrorCountOV,062000]; *Output only MC[ENXLateCollision,063000]; *Output only *State Register command words MC[ENXResetAll,0]; *Resets InState and OutState registers *InState Register command words MC[ENXDisableInput,Or[LShift[1,14],0]]; *Resets: Enable Input MC[ENXEnableInput,Or[LShift[11,14],0]]; *Sets: Enable Input MC[ENXSetPurgeMode,Or[LShift[15,14],0]]; *Sets: Enable Input, PurgeMode *OutState Register command words MC[ENXSetOutputEOP,340]; *Sets: Enable Output, OutputEOP MC[ENXEnableOutput,300]; *Sets: Enable Output, Output Buffer MC[ENXDisableOutput,0]; *Clears: Output state register, Reset Output Buffer MC[ENXResetBuffer,200]; *Clears: Reset Output Buffer *Preamble word constant definition MC[ENXPream,125]; *Ethernet II preamble byte (0101010101010101) MC[ENXPreamLast,200]; *to set bit for last preamble word (0101010111010101) *Timer definitions SetTask[TTask]; Set[TimerRunning,5]; *State 5 is simple timer Set[TimerIdle,4]; *State 4 is idle MC[ENXTimerMask,LShift[TimerRunning,14]]; MC[ENXIdleTimer,LShift[TimerIdle,14]]; MC[pENXRandomReg,IP[ClockLo]]; * random number % *CSB definitions 00 Short pointer to First OCB 01 Output wakeup bit mask 02 Input wakeup bit mask 03 Packets missed (for debugging) 04 Short pointer to First ICB 05 Host Address (MSB) 06 Host Address 07 Host Address (LSB) 10 Quadword scratch buffer 11 Quadword scratch buffer 12 Quadword scratch buffer 13 Quadword scratch buffer 14 Used by test program 15 Used by test program 16 Used by test program 17 Used by test program *ICB definitions 00 Link to next ICB 01 Unused 02 Unused 03 Completion word (Ending Status) 04 Bytes used in buffer 05 Length of buffer (number of bytes) 06 Low pointer to buffer 07 High pointer to buffer *OCB definitions 00 Link to next OCB 01 Retransmission Mask 02 Unused 03 Completion word (Ending Status) 04 Unused 05 Length of buffer (number of bytes) 06 Low pointer to buffer 07 High pointer to buffer % SetTask[ZeroModFOur]; ******** INITIALIZATION MICROCODE ********(1376)\f5 *Microcode is notified at ENXInitLoc (from Initialize) to setup things. ENXInitSetup: ENXTemp3 _ IP[enxNotify]C, GoTo[ENXInitMain], At[ENXInitLoc]; ENXTemp3 _ IP[enxNotify2]C, GoTo[ENXInitMain], At[ENXInitLoc2]; ENXTemp3 _ IP[enxNotify3]C, GoTo[ENXInitMain], At[ENXInitLoc3]; ENXInitMain: T _ (SStkP&NStkP) xor (377C); StkP _ ENXTemp3, ENXTemp3 _ T, NoRegILockOK; *Compute value for ENXONotify register, used for notify after timer wakeup. T _ (CTask&NCIA) and (170000C); ENXTemp _ T; ENXTemp _ (ENXTemp) or (LoA[ENXOTimerDoneLoc]); T _ ENXTemp _ (ENXTemp) or (HiA[ENXOTimerDoneLoc]); Stack _ T; StkP _ ENXTemp3; T _ ENXInitialCSB; ENXCSBPtr _ T; LoadPage[ENXPage]; ENXCSBPtrHi _ 0C, GoToP[ENXReset]; *Also zeroes ENXZeroBase *Microcode is notified at ENXReset (from StartIO) to reset things. OnPage[ENXPage]; ENXReset: Input[ENXTemp,ENXXmtStatus], At[ENXStartLoc]; ENXTemp _ ENXResetAll; Output[ENXTemp, ENXResetState]; ENXXmtPtrHi _ 1C; *See comments at ENXOTimerDone ENXXmtIntLo _ ENXIdleTimer, Call[ENXLoadTimer]; Call[ENXSetupLink]; %Sleep here until awaken by by an Input or Output wake request microcode is set to Recv Mode whenever microcode is re-initialized ******** INPUT MICROCODE ********\f5 set Purge Mode We get here for 3 reasons: 1) The filter rejected this packet, 2) We wanted it, but didn't have an IOCB setup yet, or 3) We just finished processing a packet. % ENXIPurge: ENXTemp _ ENXSetPurgeMode, Call[ENXWriteInState]; * Call[ENXRet]; *Pipeline delay krock *Idle state of input microcode *Wait for the next packet to arrive *or the driver to poke the output hardware *or the the timer to go off and poke the output hardware Input[ENXTemp,ENXStatus1], Call[ENXDispatcher]; * ADDRESS RECOGNITION: * Check if the packet, * is explicitly for this host (dest=us), * or is broadcast (dest bit 7 set), * or if we are promiscuous (sign bit of word is on). ENXIBegin: IOStore4[ENXCSBPtr,ENXIData,ENXIndexScratch!], Call[ENXRet]; PFetch4[ENXCSBPtr,ENXNextICB,ENXIndexICB!], Call[ENXRet]; *Host Address T _ ENXHostID1; PFetch4[ENXCSBPtr, ENXDestID1, ENXIndexScratch!], Skip[ALU>=0]; LU _ ENXNextICB, GoTo[ENXIForMe]; *sign bit on => we are promiscuous T _ LCy[ENXDestID1,7]; T _ (LCy[ENXHostID1,7]) xor T, Skip[ALU>=0]; LU _ ENXNextICB, GoTo[ENXIMulticast]; *Multicast Mode T _ ENXDestID2, Skip[ALU=0]; GoTo[ENXIPurge]; *First word didn't match T _ (ENXHostID2) xor T; T _ ENXDestID3, Skip[ALU=0]; GoTo[ENXIPurge]; *Second word didn't match T _ (ENXHostID3) xor T; LU _ ENXNextICB, Skip[ALU=0]; GoTo[ENXIPurge]; *Third word didn't match ENXIMulticast: *take all Multicast for now ENXIForMe: GoTo[ENXIRead,ALU#0]; * No buffer for this packet, bump counter for debugging PFetch1[ENXCSBPtr,ENXTemp,ENXIndexNoICBCount!]; ENXTemp _ (ENXTemp) + 1; PStore1[ENXCSBPtr,ENXTemp,ENXIndexNoICBCount!], GoTo[ENXIPurge]; ENXIRead: T _ (ENXNextICB) + (ENXIndexBuffer), Call[ENXRcvSetup]; LU _ (ENXRcvCount) - (22C); * (4 for end test, 4 for first clump) * 2 for bytes ENXRcvCount _ (ENXRcvCount)-(20C), Skip[ALU>=0]; T _ ENXCompletion _ ENXErrorZeroBuf, GoTo[ENXIMark]; * Hackery for off-by-one buffer alignment PStore1[ENXRcvPtr, ENXDestID1, 0]; PStore2[ENXRcvPtr, ENXDestID2, 1], ODDOK; PStore1[ENXRcvPtr, ENXDestID4, 3], Call[ENXRet]; Input[ENXTemp, ENXInData]; PStore1[ENXRcvPtr, ENXTemp, 4], Call[ENXRet]; ENXRcvCount _ (ENXRcvCount)-(2C); (ENXRcvIndex) _ (ENXRcvIndex)+(1C); * Hackery to test Overrun: * ENXTemp _ 10000C, Call[ENXRet]; * ENXTemp _ (ENXTemp)-1, Skip[R<0]; * GoTo[ENXRet]; * NOP; ENXIReLoop: Call[ENXRet]; * set up TPC for data transfer loop ENXILoop: ENXRcvCount _ (ENXRcvCount) - (10C), Skip[R>=0]; T _ ENXRcvIndex _ (ENXRcvIndex) + (4C), GoTo[ENXIFull]; T _ ENXRcvIndex _ (ENXRcvIndex) + (4C); IOStore4[ENXRcvPtr,ENXIData]; ENXRcvCount _ (ENXRcvCount) - (10C), Skip[R>=0]; T _ ENXRcvIndex _ (ENXRcvIndex) + (4C), GoTo[ENXIFull]; T _ ENXRcvIndex _ (ENXRcvIndex) + (4C), GoTo[ENXILoopAttn,IOAtten']; IOStore4[ENXRcvPtr,ENXIData], Return; ENXILoopAttn: Input[ENXTemp,ENXStatus1], Call[ENXDispatcher]; T _ ENXRcvIndex; IOStore4[ENXRcvPtr,ENXIData], GoTo[ENXIReLoop]; *Finish up the receive packet *Calculate packet length ENXIEnd: Input[ENXTemp1,ENXExcessCount], At[ENXDispTableLoc,1]; T _ LdF[ENXTemp1,13,5], Call[ENXRet]; * Get excess byte count ENXRcvIndex _ LSh[ENXRcvIndex,1]; * change words to bytes ENXRcvIndex _ (ENXRcvIndex) - T; *adjust total byte count *Without this test, tiny packets look huge because of underflow *Spec calls for min of 60 bytes (30 words) LU _ (ENXRcvIndex) - (15C); *8 bytes is 4 data words ENXRcvIndex _ (ENXRcvIndex) - (5C), Skip[ALU>=0]; * 4 CRC and 1 mumble GoTo[ENXIPurge]; Input[ENXTemp2, ENXRcvStatus]; T _ ENXTemp2 _ (ENXTemp2) and (ENXISMask); ENXCompletion _ T, Skip[ALU#0]; ENXCompletion _ ENXGoodPacket, GoTo[ENXIMark]; *Buffer is full. Check for end of packet before posting error. ENXIFull: Input[ENXTemp,ENXStatus1], Call[ENXDispatcher]; T _ ENXCompletion _ ENXPktBufOverrun, GoTo[ENXIMark]; ENXIMark: ENXCompletion _ (ENXCompletion) or T, Call[ENXFetchICB]; T _ (ENXICBPtr) + (ENXIndexCompletion), Call[ENXStoreCompletion]; T _ (ENXICBPtr) + (ENXIndexBuffer), Call[ENXStoreWordsUsed]; T _ ENXIndexIWake, Call[ENXInterrupt]; T _ (ENXICBPtr) + (ENXIndexNext), Call[ENXChainToNextIOCB]; PStore1[ENXCSBPtr,ENXICBPtr,ENXIndexICB!], GoTo[ENXIPurge]; \f5 242b2B4b33B1647b157B4b11B6b54B4b82B1b47B4b35B5b9B4b17B ******** OUTPUT MICROCODE ********\f5 ENXSetupLink: UseCTask; T _ APC&APCTASK; ENXLink _ T, GoTo[ENXOIdle]; ENXNoWork: ENXTemp _ ENXDisableOutput, Call[ENXWriteOutState]; * Call[ENXRet]; * Pipeline delay krock * Idle state of output microcode. * on a wakeup, is it a transmit wake or receive wake? ENXOIdle: Input[ENXTemp, ENXStatus1], Call[ENXDispatcher]; PFetch1[ENXCSBPtr, ENXOCBPtr, ENXIndexOCB!], GoTo[ENXOCheckNext]; ENXOCheck: T _ (ENXOCBPtr)+(ENXIndexXmtMask), Skip[ALU#0]; * ALU=0 => no IOCB, no packet to transmit GoTo[ENXNoWork]; OddPFetch1[ENXZeroBase,ENXXmtMask], Call[ENXRet]; ENXXmtMask _ (LSh[ENXXmtMask,1])+1, Skip[R>=0]; * R<0 => overflow T _ ENXCompletion _ ENXErrorCountOV, GoTo[ENXOMark]; OddPStore1[ENXZeroBase,ENXXmtMask]; % We are about to start sending a packet. In order to help hosts with half duplex interfaces avoid missing packets in the window behind a packet we just sent, we would like to delay for 1ms or so if we are sending them another packet. We approximate that by always waiting. Taft+Dorado do it a bit better by remembering the time that the last packet finished getting sent. Maybe we should do that too. % NOP; *Aviod bypass kludge ENXOWait: T _ (ENXXmtMask) xor (1C); *is this the first attemp to transmit? Skip[ALU#0]; ENXXmtIntLo _ 24C, GoTo[ENXOCountdown]; *yes, count 1 msec, 24B is 20D = 1000/50 *Compute countdown interval, Use high resolution clock for random number T _ GETRSPEC[103] XOR (377C); *Stkp (reads inverted) ENXTemp _ pENXRandomReg; *point to "random" register StkP _ ENXTemp, ENXTemp _ T, NoRegILockOK; *Form new transmission interval mask, check if old has overflowed T _ CTask; *in case we have 2 boards on the same machine T _ (LdF[Stack,6,12]) xor T; *Get low 10 bits StkP _ ENXTemp; *Restore stack-pointer ENXXmtIntLo _ (RSH[ENXXmtMask,1]) and T; *Mask random number with old value *ENXXmtIntLo now has (10bit) number of Ethernet ticks to wait. % We need a tick size of 512 bit times. The timers on the D0 have a basic tick size of 64 times the clock speed. If the clock is 100ns, thats 6.4 microsec. 8*6.4 is 51.2. How convient. 8=2^3, so thats why there is this LSH by 3. % ENXOCountdown: ENXXmtIntLo _ LSH[ENXXmtIntLo,3], Skip[ALU#0]; GoTo[ENXOGo]; *At this point, ENXXmtIntLo contains the number of D0 ticks to wait. (13 bits) *Unfortunately, that doesn't fit into a simple timer. *We process the overflow with software. *The timer holds 7 bits, but we use only 6 to save an instruction. ENXXmtPtrHi _ 0C; T _ (LdF[ENXXmtIntLo,3,7]); ENXXmtIntHi _ T, Call[ENXRet]; ENXXmtIntLo _ LdF[ENXXmtIntLo,12,6]; ENXOLoadTimer: ENXXmtIntLo _ LSh[ENXXmtIntLo,4]; ENXXmtIntLo _ (ENXXmtIntLo) or (ENXTimerMask), Call[ENXLoadTimer]; * Call[ENXRet]; * Pipeline delay krock *Get here if a packet arrives or the timer goes off and enables output *(or a buggy driver enables output) ENXODisp: Input[ENXTemp,ENXStatus1], Call[ENXDispatcher]; *Return here when output enabled by timer *Glitch Chasing Input[ENXXmtIntLo, ENXStatus2]; LU _ (ENXXmtIntLo) AND (100000C); * Reset Output' Skip[ALU=0]; PFetch1[ENXCSBPtr, ENXOCBPtr, ENXIndexOCB!], GoTo[ENXOGo]; *Barf, input got turned off too late to back put of the pipeline *This happens if the tail of a packet was generating wakeups and *PktGap gets reset when another packet arrives. NOP; * Allocation Call[ENXRet]; GoTo[ENXODisp]; *Glitch Chasing * PFetch1[ENXCSBPtr, ENXOCBPtr, ENXIndexOCB!], GoTo[ENXOGo]; *Timer has expired, Timer task notifies at ENXOTimerDoneLoc. Note that we *can't smash our TPC, or any temp RM registers, or T because we may be in *the middle of receiving a packet. ENXOTimerDone: *ENXXmtPtrHi=0 indicates we are expecting a Timer to go off. *There is a race condition when trying to kill timers, * and/or a buggy driver might wake us up at a bad time. *Even if we are transferring into the first 64K, it will be non zero. LU _ ENXXmtPtrHi, At[ENXOTimerDoneLoc]; ENXXmtIntHi _ (ENXXmtIntHi) - 1, Skip[ALU=0]; PleaseTellHGMAboutThis: *This is possible, but very unlikely BreakPoint, ENXXmtIntHi _ (ENXXmtIntHi) + 1, Return; *Check if still more time to elapse before start of transmission. ENXXmtIntLo _ (2000C), Skip[ALU<0]; *2000C is 100C LSH 4 ENXXmtIntLo _ (ENXXmtIntLo) or (ENXTimerMask), GoTo[ENXLoadTimer]; ENXXmtIntLo _ ENXEnableOutput, GoTo[ENXWriteOutStateViaXmtIntLo]; *Load timer from data in ENXXmtIntLo, and disable the output hardware. *Note: There must be at least 14 cycles between timer instructions. *This code must be coordinated with Timer.mc and anything else that loads timers. *Note that we ABORT for a long time after the OUTPUT. ENXLoadTimer: ENXXmtPtr _ T; T _ CTask; *Timer slot is same as task number. ENXXmtIntLo _ (ENXXmtIntLo) or T; T _ ENXXmtPtr; LoadTimer[ENXXmtIntLo]; ENXXmtIntLo _ ENXDisableOutput; ENXWriteOutStateViaXmtIntLo: Output[ENXXmtIntLo, ENXOState]; ENXXmtIntLo _ ENXXmtIntLo, GoTo[ENXRet]; * Wakeup Pipeline *Countdown interval is over *Start to send words to the hardware. The hardware will start *the transmission to the Wire as soon as the buffer has *> 128 words in the hardware buffer, or the OutputEOP bit *is set (for a packet of less than 127 words) ENXOGo: T _ (ENXOCBPtr) + (ENXIndexBuffer), Call[ENXXmtSetup]; *Hackery below sends at least 5 words. (spec calls for 30) T _ (ENXXmtCount) - (13C); T _ (ENXOCBPtr) + (ENXIndexXmtMask), Skip[ALU>=0]; T _ ENXCompletion _ ENXErrorZeroBuf, GoTo[ENXOMark]; *Buffer too small *Send 4 word preamble (hardware doesn't compute CRC on first 4 words sent) T _ ENXTemp _ ENXPream; ENXTemp _ (LSh[ENXTemp,10]) or T; Output[ENXTemp,ENXOutData], Call[ENXILockENXTemp]; Output[ENXTemp,ENXOutData], Call[ENXILockENXTemp]; Output[ENXTemp,ENXOutData], Call[ENXILockENXTemp]; ENXTemp _ (ENXTemp) or (ENXPreamLast); Output[ENXTemp,ENXOutData], Call[ENXILockENXTemp]; ** Hackery for off-by-one buffer alignment PFetch1[ENXXmtPtr,ENXTemp,0]; ENXXmtIndex _ (ENXXmtIndex) + 1; ENXXmtCount _ (ENXXmtCount) - (2C); LU _ ENXTemp; Output[ENXTemp,ENXOutData], Call[ENXILockENXTemp]; ENXXmtCount _ (ENXXmtCount) - (11C); ENXXmtIndex _ (ENXXmtIndex) - (4C); ENXOReLoop: Call[ENXRet]; * Leaves TPC setup for ENXOLoop ENXOLoop: * Do 12 word transfers to experiment with Overruns * ENXXmtCount _ (ENXXmtCount) - (10C), Skip[R>=0]; * ENXXmtCount _ (ENXXmtCount) + (20C), GoTo[ENXOLastXfer]; * T _ ENXXmtIndex _ (ENXXmtIndex) + (4C); * IOFetch4[ENXXmtPtr,ENXOData]; ENXXmtCount _ (ENXXmtCount) - (10C), Skip[R>=0]; ENXXmtCount _ (ENXXmtCount) + (20C), GoTo[ENXOLastXfer]; T _ ENXXmtIndex _ (ENXXmtIndex) + (4C); IOFetch4[ENXXmtPtr,ENXOData]; ENXXmtCount _ (ENXXmtCount) - (10C), Skip[R>=0]; ENXXmtCount _ (ENXXmtCount) + (20C), GoTo[ENXOLastXfer]; T _ ENXXmtIndex _ (ENXXmtIndex) + (4C), GoTo[ENXOLoopAttn,IOAtten']; IOFetch4[ENXXmtPtr,ENXOData], Return; ENXOLoopAttn: IOFetch4[ENXXmtPtr,ENXOData]; Input[ENXTemp,ENXStatus1], Call[ENXDispatcher]; GoTo[ENXOReLoop]; *Normal exit from Output Loop is here ENXOLastXfer: ENXXmtCount _ (ENXXmtCount) xor (377C); ENXXmtCount _ (ENXXmtCount) and (37C); ENXXmtCount _ (ENXXmtCount) or (ENXSetOutputEOP); Output[ENXXmtCount,ENXOState], Call[ENXILockENXXmtCount]; T _ (ENXXmtIndex) _ (ENXXmtIndex) + (4C); IOFetch4[ENXXmtPtr,ENXOData], IOStrobe, Call[ENXRet]; *wakeup service requested when the packet is fully transmitted *or a packet arrives. ENXOAttn: Input[ENXTemp,ENXStatus1], Call[ENXDispatcher]; * Shouldn't return BreakPoint; ENXOEnd: Input[ENXTemp,ENXStatus1], At[ENXDispTableLoc,5]; Call[ENXFetchOCB]; *Beware: ENXCompletion is the same as ENXTemp T _ ENXTemp _ (ENXTemp) and (ENXOSMask); LU _ (ENXTemp) xor (ENXOCollisionMask), Skip[ALU#0]; ENXCompletion _ ENXGoodPacket, GoTo[ENXOMark]; *No errors LU _ (ENXXmtIndex) - (250C), Skip[ALU=0]; ENXCompletion _ T, GoTo[ENXOMark]; *Some error other than collision *Collision only, check for a late one (hardware debugging) *250C is 128 for buffer, 512bits=32 wds for collision, 8 slop T _ (ENXOCBPtr) + (ENXIndexBuffer), Skip[ALU>=0]; GoTo[ENXOFlap]; ENXXmtIndex _ LSh[ENXXmtIndex,1]; OddPStore1[ENXZeroBase,ENXXmtIndex]; T _ ENXCompletion _ ENXLateCollision; ENXOMark: ENXCompletion _ (ENXCompletion) or T; T _ (ENXOCBPtr)+(ENXIndexCompletion), Call[ENXStoreCompletion]; T _ ENXIndexOWake, Call[ENXInterrupt]; ENXONextBuf: T _ (ENXOCBPtr) + (ENXIndexNext), Call[ENXChainToNextIOCB]; PStore1[ENXCSBPtr,ENXOCBPtr,ENXIndexOCB!]; *Flap output enable to tell hardware that we have finished processing this packet ENXOFlap: ENXTemp _ ENXDisableOutput; Output[ENXTemp,ENXOState]; ENXTemp _ ENXTemp; ENXTemp _ ENXEnableOutput, Call[ENXWriteOutState]; ENXOCheckNext: LU _ ENXOCBPtr, GoTo[ENXOCheck]; \f5 140b2B4b34B2547b2B4b34B210b17B6b92B62b202B4b11B4b28B2482b102B3b31B3b22B7b21B4b19B146b80B3b7B5b47B4b90B79b3B59b3B116b3B59b3B ******** SUBROUTINES ********\f5 ENXDispatcher: UseCTask; T _ APCTask&APC; Dispatch[ENXTemp,14,3]; Disp[ENXDispTable]; ENXDispTable: *000 Receive Data wakeup from idle state (no Atten) ENXRet: Return, At[ENXDispTableLoc,0]; *ENXIEnd has an At[ENXDispTableLoc,1]; *001 Receive End-of-packet *010 Output Packet Done Input[ENXTemp,ENXXmtStatus], At[ENXDispTableLoc,2]; ENXLink _ T, GoTo[ENXOEnd]; *011 Transmit Data Wake Request Input[ENXTemp,ENXXmtStatus], At[ENXDispTableLoc,3]; ENXSwitch: APCTask&APC _ ENXLink, ENXLink _ T, NoRegILockOK, GoTo[ENXRet]; *100 Transmit Data wakeup from idle state or timer wait (no Atten) Return, At[ENXDispTableLoc,4]; *101 Output Packet Done * ENXOEnd has an At[ENXDispTableLoc,5]; *110 Receive End-of-Packet Input[ENXTemp,ENXRcvStatus], At[ENXDispTableLoc,6]; ENXLink _ T, GoTo[ENXIEnd]; *111 Receive Data Wake Request Input[ENXTemp,ENXRcvStatus], GoTo[ENXSwitch], At[ENXDispTableLoc,7]; *Notify the driver by generating appropiate interrupts. *This could be merged with ENXChainToNextIOCB, * but that would be too long between TASKs. * T=ENXIndexIWake notifies receive software with receive bit mask * T=ENXIndexOWake notifies transmit software with transmit bit mask ENXInterrupt: PFetch1[ENXCSBPtr,ENXTemp]; *Fetch wakeup bitmask LoadPage[NotifyInterruptPage]; T _ ENXTemp, GoToP[NotifyInterrupt]; * Head assumes that this is Atomic. ENXChainToNextIOCB: * assumes that ENXICBPtr is the same as ENXOCBPtr OddPFetch1[ENXZeroBase,ENXOCBPtr]; LU _ ENXOCBPtr, UseCTask, GoTo[ENXRet]; ENXWriteInState: Output[ENXTemp,ENXIState], GoTo[ENXILockENXTemp]; ENXWriteOutState: Output[ENXTemp,ENXOState], GoTo[ENXILockENXTemp]; ENXILockENXTemp: ENXTemp _ ENXTemp, GoTo[ENXRet]; *Dally for Wakeup Pipeline ENXILockENXXmtCount: ENXXmtCount _ ENXXmtCount, Return; * Handy subroutines convient for tasking even if they are only called once. * BEWARE of bypass problems ENXStoreCompletion: OddPStore1[ENXZeroBase, ENXCompletion], GoTo[ENXRet]; ENXStoreWordsUsed: OddPStore1[ENXZeroBase, ENXRcvIndex], GoTo[ENXRet]; ENXFetchICB: PFetch1[ENXCSBPtr,ENXICBPtr,ENXIndexICB!], GoTo[ENXRet]; ENXFetchOCB: PFetch1[ENXCSBPtr,ENXOCBPtr,ENXIndexOCB!], GoTo[ENXRet]; \f5 324f0 3f5 113f0 3f5 175f0 3f5 63f0 3f5 79f0 3f5 129f0 3f5 *Get buffer descriptor pointed to by T, fix base pointers *(ENXxxxPtr,ENXxxxPtrHi) by transforming ENXxxxPtrHi from (0, x) to (x, x+1) * zero out ENXxxxIndex ENXXmtSetup: OddPFetch4[ENXZeroBase,ENXXmtIndex]; ENXXmtIndex _ (ENXXmtIndex) and (0C); T _ (ENXXmtPtrHi) + 1; T _ ENXXmtPtrHi _ (LSH[ENXXmtPtrHi,10]) or T; ENXXmtPtrHi _ (FixVA[ENXXmtPtrHi]) or T, Return; ENXRcvSetup: OddPFetch4[ENXZeroBase,ENXRcvIndex]; ENXRcvIndex _ (ENXRcvIndex) and (0C); T _ (ENXRcvPtrHi)+1; T _ ENXRcvPtrHi _ (LSH[ENXRcvPtrHi,10]) or T; ENXRcvPtrHi _ (FixVA[ENXRcvPtrHi]) or T, Return; END[ENXTask];\f5 :ELSE; ********************************************** TITLE[No.10MB.Ethernet.Microcode]; :ENDIF; ********************************************* (1792)\f5