{File name: EtherDLion.mc Description: Dandelion Ethernet Microcode, Author: JGS , Created: October 23, 1980 10:42 AM, Last Edited: Fiala 8-Sep-86 17:37:04 Add remap check after other header word since buffers are no longer constrained to start at locations which are 3 mod 4; absorb E ← 0 and Noop in ECNextPage subr now called in 2 places; eliminate useless "at" clauses in 10 places below NoWd3Remap; bum out 2 noops, useless MAR←, useless rhEE ← 0, and useless rhE ← cedarIOPageHigh at EColdStart (saves 3 mi). Fiala 5-Sep-86 12:09:47 Change IOPageHigh to cedarIOPageHigh three places. Dennis DEG , 1-Sep-84 19:34:45, Add copyright notice. AEF AEF , 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. AEF AEF , August 3, 1982 12:09 PM, Move definitions to Dandelion.df for new IOPage layout. Jim JXF , March 16, 1982 11:49 AM, Fix bug that was storing the mask into the wrong word. Jim JXF , March 5, 1982 3:01 PM, Fix bug encountered if 1st word of receive packet buffer lies in last word page. AEF AEF , February 18, 1982 12:02 PM, Call EMap to restore register E after MAR on second word of input buffer Dan DXD , 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 JXF , 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) JGS , November 12, 1980 11:42 AM HGM, June 10, 1981 9:38 PM, NegBr @EDeferLoop to avoid long delays RXG , April 21, 1981 8:01 PM: rhEE←0 @EFullMask Copyright (C) 1980, 1981, 1982, 1986 by Xerox Corporation. All rights reserved. Notes: (1) The instructions at the return from the two calls to ECMap and the two calls to ECNextPage are identical and could be bummed by absorption into the subroutine body. (2) When sending to or receiving from the Ethernet at 1 word/click, slightly more than 25% of all DLion cycles are consumed by the transfer. There appear to be about 25 overhead clicks/packet + 4 clicks/page cross, so a packet with 512 data bytes requires about 29% of all DLion cycles altogether. } {***************************************************************************** Definitions *****************************************************************************} {Offsets and Magic locations} 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.RemapW3, 8]; Set[L6.ERCross, 0C]; {in instruction with GOTO[ERead]} Set[L6.ERCrossEnd, 9]; SetTask[2]; StartAddress[EColdStart]; {***************************************************************************** Initial code *****************************************************************************} EColdStart: rhE ← cedarIOPageHigh, CANCELBR[$,0F], c1; Xbus ← EStatus, XLDisp, c2; E ← uIOPage, 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]; 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; uEHost2 ← EE, GOTO[ECold1], c1, at[L6.Host2, 10, EFetch]; EStart: E ← uIOPage, CANCELBR[$, 0F], c1; {c1} ECold1: Xbus ← EStatus, XLDisp, c2; rhEE ← 0, DISP2[EGo], c3; ERecieve: MAR ← [rhE, IOPage.ECSB.icb+0], c1, at[2, 4, EGo]; {c2} L6 ← L6.EReceive, CALL[ECBFetch], c2; {map input buffer} ERGotIOCB: Map ← [rhEE, EE], CALL[EMap2], c1, at[L6.EReceive, 10, EFetch]; {c7} MAR ← [rhE, EE+0], c1, at[L6.EReceive, 10, EMapRets]; {c8} 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]; {c9} MDR ← EE ← EIData, BRANCH[NoWd2Remap, $, 1], c2; {Get here if the second word of the packet was on a new page. uEBuffer contains the value of EE at the time of the MAR ← above.} RemapWord2: uETemp2 ← EE, CALL[ECNextPage], c3; MAR ← E ← [rhE, 0+0], c1, at[L6.Remap, 10, EMapRets]; MDR ← EE, c2; NoWd2Remap: uEDest1 ← EE, c3; {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.} MAR ← E ← [rhE, E+1], L6 ← L6.RemapW3, c1; {c10} MDR ← EE ← EIData, BRANCH[NoWd3Remap, $, 1], c2; uETemp2 ← EE, CALL[ECNextPage], c3; MAR ← E ← [rhE, 0+0], c1, at[L6.RemapW3, 10, EMapRets]; MDR ← EE, c2; NoWd3Remap: [] ← EE xor uEHost2, NZeroBr, c3; {start check for correct address} EE ← uEDest1, BRANCH[$, ECheckBCast2], c1; {c11} [] ← EE xor uEHost1, NZeroBr, c2; EE ← uEDest0, BRANCH[ECheckAddr0, $], c3; GOTO[ECheckBCast2], c1; {c12ab} ECheckAddr0: [] ← EE xor uEHost0, ZeroBr, c1; {c12aa} ECheckBCast2: EE ← ~uEDest0, ZeroBr, BRANCH[$, MyAddr], c2; EE ← ~uEHost0, ZeroBr, BRANCH[ECheckAny, EInput], c3; MyAddr: CANCELBR[EInput,1], c3; ECheckAny: EE ← uESize, BRANCH[NotMeC2, EInputC2], c1; {c12ba, c13aa} EInput: EE ← uESize, CANCELBR[$, 1], c1; {c12bb, c13ab} EInputC2: EE ← EE - 2, L6 ← L6.ERCross, GOTO[ERead], c2; NotMeC2: EStrobe, GOTO[EDone], c2; {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; {c13b, c14a} EE ← EE - 6, c2; [] ← EE, NegBr, c3; EE ← EE + 6, BRANCH[$, EReadPurgeC2], c1; {c14b, c15a} 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]; {c16ba, c17aa} 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; {c23ba, c24aa} {EStrobe in c2 means purge (rest of) input packet} EReadPurgeC2: EStrobe, c2; EDone: rhE ← cedarIOPageHigh, 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 ← cedarIOPageHigh, 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 on word 2 or 3 of packet buffer while processing header} ECNextPage: E ← 0, c1; Noop, GOTO[ECMapy], c2; {page cross map update for inner loops} ECMap: E ← 0FF, c2; ECMapy: 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}