*----------------------------------------------------------------------------
Title[Pilot10MBEther.Mc...May 22, 1984 10:10 AM...W.D. Evans];
* [Pilot10MBEther.mc...May 7, 1986 5:51:13 pm PDT...Willie-Sue]
* NS Ethernet microcode for Pilot. Rework of PilotEther.mc by Taft
*----------------------------------------------------------------------------

*----------------------------------------------------------------------------
* RM Assignments
*----------------------------------------------------------------------------

* INPUT TASK
SetRMRegion[NSIRegs];
RVN[NSIIocb];
* Must match location of NSOIocb
RVN[NSILength];
* Must match location of NSOLength
RVN[NSIPtr];
* Must match location of NSOPtr
RVN[NSITemp1];
* Must match location of NSOTemp1
RVN[NSIHost0];
* Host number checking storage
RVN[NSIHost1];
* Host number checking storage
RVN[NSIHost2];
* Host number checking storage

* OUTPUT TASK
SetRMRegion[NSORegs];
RVN[NSOIocb];
* Must match location of NSIIocb
RVN[NSOLength];
* Must match location of NSILength
RVN[NSOPtr];
* Must match location of NSIPtr
RVN[NSOTemp1];
* Must match location of NSITemp1

* COMMON SUBROUTINES
RvRel[NSxCB,And[IP[NSIIocb],17]];
* IOCB Base
RvRel[NSxTemp1,And[IP[NSITemp1],17]];
RvRel[NSxPtr,And[IP[NSIPtr],17]];
RvRel[NSxLength,And[IP[NSILength],17]];

BR[NSIBR, 16];
* 10 MB Ethernet Input base register
BR[NSOBR, 17];
* 10 MB Ethernet Output base register

*----------------------------------------------------------------------------
* Data structures
*----------------------------------------------------------------------------

% CSB: TYPE = LONG POINTER TO ControllerStatusBlock;
* ControllerStatusBlock: TYPE = MACHINE DEPENDENT RECORD [
* host: NSPilotSystem.HostNumber, -- Three words. First all ones for all packets
* input: ShortIocb,
* inputWakeups: WORD,
* output: ShortIocb,
* outputWakeups: WORD,
* missed: CARDINAL,
* lastInput: IOCB, -- last IOCB on input queue, valid if input#noIocb
* lastOutput: IOCB]; -- last IOCB on output queue, valid if output#noIocb

NOTE:
The wakeups are indexed to relative to the shortiocb location in the csb by a common subroutine.)
%
MC[NSCSB.host, 177700];
* Three word Host number
MC[NSCSB.NextIn,NSCSB.host, 3];
* ShortIocb pointer to input IOCB
MC[NSCSB.NextOut,NSCSB.host, 5];
* ShortIocb pointer to output IOCB
MC[NSCSB.missed,NSCSB.host, 7];

% IOCB: TYPE = LONG POINTER TO IOControlBlock;
* ShortIocb: TYPE = Base RELATIVE POINTER TO IOControlBlock;
* IOControlBlock: TYPE = MACHINE DEPENDENT RECORD [
* bufferLen: CARDINAL,
-- 0 Buffer length in Bytes
* buffer: LONG POINTER,
-- 1,2
* retries: WORD,
-- 3 Retry Count
* packetLen: CARDINAL,
-- 4 Packet Length in bytes
* completion: WORD,
-- 5 Ending Status
* next: ShortIocb];
-- 6 Next IOCB location
%

MC[IOCB.bufferLen, 0];
MC[IOCB.bufferHi, 2];
MC[IOCB.bufferLow, 1];
MC[IOCB.retries, 3];
MC[IOCB.packetLen, 4];
MC[IOCB.completion, 5];
MC[IOCB.next, 6];

*----------------------------------------------------------------------------
* TASK assignments
*----------------------------------------------------------------------------
TaskN[NSIT,12];
TaskN[NSOT,10];

*----------------------------------------------------------------------------
* TIOA assignments
*----------------------------------------------------------------------------
Device[NSData, 25];
* 10 Megabit Ethernet data input/output
Device[NSControl, 26];
* NS Ethernet control reg [Tx,Rx,Test]

*----------------------------------------------------------------------------
* Status Bits -- Transmitter and Control
*----------------------------------------------------------------------------
* Transmitter and general interface status bits (NSControl)
* Left byte is Ethernet host address.
* Bits 5 and 6 of the low byte are the IEC transmitter status, valid on abort or end of
* a packet. Their meanings are:
* 00 = Good transmission
* 01 = Late data (underrun) to the IEC during the transmission
* 10 = Collision
* 11 = The 16th collision has occured during this transmission

MC[RxOn, 200];
* Receiver on
MC[TxOn, 100];
* Transmitter on
MC[LoopBack, 40];
* Loop-back mode on
MC[IECBypass, 20];
* The IEC chip is bypassed for testing
MC[NoWakeups, 10];
* Task wakeups enabled if 0, disabled if 1
MC[TxDataLate, 4];
* Output data late
MC[TxFifoPE, 1];
* Transmitter-detected IOB or Fifo parity error

*----------------------------------------------------------------------------
* Status Bits -- Receiver
*----------------------------------------------------------------------------
* The receiver status is passed to user, via IOCB, in the upper byte of the completion code.
* When one, the bits of the word have the following meaning:
* Bit.0 = Buffer overrun on input. Set by the MC
* Bit.1 = Receiver had good alignment
* Bit.3=Packet had a good CRC
* Bit.6 = Packet contained an odd number of bytes.
* Bit.7 = The receiver overrun the Dorado input and data was lost
* All others will be zero.

MC[NSBufferOver,100000];
MC[OddByteStatus,1000];

*----------------------------------------------------------------------------
* Control Register Constants
*----------------------------------------------------------------------------
* Control bits (NSControl) -- Transmitter control (bits 0-3)
MC[TxCmdEnbl, 007777];
* Bit 0 = 0 enables decoding of output commands
MC[STxOn, 40000];
* Turn on output if 1, off if 0
MC[STxEOP, 20000];
* Send end of packet if 1
MC[STxOdd, 10000];
* Send on upper byte of next word (Odd byte length packet)
MC[TurnOnTx,TxCmdEnbl,STxOn];
MC[TurnOffTx,TxCmdEnbl];


* Control bits (NSControl) -- Receiver control (bits 4-7)
MC[RxCmdEnbl, 170377];
* Bit 4 = 0 enables decoding of input commands
MC[SRxOn, 2000];
* Turn on input if 1, off if 0
MC[SRxBOP’, 1000];
* Wait for beginning of next packet if 0

MC[TurnOnRx, RxCmdEnbl, SRxOn, SRxBOP’];
MC[TurnOffRx, RxCmdEnbl];
MC[WaitForBOP, RxCmdEnbl, SRxOn];

* Control bits (EControl) -- Test control (bits 8-15)
MC[TestCmdEnbl, 177400];
* Bit 8 = 0 enables decoding of test commands
MC[SLoopBack, 100];
* Turn on loop-back mode if 1, off if 0
MC[STxReset, 40];
* Turn on single-step mode if 1, off if 0
MC[SNoWakeups, 20];
* Enable task wakeups if 0, disable if 1
MC[SIECByPass, 2];
* Bypass the IEC and reset its Rx
MC[SCollisionTest, 1];
* Force a collision in the IEC


* Values needed
MC[STxEnd,STxEOP,STxOn,TxCmdEnbl];
* Turn on Tx EOP and leave others on
MC[STxOddEnd,STxEOP,STxOn,STxOdd,TxCmdEnbl];
* Turn on Tx EOP and leave others on
MC[STxOddOn,STxOn,STxOdd,TxCmdEnbl];
* Only change the Odd Byte control in Tx
MC[SByTxR,STxReset,SIECByPass];
* Cause both in IEC to reset


*----------------------------------------------------------------------------
* Emulator Task -- TurnOnNS instruction.
* Branched to from Mesa/Lisp emulator to reset NS Ethernet hardware and tasks.
* the hardware is left turned off, the tasks are re-inited..
*----------------------------------------------------------------------------
Set[XTask, IP[EMU]];
* Emulator mode
TopLevel;
DontKnowRBase;

Reset10MBEther:
T← NSControl;
TIOA← T;
TaskingOff;* Smash the hardware off
T← SByTxR, Call[OutputGetsT];
T← A0, Call[OutputGetsT];* Now they are reseting for microseconds
* leave hardware off
Call[NSITInitPC];* Reset the tasks
LdTPC← T, Wakeup[NSIT];
Call[NSOTInitPC];
LdTPC← T, Wakeup[NSOT];
T← A0, TaskingOn;
TIOA← T, IFUJump[0];

*----------------------------------------------------------------------------
* Initialization code for Input Task
*----------------------------------------------------------------------------
Set[XTask, IP[NSIT]];

Subroutine;
NSITInitPC: T← NSIT, CoReturn;
TopLevel;

T← NSControl;
TIOA← T;* Allow a long branch!
Block, Branch[NSIIdle];* full TIOA for initialization

*----------------------------------------------------------------------------
* Initialization code for Output Task
*----------------------------------------------------------------------------
Set[XTask, IP[NSOT]];

Subroutine;
NSOTInitPC: T← NSOT, CoReturn;
TopLevel;
T← NSControl;
TIOA← T;* Allow a long branch!
Block, Branch[NSOIdle];* full TIOA for initialization

*----------------------------------------------------------------------------
* Input Task
*----------------------------------------------------------------------------

Set[XTask, IP[NSIT]];
* Non-emulator mode

* Idle state of the NS Ethernet input task.
* Wake up here when first word of a new packet arrives.

NSIIdle:
MemBase← IOBR;
RBase← RBase[NSIRegs];
NSIPtr ← NSCSB.host;
TIOA[NSData];
NSITemp1 ← A0,Call[NSINxtHost];* Get first packet host word
NSIHost0 ← (NSILength),Branch[NSIAll,alu<0];* Test if we are to take all, save (1)
Branch[.+1];* Placement
Call[NSINxtHost];* Get second host word
NSIHost1 ← (NSILength),Call[NSINxtHost];* Get the third word
Pd ← NSITemp1;* See if our packet
NSIHost2 ← (NSILength),Branch[NSIIgnore1,alu#0];* Go if not ours
Call[NSITstBuf];* See if we have a place to put it
Call[NSIOCBSetup];*++++++++++ We do so get it ready (Long call)
T ← (NSIHost0),Call[NSIPutIt];* Place first one in buffer
T ← (NSIHost1),Call[NSIPutIt];* Place second one in buffer
T ← (NSIHost2),Branch[NSILoop];* Go due rest of packet

NSIAll:
Call[NSITstBuf];* See if there is room to put it
Call[NSIOCBSetup];*++++++++++ Set "In" flg, get location and count
T ← (NSIHost0),Branch[NSILoop];* Let main loop put it in buffer

NSIIgnore1:
Branch[NSIIgnore];* Placement -- Conditional to Call
NSIIgnore2:
T ← Input;* Get status -- Call nxt is placement
Call[NSIOverTst];* Go see if this is overrun condition
NSIIgnore:
Call[NSISkipP];* Skip remainder of packet
Block,Branch[NSIIdle];* Go wait for another

Subroutine;
NSIPutIt:
NSIPtr ← (Store ← NSIPtr)+1,DBuf ← T,Return;* Place one in buffer

NSINxtHost:
NSIPtr ← (Fetch←NSIPtr)+1,
Branch[NSIIgnore2,IOAtten];* Get our number and test end packet
NSILength ← T ← Input,Block;* Get first word and wait a while
T ← T xor MD;* Compare the numbers
NSITemp1 ← (NSITemp1) or T;* Keep a running total of diff’s
Pd ← (NSILength) or MD,Return;* Do the accept all test
NSITstBuf:
T ← NSCSB.NextIn;* Get pointer to buffer
Fetch ← T,NSIPtr ← A0;* Does this work? Need to get long call
pd← Md;* In location SetUp needs
NSIIocb← Md, Branch[NSINoCB,alu=0];* Don’t have one so add to missed
Return;
TopLevel;

* Input main loop.
* Each iteration stores the word previously input from the interface
* and inputs the next word. This facilitates end-of-packet handling.
* EIPtr has negative of number of words remaining in the buffer.
* IOAtten branches if the data word being read is the end-of-packet status.

NSILoop:
NSIPtr ← (Store← NSIPtr)+1, DBuf← T, Branch[NSIEnd, IOAtten];
T ← Input;
PD ← NSIPtr;
Block, DblBranch[NSILoop,NSILast,alu#0];

* Get here when buffer is exactly full (NSIPtr=0).
* We know that the word in T is not EOP status since we would have taken
* the IOAtten branch if it were. There can be one, or zero CRC words in the buffer at this
* point. If there is one the word up for reading is the second and we can throw it out and try
* for the status. If not there then we have overflow.

NSILast:
NSIPtr ← (-2C);* Set packet length for one CRC
T ← ((NSILength)+1) rsh 1;* When incremented at instruction
NSILastLp:
NSIPtr ← (NSIPtr) + 1,Branch[NSIEnd1,IOAtten];* See if this is the status byte
NSIHost2 ← Input,Block,Branch[NSILastLp,alu#0];* Throw away what can be CRC
Call[NSISkipP];* Will not fit--skip remainder
T ← NSBufferOver,Branch[NSIPost];* Set the overflow status bit

NSIEnd:
T ← ((NSILength) - 1) rsh 1;
NSIEnd1:
NSIPtr← ((NSIPtr)+T) Lsh1;* Actual packet length in bytes
T ← Input,Call[NSIOverTst];* Get status and test of overrun
Pd ← (NSIPtr) - (16C);* As in DLion?? Require 6 words
Branch[.+2,alu>=0];* Status byte (in upper byte)
Branch[NSIShort];* Placement (Better way??)
Pd ← (T) and (OddByteStatus);
T ← (T) and (177400C),Branch[NSIPost,alu=0];* Input status is upper byte
NSIPtr ← (NSIPtr) - 1;
* Post input completion status now in T and length (in bytes) in NSIPtr.
NSIPost:
NSITemp1← NSCSB.NextIn;
Call[NSPost];*+++++++++ Allow long Call for placement
NSIShort:
TIOA[NSControl],Block, Branch[NSIIdle];* Await next packet


* Discard packet because IOCB queue is empty.

NSINoCB:
T← NSCSB.missed; * Increment ICSB.missed
Fetch← T,Call[NSISkipP];* Ignore rest of packet
NSITemp1← MD+1;
Store← T,DBuf← NSITemp1,Block,Branch[NSIIdle];

Subroutine;
NSISkipP:
TIOA[NSControl];* Throw away rest of packet
NSISkipP1:
NSITemp1 ← WaitForBOP;* Save T for no iocb routine
Output ← NSITemp1,Return;* By using temp

* Routine to test for an overflow and handle the IEC kludge for the condition.
* We must turn off and then on the controller and skip the next packet.
* T has status on input. T must be saved in the testing.
Subroutine;
NSIOverTst:
Pd ← (T) and (400C);* get the overrun status bit
NSITemp1 ← TurnOffRx,Branch[NSIOverTstX,alu=0];
TIOA[NSControl];
Output ← NSITemp1;* Set Rx off
NSITemp1 ← TurnOnRx;
Output ← NSITemp1,Branch[NSISkipP1];* Logic is clear when it comes on

NSIOverTstX:
Return;
TopLevel;

*----------------------------------------------------------------------------
* Output Task
*----------------------------------------------------------------------------

Set[XTask, IP[NSOT]];

* Idle state of the NS Ethernet output task. TIOA=NSControl.
* Wake up here at end of previous packet or when poked by software.
NSOIdle:
MemBase← IOBR;
RBase← RBase[NSORegs];
T← NSCSB.NextOut;* Fetch CSB.next for output
Fetch← T,;
NSOIocb ← MD;* Save base of IOCB for later
Branch[NSONoIocb, ALU=0];* Go shut off transmitter if no IOCB
NSOPtr ← (-1C);* Signal who we are for BR selection

Call[NSIOCBSetup];* Get data location and length
NSOPtr ← (Fetch ← NSOPtr) + 1;* Get first word to output
Branch[NSONone,alu>=0];* Post end if there are no more words
TIOA[NSData];* Ready to do the packet output

* Output main loop.
* Data transfer is pipelined: each iteration outputs a data word
* fetched in the previous iteration and starts the fetch of the data for
* the next iteration. This way, cache misses generally occur while our
* task is blocked, and lower-priority tasks are permitted to run while
* the cache is being loaded.
* NSOPtr is the negative of the number of words remaining to be fetched.
* IOAtten branches if a collision, data late, or Fifo PE has aborted output.

NSOPtr← (Fetch← NSOPtr)+1, T← MD, Branch[NSOAbort, IOAtten];
Output← T, Block, Branch[.-1, ALU#0];

* Next-to-last word has been output and last word has been fetched
* Now see if this is an odd or even length packet

NSOEnd:
NSOPtr ← (NSOLength),
Branch[NSOAbort1, IOAtten];* See if trouble
NSOLength, T ← STxEnd,* No problems so see if odd or even
DblBranch[NSOOddEnd,NSOEnd1,R odd];

NSOAbort:
TIOA[NSControl],Branch[NSONone];* Ready for status--Second IOAtten does it

NSOOddEnd:
TIOA[NSControl];
T ← STxOddOn;* Set the odd byte control on
Output ← T;* For the last load of TxBusReg
TIOA[NSData];
T ← STxOddEnd;

NSOEnd1:
Output ← MD;* Output the last word
TIOA[NSControl];
Output ← T,Block,Branch[NSONone];* End output and wait till end

* Wake up here only when packet completely sent or collision has occurred.

NSOAbort1:
TIOA[NSControl];* Got bad before sent last word--get status
NSONone:
T ← Input;* Get the ending status bad or good here
NSOTemp1 ← T ← T and (377C),* Need status in T for Done if no IOAtten
Branch[NSOAbort2, IOAtten];* Remove the host number or trash
NSOPtr ← (NSOLength),Branch[NSODone];* Set all gone-post status-go for next

NSOAbort2:
* All bad if we get here -- Now ans why
Pd ← (NSOTemp1) and (4C);* Isolate the collsion bit
MemBase ← IOBR,Branch[.+2,alu#0];* Go post if none
T ← (NSOTemp1),Branch[NSODone];* Not collsion--let Head find it
T ← (NSOIocb) + (IOCB.Retries);* Up count for the user
Fetch ← T;
NSOLength ← MD + 1;* Don’t need length any more
Store ← T,DBuf ← NSOLength;
Pd ← (NSOTemp1) and (2C);* Check if this is the last collision (16th)
T ← TurnOffTx,Branch[.+2,alu=0];* End it if too many
T ← (NSOTemp1),Branch[NSODone];
NSOGoAgain:
Output ← T;* Try again with the same packet
T ← TurnOnTx;* Or new IOCB if there was one
Output ← T,Block,Branch[NSOIdle];* Off then on clears abort status

NSODone:
NSOTemp1 ← NSCSB.NextOut;* Post status and go for next IOCB
Call[NSPost];*++++++++++ Allow Long Call for placement
Pd ← T;
T ← TurnOffTx,Branch[.+2,alu=0];* T has next ShortIOCB on return
Branch[NSOGoAgain];* There was another one so go to it
Output ← T,Block,Branch[NSOIdle];* No more so stop
NSONoIOCB:
T ← TurnOffTx,Branch[.-1];* Who knows how but kill it

*----------------------------------------------------------------------------
* Task-independent Subroutines
*----------------------------------------------------------------------------

*----------------------------------------------------------------------------
NSIOCBSetup:
* Set up IOCB pointers and counts
* Enter:
NSxCB = IOCB being worked on
*
NSxPtr = 0 for input, -1 for output
*
MemBase = IOBR
*
* Exit:
NSxPtr = negative word count
*
NSxLength = Byte Length from IOCB
*
MemBase = NSxBR
*
NSxBR = base register properly set up for indexing by ExPtr
* Points the base register beyond the end of the buffer -2↑16, and
* addresses the buffer with a negative index in ExPtr.
* Clobbers NSxTemp1, T
*----------------------------------------------------------------------------
Subroutine;

T← (NSxCB)+(IOCB.bufferLen);
T← (Fetch← T)+1;* Fetch IOCB.length
NSxLength← MD;
T← (Fetch← T)+1;* Fetch low buffer pointer
NSxTemp1 ← (NSxLength)+1;* Form the length in words
NSxTemp1 ← (NSxTemp1) rsh 1;
Fetch← T, T← MD,* Fetch high buffer pointer
NSxPtr, Branch[.+2, R<0];* Which base register?
T← T+(NSxTemp1), MemBase← NSIBR, Branch[.+2]; * Low pointer + length
T← T+(NSxTemp1), MemBase← NSOBR;* Low pointer + length
T← MD, BRLo← T;
T← T-1, XorSavedCarry;* Adjust high for negative indexing
T← NSxTemp1, BRHi← T;
NSxPtr← (0S)-T, Return;* NSxPtr← negative count

*----------------------------------------------------------------------------
NSPost:
* Post command completion.
* Enter: T = completion status word
*
NSxPtr = actual packet length
*
NSxCB = IOCB being worked on
*
NSxTemp1 = Position of next IOCB short pointer (Interupt word follows it)
* Exit:
MemBase = IOBR
*
T = Shortpointer to next IOCB
* Clobbers T, NSxCB, NSxTemp1, RBase
*----------------------------------------------------------------------------
Subroutine;

MemBase← IOBR;
NSxCB ← (NSxCb) + (IOCB.next);
NSxCB← (Fetch← NSxCB)-1;* Fetch IOCB.next
T← (Store← NSxCB)-1, DBuf← T;* Store IOCB.completion
Store← T, DBuf← NSxPtr, T← MD;* Store IOCB.wordsUsed
NSxTemp1← (Store← NSxTemp1)+1, DBuf← T;* Store xCSB.next
Fetch← NSxTemp1;* Fetch xCSB.interruptMask
RBase← RBase[NWW];* Initiate interrupts
NWW← (NWW) OR MD, Reschedule, Return;
TopLevel;