:IF[With10MB]; ************************************** TITLE[ENXTask]; %Ed Fiala 23 March 1984: Bum 40b mi net; use 4 Input's rather than an IOStore4 and PFetch4 at ENXIBegin to speed up message filter while eliminating 4-word CSB scratch area. Eliminate ENXTemp4 (already unused), ENXTemp3 (used only in microcode init), ENXTemp2 (only in ENXIEnd), and ENXTemp1 (only in ENXIEnd). Ed 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 Possible improvements: 1) Store the 4-word preamble somewhere in the first 64k words and output it with IOFetch4 rather than 4 Outputs (saves 5 mi, ~34 cycles/packet). 2) Wait 1 msec (or controllable variable) from the end of the last output packet rather than from the time of a new output request before beginning transmission of a new packet. 3) Find out why the input and output buffers are misaligned by 1 word. % 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[DestID2,0]; *Input Packet DestinationID, second word *Also used by NotifyInterrupt. ENXReg[DestID3,1]; *Input Packet DestinationID, third word *Also used by NotifyInterrupt. ENXReg[DestID4,2]; *Input Packet, fourth word ENXReg[DestID1,3]; *Input Packet DestinationID, first word ENXReg[RcvIndex,4]; *receive buffer displacement, points to next word in buffer ENXReg[RcvCount,5]; *receive buffer byte counter, bytes 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 byte counter, bytes 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 APCTask&APC 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[DestID5,3]; *Input packet, fifth 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[ENXIndexOWake,1]; *Wakeup Bitmask MC[ENXIndexIWake,2]; *Wakeup Bitmask MC[ENXIndexNoICBCount,3]; *Count of times when no ICB chain (For test only) MC[ENXIndexICB,4]; *current ICB pointer *10-13b in CSB, formerly scratch area, now unused *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 (1st 4 bits duplicate those in 2nd 4 bits) *100000b InOverrun * 40000b InBadCRC * 20000b InBadAlignment * 10000b 0 (always 0) 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 * 10b XmtMicrocodeFlag MC[ENXISMask,OR[ENXSIBadPkt,ENXSIORun,ENXSICRC,ENXSIBA]]; *Input Status 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 definitions Set[ENXPream,52525]; *Ethernet II preamble MC[ENXPreamLast,200]; *to set bit for last preamble word (52725b) *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-07 Host Address 10-13 Unused 14-17 Used by test program *ICB definitions 00 Link to next ICB 01-02 Unused 03 Completion word (Ending Status) 04 Bytes used in buffer 05 Length of buffer (number of bytes) 06-07 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-07 Pointer to buffer % SetTask[ZeroModFOur]; ******** INITIALIZATION MICROCODE ******** *Microcode is notified at ENXInitLoc (from Initialize) to setup things. ENXInitSetup: ENXTemp _ IP[enxNotify]C, GoTo[ENXInitMain], At[ENXInitLoc]; ENXTemp _ IP[enxNotify2]C, GoTo[ENXInitMain], At[ENXInitLoc2]; ENXTemp _ IP[enxNotify3]C, GoTo[ENXInitMain], At[ENXInitLoc3]; ENXInitMain: T _ (SStkP&NStkP) xor (377C); StkP _ ENXTemp, ENXTemp _ T, NoRegILockOK; *Compute value for ENXONotify register, used for notify after timer wakeup. T _ (CTask&NCIA) and (170000C); Stack _ T; Stack _ (Stack) or (LoA[ENXOTimerDoneLoc]); Stack _ (Stack) or (HiA[ENXOTimerDoneLoc]); StkP _ ENXTemp; T _ ENXInitialCSB; ENXCSBPtr _ T, LoadPage[ENXPage]; ENXCSBPtrHi _ 0C, GoToP[ENXReset]; *Also zeroes ENXZeroBase *Microcode is notified at ENXReset (from StartIO via XWSIO2.mc) to reset *things. ENXReset: Input[ENXTemp,ENXXmtStatus], At[ENXStartLoc]; *On ENXPage ENXTemp _ ENXResetAll; Output[ENXTemp,ENXResetState]; ENXXmtPtrHi _ 1C; *See comments at ENXOTimerDone ENXXmtIntLo _ ENXIdleTimer, Call[ENXLoadTimer]; UseCTask, Call[ENXSetupLink]; ******** INPUT MICROCODE ********(1376)\f2 8448b623B309b31B %Sleep here until awakened by by an Input or Output wakeup request. Microcode is set to Recv Mode whenever it is reinitialized. Set Purge Mode for one of 3 reasons: The filter rejected this packet, No IOCB was setup, or Microcode just finished processing a packet. % ENXIPurge: ENXTemp _ ENXSetPurgeMode; ENXIPurge1: Output[ENXTemp,ENXIState], Call[ENXILockENXTemp]; %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. Receive the packet iff: this host is promiscuous (sign bit of ENXHostID1 is 1), packet is broadcast (dest bit 7 set), packet is explicitly for this host (dest=us). Timing to reject the packet: 13 cycles to call and return from dispatcher + 25, 31, or 35 cycles to reject the packet for destination mismatch + 12 cycles to purge the packet. = 50, 56, or 60 cycles to reject the packet % Input[ENXTemp,ENXStatus1], Call[ENXDispatcher]; ENXIBegin: *Fetch NextICB, HostID1, HostID2, and HostID3 PFetch4[ENXCSBPtr,ENXNextICB,ENXIndexICB!], Call[ENXRet]; Input[ENXDestID1,ENXInData]; *In this mi, ENXHostID1 is assembled into the otherwise unused base *register field of the Input memory reference, where the R<0 branch *condition can test it. Input[ENXDestID2,ENXInData,ENXHostID1], GoTo[ENXIForMe,R<0]; T _ LCy[ENXDestID1,7]; LU _ (LCy[ENXHostID1,7]) xor T, GoTo[ENXIMulticast,ALU<0]; Input[ENXDestID3,ENXInData], Skip[ALU=0]; ENXTemp _ ENXSetPurgeMode, GoTo[ENXIPurge1]; *1st word mismatch T _ ENXDestID2; LU _ (ENXHostID2) xor T; T _ ENXDestID3, Skip[ALU=0]; ENXTemp _ ENXSetPurgeMode, GoTo[ENXIPurge1]; *2nd word mismatch LU _ (ENXHostID3) xor T; LU _ ENXNextICB, GoTo[ENXIRead,ALU=0]; ENXTemp _ ENXSetPurgeMode, GoTo[ENXIPurge1]; *3rd word mismatch *No buffer for this packet, bump counter for debugging ENXINoBuffer: PFetch1[ENXCSBPtr,ENXTemp,ENXIndexNoICBCount!]; ENXTemp _ (ENXTemp) + 1; PStore1[ENXCSBPtr,ENXTemp,ENXIndexNoICBCount!], GoTo[ENXIPurge]; *Hackery for off-by-one buffer alignment; this subr is to task after PStore1. ENXIReadAlign: ENXCompletion _ ENXErrorZeroBuf, Skip[ALU>=0]; ENXRcvIndex _ 0C, GoTo[ENXIMark]; PStore1[ENXRcvPtr,ENXDestID1,0]; ENXRcvIndex _ 1C, Return; ENXIMulticast: *Take all Multicast for now Input[ENXDestID3,ENXInData], Skip; ENXIForMe: *Take all when this host is promiscuous Input[ENXDestID3,ENXInData]; LU _ ENXNextICB; ENXIRead: Input[ENXDestID4,ENXInData], GoTo[ENXINoBuffer,ALU=0]; T _ (ENXNextICB) + (ENXIndexBuffer), Task; *Fetch RcvIndex, RcvCount, RcvPtr, and RcvPtrHi. OddPFetch4[ENXZeroBase,ENXRcvIndex]; T _ RHMask[ENXRcvPtrHi]; ENXRcvPtrHi _ (LSh[ENXRcvPtrHi,10]) + T + 1; *(4 for end test, 4 for first clump) * 2 for bytes + 2 for the word done *singly to get quadword alignment. ENXRcvCount _ (ENXRcvCount) - (22C), Call[ENXIReadAlign]; *Now quadword aligned. Store DestID2, DestID3, DestID4, and DestID5. Input[ENXDestID5,ENXInData]; PStore4[ENXRcvPtr,ENXDestID2,1], GoTo[ENXISetupLoop]; ENXIReLoop: Input[ENXTemp,ENXStatus1], Call[ENXDispatcher]; *Note that the call to ENXDispatcher cannot setup the loop because *sometimes the Dispatcher returns after APCTask&APC_ with TPC smashed. ENXISetupLoop: Call[ENXRet]; %Data transfer loop. A receive wakeup occurs when the receiver buffer has 16 or more words; this wakeup can be inhibited by disabling input via the State register by the signal ResetInput'. One IOStore4 must be done every 64 cycles to "keep up" with the hardware, so at least two IOStore4's should be done between tasks, I think. The unusual circumstances which may give rise to IOAtten are: 1) Receive end-of-packet occurs. Wakeups are then requested continuously until purge mode is set. 2) Output packet done occurs (normally or due to an output packet fault). 3) A transmit data wakeup occurs. % 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), Skip[IOAtten']; IOStore4[ENXRcvPtr,ENXIData], Return; IOStore4[ENXRcvPtr,ENXIData], GoTo[ENXIReLoop]; %Finish up the receive packet; calculate packet length. Get here from the dispatcher. Check ENXRcvIndex .ge. 5 is so that software will examine packets between 5 and 60 bytes, even though they wind up rejected. Hal also had the mysterious comment "Without this test tiny packets look huge because of underflow"; but I don't see how this could happen. Also, I have no idea why 3 is added to ENXRcvIndex at the end. % ENXIEnd: Input[ENXTemp,ENXExcessCount], At[ENXDispTableLoc,1]; T _ LdF[ENXTemp,13,5], Task; *Get excess byte count *Change words to bytes & adjust total byte count. ENXRcvIndex _ (LSh[ENXRcvIndex,1]) - T; *Spec calls for min of 60 bytes (30 words) LU _ (ENXRcvIndex) - (5C); *8 bytes is 4 data words *4 CRC and 1 mumble ENXRcvIndex _ (ENXRcvIndex) + (3C), Skip[ALU>=0]; GoTo[ENXIPurge]; Input[ENXCompletion,ENXRcvStatus]; ENXCompletion _ (ENXCompletion) and (ENXISMask); ENXCompletion _ (ENXCompletion) or (ENXGoodPacket), GoTo[ENXIMark,ALU=0]; *Buffer is full. Check for end of packet before posting error. ENXIFull: Input[ENXTemp,ENXStatus1], Call[ENXDispatcher]; ENXCompletion _ ENXPktBufOverrun; ENXIMark: PFetch1[ENXCSBPtr,ENXICBPtr,ENXIndexICB!], Call[ENXRet]; T _ (ENXICBPtr) + (ENXIndexCompletion), Call[ENXStoreCompletion]; T _ (ENXICBPtr) + (ENXIndexBuffer); OddPStore1[ENXZeroBase,ENXRcvIndex], Call[ENXRet]; T _ ENXIndexIWake, Call[ENXInterrupt]; T _ (ENXICBPtr) + (ENXIndexNext), Call[ENXChainToNextIOCB]; PStore1[ENXCSBPtr,ENXICBPtr,ENXIndexICB!], GoTo[ENXIPurge]; ******** OUTPUT MICROCODE ******** ENXSetupLink: T _ APCTask&APC; ENXLink _ T, GoTo[ENXOIdle]; ENXNoWork: ENXTemp _ ENXDisableOutput, Call[ENXWriteOutState]; *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]; %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 1 ms 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. % ENXOCheck: T _ (ENXOCBPtr) + (ENXIndexXmtMask), Skip[ALU#0]; *ALU=0 => no IOCB, no packet to transmit GoTo[ENXNoWork]; *The range of the retransmission interval doubles each time through here. OddPFetch1[ENXZeroBase,ENXXmtMask], Call[ENXRet]; ENXXmtMask _ (LSh[ENXXmtMask,1]) + 1, Skip[R>=0]; *R<0 => overflow ENXCompletion _ ENXErrorCountOV, GoTo[ENXOMark]; OddPStore1[ENXZeroBase,ENXXmtMask]; **Aviod bypass kludge on ENXXmtMask here! ENXTemp _ pENXRandomReg; *point to "random" register LU _ (ENXXmtMask) xor (1C); *Is this the first attemp to transmit? T _ (SStkP&NStkP) xor (377C), Skip[ALU#0]; *T _ garb,,StkP ENXXmtIntLo _ 24C, GoTo[ENXOCountdown]; *yes, count 1 msec, 24B is 20D = 1000/50 *Compute countdown interval, Use high resolution clock for random number StkP _ ENXTemp, ENXTemp _ T, NoRegILockOK; *Form new transmission interval mask, check if old has overflowed. Xor by *CTask in case we have 2 controllers on the same Ethernet. T _ CTask; T _ (LdF[Stack,6,12]) xor T; *Get low 10 bits StkP _ ENXTemp; *Restore StkP ENXXmtIntLo _ (RSh[ENXXmtMask,1]) and T; *Mask random number with old value %ENXXmtIntLo now has (10-bit) number of Ethernet ticks to wait. We need a tick size of 512 bit times or 5120 ns. D0 timers have a tick size of 64 times the clock speed = 640 ns, if the clock is 100 ns. And 5120/640 = 8. How convenient. So to get the number of D0 timer ticks, LSh 3 the number of Ethernet ticks. % ENXOCountdown: T _ LdF[ENXXmtIntLo,6,7], GoTo[ENXOGo,ALU=0]; *At this point, ENXXmtIntLo contains the number of timer ticks to wait *and has 13 bits. Unfortunately, that doesn't fit into a simple timer. *Process the overflow with software. The timer holds 7 bits, but we use only *6 to save an instruction. ENXXmtPtrHi _ 0C, Task; ENXXmtIntHi _ T; ENXXmtIntLo _ LdF[ENXXmtIntLo,15,3]; ENXXmtIntLo _ LSh[ENXXmtIntLo,7]; ENXXmtIntLo _ (ENXXmtIntLo) or (ENXTimerMask), Call[ENXLoadTimer]; *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 the enable-output executed at ENXOTimerDone wakes up this task. Unfortunately, there is another case when the hardware thinks it got an output wakeup but, in fact, got an input wakeup. This happens at the end of one input packet, when another input packet has started arriving; PktGap gets reset when another packet arrives. Input wakeups were occurring because the EOP had been seen for the first input packet, but as soon as the second input packet began arriving, the reason for input wakeups switched to "16 or more words in the buffer"; however, there aren't 16 words there yet; meantime the wakeup reason looks like an output wakeup. To avoid problems, the code here tests the wakeup reason being "Output"; if so, Go; if not, block and let the Dispatcher try again. % Input[ENXXmtIntLo,ENXStatus2], Call[.+2]; GoTo[ENXODisp]; LU _ ENXXmtIntLo, Skip[R>=0]; *Reset Output' PFetch1[ENXCSBPtr,ENXOCBPtr,ENXIndexOCB!], GoTo[ENXOGo]; Return; %Timer has expired, Timer task notifies at ENXOTimerDoneLoc. Note that we can't smash TPC, any temp RM registers, or T because we may be in the middle of receiving a packet. 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, ENXXmtPtrHi will be non-zero. % ENXOTimerDone: LU _ ENXXmtPtrHi, At[ENXOTimerDoneLoc]; ENXXmtIntHi _ (ENXXmtIntHi) - 1, Skip[ALU=0]; PleaseTellHGMAboutThis: *This is possible, but very unlikely BreakPoint, ENXXmtIntHi _ (ENXXmtIntHi) + 1, Return; *Skip if still more time to elapse before start of transmission. Preserve *the timer slot (which is equal to the task number). ENXXmtIntLo _ (ENXXmtIntLo) and (17C), Skip[ALU>=0]; ENXXmtPtr _ ENXEnableOutput, GoTo[ENXWriteOutStateViaXmtPtr]; *2000C is 100C LSh 4 ENXXmtIntLo _ (ENXXmtIntLo) or (Or[ENXTimerMask!,2000]C); *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. ENXLoadTimer1: LoadTimer[ENXXmtIntLo]; ENXXmtPtr _ ENXDisableOutput; ENXWriteOutStateViaXmtPtr: Output[ENXXmtPtr,ENXOState]; ENXXmtPtr _ ENXXmtPtr, GoTo[ENXRet]; *Wakeup Pipeline ENXLoadTimer: T _ CTask; *Timer slot is the same as the task number. ENXXmtIntLo _ (ENXXmtIntLo) or T, GoTo[ENXLoadTimer1]; *Countdown interval is over *Start to send words to the hardware. The hardware will start transmission *to the Wire as soon as the hardware buffer has > 128 words, or when the *OutputEOP bit is set (for a packet of less than 127 words) ENXOGo: T _ (ENXOCBPtr) + (ENXIndexBuffer); *Fetch XmtIndex, XmtCount, XmtPtr, and XmtPtrHi. OddPFetch4[ENXZeroBase,ENXXmtIndex]; *Setup preamble word. ENXTemp _ HiA[ENXPream], Task; ENXTemp _ (ENXTemp) or (LoA[ENXPream]); *Put XmtPtrHi into base register format. T _ RHMask[ENXXmtPtrHi]; ENXXmtPtrHi _ (LSh[ENXXmtPtrHi,10]) + T + 1; ENXXmtIndex _ (ENXXmtIndex) and (0C); *Hackery below sends at least 5 words. (spec calls for 30) ENXXmtCount _ (ENXXmtCount) - (13C); T _ (ENXOCBPtr) + (ENXIndexXmtMask), Skip[ALU>=0]; ENXCompletion _ ENXErrorZeroBuf, GoTo[ENXOMark]; *Buffer too small *Send 4 word preamble (hardware doesn't compute CRC on first 4 words sent) Output[ENXTemp,ENXOutData]; Output[ENXTemp,ENXOutData], Call[ENXILockENXTemp]; Output[ENXTemp,ENXOutData]; ENXTemp _ (ENXTemp) or (ENXPreamLast); Output[ENXTemp,ENXOutData], Call[ENXILockENXTemp]; ** Hackery for off-by-one buffer alignment PFetch1[ENXXmtPtr,ENXTemp,0]; Output[ENXTemp,ENXOutData], Call[ENXILockENXTemp]; ENXXmtIndex _ (ENXXmtIndex) - (3C), GoTo[.+2]; ENXOReLoop: Input[ENXTemp,ENXStatus1], Call[ENXDispatcher]; Call[ENXRet]; *This Call above leaves TPC setup for this loop. %An output wakeup implies 16d words of buffer space available (less than 128 words in the transmit buffer), so the loop can do up to 16d words here. However, if IOFetch16 is used, IOStrobe has to be used to prevent wakeups until the IOFetch16 in progress has completed; IOStrobe is not needed when 1 to 3 IOFetch4's are used in the inner loop. Also the software buffers aren't aligned for IOFetch16's. The unusual circumstances which may give rise to IOAtten' are: 1) Output packet done occurs (normally or due to an output packet fault). 2) Receive end-of-packet occurs. The microcode will read the status reg. and change to receive mode to process the ending condition. 3) A receive data wakeup occurs. The microcode will read the status register and change to receive mode. % ENXXmtCount _ (ENXXmtCount) - (10C); T _ ENXXmtIndex _ (ENXXmtIndex) + (4C), Skip[ALU>=0]; ENXOLastXfer1: ENXXmtCount _ (ENXXmtCount) + (20C), GoTo[ENXOLastXfer]; IOFetch4[ENXXmtPtr,ENXOData]; ENXXmtCount _ (ENXXmtCount) - (10C), Skip[R>=0]; T _ ENXXmtIndex _ (ENXXmtIndex) + (4C), GoTo[ENXOLastXfer1]; T _ ENXXmtIndex _ (ENXXmtIndex) + (4C), Skip[IOAtten']; IOFetch4[ENXXmtPtr,ENXOData], Return; *Loop *Events causing IOAtten' are ? here. IOFetch4[ENXXmtPtr,ENXOData], GoTo[ENXOReLoop]; *Normal exit from Output Loop is here ENXOLastXfer: ENXXmtCount _ (ENXXmtCount) and (37C); ENXXmtCount _ (ENXXmtCount) xor (Or[ENXSetOutputEOP!,37]C); Output[ENXXmtCount,ENXOState], Call[ENXILockENXXmtCount]; 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[ENXCompletion,ENXStatus1], At[ENXDispTableLoc,5]; PFetch1[ENXCSBPtr,ENXOCBPtr,ENXIndexOCB!], Call[ENXRet]; ENXCompletion _ (ENXCompletion) and (ENXOSMask); LU _ (ENXCompletion) xor (ENXOCollisionMask), Skip[ALU#0]; ENXCompletion _ (ENXCompletion) or (ENXGoodPacket), GoTo[ENXOMark]; *No errors *Jump if some error other than collision. LU _ (ENXXmtIndex) - (250C), GoTo[ENXOMark,ALU#0]; *Collision only, check for a late one (hardware debugging) *250C is 128 for buffer, 512bits=32 wds for collision, 8 slop T _ (ENXOCBPtr) + (ENXIndexBuffer), GoTo[ENXOFlap,ALU<0]; ENXXmtIndex _ LSh[ENXXmtIndex,1]; OddPStore1[ENXZeroBase,ENXXmtIndex]; ENXCompletion _ ENXLateCollision; ENXOMark: 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; *Know ENXDisableOutput=0 Output[ENXTemp,ENXOState]; ENXTemp _ (ENXTemp) or (ENXEnableOutput), Call[ENXWriteOutState]; ENXOCheckNext: LU _ ENXOCBPtr, GoTo[ENXOCheck]; \f2 1011b822B212b239B1b245B131b455B138b15B1014b49B430b547B124b35B4129b134B2030b152B60b38B473b1014B315b190B53b101B ******** SUBROUTINES ********\f2 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]; 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]; END[ENXTask]; :ELSE; ********************************************** TITLE[No.10MB.Ethernet.Microcode]; :ENDIF; ********************************************* (1792)\f2