{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 Fiala 25-Jul-86 9:20:53 use cedarIOPageHigh instead of uIOPage for initializing rh of base registers at EColdStart, EStart, EREnd, and ETEnd. } {***************************************************************************** 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: E _ uIOPage, CANCELBR[$,0F], c1; rhE ¬ cedarIOPageHigh, c2; E ¬ E + 1, c3; Noop, 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: E _ uIOPage, CANCELBR[$,0F], c1; rhE ¬ cedarIOPageHigh, c2; E ¬ E + 1, c3; Noop, 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; k40 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]; k40 {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; k40 MAR _ E _ [rhE, 0+0], EtherDisp, GOTO[ERCrossStore], c1, at[L6.ERCrossEnd,10,EMapRets]; k40 {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; k40 ERunt: GOTO[ENotMe3], c2; ENotMe1: Noop, c1; ENotMe2: Noop, c2; ENotMe3: GOTO[EReadPurge], c3; k40 {***************************************************************************** Tranmit Code *****************************************************************************}k40 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;k40 {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]; k40 {attention during transmit loop} EXmitAttn: EOCtl _ eOff, rhEE _ 0, DISP2[EXmitA], c1; EXmitA: EOCtl _ eEnableTrn, GOTO[EMapIOCB], c2, at[0,4,EXmitA];k40 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; k40 {***************************************************************************** Stopping code *****************************************************************************}k40 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; k40 {***************************************************************************** Common Code *****************************************************************************}k40 {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 _ cedarIOPageHigh, BRANCH[EREnd, ETEnd, 2], c2; EREnd: EE _ MD, c3; E ¬ uIOPage, c1; E ¬ E + 1, c2; Noop, c3; MAR _ E _ [rhE, IOPage.ECSB.icb+0], GOTO[ECSBUpdate], c1; ETEnd: EE _ MD, c3; E ¬ uIOPage, c1; E ¬ E + 1, c2; Noop, c3; } MAR _ [rhE, EE+IOCB.next], c1; rhE _ cedarIOPageHigh, CANCELBR[$, 0], c2; EE _ MD, c3; E ¬ uIOPage, c1; E ¬ E + 1, 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: 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}