*----------------------------------------------------------------------------
Title[LNSEther.Mc];
* Edit history
* 18-Mar-85 19:46:37, Masinter, move defs to beginning
* May 22, 1984  10:10 AM...W.D. Evans];
*  [PilotNSEther.mc...July 13, 1984  2:47 PM...Willie-Sue]
* NS Ethernet microcode for Pilot.  Rework of PilotEther.mc by Taft
*----------------------------------------------------------------------------
* Lisp definitions for 10MB Ether

BR[NSIBR, 14];	* 10 MB Ethernet Input base register
BR[NSOBR, 15];	* 10 MB Ethernet Output base register
BR[IOBR, 36]; * = MDS
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];


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

* INPUT TASK
Set[!NSIRegs, !Region16];	* RMRegion[NSIRegs]
 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
Set[!NSORegs, !Region17];	* RMRegion[NSORegs]
  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]];


*----------------------------------------------------------------------------
* 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.)
%

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