{File name: EtherDLion.mc
Description: Dandelion Ethernet Microcode,
Author: Sandman,
Created: October 23, 1980 10:42 AM,
Last Edited:
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
*****************************************************************************}
{Offsets and Magic locations}
Set[IOPage.ECSB, 0C0];
Set[ECSB.host0, 0];
Set[ECSB.host1, 1];
Set[ECSB.host2, 2];
Set[ECSB.icb, 3];
Set[ECSB.iWakeup, 4];
Set[ECSB.ocb, 5];
Set[ECSB.oWakeup, 6];
Set[ECSB.lost, 7];
Set[IOPage.ECSB.host0, Add[IOPage.ECSB, ECSB.host0]];
Set[IOPage.ECSB.host1, Add[IOPage.ECSB, ECSB.host1]];
Set[IOPage.ECSB.host2, Add[IOPage.ECSB, ECSB.host2]];
Set[IOPage.ECSB.icb, Add[IOPage.ECSB, ECSB.icb]];
Set[IOPage.ECSB.iWakeup, Add[IOPage.ECSB, ECSB.iWakeup]];
Set[IOPage.ECSB.ocb, Add[IOPage.ECSB, ECSB.ocb]];
Set[IOPage.ECSB.oWakeup, Add[IOPage.ECSB, ECSB.oWakeup]];
Set[IOPage.ECSB.lost, Add[IOPage.ECSB, ECSB.lost]];
Set[IOCB.size, 0];
Set[IOCB.buffer, 1];
Set[IOCB.bufferHi, 2];
Set[IOCB.mask, 3];
Set[IOCB.used, 4];
Set[IOCB.completion, 5];
Set[IOCB.next, 6];
Set[IOCB.spare, 7];
Set[eEnableRcv, 1];
Set[eTurnOff, 2];
Set[eLocalLoop, 4];
Set[eLoopBack, 8];
Set[eOff, 0];
Set[eEnableTrn, 1];
Set[eLastWord, 2];
Set[eDefer, 4];
Set[eEnableTrnDefer, 5];
Set[eEnableTrnLastWord, 3];
Set[eInitialCountdown, 14]; {14X = 20D; 51.2us*20 ~ 1ms}
Set[L6.EReceive, 0];
Set[L6.EXmit, 1];
Set[L6.Host0, 2];
Set[L6.Host1, 3];
Set[L6.Host2, 4];
Set[L6.NoICB, 5];
Set[L6.ERMap, 2];
Set[L6.ETMap, 3];
Set[L6.EREndMap, 4];
Set[L6.EXmitAbort, 5]; {1 MOD 2}
Set[L6.ETEndMap, 6];
Set[L6.ETCross, 7]; {1 MOD 2}
Set[L6.ERCross, 8];
Set[L6.ERCrossEnd, 9];
SetTask[2];
StartAddress[EColdStart];
{*****************************************************************************
Initial code
*****************************************************************************}
EColdStart:rhE ← E ← uIOPage, CANCELBR[$,0F],c1;
Xbus ← EStatus, XLDisp,c2;
rhEE ← 0, DISP2[ECold],c3;
{Fetch host and input IOCB from CSB}
EHostT:MAR ← [rhE, IOPage.ECSB.host0+0], L6←L6.Host0, GOTO[EHost],c1, at[0,4,ECold];
EHostR:MAR ← [rhE, IOPage.ECSB.host0+0], L6←L6.Host0,c1, at[2,4,ECold];
EHost: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:rhE ← 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;
EE ← EE + 1, uEDest0 ← E, CALL[EMap],c3;
MAR ← E ← [rhE, EE+0],c1, at[L6.ERMap,10,EMapRets];
MDR ← EE ← EIData,c2;
uEDest1 ← EE,c3;
MAR ← E ← [rhE, E+1],c1;
MDR ← EE ← EIData, CANCELBR[$,0],c2;
uEDest2 ← EE,c3;
{check for correct address}
ECheckAddr:[] ← ~uEHost0, ZeroBr,c1;
EE ← ~uEDest0, ZeroBr, BRANCH[$, EAny],c2;
[] ← EE xor ~uEHost0, NZeroBr, BRANCH[$, EBCast],c3;
EE ← uEDest1, BRANCH[$, ENotMe2],c1;
[] ← EE xor uEHost1, NZeroBr,c2;
EE ← uEDest2, BRANCH[$, ENotMe1],c3;
[] ← EE xor uEHost2, NZeroBr,c1;
EInputz:EE ← uESize, BRANCH[$, ENotMe3],c2;
EInput:EE ← EE - 3, L6←L6.ERCross, GOTO[EInLoop]{got dest already},c3;
EBCast:CANCELBR[EInputz,1],c1;
EAny:CANCELBR[EBCast,1],c3;
{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:E ← 0, CANCELBR[$,0F],c2;
EE ← 0, GOTO[EReadEnd],c3;
EReadEnd:EE ← E - EE - 1, rhEE ← 0,c1;
EE ← EE - 6,c2;
[] ← EE, NegBr,c3;
EE ← EE + 6, BRANCH[$,ERunt],c1;
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}
EStrobe,c2;
EDone:GOTO[EStart],c3;
ERunt:GOTO[ENotMe3],c2;
ENotMe1:Noop,c1;
ENotMe2:Noop,c2;
ENotMe3:GOTO[EReadPurge],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];
uEBuffer ← EE, CANCELBR[$,0],c2;
EE ← MD, XHDisp,c3;
EE ← EE LShift1, SE ← 1, ZeroBr, uEOldMask ← EE, BRANCH[$,EFullMask,1],c1;
L6 ← 0, BRANCH[EOldMask, ENewMask],c2;
EOldMask:GOTO[ESaveMask],c3;
ENewMask:GOTO[ESaveMask],c3;
ESaveMask:MAR ← [rhE, E+IOCB.mask], L6Disp,c1;
MDR ← EE, E ← eInitialCountdown, BRANCH[ERandom, EFirst, 2],c2;
ERandom:E ← uClockLow, GOTO[EHaveCount],c3;
EFirst:EE ← EE xor ~EE, GOTO[EHaveCount],c3;
{EOCtl ← anything resets the timer.
It must be in c1 or c2 to be sure TickElapsed has time to get reset.}
EHaveCount:E ← E and EE,c1;
EOCtl ← eEnableTrnDefer,c2;
EE ← uEBuffer,c3;
{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],c1;
EOCtl ← eEnableTrnDefer, BRANCH[$,EStartXmit],c2;
rhEE ← uEBufferHi, Ybus ← 0, EtherDisp, GOTO[EDeferLoop],c3;
EStartXmit:EOCtl ← eEnableTrn,c3;
{send preamble}
E ← 3,c1;
EPreamble:E ← E - 1, ZeroBr,c2;
EOData ← uEPreamble, BRANCH[$, ELastPreamble],c3;
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];
ERecieveUnder: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, CANCELBR[$,1],c2;
Noop,c3;
MAR ← [rhE, E + IOCB.mask],c1;
MDR ← EE, CANCELBR[ENoColl,0],c2;
ECollision:EOCtl ← eOff, GOTO[EXmitColl],c1;
{*****************************************************************************
Stopping code
*****************************************************************************}
EStop:EOCtl ← eOff, GOTO[EStopx],c1, at[1,4,ECold];
EOCtl ← eOff, GOTO[EStopx],c1, at[3,4,ECold];
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], L6Disp,c1;
rhE ← E ← uIOPage, BRANCH[EREnd, ETEnd, 2],c2;
EREnd:EE ← MD,c3;
MAR ← E ← [rhE, IOPage.ECSB.icb+0], GOTO[ECSBUpdate],c1;
ETEnd:EE ← MD,c3;
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:EE ← uEBuffer,c2;
E ← 0FF,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}