:TITLE[Ether];

* Ethernet I/O Address Registers
Set[EIData, 3];
* Input data used with Input instructions
Set[EImData, ADD[LSHIFT[EITask,4],3]]; *Input data used with memory references

Set[EIHost, 1];
* Input data
Set[EStatus, 2];
* Status/State register (read)
Set[EOData, 1];
* Output data used with Output instructions
Set[EOmData, ADD[LSHIFT[EOTask,4],1]]; *Output data used with memory references
Set[EReadState, 2];
* State register read
Set[EWriteState, 0];
* State register write

* State Register command words
MC[ESetPurgeMode, 260];
* Enables input
MC[ESetOutputEOP, 107];
* Enables output, Jam
MC[EEnableInput, 220];
MC[EEnableOutput, 103];
* Enables Jam
MC[EDisableInput, 200];
MC[EDisableOutput, 100];
* Clears OutputEOP, disables Jam
MC[EDisableInputOutput, 300];
* Disables input, output, clears outputEOP, Jam

MC[EOReset,OR[LSHIFT[EOTask,4],0]];


* Status bits
SET [ESICOLL, 200];
* Receiver-detected collision (Jam)
SET [ESODL, 100];
* Output data late (Underrun)
SET [ESIDL, 40];
* Input data late (Overrun)
SET [ESOCOLL, 20];
* Transmitter-detected collision (Collision)
SET [ESCRC, 10];
* Bad CRC
SET [ESOFAULT, 4];
* Output DataFault (masked for now)
SET [ESOPAR, 2];
* Output Bad Parity (masked for now)
SET [ESICMD, 4];
* Input command issued ** Not in hardware:
SET [ESOCMD, 2];
* Output command issued ** for Alto emulation only
SET [ESIT, 1];
* Incorrectly terminated packet (Bad Alignment)

MC[EISMASK, ESIDL, ESCRC, ESIT];
* Status bits reported for input command
MC[EOSMASK, ESODL, ESOCOLL];
* Status bits reported for output command
MC[ECMDBITS, ESICMD, ESOCMD];
* Command bits


SetTask[EOTask];
* R-registers for output task
SET[eoRB,LSHIFT[AND[EOTask,3],4]]; *enforces register allocation conventions
RV[EOTemp2, ADD[eoRB,0]];
RV[EOTemp1, ADD[eoRB,1]];

RV[EOPtr, ADD[eoRB,2]];
* Buffer base register
RV[EOPtrHi, ADD[eoRB,3]];
RV[EOCount, ADD[eoRB,4]];
* Main loop counter
RV[EOTemp, ADD[eoRB,5]];
* Temporary registers

*MDS register for Ethernet
RV[eMDS,76]; *Contains 400b
RV[eMDShi,77]; *Contains MDS
SetTask[EITask];
* R-registers for input task (EOTask+1)
SET[eiRB,LSHIFT[AND[EITask,3],4]]; *enforces register allocation conventions
RV[EITemp2, ADD[eiRB,0]];
RV[EITemp1, ADD[eiRB,1]];

RV[EIPtr, ADD[eiRB,2]];
* Buffer base register
RV[EIPtrHi, ADD[eiRB,3]];
RV[EICount, ADD[eiRB,4]];
* Main loop counter
RV[EITemp, ADD[eiRB,5]];
* Temporary registers
RV[EFlag, ADD[eiRB,10]];
* input under output flag (reg 10 of EITask)
MC[pERandomReg, 377];
* Pointer to random number (REFR register)

* Control block addresses (for Alto emulation, relative to 400)
MC[EPLOC, 200];
* Post location
MC[EBLOC, 201];
* Interrupt bit mask
MC[EELOC, 202];
* Ending word count
MC[ELLOC, 203];
* Load mask
MC[EICLOC, 204];
* Input count
MC[EIPLOC, 205];
* Input pointer
MC[EOCLOC, 206];
* Output count
MC[EOPLOC, 207];
* Output pointer
MC[EHLOC, 210];
* Host address for address recognition

* Timer masks (slot number is EOTask)
Set [ETimerState, 5];
* Use Timer State 5 for simple timer
MC[ETimerMask, LSHIFT [ETimerState, 14]];

*Ether constants required in both init and code

*Microcode post codes (small integer in left half, ones in right half for XOR).
*Note: value is complemented to get constant less than 8 bits. Use XNOR for formation of post code.
MC[ESIDON, NOT [377]];
* Input done
MC[ESODON, NOT [777]];
* Output done
MC[ESIFUL, NOT [1377]];
* Input buffer overflowed
MC[ESLOAD, NOT [1777]];
* Load overflow
MC[ESCZER, NOT [2377]];
* Word count zero in input or output command
MC[ESABRT, NOT [2777]];
* Command aborted (by SIO)

* Miscellaneous
MC[ECOLLMASK, 10000];
* Mask for collision detection

SET TASK [EITask];
* ETHERNET INITIALIZATION subroutine (executed at EITask).
* Will only be called if there is an Ethernet board in the machine.
* Read Ethernet ID from board and form constant to be returned by SIO.

ON PAGE [EtherInitPage];
EtherInit:T ← GETRSPEC[103] xor (377C), at[EtherInitLoc]; *Stkp (inverted)
EITemp ← pEHostRegx; *EHostReg is in the emulator’s R space
Stkp ← EITemp, EITemp ← T, NoRegILockOK;
Input[Stack,EIHost];
Stack ← (Stack) AND (377C);* ID in right half
Stack ← (Stack) OR (77400C);
Stkp ← EITemp; *restore Stkp
eMDS ← 400c; *initialize base register
eMDShi ← 0c, return; *this will disappear when the MDS switch is made


SET TASK [0];
* EMULATOR TASK -- SIO instruction

ON PAGE [EEPage];
*This code executes at task 0
* We get here after the SIO instruction has been issued.
*The SIO control bits are in T (bits 16,17).
* Get host address constant for Ethernet
*RTEMP1 = 1 if called from Mesa, 0 if called from Nova
*This code is really part of the emulator, and uses its temporary registers
EESIO:
AC0 ← T;
T ← 177C; *save control bits (useful only if called from Mesa)
T ← (ldf[EHostReg,0,10]) xor (T); *these bits will be 177b if the init code was run
*(i.e. if there is an Ethernet board in the machine), and will be zero otherwise
T ← EHostReg, goto[EESIODisp,ALU=0];
AC0 ← 0C;
AC0 ← (AC0) xnor (100000C),goto[EESIO0]; *return 77777b in AC0 if no Ethernet board

* Dispatch on low 2 bits of AC0
EESIODisp:
Dispatch[AC0,16,2];
AC0 ← T, DISP [EESIO0];* Host Address
* 00 -- Do nothing
EESIO0:
lu ← RTEMP1, dblgoto[EEMesaRet,EENovaRet,Rodd], AT[EESIOLoc, 0];

EENovaRet:
LoadPage[nePage];
CSkipData, GotoP[neTask1st];

EEMesaRet:

loadpage[7];
PFetch1[LOCAL,LocalCache0,4], gotop[P7Tail]; *AC0 access above smashed local 0


* 01 -- Start transmitter
* Form APC&APCTask word to notify the output microcode
EESIO1:
RTEMP ← AND[0377, EOStartLoc]C, AT[EESIOLoc, 1];* Low 8 bits of APC
GOTO [EESIONotify], RTEMP ← (RTEMP) OR (OR[lshift[EOTask,14],AND[007400, EOStartLoc]]C);
* 10 -- Start receiver
* Form APC&APCTask word to notify the input microcode
EESIO2:
RTEMP ← AND[0377, EIStartLoc]C, AT[EESIOLoc, 2];* Low 8 bits of APC
EESIO2A:
GOTO [EESIONotify], RTEMP ← (RTEMP) OR (OR[lshift[EITask,14],AND [007400, EIStartLoc]]C);
* 11 -- Reset interface, i.e. abort. Input task is notified to post abort.
EESIO3:
GOTO[EESIO2A], RTEMP ← AND[0377, EIAbortLoc]C, AT[EESIOLoc, 3];* Low 8 bits of APC

* Notify appropriate code
EESIONotify: RCNT ← (EDisableInputOutput);
T ← EOReset;
OUTPUT[RCNT];
CALL[ETaskRet], APC&APCTASK ← RTEMP;
lu ← RTEMP1, dblgoto[EEMesaRet,EENovaRet,Rodd]; *control returns to here when emulator next runs


*
*
* INPUT TASK MICROCODE
*
SetTask[EITask];
ON PAGE [EIPage];
* Input microcode is notified at EIStart by the emulator (at SIO).
* Some initialization is done, and the TPC set up to EIIDLE.
* Initialize, enable hardware, and TASK
EIStart:
CALL [EINIT], EITemp ← EEnableInput, AT [EIStartLoc];
* Wake up here when first word of a new packet arrives.
* Address filtering.
EIIDLE:
Input[EITemp1, EStatus]; *Check status for malformed packet
lu ← (EITemp1) and (100400c); *Jam and bad alignment bits
EITemp ← ESetPurgeMode, goto[.+2, ALU=0];
goto[EIPurge];*flush bad packet
INPUT [EITemp2, EIData];* Read in first word
T ← EHLOC;
PFetch1[eMDS,EITemp1]; *Start fetch of host address
T ← RSH [EITemp2, 10];* Right justify destination host in T
SKIP [ALU#0];* Check for broadcast
GOTO [EIBegin], EFlag ← 1C;* Broadcast packet
LU ← (EITemp1) XOR (T);*Compare with host address
SKIP [ALU#0], LU ← EITemp1;* ALU=0 => destination host = me
GOTO [EIBegin], EFlag ← 1C;* Packet for me
GOTO [EIPurge, ALU#0];* ALU=0 => host is promiscuous
GOTO [EIBegin], EFlag ← 1C;* Packet for me

* Packet not accepted by filter, so tell hardware to ignore the rest of this packet.
* Purge packet, and TASK
EIPurge:
OUTPUT [EITemp, EWriteState], call[ERet];
* Wakeup here at start of next packet
GOTO [EIIDLE];

* Packet accepted by filter.
* EFLAG is set to 1 to tell the output microcode that a packet came in
* (used for input under output).
* Disable output task if it is on (probably in retransmission wait).
EIBegin:
EITemp1 ← EDisableOutput;
OUTPUT [EITemp1, EWriteState], call[ERet];
* Set up EIPtr and EICount for single word transfers.
CALL[EBSetup], T ← EICLOC;* Set up EIPTR, EICount
* Subroutine returns with: EIPtr = IPtr + ICount - 1, EICount = - ICount
* Check if buffer count zero. If not first word gets stored in EIAlign loop.
GOTO [EICountZero, R>=0], T ← EICount ← (EICount) + 1;* R>=0 => count is zero

* Check buffer alignment.
* Compute how many singles before first quadword, and form loop counter in EITemp1.
*
Address: x00 => no singles, loop count = -1
*
Address: x01 => 3 singles, loop count = 2
*
Address: x10 => 2 singles, loop count = 1
*
Address: x11 => 1 singles, loop count = 0
T ← (EIPtr) + (T) + 1;* Form start address in T
EITemp1 ← (ZERO) - (T);* Complement, increment
EITemp1 ← (LDF[EITemp1, 16, 2]) - 1;
CALL [EIAlignE], T ← EICount;* Set return for EIAlign loop
EIAlign:
GOTO [EIQuad, R<0], EITemp1 ← (EITemp1) - 1;
GOTO [EIBufFull1, R>=0], T ← EICount ← (EICount) + 1;
INPUT [EITemp2, EIData];
LU ← EITemp2;* Abort
EIAlignE:
RETURN, PStore1 [EIPtr, EITemp2];
* Now start quadword output.
* Adjust EIPtr and EICount for 4-word transfers.
EIQuad:
EICount ← (EICount) + (3C);
CALL [EILoop], EIPtr ← (EIPtr) - (6C);* Set return address for EILoop
* Read the Hardware Input Buffer into the Main Memory In Buffer.
EILoop:
GOTO [EIQuadFull, R>=0], T ← EICount ← (EICount) + (4C);
GOTO[EIAttn, IOATTEN];
IOStore4[EIPtr, EImData];
nop;
nop;
nop;
goto[ERet]; *This is so that if the IOStore4 causes an H4PE, it won’t cause a LoadPage
*error in another task.

* Get here when no more room for quadwords in buffer.
* Check IOATTEN is high to see if end of packet.
EIQuadFull:
GOTO [EIAttn1, IOATTEN];
* Not end of packet. Do singles to fill buffer.
* 7-EICount = number of singles remaining in buffer.
* Set up loop counter as (- No. singles), and read in singles.
EICount ← (EICount) - (7C);
CALL [EISingles];
EISingles:
GOTO [EIBufFull, R>=0], EICount ← (EICount) + 1;
GOTO [EIAttnS, IOATTEN], T ← (EICount) + (6C);* Set up T for PStore1
INPUT [EITemp, EIData];
LU ← EITemp;
RETURN, PStore1 [EIPtr, EITemp];

* We get here when IOATTEN is detected in EILoop.
* Number of word left in buffer = 7 - EICount + 1 (CRC) + Excess count.
EIAttn1:
NOP;
EIAttn:
INPUT [EITemp, EStatus];* Read Status
T ← LDF[EITemp, 10,2];* Isolate Excess Count
EICount ← (EICount) XNOR (0C);* Complement
EICount ← (EICount) + (11C);* Increment, add 8
EICount ← (EICount) + (T);* Add excess count
EIAttn2:
EITemp ← (RSH[EITemp, 10]);* Shift down status
EITemp ← (EITemp) AND (EISMASK);* Mask out uninteresting status bits
EITemp ← (EITemp)XNOR(ESIDON);* Post input done status
* Store EECLOC.
EIPost:
T ← EELOC;
CALL[ETaskRet], PStore1 [eMDS, EICount];

* Post status, disable interface (purge packet too), and TASK.
* Post status in EITemp, disable value in EICount.
EIPostA:
CALL [EPost], EICount ← EDisableInput;
* End of packet.
GOTO [EIIDLE];


* Get here when an IOATTEN is detected during the EISingles loop.
* Number of words left in buffer = 1 - EICount + 1 (CRC).
EIAttnS:
EICount ← (EICount) XNOR (0C);* Complement
EICount ← (EICount) + (3C);* Increment, add 2
GOTO [EIAttn2], INPUT [EITemp, EStatus];
* We get here when the input buffer is exactly full.
* First check if IOATTEN is high, indicating that the last word was the CRC.
EIBufFull1:
NOP;
EIBufFull:
SKIP[NOATTEN], EITemp ← 1C;
GOTO [EIAttn2], INPUT [EITemp, EStatus];*Last word input was CRC
* Read one more word to see if the next is the CRC word (which we will discard).
EICount ← 0C;* No words left in buffer
CALL[ETaskRet], INPUT [EITemp, EIData];
* After wakeup, check IOATTEN.
NOP;* Can’t check for Attn here
GOTO [EIAttn2, IOATTEN], INPUT [EITemp, EStatus];* IOATTEN => Word was CRC
EITemp ← 0C;
GOTO [EIPOST], EITemp ← (EITemp) XNOR (ESIFUL);* Input buffer overrun, post status

* Get here if input buffer has zero word count. Post.
EICOuntZero:
EITemp ← 0C;
GOTO [EIPost], EITemp ← (EITemp) XNOR (ESCZER);


* Input microcode is notified here by emulator SIO when AC0[16:17] = 3.
* Manufacture "Abort" status and post. (Input hardware will be disabled again, which doesn’t matter.)
EIAbort:
EITemp ← ECMDBITS, AT [EIAbortLoc];
GOTO [EIPostA], EITemp ← (EITemp) XNOR (ESABRT);
*
*
* Alto-emulation OUTPUT MICROCODE
*
SetTask[EOTask];

* Output microcode is notified at EOStart by the emulator (at SIO).
* Some initialization is done, and the TPC set up to EOIDLE, enable hardware and TASK.
EOStart:
CALL [EINIT], EOTemp ← EEnableOutput, AT [EOStartLoc];

* Idle state of the Ethernet output task

EOIDLE:
T ← ELLOC;
PFetch1 [eMDS, EOTemp1];* Fetch current load
SKIP [R>=0], T ← (LSH[EOTemp1 , 1]) + 1; * Form new load, check if old overflowed
GOTO [EOLDOV], EOTemp ← 0C;* Post Load overflow status
EOTemp ← T;* Store updated load in ELLOC
T ← ELLOC, TASK;
PStore1 [eMDS, EOTemp];* Store new load
* Compute countdown interval
* Get random number from "random" register (REFR register used).
T ← (GETRSPEC[103]) xor (377c);* Save Stkp and
EOTemp2 ← pERandomReg;* point to "random" register
Stkp ← EOTemp2, EOTemp2 ← T, NoRegILockOK;
T ← LDF [Stack, 4, 10];* Get bits 4-13
Stkp ← EOTemp2;* and restore
EOTemp1 ← (EOTemp1) AND (T);* Mask random number (EOTemp1 has Load mask)
GOTO[EOSetup];
GOTO [EOSetup, ALU=0], EOTemp1 ← LSH[EOTemp1,1];* Scale for 5.44 us ticks
T ← (LDF[EOTemp1, 7, 2]) - 1;
EOCount ← T, TASK;* Save high part (minus 1) (2 bits)
EFlag ← 0C;* Clear Input under Output Flag
EOTemp1 ← LDF[EOTemp1, 11, 7];* EOTemp1 now has low 7 bits of random number
* Before starting timer, check if input is set up.
T ← EICLOC;
PFetch1 [eMDS, EOTemp];
LU ← EOTemp;
* Disable output. If the input word count is nonzero, enable the receiver while waiting to transmit.
SKIP [ALU=0], EOTemp ← EDisableOutput;* ALU=0 => no input set up
EOTemp ← (EOTemp) OR (EEnableInput);
OUTPUT [EOTemp, EWriteState];
* Start simple timer with low 7 bits of random number.
* Timer slot is EOTask.
T ← ETimerMask;* Compute timer word
T ← (CTASK) OR (T);
EOLoadTimer:
EOTemp1 ← (LSH[EOTemp1, 4]) or (T);
LoadTimer[EOTemp1], return; *we don’t need to have TPC correct here, since control
*returns to this task via a Timer notify.

* Timer has expired (notified here by task 16). Check if input (under output) came in.
EOTimerDone:
GOTO [EOMoreTime, R EVEN], LU ← EFlag, AT [EOTimerDoneLoc];* Check if pkt came in (EFlag = 1)
EOTemp ← EDisableOutput;* If so, abort output
OUTPUT [EOTemp, EWriteState], call[ERet]; *the transmitter is disabled, so...
breakpoint; *we will never get to this instruction.

* Check if still more time to elapse before start of transmission (High part of random number >=0).
EOMoreTime:
EOTemp1 ← 177C;* Set up maximum timer value
GOTO [EOLoadTimer, R>=0], EOCount ← (EOCount) - 1;
* Enable output and shut off the receiver (in case it was turned on).
EOBegin:
EOTemp ← EDisableInput;
EOTemp ← (EOTemp) OR (EEnableOutput);
OUTPUT [EOTemp, EWriteState];
* Set up EOPtr and EOCount for single word transfers.
EOSetup:
T ← EOCLOC;
CALL[EBSetup];
* Subroutine returns with: EOPtr = OPtr + OCount - 1, EOCount = -OCount
* Check for zero count.
GOTO [EOCountZero, R>=0], LU ← EOCount;* R<0 => count is zero
* Compute how many singles before first quadword, and form loop counter in EOTemp1.
*
Address: x00 => no singles, loop count = -1
*
Address: x01 => 3 singles, loop count = 0
*
Address: x10 => 2 singles, loop count = 1
*
Address: x11 => 1 singles, loop count = 2
T ← EOCount;
T ← (EOPtr) + (T) + 1;* Form start address in T
EOTemp1 ← (ZERO) - (T);* Complement, increment
CALL [EOAlign], EOTemp1 ← (LDF[EOTemp1, 16, 2]) - 1;
EOAlign:
GOTO [EOQuad, R<0], EOTemp1 ← (EOTemp1) - 1;
GOTO [EONoMore1, R>=0], T ← EOCount ← (EOCount) + 1;
PFetch1 [EOPtr, EOTemp];
LU ← EOTemp;* Abort
GOTO[ERet], OUTPUT [EOTemp, EOData];

* Now start quadword output.
* Adjust EOPtr and EOCount for 4-word transfers.
EOQuad:
EOCount ← (EOCount) + (3C);
CALL [EOLoop], EOPtr ← (EOPtr) - (6C);* Set return address for out loop
* Output from the Main Memory Output Buffer to the Hardware Output Buffer.
EOLoop:
GOTO [EOQuadEmpty, R>=0], T ← EOCount ← (EOCount) + (4C);
GOTO [EOAbort, IOATTEN];
RETURN, IOFetch4 [EOPtr, EOmData];

* Normal exit from Output Loop is here
* 7 - XWCount = number of singles remaining
* T is set up for next location.
EOQuadEmpty:
EOCount ← (EOCount) XOR (7C);
CALL[EOSingles], EOCount ← (EOCount) - 1;
EOSingles:
GOTO [EONoMore, R<0], EOCount ← (EOCount) - 1;
PFetch1 [EOPtr, EOTemp];
T ← (ZERO) + (T) + 1, EOTemp;* Abort
GOTO[ERet], OUTPUT [EOTemp, EOData];
* We’re done outputing words. Set output EOP.
EONoMore1:
NOP;
EONoMore:
EOTemp ← ESetOutputEOP;
OUTPUT[EOTemp,EWriteState], call[ERet]; * Set OutputEOP
* Should be woken up here after hardware’s done sending packet or an error
EOEND:
INPUT [EOTemp, EStatus];* Read Status
EOEND1:

LU ← (EOTemp) AND (ECOLLMASK);* Look at collision bit
GOTO [EOCOLL, ALU#0], EOTemp←EDisableOutput;* ALU#0 => Collision, try again
* If not collision, form status. Could be good packet or underrun (ODL).
EOCount ← 0C;
EOTemp ← RSH[EOTemp, 10];* Shift down status
EOTemp ← (EOTemp) AND (EOSMASK);* Remove uninteresting bits
EOTemp ← (EOTemp) XNOR (ESODON);
EOPost:
T ← EELOC;
CALL[ETaskRet], Pstore1 [eMDS, EOCount];* Store end count
CALL [EPost], EOCount ← EDisableOutput;
breakpoint;* Shouldn’t get here.
* We arrive here after an IOATTEN is detected in the main loop, indicating an error condition.
* IOATTEN will be true if a collision or underrun has occurred.
EOAbort:
GOTO [EOEND1], INPUT [EOTemp, EStatus];* Now read status

* Collision encountered, disable hardware to clear collision, enable and try again.
EOCOLL:
OUTPUT[EOTemp, EWriteState];
EOTemp ← EEnableOutput;
OUTPUT[EOTemp, EWriteState], call[ERet];
GOTO [EOIDLE];
* Load overflow, post status (NOT [ESLOAD], EOTemp is 0)
EOLDOV:
GOTO [EOPost], EOTemp ← (EOTemp) XNOR (ESLOAD);

* Output buffer count is zero. Post (NOT [ESCZR]).
EOCountZero:
EOTemp ← 0C;
GOTO [EOPost], EOTemp ← (EOTemp) XNOR (ESCZER);


* Task-independent Subroutines
*These subroutines will work properly if
*called from EITask or EOTask, due to the identical ordering of the registers in the two tasks.
SetTask[AND[EITask,14]]; *Task 0 mod 4 of EI/EOTask block
RV[ExTemp2,0 ];
RV[ExPtr,2];
RV[ExPtrHi,3];
RV[ExCount,4 ];
RV[ExTemp,5 ];

*SUBROUTINE [EPost];
* Posts the command completion, and starts an interrupt.
* Expects post code and status in ExTemp. ExCount has disable code to be sent to State register.
EPost:
T ← EBLOC;
PFetch1 [eMDS, ExTemp2];* Fetch wakeup mask;
T ← EPLOC;
PStore1 [eMDS, ExTemp];* Store ending status in EPLOC
* Now wakeup driver.
LoadPage[0];
OUTPUT [ExCount, EWriteState], gotop[DoRIntT];

* SUBROUTINE [EBSetup].
* Set up EPtr and ECount register.
* On entry T has pointer to EICLOC or EOCLOC.
* Subroutine returns with:
*
EPtr = Buffer Pointer + Count - 1
*
ECount = - Count
* The appropriate input or output pointer and count locations are used.
* Subroutine knows that
EIPLOC = EICLOC + 1, and EOPLOC = EOCLOC + 1.
EBSetup:
PFetch1 [eMDS, ExCount];* Fetch count
NOP;* So T can be written
T ← (ZERO) + (T) + 1;* Point to ExPLOC
PFetch1 [eMDS, ExPtr];* Fetch pointer
T ← (ExCount) - 1;
ExPtr ← (ExPtr) + (T);* Ptr ← Ptr + count - 1
RETURN, ExCount ← (ZERO) - (T) - 1;* Count ← - Count

* Subroutine [EINIT].
* Initialization subroutine.
* Called by both input and output task.
* ETemp contains the enable code to be used to enable the hardware.
EINIT:
T ← eMDShi;
ExPtrHi ← T;* Set up high part of Buffer pointer
GOTO[ERet], OUTPUT [ExTemp, EWriteState];


* These instructions used for Tasking after OUTPUT instructions.
ERet:
NOP;
ETaskRet:
EOTemp ← EOTemp, RETURN; *Interlock output operations

:END[Ether];