{File name: EtherDLion.mc
Description: Dandelion Ethernet Microcode,
Author: Sandman,
Created: October 23, 1980 10:42 AM,
Last Edited:
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
*****************************************************************************}
{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.Remap, 1];
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, 0C]; {in instruction with GOTO[ERead]}
Set[L6.ERCrossEnd, 9];
SetTask[2];
StartAddress[EColdStart];
{*****************************************************************************
Initial code
*****************************************************************************}
EColdStart:rhE ← E ← uIOPage, CANCELBR[$,0F],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: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;
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: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[$,EReadPurgeC2],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}
EReadPurgeC2: EStrobe,c2;
EDone: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,1],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, CANCELBR[$,0],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[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], 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: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}