; ExtraEther2.mu -- microcode for the second extra Ethernet board

; Last modified July 19, 1978  6:28 PM

;4-way branches using NEXT6 and NEXT7
!17,20,xIFB00,xODOK,xOEOK,xNOCMD,xIFB01,xODPST,xOEPST,xOREST,xIFB10,xODCOL,xOECOL,xIREST,xIFB11,xODUGH,xOEUGH,xRBRES;

;2-way branches using NEXT7
;xOCDW1, xOCDWX, and xIGO are all related.  Be careful!
!7,10,,xIFOK,,xOCDW1,,xIFBAD,xOCDWX,xIGO;

;Miscellaneous address constraints
!7,10,,xOCDW0,xODATA,xIDFUL,xIDZ4,xOCDRS,xIDATA,xPOST;
!7,10,,xIDOK,,,xIDMOR,xIDPST;
!1,1,xIFB1;
!1,1,xIFRST;

;2-way branches using NEXT9
!1,2,xOINPR,xOINPN;
!1,2,xODMOR,xODEND;
!1,2,xOLDOK,xOLDBD;
!1,2,xIFCHK,xIFPRM;
!1,2,xOCDWT,xOCDGO;
!1,2,xCNTOK,xCNTZR;
!1,2,xIFIGN,xISET;
!1,2,xIFNBC,xIFBC;

;Top of Ethernet Task loop

;Ether Rest Branch Function - ERBFCT
;merge ICMD and OCMD Flip Flops into NEXT6 and NEXT7
;ICMD and OCMD are set from AC0 [14:15] by the SIO instruction
;	00  neither 
;	01  OCMD - Start output
;	10  ICMD - Start input
;	11  Both - Reset interface

;in preparation for a hack at xIREST, zero xPNTR

xREST:	L← 0,ERBFCT;		What's happening ?
	xPNTR← L,:xNOCMD;	[xNOCMD,xOREST,xIREST,xRBRES]

xNOCMD:	L← ESNEVR,:xPOST;	Shouldn't happen
xRBRES:	L← ESABRT,:xPOST;	Reset Command

;Post status and halt.  Microcode status in L.
;Put microstatus,,hardstatus in EPLOC, merge c(EBLOC) into NWW.
;Note that we write EPLOC and read EBLOC in one operation

;Ether Post Function - EPFCT.  Gate the hardware status
;(LOW TRUE) to Bus [10:15], reset interface.

xPOST:	T← 14;
	MAR← 630+T;		(EELOC)
	xPNTR← L,TASK;		Save microcode status in xPNTR
	MD← xCNTR;		Save ending count

	MAR← 642;		(EPLOC) double word reference
	T← NWW;
	MD← xPNTR,EPFCT;	BUS AND xPNTR with Status
	L← MD OR T,TASK;	NWW OR c(EBLOC)
	NWW← L,:xREST;		Done.  Wait for next command

;This is a subroutine called from both input and output (xOCDGO
;and xISET).  The return address is determined by testing ECBFCT,
;which will branch if the buffer has any words in it, which can
;only happen during input.

xSETUP:	NOP;
	L← MD,BUS=0;		check for zero length
	T← MD-1,:xCNTOK;	[xCNTOK,xCNTZR] start-1

xCNTZR:	L← ESCZER,:xPOST;	Zero word count.  Abort

;Ether Countdown Branch Function - ECBFCT.
;NEXT7 = Interface buffer not empty.

xCNTOK:	xCNTR← L,L← T,ECBFCT,TASK;
	xPNTR← L,:xODATA;	[xODATA,xIDATA]

;Ethernet Input

;It turns out that starting the receiver for the first time and
;restarting it after ignoring a packet do the same things.

xIREST:	:xIFIGN;		Hack

;Address filtering code.

;When the first word of a packet is available in the interface
;buffer, a wakeup request is generated.  The microcode then
;decides whether to accept the packet.  Decision must be reached
;before the buffer overflows, within about 14*5.44 usec.
;if EHLOC is zero, machine is 'promiscuous' - accept all packets
;if destination byte is zero, it is a 'broadcast' packet, accept.
;if destination byte equals EHLOC, packet is for us, accept.

;xIFRST is really a subroutine that can be called from xIREST
;or from xIGO, output countdown wait.  If a packet is ignored
;and xPNTR is zero, xIFRST loops back and waits for more
;packets, else it returns to the countdown code.

;Ether Branch Function - EBFCT
;NEXT7 = IDL % OCMD % ICMD % OUTGONE % INGONE (also known as POST)
;NEXT6 = COLLision - Can't happen during input

xIFRST:	T← 22;
	MAR← 630+T;		(EHLOC) Get Ethernet address
	T← 377,EBFCT;		What's happening?
	L← MD AND T,BUS=0,:xIFOK;[xIFOK,xIFBAD] promiscuous?

xIFOK:	MTEMP← LLCY8,:xIFCHK;	[xIFCHK,xIFPRM] Data wakeup

xIFBAD:	ERBFCT,TASK,:xIFB1;	[xIFB1] POST wakeup; xCMD FF set?
xIFB1:	:xIFB00;		[xIFB00,xIFB01,xIFB10,xIFB11]

xIFB00:	:xIFIGN;		IDL or INGONE, restart rcvr
xIFB01:	L← ESABRT,:xPOST;	OCMD, abort
xIFB10:	L← ESABRT,:xPOST;	ICMD, abort
xIFB11:	L← ESABRT,:xPOST;	ICMD and OCMD, abort

xIFPRM:	TASK,:xIFBC;		Promiscuous. Accept

;Ether Look Function - EILFCT.  Gate the first word of the 
;data buffer to the bus, but do not increment the read pointer.

xIFCHK:	L← T← 177400,EILFCT;	Mask off src addr byte (BUS AND)
	L← MTEMP-T,SH=0;	Broadcast?
	SH=0,TASK,:xIFNBC;	[xIFNBC,xIFBC] Our Address?

xIFNBC:	:xIFIGN;		[xIFIGN,xISET]

xIFBC:	:xISET;			[xISET] Enter input main loop

;Ether Input Start Function - EISFCT.  Start receiver.  Interface
;will generate a data wakeup when the first word of the next
;packet arrives, ignoring any packet currently passing.

xIFIGN:	SINK← xPNTR,BUS=0,EPFCT;Reset; Called from output?
	EISFCT,TASK,:xOCDWX;	[xOCDWX,xIGO] Restart rcvr

xOCDWX:	EWFCT,:xOCDWT;		Return to countdown wait loop

xISET:	T← 16;
	MAR← 630+T,:xSETUP;	(EICLOC) Double word reference

;Input Main Loop

;Ether Input Data Function - EIDFCT.  Gate a word of data to
;the bus from the interface data buffer, increment the read ptr.
;		* * * * * W A R N I N G * * * * *
;The delay from decoding EIDFCT to gating data to the bus is
;marginal.  Some logic in the interface detects the situation
;(which only happens occasionally) and stops SysClk for one cycle.
;Since memory data must be available during cycle 4, and SysClk
;may stop for one cycle, this means that the MD← EIDFCT must
;happen in cycle 3.  There is a bug in this logic which occasionally
;stops the clock in the instruction following the EIDFCT, so
;the EIDFCT instruction should not be the last one of the task,
;or it may screw up someone else (such as RDRAM).

;xIDOK, xIDMOR, and xIDPST must have address bits in the pattern:
;xxx1   xxx4        xxx5
;ECBFCT is used to force an unconditional branch on NEXT7

xIDATA:	T← xCNTR-1, BUS=0;
	MAR← L← xPNTR+1, EBFCT;	[xIDMOR,xIDPST] What's happening
xIDMOR:	xPNTR← L, L← T, ECBFCT;	[xIDOK,xIDPST] Guaranteed to branch
xIDOK:	MD← EIDFCT, TASK;	[xIDZ4] Read a word from the interface
xIDZ4:	xCNTR← L, :xIDATA;

; We get to xIDPST for one of two reasons:
; (1) The buffer is full.  In this case, an EBFCT (NEXT[7]) is pending.
;     We want to post "full" if this is a normal data wakeup (no branch)
;     but just "input done" if hardware input terminated (branch).
; (2) Hardware input terminated while the buffer was not full.
;     In this case, an unconditional branch on NEXT[7] is pending, so
;     we always terminate with "input done".
xIDPST:	L← ESIDON, :xIDFUL;	[xIDFUL,xPOST] Presumed to be INGONE
xIDFUL:	L← ESIFUL, :xPOST;	Input buffer overrun

;Ethernet output

;It is possible to get here due to a collision.  If a collision
;happened, the interface was reset (EPFCT) to shut off the
;transmitter.  EOSFCT is issued to guarantee more wakeups while
;generating the countdown.  When this is done, the interface is
;again reset, without really doing an output.

xOREST:	T← 15;
	MAR← 630+T;		(ELLOC) Get load
	L← R37;			Use clock as random # gen
	xPNTR← LRSH1;		Use bits [6:13]
	L← MD,EOSFCT;		L← current load
	SH<0,xCNTR← L;		Overflowed?
	MTEMP← LLSH1,:xOLDOK;	[xOLDOK,xOLDBD]

xOLDBD:	L← ESLOAD,:xPOST;	Load overlow

xOLDOK:	L← MTEMP+1;		Write updated load
	MAR← 630+T;		(ELLOC);
	MTEMP← L,TASK;
	MD← MTEMP,:xORST1;	New load = (old lshift 1) + 1

xORST1:	L← xPNTR;		Continue making random #
	xPNTR← LRSH1;
	T← 377;
	L← xPNTR AND T,TASK;
	xPNTR← L,:xORST2;

;At this point, xPNTR has 0,,random number, xNCTR has old load.

xORST2:	T← 16;
	MAR← 630+T;		(EICLOC) Has an input buffer been set up?
	T← xCNTR;
	L← xPNTR AND T;		L← Random & Load
	SINK← MD,BUS=0;
	xCNTR← L,SH=0,EPFCT,:xOINPR;[xOINPR,xOINPN] 

xOINPR:	EISFCT,:xOCDWT;		[xOCDWT,xOCDGO] Enable in under out

xOINPN:	:xOCDWT;		[xOCDWT,xOCDGO] No input.

;Countdown wait loop.  MRT will generate a wakeup every
;37 usec which will decrement xCNTR.  When it is zero, start
;the transmitter.

;Ether Wake Function - EWFCT.  Sets a flip flop which will cause
;a wakeup to this task the next time MRT wakes up (every 37 usec).
;Wakeup is cleared when Ether task next runs.  EWFCT must be
;issued in the instruction AFTER a task.

xOCDWT:	L← 177400,EBFCT;	What's happening?
	xPNTR← L,ECBFCT,:xOCDW0;[xOCDW0,xOCDRS] Packet coming in?
xOCDW0:	L← xCNTR-1,BUS=0,TASK,:xOCDW1; [xOCDW1,xIGO]
xOCDW1:	xCNTR← L,EWFCT,:xOCDWT;	[xOCDWT,xOCDGO]

xOCDRS:	L← ESABRT,:xPOST;	[xPOST] POST event

xIGO:	:xIFRST;		[xIFRST] Input under output

;Output main loop setup

xOCDGO:	T← 20;
	MAR← 630+T;		(EOCLOC) Double word reference
	EPFCT;			Reset interface
	EOSFCT,:xSETUP;		Start Transmitter

;Ether Output Start Function - EOSFCT.  The interface will generate
;a burst of data requests until the interface buffer is full or the
;memory buffer is empty, wait for silence on the Ether, and begin
;transmitting.  Thereafter it will request a word every 5.44 us.

;Ether Output Data Function - EODFCT.  Copy the bus into the
;interface data buffer, increment the write pointer, clears wakeup
;request if the buffer is now nearly full (one slot available).

;Output main loop

xODATA:	L← MAR← xPNTR+1,EBFCT;	What's happening?
	T← xCNTR-1,BUS=0,:xODOK; [xODOK,xODPST,xODCOL,xODUGH]
xODOK:	xPNTR← L,L← T,:xODMOR;	[xODMOR,xODEND]
xODMOR:	xCNTR← L,TASK;
	EODFCT← MD,:xODATA;	Output word to transmitter

xODPST:	L← ESABRT,:xPOST;	[xPOST] POST event

xODCOL:	EPFCT,:xOREST;		[xOREST] Collision

xODUGH:	L← ESABRT,:xPOST;	[xPOST] POST + Collision

;Ether EOT Function - EEFCT.  Stop generating output data wakeups,
;the interface has all of the packet.  When the data buffer runs
;dry, the interface will append the CRC and then generate an
;OUTGONE post wakeup.

xODEND:	EEFCT;			Disable data wakeups
	TASK;			Wait for EEFCT to take
	:xOEOT;			Wait for Outgone

;Output completion.  We are waiting for the interface buffer to
;empty, and the interface to generate an OUTGONE Post wakeup.

xOEOT:	EBFCT;			What's happening?
	:xOEOK;			[xOEOK,xOEPST,xOECOL,xOEUGH]

xOEOK:	L← ESNEVR,:xPOST;	Runaway Transmitter. Never Never.

xOEPST:	L← ESODON,:xPOST;	POST event.  Output done

xOECOL:	EPFCT,:xOREST;		Collision

xOEUGH:	L← ESABRT,:xPOST;	POST + Collision