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

%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];

******** SUBROUTINES ********


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];
*001Receive 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; *********************************************