{File name:  EtherDLion.mc
 Description: Dandelion Ethernet Microcode,
 Author: Sandman,
 Created: October 23, 1980  10:42 AM,
 Last Edited:
	Amy Fasnacht,  7-Oct-83 14:46:51,
		Move the IOPage for larger VM.
	HGM, 19-Sep-82 19:06:26, Fixes for 16 Retries, save 3 mi.
	Amy Fasnacht, August 3, 1982  12:09 PM,
		Move definitions to Dandelion.df for new IOPage layout.
	Jim Frandeen, March 16, 1982  11:49 AM,
		Fix bug that was storing the mask into the wrong word.
	Jim Frandeen, March 5, 1982  3:01 PM,
		Fix bug encountered if 1st word of receive packet buffer
		lies in last word page.
	Amy Fasnacht, February 18, 1982  12:02 PM,
		Call EMap to restore register E after MAR on second word of input buffer
	Dan Davies, February 17, 1982  3:38 PM,
		Fix bug encountered if 1st word of receive packet buffer
		lies in last word of 64K space.  Also shorten code.
	Jim Frandeen, February 3, 1982  9:53 AM,
		If waiting to transmit is interrupted, remember where we
		we were so we can continue there next time.
		Limit random number to 10 bits.
	HGM, August 28, 1981  10:45 AM,
		Flap transmitter after sending (2000ft bug) (1mi)
	HGM, June 25, 1981  11:30 PM,
		Load host number if transmit first
		Move Purge EStrobe to c2 for hardware InUnderOut fix (1 mi)
	HGM, June 11, 1981  12:40 AM, Runt filter (4 mi)
	Sandman, November 12, 1980  11:42 AM
	HGM, June 10, 1981  9:38 PM, NegBr @EDeferLoop to avoid long delays
	Garner, April 21, 1981  8:01 PM:  rhEE←0 @EFullMask}

{*****************************************************************************
Definitions
*****************************************************************************}

	SetTask[2];
	StartAddress[EColdStart];

{*****************************************************************************
Initial code
*****************************************************************************}
EColdStart:	E ← uIOPage, CANCELBR[$,0F],	c1;
	Noop,	c2;
	Noop,	c3;
	
	rhE ← IOPageHigh,	c1;
	Xbus ← EStatus, XLDisp,	c2;
	rhEE ← 0, BRANCH[EHostTR, EStop, 2],	c3;

{Fetch host and input IOCB from CSB}
EHostTR:	MAR ← [rhE, IOPage.ECSB.host0+0], L6←L6.Host0,	c1, at[2,4,EStop];
EHost:	uXmitInterrupt ← 0, L6Disp, CALL[EFetch3],	c2;

	MAR ← [rhE, IOPage.ECSB.host1+0], L6←L6.Host1,	c1, at[L6.Host0,10,EFetch];
	uEHost0 ← EE, L6Disp, CALL[EFetch3],	c2;

	MAR ← [rhE, IOPage.ECSB.host2+0], L6←L6.Host2,	c1, at[L6.Host1,10,EFetch];
	uEHost1 ← EE, L6Disp, CALL[EFetch3],	c2;

	MAR ← [rhE, IOPage.ECSB.icb+0]	c1, at[L6.Host2,10,EFetch];
	uEHost2 ← EE, L6←L6.EReceive, GOTO[EDone],	c2;

EStart:	E ← uIOPage, CANCELBR[$,0F],	c1;
	Xbus ← EStatus, XLDisp,	c2;
	rhEE ← 0, DISP2[EGo],	c3;

ERecieve:	MAR ← [rhE, IOPage.ECSB.icb+0],	c1, at[2,4,EGo];
	L6←L6.EReceive, CALL[ECBFetch],	c2;

{map input buffer}
ERGotIOCB:	Map ← [rhEE, EE], CALL[EMap2],	c1, at[L6.EReceive,10,EFetch];

	MAR ← [rhE, EE+0],	c1, at[L6.EReceive,10,EMapRets];
	MDR ← E ← EIData, L6 ← L6.ERMap,	c2;
	uEDest0 ← E, CALL[EMap], {to restore E only}	c3; {be prepared for page cross}

	MAR ← E ← [rhE, EE+1], L6 ← L6.Remap,	c1, at[L6.ERMap, 10, EMapRets];
	MDR ← EE ← EIData, BRANCH[$, RemapWord2, 1],	c2;
NoWd2Remap:	uEDest1 ← EE, GOTO[StoreWord3],	c3, at[1,4,RemapWord2];

{Get here if the second word of the packet was on a new page.  Packets buffers begin at addresses ending in 3 MOD 4.  A page boundry can only occur after the first word.
uEBuffer contains the value of EE at the time of the MAR ← above.}
RemapWord2:	
	uEDest1 ← EE, 	c3, at[3,4,NoWd2Remap];

	E ← 0,			c1;
	Noop,CALL[ECNextPage],	c2;

	MAR ← E ← [rhE,0+0],	c1, at[L6.Remap,10, EMapRets];
	MDR ← EE ← uEDest1, GOTO[NoWd2Remap],	c2;

{Get here whether or not the second word was on a new page.  Store the third packet word which is also the final destination address word.}
StoreWord3:	MAR ← E ← [rhE, E+1],	c1;
	MDR ← EE ← EIData, CANCELBR[$,0],	c2;
	[] ← EE xor uEHost2, NZeroBr,	c3; {start check for correct address}

	EE ← uEDest1, BRANCH[$,ECheckBCast2],	c1;
	[] ← EE xor uEHost1, NZeroBr,	c2, at[0,2,ECheckBCast2];
	EE ← uEDest0, BRANCH[ECheckAddr0, $],	c3;

	GOTO[ECheckBCast2],	c1, at[1,2,ECheckAddr0];
ECheckAddr0:	[] ← EE xor uEHost0, ZeroBr,	c1, at[0,2,ECheckAddr0];
ECheckBCast2:	EE ← ~uEDest0, ZeroBr, BRANCH[$, MyAddr],	c2, at[1,2,ECheckBCast2];
	EE ← ~uEHost0, ZeroBr, BRANCH[ECheckAny, EInput],	c3, at[0,2,MyAddr];
MyAddr:	CANCELBR[EInput,1],	c3, at[1,2,MyAddr];

ECheckAny:	EE ← uESize, BRANCH[NotMeC2, EInputC2],	c1, at[0,2,EInput];
EInput:	EE ← uESize, CANCELBR[$,1],	c1, at[1,2,EInput];
EInputC2:	EE ← EE-2, L6 ← L6.ERCross, GOTO[ERead],	c2, at[1,2,NotMeC2];
NotMeC2:	EStrobe, GOTO[EDone],	c2, at[0,2,NotMeC2]; {purge rest of
					      packet and quit}
{main input loop}
EInLoop:	MAR ← E ← [rhE, E + 1], EtherDisp, BRANCH[$,EITooLong],	c1;
	MDR ← EIData, DISP4[ERead, 0C],	c2;
ERead:	EE ← EE - 1, ZeroBr, GOTO[EInLoop],	c3, at[0C,10,ERead];
	E ← uESize, GOTO[EReadEnd],	c3, at[0D,10,ERead];
	E ← EIData, uETemp2 ← EE, GOTO[ERCross],	c3, at[0E,10,ERead];
	E ← EIData, uETemp2 ← EE, L6←L6.ERCrossEnd, GOTO[ERCross],	c3, at[0F,10,ERead];


{remap buffer page}
ERCross:	uETemp ← E, CALL[ECMap],	c1;

	MAR ← E ← [rhE, 0+0],	c1, at[L6.ERCross,10,EMapRets];
ERCrossStore:	MDR ← uETemp, DISP4[ERead, 0C],	c2;

	MAR ← E ← [rhE, 0+0], EtherDisp, GOTO[ERCrossStore],	c1, at[L6.ERCrossEnd,10,EMapRets];

{packet too big.  make it look like size was -1}
EITooLong:	EE ← EE xor ~EE, CANCELBR[$,0F],		c2;
	rhEE ← 0,						c3;
	GOTO[EDoMap],						c1;

EReadEnd:	EE ← E - EE - 1, rhEE ← 0,	c1;
	EE ← EE - 6,		c2;
	[] ← EE, NegBr,		c3;

	EE ← EE + 6, BRANCH[$,EReadPurgeC2],	c1;
EDoMap:	uETemp ← EE, L6←L6.EREndMap, CALL[EMapIOCB],	c2;

{store final count, update csb, set interrupt, return to EReadPurge}
	MAR ← [rhE, EE+IOCB.used],	c1, at[L6.EREndMap,10,EMapRets];
	MDR ← uETemp, CANCELBR[$,0],	c2;
	L6←L6.EReceive, CALL[EFinish],	c3;

{set purge mode, EStrobe must be in c1 or c2, for wakeup pipeline}
EReadPurge:	Noop,			c1;
{EStrobe in c2 means purge (rest of) input packet}
EReadPurgeC2: EStrobe,			c2;
EDone:	rhE ← IOPageHigh, GOTO[EStart],		c3;
	

{*****************************************************************************
Tranmit Code
*****************************************************************************}

ETransmit:	MAR ← [rhE, IOPage.ECSB.ocb+0], L6←L6.EXmit,	c1, at[0,4,EGo];
EXmitx:	CALL[ECBFetch],	c2;

{get backoff mask}
ETGotIOCB:	MAR ← [rhE, E+IOCB.mask],	c1, at[L6.EXmit,10,EFetch]; {save addr of
						mask}
	uEBuffer ← EE, CANCELBR[$,0],	c2;
	EE ← MD, XHDisp,	c3;

	MAR ← [rhE, E+IOCB.mask], BRANCH[$,EFullMask,2],	c1;
	MDR ← EE+EE+1, CANCELBR[$,0],		c2; {write back shifted mask}
	uEOldMask ← EE, EE ← EE LShift1, SE ← 1, ZeroBr,	c3; {save old mask and shift it.  If
						it was zero, this is the first time
						for this packet.}

	E ← eInitialCountdown, BRANCH[ERandom,EFirst],	c1; {load time to wait before first packet
						in case it is needed and decide.
						NOTE rhEE was loaded in ECBFetch.}
ERandom:	E ← uClockLow, GOTO[EHaveCntAndMsk],	c2, at[0,2,EFirst]; {load random number}
EFirst:	EE ← 0FF,			c2, at[1,2,ERandom]; {Load Noop mask}
EHaveCntAndMsk: [] ← uXmitInterrupt, ZeroBr,	c3; {do we want the calculated mask
						at all?}

EHaveCount:	EOCtl ← eEnableTrnDefer, E ← E and EE, BRANCH[EContinueLoop, EStartDeferLoop], c1; {start wait, finish mask
						calculation and decide if calculated
						mask is needed.}
{Come here if we are we got interrupted by a packet during the defer loop. We continue where we left off.}
EContinueLoop: E ← uXmitInterrupt, GOTO[EDeferLoopx],	c2, at[0,2,EStartDeferLoop];
EStartDeferLoop: E ← E and uE3FF, GOTO[EDeferLoopx],	c2, at[1,2,EContinueLoop]; {limit defer
						count to 10 bits.}
{This loop tests Attn from the previous wakeup.
If Attn is on, we will get another wakeup right away.
If the timer counts out at the wrong time we will (slowly) discover it
after trying to send the preamble. (The hardware will ignore that.)}
EDeferLoop:
	E ← E - 1, NegBr, BRANCH[$, ERecieveUnder,0E],	c1;
	EOCtl ← eEnableTrnDefer, BRANCH[$,EStartXmit],	c2;
EDeferLoopx:
	EE ← uEBuffer, EtherDisp, GOTO[EDeferLoop],	c3;

EStartXmit:	EOCtl ← eEnableTrn,	c3;

{send preamble}
	E ← 3,			c1;
EPreamble:	E ← E - 1, ZeroBr,	c2;
	EOData ← uEPreamble, BRANCH[$, ELastPreamble],	c3;

	uXmitInterrupt ← 0, EStrobe, GOTO[EPreamble],	c1;

ELastPreamble:	Map ← [rhEE, EE], EStrobe, L6←L6.ETMap,	c1;
	EOData ← uELastPreamble, L6Disp, CALL[EMapFetch],	c2;

	MAR ← E ← [rhE, EE + 0], EStrobe,	c1, at[L6.ETMap,10,EMapRets];
	EE ← uESize, GOTO[EXmit],	c2;

EOutLoop:	MAR ← E ← [rhE, E + 1], EtherDisp, EStrobe,	c1;
EOutLoopx:	EE ← EE - 1, rhEE ← 0, ZeroBr, DISP4[EXmit, 0C],	c2;
EXmit:	EOData ← MD, BRANCH[EOutLoop, EOutDone],	c3, at[0C,10,EXmit];
	Xbus ← EStatus, XwdDisp, L6←L6.EXmitAbort, CANCELBR[EXmitAttn,1],	c3, at[0D,10,EXmit];
	EE ← EE + 1, L6←L6.ETCross, CANCELBR[EXmitCross,1],	c3, at[0E,10,EXmit];
EXmitQuit:	Xbus ← EStatus, XwdDisp, L6←L6.EXmitAbort, CANCELBR[EXmitAttn,1],	c3, at[0F,10,EXmit];

EOutDone:	EStrobe,			c1;
	EOCtl ← eEnableTrnLastWord,	c2;
	EStrobe,			c3;

EXmitEnd:	Xbus ← EStatus, XwdDisp,	c1;
	EOCtl ← eEnableTrn, BRANCH[ENoColl, EColl, 1],	c2;
EColl:	GOTO[ECollision],	c3;
ENoColl:	EE ← uEIOCB, L6←L6.ETEndMap, GOTO[EMap],	c3;

{store status, update csb, set interrupt, return to EStart}
EXmitEndx:	MAR ← [rhE, EE + IOCB.completion], L6←L6.EXmit, CALL[EFinish2],	c1, at[L6.ETEndMap,10,EMapRets];

{Turn tran off to reset transmitter, and
  back on for more wakeups in case there is another IOCB to process}
EXmitDone:	EOCtl ← eOff, GOTO[EXmitColl],	c1;

{page cross on transmit}
EXmitCross:	uETemp2 ← EE, CALL[ECMap],	c1;

	MAR ← E ← [rhE, 0+0], GOTO[EOutLoopx],	c1, at[L6.ETCross,10,EMapRets];

{attention during transmit loop}
EXmitAttn:	EOCtl ← eOff, rhEE ← 0, DISP2[EXmitA],	c1;
EXmitA:	EOCtl ← eEnableTrn, GOTO[EMapIOCB],	c2, at[0,4,EXmitA];
	EOCtl ← eEnableTrn, GOTO[ENoColl],	c2, at[1,4,EXmitA];
EXmitColl:	EOCtl ← eEnableTrn, GOTO[EDone],	c2, at[2,4,EXmitA];
	EOCtl ← eEnableTrn, GOTO[EDone],	c2, at[3,4,EXmitA];

{Come here if we are interrupted while waiting to transmit. Save the count so that we can continue where we left off.}
ERecieveUnder:	uXmitInterrupt ← E, CANCELBR[EXmitQuit,1],	c2;

EXmitAbortx:	MAR ← [rhE, EE + IOCB.mask],	c1, at[L6.EXmitAbort,10,EMapRets];
	MDR ← uEOldMask, CANCELBR[EDone,0],	c2;

{backoff mask full}
EFullMask:
	rhEE ← 0, MDR ← 0, CANCELBR[ENoColl,0],		c2; {mask of 2 => too many retransmissions}

ECollision:	EOCtl ← eOff, GOTO[EXmitColl],	c1;

{*****************************************************************************
Stopping code
*****************************************************************************}
EStop:	EOCtl ← eOff, GOTO[EStopx],	c1, at[3,4,EStop];
	EOCtl ← eOff, GOTO[EStopx],	c1, at[1,4,EGo];
	EOCtl ← eOff, GOTO[EStopx],	c1, at[3,4,EGo];
EStopx:	EICtl ← eOff,	c2;
	GOTO[EColdStart],	c3;

{*****************************************************************************
Common Code
*****************************************************************************}

{Store status, fetch next iocb}
EFinish:	MAR ← [rhE, EE+IOCB.completion],	c1;
EFinish2:	MDR ← EStatus, CANCELBR[$,0],	c2;
	Noop,	c3;

	MAR ← [rhE, EE+IOCB.next],	c1;
	E ← uIOPage, CANCELBR[$, 2],	c2;
	EE ← MD,	c3;

	rhE ← IOPageHigh,	c1;
	L6Disp,	c2;
	BRANCH[EREnd, ETEnd, 2],	c3;

EREnd:	MAR ← E ← [rhE, IOPage.ECSB.icb+0], GOTO[ECSBUpdate],	c1;

ETEnd:	MAR ← E ← [rhE, IOPage.ECSB.ocb+0], GOTO[ECSBUpdate],	c1;

ECSBUpdate:	MDR ← EE,	c2;
	Noop,	c3;

	MAR ← [rhE, E+1],	c1;
	CANCELBR[$,0],	c2;
	E ← MD,	c3;

	EE ← uWP, MesaIntRq,	c1;
	EE ← E or EE, L6Disp,	c2;
	uWP ← EE, BRANCH[EReadPurge, EXmitDone],	c3;


{page cross map update for inner loops}
ECMap:	E ← 0FF,		c2;
ECNextPage:
	EE ← uEBuffer,	c3;

	E ← EE + E + 1, CarryBr,	c1;
	uEBuffer ← E, BRANCH[$, ECrossCarry],	c2;
ECMapx:	rhEE ← uEBufferHi,	c3;

	Map ← [rhEE, E],	c1;
	EE ← uETemp2, L6Disp, GOTO[EMapFetch],	c2;

ECrossCarry:	EE ← uEBufferHi,	c3;

	EE ← EE + 1,	c1;
	uEBufferHi ← EE, GOTO[ECMapx],	c2;

{general mapping; don't care if uEBuffer smashed if mapping IOCB}
EMapIOCB:	EE ← uEIOCB,	c3;

EMap:	Map ← [rhEE, EE],	c1;
EMap2:	uEBuffer ← EE, L6Disp,	c2;
EMapFetch:	rhE ← E ← MD, RET[EMapRets],	c3;

{IOCB fetch}
ECBFetch:	EE ← MD,	c3;

	Map ← uEIOCB ← [rhEE, EE], ZeroBr,	c1;
	L6Disp, BRANCH[$, ENoCB],	c2;
	rhE ← E ← MD, CANCELBR[$,1],	c3;

	MAR ← E ← [rhE, EE+IOCB.size],	c1;
	CANCELBR[$,0],	c2;
	EE ← MD,	c3;

	MAR ← [rhE, E+IOCB.bufferHi],	c1;
	uESize ← EE, CANCELBR[$,0],	c2;
	rhEE ← EE ← MD,	c3;

	MAR ← [rhE, E+IOCB.buffer],	c1;
EFetch2:	uEBufferHi ← EE, L6Disp, CANCELBR[$,0],	c2;
EFetch3:	EE ← MD, RET[EFetch],	c3;

ENoCB:	BRANCH[ENoICB, ENoOCB],	c3;

ENoOCB:	EOCtl ← eOff,	c1;
	Noop, GOTO[EDone],	c2;

ENoICB:	MAR ← [rhE, IOPage.ECSB.lost+0], L6←L6.NoICB, GOTO[EFetch2],	c1;

	MAR ← [rhE, IOPage.ECSB.lost+0],	c1, at[L6.NoICB,10,EFetch];
	MDR ← EE + 1, EStrobe, GOTO[EDone],	c2;


{END}