:IF[With3MB]; ***************************************

TITLE[XWTask];
*3 MB Ethernet microcode
%
Ed Fiala 19 May 1982: Add PFetch4 at XOTimerDone+4 to fix bug reported by
Hal Murray.
Ed Fiala 10 May 1982: Bum 2 mi (at XIIndexOK+6 and XINops+4); introduce
XW1stQW0 quadword.
Ed Fiala 1 April 1982: Reformat and absorb XWInit.Mc and XWDefs.Mc
into XWTask.Mc; eliminate separate input init for 2nd board; arrange
register definitions for reasonable printout in Pilot1.Regs; change
XWRandomReg from Refr to ClockLo; bum 3b mi; assemble only if With3MB.
Ed Fiala 8 October 1981: Crank time between successive outputs down to
500 us from 1 ms and eliminate unnecessary waits; reformat file according
to standard conventions; bum 31b mi.
HGM 17 June 1981: Notice new input buffer sooner.
HGM 16 June 1981: Interlock XWIndex and XWBSetup.
Jim Frandeen October 6, 1980 12:01 PM: Try extra Nop after IOStore4 for 736 problem.
Jim Frandeen September 2, 1980 2:31 PM: Change DoInt to NotifyInterrupt.
HGM June 28, 1980 10:55 PM: Ether/XW things, fix AR 3253
HGM April 28, 1980 9:47 PM: Nops for H4PE vs IOStore4
HGM April 24, 1980 4:04 PM: Interlock Output

Jim Frandeen January 20, 1980 11:04 AM: cleanup for D0Lang Version 6.
Jim Frandeen December 22, 1979 9:53 PM: cleanup for D0Lang Version 5.
Hal Murray on September 21, 1979 3:01 AM
Hal Murray on September 15, 1979 Base reg changes
Hal Murray on September 13, 1979 7:47 PM
Written by Roy Ogus (early 79).

This module contains basic code for a single controller.
File XWSIO2 contains StartIO code for two boards.
This version has Ethernet address recognition.
Data buffers must be quadaligned, and at least 4 words long.
Prefetching of CSB at end of packet.

RM ALLOCATION

R0
XWTemp1- Temporary register; used by DoInt
R1
XWTemp2- Temporary register; used by DoInt
R2
XWTemp- Temporary register
R3
XWCompletion- Completion word

R4
XWIndex- Buffer displacement
R5
XWCount- Buffer word counter (quadword buffer)
R6
XWPtr- Buffer ptr (low) (current IOCB)
R7
XWPtrHi- Buffer ptr (high) (current IOCB)

R10
XWIOCB - Base register for current IOCB (low)
R11
XWIOCBHi - Base register for current IOCB (high)
R12
XWCSB- Base register for CSB (low)
R13
XWCSBHi- Base register for CSB (high)

R14
XW1stQW0- First quadword of message (input only)
R15
XW1stQW1
R16
XW1stQW2
R17
XW1stQW3

RM registers that overlay normal use
R4
XWCSB1- first word of CSB (not currently used)
R5
XWNextIOCB- Short pointer to next IOCB
R6
XIHALo- Host address (low - input only)
R7
XIHAHi- Host address (high - input only)

H4PE Hardware Problem

NOPs in the code are because the input hardware generates H4PE’s. Even on
the Rev P boards they squeak through occasionally. The fault handler simply
resumes after any H4PE (A flag in FFault allows crashing, if you prefer).
Successful recovery requires several mi after Inputs or IOStore4s for several
reasons:
1) In the sequence Input (or IOStore4) then another memory reference, the MC1
microcode does a wild branch after an H4PE. 3 non-memory mi are needed
after an Input and 4 mi after an IOStore4 to avoid this. Interlocking an
Input allows another reference on the mi following the interlock (?or is one
more intervening mi required?; if a PStore tries to store the data which
just got H4PE, is that fatal?).
2) An H4PE will abort the 6th mi following an IOStore4 or the 4th mi
following Input. LoadPage may occur in the 1st mi after a tasking return,
and the mi after the LoadPage must not be aborted, so at least 5 mi following
IOStore4 and 3 following Input must occur before tasking.

INPUT MICROCODE

Input CSB

00
(reserved for emulator control)
01
Short pointer to First IOCB
02
Host address (low word)
03
Host address (high word)
04
Input wakeup bit mask
05
Packets missed (for debugging)
06-07
(unused)
10-13
quadword scratch bufer
14-17
Used by test program

Input IOCB

00
Link to next IOCB
01
(unused)
02
Command word
03
Completion word
04
Amount used in buffer
05
Length of buffer
06
Low pointer to buffer
07
High pointer to buffer
%

*Use to write state from Task 0
MC[XOWriteState,Or[LShift[XOTask,4],0]];
MC[XIWriteState,Or[LShift[XITask,4],0]];
MC[XOWriteState2,Or[LShift[XOTask2,4],0]];
MC[XIWriteState2,Or[LShift[XITask2,4],0]];

*I/O Address Registers
Set[XIData,3];
*Input data
*Set[XIHost,1];
*Host ID (3mb Ethernet only)
Set[XWStatus,2];
*Status/State register
Set[XOData,1];
*Output data
Set[XWReadState,2];
*State register read
Set[XWWriteState,0];
*State register write

%D0Lang foils assembly of this module because assigned tasks are 4/5 and
10/11 rather than being in the same group of four tasks. To keep the current
task assignment, we assemble the code as task 0. However, we then run afoul
because SRCDES=0 is interpreted by the hardware as a STACK reference in
PFetch/PStore, and we would otherwise use register 0 as the target for memory
references.
%

*These macros define xw#1 for use by the code and xo1#1, xo2#1, xi1#1, and
*xi2#1 for the Pilot1.Regs file.
Macro[XIReg,(
SetTask[xiTask] RV[xi1#1,Add[LShift[And[xiTask,3],4],#2]],
SetTask[xiTask2] RV[xi2#1,Add[LShift[And[xiTask2,3],4],#2]],
SetTask[0] RV[xw#1,#2])];
Macro[XOReg,(
SetTask[xoTask] RV[xo1#1,Add[LShift[And[xoTask,3],4],#2]],
SetTask[xoTask2] RV[xo2#1,Add[LShift[And[xoTask2,3],4],#2]])];
Macro[XWReg,(XIReg[#1,#2],XOReg[#1,#2])];

*An identical set of these task-specific registers exists for both the
*input and the output task.
*Note: reg. 0 and 1 used by DoInt.
XWReg[InitialCSB,0];
*CSB ptr passed in this reg from Initialize.mc

*These 4 registers are used as a quadword by the input task.
XWReg[Temp1,0];
XWReg[Temp2,1];
XWReg[Temp,2];
XWReg[Completion,3];
*Not used very often

*These must match the layout in the IOCB
XWReg[Index,4];
XWReg[Count,5];
XWReg[Ptr,6];
XWReg[PtrHi,7];

*Register definitions for first 4 words of CSB (overlay others)
XWReg[CSB1,4];
XWReg[NextIOCB,5];
XIReg[HALo,6];
*Input only
XIReg[HAHi,7];
*Input only

XWReg[IOCB,10];
XWReg[IOCBHi,11];
XWReg[CSB,12];
XWReg[CSBHi,13];

XIReg[1stQW0,14];
XIReg[1stQW1,15];
XIReg[1stQW2,16];
XIReg[1stQW3,17];

*State Register command words
MC[XWSetPurgeMode,260];
*Sets: Enable Input, PurgeMode
MC[XWSetOutputEOP,107];
*Sets: Enable Output, OutputEOP, JamEnable
MC[XWEnableInput,220];
*Sets: Enable Input
MC[XWEnableOutput,103];
*Sets: Enable Output, JamEnable
MC[XWDisableInputOutput,300];
*Clears: Input and Output
MC[XWDisableInput,200];
*Clears: Input state register
MC[XWDisableOutput,100];
*Clears: Output state register
MC[XWPream,252];
*Xerox Wire preamble

*Status bit definitions: as read with Read State/status command.
*Status bits (Ethernet)
Set[XSIJam,100000];
*Receiver-detected collision (Jam)
Set[XSOURun,40000];
*Output Underrun
Set[XSIORun,20000];
*Input overrun
Set[XSOCOLL,10000];
*Transmitter-detected collision (Collision)
Set[XSICRC,4000];
*Input Bad CRC
Set[XSOFAULT,2000];
*Output Data Fault
Set[XSOPAR,1000];
*Output Bad Parity
Set[XSIBA,400];
*Input Bad Alignment

MC[XISMASK,Or[XSIJam,XSIORun,XSICRC,XSIBA]];
*Status bits reported for input command
MC[XOSMASK,Or[XSOURun,XSOCOLL,XSOFAULT,XSOPAR]];
*Output Status bits
MC[XOCollisionMask,XSOCOLL];

*Completion codes
MC[XWGoodPacket,040000];
MC[XWErrorBadHWPkt,070000];
MC[XWErrorZeroBuf,064000];
*On input (length<3) and output (length=0)
MC[XWPktBufOverrun,061000];
*Input only
MC[XWErrorCountOV,062000];
*Output only

*Timer definitions
Set[XWTimerRunning,5];
*State 5 is simple timer
Set[XWTimerIdle,4];
*State 4 is idle
MC[XWTimerMask,LShift[XWTimerRunning,14]];
MC[XWIdleTimer,LShift[XWTimerIdle,14]];
MC[pXWRandomReg,IP[ClockLo]];
*Pointer to register containing random no.

*Indices into blocks

*IOCB’s
MC[XWIndexNext,0];
*(Short) Pointer to next IOCB
MC[XWIndexTransInt,1];
*Retransmission Mask
MC[XWIndexCompletion,3];
MC[XWIndexBuffer,4];
*Long pointer and length (Quadword aligned)

*CSB’s
MC[XWIndexHeader,0];
*First 4 words of CSB
MC[XWIndexPtr,1];
*Pointer to current IOCB
MC[XWIndexWake,4];
*Wakup Bitmask
MC[XWIndexNoICBCount,5];
*Count of times when no ICB chain (For test only)
MC[XWIndexScratch,10];
*Quadword scratch area

SetTask[0];

*3 MB ETHERNET controller INITIALIZATION.
*Will only be called if there is an Ethernet board in the machine.

OnPage[xwInitPage];

xiInit:
Call[xwSetup], At[xwInInitLoc];
StkP ← xwTemp, Return;*Restore Stkp


*Compute value for xoNotify register, used for notify after timer wakeup.
xoInit:
xwTemp ← IP[xoNotify]C, Call[xwSetup], At[xwOutInitLoc];
Stack ← HiA[xoTimerDoneLoc,xoTask];
xoInitx:
Stack ← (Stack) or (LoA[xoTimerDoneLoc]);
StkP ← xwTemp, Return;*Restore Stkp


*Second Board
*Compute value for XONotify2 register, used for notify after timer wakeup.
xoInit2:
xwTemp ← IP[xoNotify2]C, Call[xwSetup], At[xwOutInitLoc2];
Stack ← HiA[xoTimerDoneLoc,XOTask2], GoTo[xoInitx];


xwSetup:
T ← (SStkP&NStkP) xor (377C);
StkP ← xwTemp, xwTemp ← T, NoRegILockOK;
xwCSBHi ← 0C;
xwIOCBHi ← 0C;
T ← xwInitialCSB, UseCTask;
xwCSB ← T, Return;

OnPage[XWPage];

*Input microcode is notified at XIStart to initialize.
XIStart:
XWTemp ← XWDisableInput, Call[XWOutState], At[XIStartLoc];

*ADDRESS RECOGNITION: Ethernet encapsulation assumed.
* Check if the packet,
* is explicitly for this host (dest=us),
* or is broadcast (dest=0),
* or if the host is promiscuous (us=0).

*There are two wakeup points.
* XIBegin: No prefetch of CSB has been done.
* XIFastBegin: Prefetch of CSB has been done.
XIBegin:
PFetch4[XWCSB,XWCSB1,XWIndexHeader!];*Fetch 1st 4 words of CSB
XIFastBegin:
*Read 4 words into CSB scratch area
IOStore4[XWCSB,XIData,XWIndexScratch!], Call[XINops];
LU ← RHMask[XWHALo];
PFetch4[XWCSB,XW1stQW0,XWIndexScratch!], Skip[ALU#0];
T ← XWNextIOCB, GoTo[XIForMe];*We are promiscuous
T ← RSh[XW1stQW0,10];*Right justify dest host in T
LU ← (RHMask[XWHALo]) xor T, Skip[ALU#0];
T ← XWNextIOCB, GoTo[XIForMe];*Broadcast
T ← XWNextIOCB, GoTo[XIForMe,ALU=0];*Packet explicitly for me

*Packet unaccepted. Tell hardware to ignore (i.e., purge) rest of packet.
XIPurge:
XWTemp ← XWSetPurgeMode, Call[XWOutState];
*Return here after wakeup
LU ← XWNextIOCB;
DblGoTo[XIFastBegin,XIBegin,ALU#0];


*Get buffer started. Check for no IOCB available (in case it was a slow wakeup).
XIForMe:
XWIOCB ← T, GoTo[XIIndexOK,ALU#0];*ALU=0 => No IOCB
***No IOCB. Bump counter in CSB. Very bad tasking here. Predicted execution
***small numbers of times per hour.
PFetch1[XWCSB,XWTemp,XWIndexNoICBCount!];
XWTemp ← (XWTemp) + 1;
PStore1[XWCSB,XWTemp,XWIndexNoICBCount!], GoTo[XIPurge];

*Check for runt packets (<= 3 words, excluding CRC).
*IOATTEN=1 => EOP => small packet
XIIndexOK:
Skip[IOAtten’];
GoTo[XIPurge];
Call[XWBSetup];
XWCount ← (XWCount) - (4C);
Skip[Carry];
T ← XWCompletion ← XWErrorZeroBuf, GoTo[XIMark];
XWCount ← (XWCount) - (4C), Task;*Leaves TPC setup
PStore4[XWPtr,XW1stQW0,0];
***This better not be the bypass kludge???
*Main loop-checks for end of buffer, then checks ATTEN for end-of-packet.
XWCount ← (XWCount) - (4C), GoTo[XIQuadFull,R<0];
T ← (XWIndex) ← (XWIndex) + (4C), Skip[IOAtten’];
Input[XWTemp,XWStatus], GoTo[XIAttn];
IOStore4[XWPtr,XIData];
XINops:
Nop;
Nop;
Nop;
Nop, GoTo[XWRet];*H4PE aborts the mi immediately after the Return.

*Get here when there is no more room for quadwords in the buffer.
*Check ATTEN to see if end of packet.
XIQuadFull:
*Number of singles remaining in buffer = XWCount+8.
*Set up XWCount as (No. singles-1), and read in singles.
XWCount ← (XWCount) + (7C);
XWIndex ← (XWIndex) + (3C), Call[.+1];
*Loop here.
XWCount ← (XWCount) - 1, GoTo[XIBufFull,R<0];
T ← (XWIndex) ← (XWIndex) + 1, Skip[IOAtten’];
Input[XWTemp,XWStatus], GoTo[XIAttn];
Input[XWTemp,XIData];
LU ← XWTemp;** H4PE timing
Nop;
PStore1[XWPtr,XWTemp], Return;

*We get here when the input buffer is exactly full.
*Check if ATTEN for EOP, indicating that the last word was the CRC.
XIBufFull:
XWIndex ← (XWIndex) + 1, Skip[IOAtten’];
Input[XWTemp,XWStatus], GoTo[XIAttn];

*Check if there is only one more word (i.e. the CRC); otherwise, mark the
*packet buffer overrun.
Input[XWTemp,XIData];*, Call[XWRet];
LU ← XWTemp;** H4PE timing
XWIndex ← (XWIndex) + 1, Skip[IOAtten’];
Input[XWTemp,XWStatus], GoTo[XIAttn]; *EOP, so only 1 more word (CRC)
T ← XWCompletion ← XWPktBufOverrun, GoTo[XIMark];


*FINISH UP PACKET

*Arrive here after an IOAtten/IOAtten’ is detected,
*indicating an end of packet.
*XWIndex has the number of words stored in the current buffer.
*XWTemp has status word
XIAttn:
T ← LdF[XWTemp,10,2];*Get excess count
XWIndex ← (XWIndex) - T - 1, Call[XWStoreIndex];*CRC too
XWTemp ← (XWTemp) and (XISMask);*Isolate InStatus
XWCompletion ← XWErrorBadHWPkt, Skip[ALU#0];
T ← XWCompletion ← XWGoodPacket, GoTo[XIMark];
*Hardware indicated problems
T ← RSh[XWTemp,10];*Right justify Ethernet status
XIMark:
XWCompletion ← (XWCompletion) or T, Call[XWStoreStatus];
Call[XWNext];
*Now check if there is a buffer.
GoTo[XIPurge];

%OUTPUT MICROCODE

Output CSB

00
(reserved for emulator control)
01
Short pointer to First IOCB
02-03
(unused)
04
Output wakeup bit mask
5-17
(unused)

Output IOCB

00
Link to next IOCB
01
Initial Retransmission Interval
02
Command word
03
Completion word
04
(Unused)
05
Length of buffer
06
Low pointer to buffer
07
High pointer to buffer
%


*Output microcode is notified at XOStart to disable and initialize TPC.
XOStart:
XWIOCB ← 0C, At[XOStartLoc];
XONoWork:
XWTemp1 ← XWIdleTimer, Call[XWLoadTimer];
*Idle state of output microcode has TPC pointing here. Software executes an
*Output opcode with argument XWEnableOutput to reenable interrupts and wake
*the microcode.
XWIOCB, GoTo[XWOutBlock,R<0];
*Fetch pointer to 1st IOCB
PFetch4[XWCSB,XWCSB1,XWIndexHeader!], GoTo[XOGO1];

XOCheck:
XWIOCB ← T, Skip[ALU#0];*ALU = 0 => no IOCB
*Block until reenabled by software if nothing to do.
GoTo[XONoWork];
Call[XWBSetup];
XWCount ← (XWCount) - (4C), Skip[R>=0];
T ← XWCompletion ← XWErrorZeroBuf, GoTo[XOMark];*Buffer empty
*Check if old transmission interval has overflowed
PFetch1[XWIOCB,XWTemp2,XWIndexTransInt!];
XWTemp2 ← (LSh[XWTemp2,1]) + 1, Skip[R>=0];*R<0 => overflow
T ← XWCompletion ← XWErrorCountOV, GoTo[XOMark];
PStore1[XWIOCB,XWTemp2,XWIndexTransInt!];
*Initialize displacement to -4
XWIndex ← (XWIndex) - (4C), Call[XWRet];
*Main loop-checks for end of buffer, then checks IOAtten for end-of-pkt.
XWCount ← (XWCount) - (4C), GoTo[XOQuadEmpty,R<0];
T ← XWIndex ← (XWIndex) + (4C), GoTo[XOAbort,IOAtten];
IOFetch4[XWPtr,XOData], Return;

*Normal exit from Output Loop is here
*Number of singles remaining in buffer = XWCount+8.
*Set up XWCount as (No. singles-1), and output singles.
XOQuadEmpty:
T ← XWIndex ← (XWIndex) + (4C);
XWCount ← (XWCount) + (7C), Call[.+1];
*Loop here
XWCount ← (XWCount) - 1, GoTo[XOEnd,R<0];
PFetch1[XWPtr,XWTemp], Skip[IOAtten’];
GoTo[XOAbort];
T ← (Zero) + T + 1;*Update T
Output[XWTemp,XOData], GoTo[XWRet];

*Hack to generate lots of JAMs -- Just wait for an UnderRun
*
Call[.+1];
*
Nop;
*
Skip[IOAtten’];
*
GoTo[XOAbort];
*
Return;
*END OF PACKET
XOEnd:
XWTemp ← XWSetOutputEOP, Call[XWOutState];*Set OutputEOP bit
*Wait for end-of-packet wakeup
XOAbort:
Input[XWTemp,XWStatus];*Read Status
XWTemp ← (XWTemp) and (XOSMask);*Isolate OutStatus
LU ← (XWTemp) xor (XOCollisionMask), GoTo[XOPacketOK,ALU=0];
*Error reported by the hardware
T ← RSh[XWTemp,10], Skip[ALU=0];
XWCompletion ← XWErrorBadHWPkt, GoTo[XOMark];
%COLLISION ONLY. Choose random retransmission interval. A tick size of ~38
us is wanted (because Altos used that value historically). Dolphin timers
have a basic tick of 64 cycles, or 6.4 us at 100 ns/cycle. 6*6.4 is 38.4
which is very close, so 6*x = 2*(2*x + x) is computed below.
%
T ← (SStkP&NStkP) xor (377C);*Save Stkp
XWTemp2 ← pXWRandomReg;*point to "random" register (Refr)
StkP ← XWTemp2, XWTemp2 ← T, NoRegILockOK;
*Form new transmission interval mask. Xor with CTask adds randomization when
*the 2nd ethernet board is on the same ethernet as the 1st board during checkout.
T ← RSh[Stack,2];*Get random number
StkP ← XWTemp2;*Restore StkP
T ← (CTask) xor T;
*Mask random number with old value of retransmission mask
T ← XWTemp1 ← (RSh[XWTemp1,1]) and T;
***Next branch implies 0 wait, but maybe 500 us is needed?
XWTemp1 ← (LSh[XWTemp1,1]) + T, GoTo[XOGO,ALU=0];
XWTemp1 ← LSh[XWTemp1,1], GoTo[XOCountDown];

*GOOD PACKET status:
XOPacketOK:
T ← XWCompletion ← XWGoodPacket;*Mark packet good
XOMark:
XWCompletion ← (XWCompletion) or T, Call[XWStoreStatus];
%To help hosts with half duplex interfaces (Altos) avoid missing
closely-spaced packets, enforce ~ 500 us delay after each output by starting
a timer here and not allowing the next output until the timer has run out.
%
XWTemp1 ← 116C, Call[XWNext];*78d ~ 500/6.4; setup next IOCB
XOCountDown:
T ← (LdF[XWTemp1,7,2]) - 1;
XWTemp2 ← T, Task;*Save high part -1 in XWTemp2
XWTemp1 ← LdF[XWTemp1,11,7];*Low 7 bits in XWTemp1
*Start simple timer 7-bit number in XWTemp1.
XOLoadTimer:
XWTemp1 ← LSh[XWTemp1,4];*align data part
XWIOCB ← 100000C;*Indicate timer running
XWTemp1 ← (XWTemp1) or (XWTimerMask), Call[XWLoadTimer];
*Wakeup here if the driver enables interrupts while we are waiting. In that
*event, block until the timer expires.
XWTemp ← XWDisableOutput, GoTo[XWOutState];

*Timer has expired.
XOTimerDone:
LU ← XWIOCB, Skip[R<0], At[XOTimerDoneLoc];
Return;*Timer expired as output was disabled--block.
*Check for more time to elapse before start of transmission (High part of
*random number >=0).
XWTemp2 ← (XWTemp2) - 1, Skip[R<0];
XWTemp1 ← 177C, GoTo[XOLoadTimer];*Setup maximum timer value
*Countdown interval is over. Enable output wakeups. Hardware transmission
*will start when .gr. 10 words are in the buffer or the OutputEOP bit is set
*(for a packet less than 12 words)
XWTemp ← XWEnableOutput, Call[XWOutState];
XOGO:
PFetch4[XWCSB,XWCSB1,XWIndexHeader!];
XOGO1:
T ← XWNextIOCB, GoTo[XOCheck];

*SUBROUTINES

*Get buffer descriptor pointed to in T, and fix pointer (in XWPtr, XWPtrHi).
*XWPtrHi goes from (0, x) to (x, x+1)
*Clears XWIndex
XWBSetup:
PFetch4[XWIOCB,XWIndex,XWIndexBuffer!];
XWIndex ← (XWIndex) and (0C);
T ← LSh[XWPtrHi,10];
XWPtrHi ← (RHMask[XWPtrHi]) + T + 1, Return;

XWStoreIndex:
PStore1[XWIOCB,XWIndex,XWIndexBuffer!], GoTo[XWRet];


*Chain to next IOCB and update pointer in CSB
*Current Drivers depend upon this action being ATOMIC.
XWNext:
PFetch4[XWCSB,XWCSB1,XWIndexHeader!];
PFetch1[XWIOCB,XWNextIOCB,XWIndexNext!];
PStore4[XWCSB,XWCSB1,XWIndexHeader!], Return;

*Notify the driver by generating appropiate interrupts.
*This could be merged with ENNext, but that would be too long between TASKs.
XWStoreStatus:
PFetch1[XWCSB,XWTemp,XWIndexWake!];*Fetch wakeup bitmask
PStore1[XWIOCB,XWCompletion,XWIndexCompletion!];
LoadPage[NotifyInterruptPage];
T ← XWTemp, GoToP[NotifyInterrupt];*Mask in T

*Load timer from data in XWTemp1, and disable output hardware.
*Note: There must be at least 7 mi between consecutive LoadTimer/AddToTimer.
*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.
XWLoadTimer:
T ← CTask;*Timer slot is same as output task number.
XWTemp1 ← (XWTemp1) or T;
LoadTimer[XWTemp1];
XWOutBlock:
XWTemp ← XWDisableOutput;
XWOutState:
Output[XWTemp,XWWriteState];
XWRet:
XWTemp ← XWTemp, Return;*Avoid Output-Output-PStore4 gotcha

END[XWTask];

:ELSE; **********************************************

TITLE[No.3.MB.Ethernet.Microcode];

:ENDIF; *********************************************