; ExtraEther2.mu -- microcode for the second extra Ethernet board ; Last modified July 19, 1978 6:28 PM ;4-way branches using NEXT6 and NEXT7 !17,20,xIFB00,xODOK,xOEOK,xNOCMD,xIFB01,xODPST,xOEPST,xOREST,xIFB10,xODCOL,xOECOL,xIREST,xIFB11,xODUGH,xOEUGH,xRBRES; ;2-way branches using NEXT7 ;xOCDW1, xOCDWX, and xIGO are all related. Be careful! !7,10,,xIFOK,,xOCDW1,,xIFBAD,xOCDWX,xIGO; ;Miscellaneous address constraints !7,10,,xOCDW0,xODATA,xIDFUL,xIDZ4,xOCDRS,xIDATA,xPOST; !7,10,,xIDOK,,,xIDMOR,xIDPST; !1,1,xIFB1; !1,1,xIFRST; ;2-way branches using NEXT9 !1,2,xOINPR,xOINPN; !1,2,xODMOR,xODEND; !1,2,xOLDOK,xOLDBD; !1,2,xIFCHK,xIFPRM; !1,2,xOCDWT,xOCDGO; !1,2,xCNTOK,xCNTZR; !1,2,xIFIGN,xISET; !1,2,xIFNBC,xIFBC; ;Top of Ethernet Task loop ;Ether Rest Branch Function - ERBFCT ;merge ICMD and OCMD Flip Flops into NEXT6 and NEXT7 ;ICMD and OCMD are set from AC0 [14:15] by the SIO instruction ; 00 neither ; 01 OCMD - Start output ; 10 ICMD - Start input ; 11 Both - Reset interface ;in preparation for a hack at xIREST, zero xPNTR xREST: L← 0,ERBFCT; What's happening ? xPNTR← L,:xNOCMD; [xNOCMD,xOREST,xIREST,xRBRES] xNOCMD: L← ESNEVR,:xPOST; Shouldn't happen xRBRES: L← ESABRT,:xPOST; Reset Command ;Post status and halt. Microcode status in L. ;Put microstatus,,hardstatus in EPLOC, merge c(EBLOC) into NWW. ;Note that we write EPLOC and read EBLOC in one operation ;Ether Post Function - EPFCT. Gate the hardware status ;(LOW TRUE) to Bus [10:15], reset interface. xPOST: T← 14; MAR← 630+T; (EELOC) xPNTR← L,TASK; Save microcode status in xPNTR MD← xCNTR; Save ending count MAR← 642; (EPLOC) double word reference T← NWW; MD← xPNTR,EPFCT; BUS AND xPNTR with Status L← MD OR T,TASK; NWW OR c(EBLOC) NWW← L,:xREST; Done. Wait for next command ;This is a subroutine called from both input and output (xOCDGO ;and xISET). The return address is determined by testing ECBFCT, ;which will branch if the buffer has any words in it, which can ;only happen during input. xSETUP: NOP; L← MD,BUS=0; check for zero length T← MD-1,:xCNTOK; [xCNTOK,xCNTZR] start-1 xCNTZR: L← ESCZER,:xPOST; Zero word count. Abort ;Ether Countdown Branch Function - ECBFCT. ;NEXT7 = Interface buffer not empty. xCNTOK: xCNTR← L,L← T,ECBFCT,TASK; xPNTR← L,:xODATA; [xODATA,xIDATA] ;Ethernet Input ;It turns out that starting the receiver for the first time and ;restarting it after ignoring a packet do the same things. xIREST: :xIFIGN; Hack ;Address filtering code. ;When the first word of a packet is available in the interface ;buffer, a wakeup request is generated. The microcode then ;decides whether to accept the packet. Decision must be reached ;before the buffer overflows, within about 14*5.44 usec. ;if EHLOC is zero, machine is 'promiscuous' - accept all packets ;if destination byte is zero, it is a 'broadcast' packet, accept. ;if destination byte equals EHLOC, packet is for us, accept. ;xIFRST is really a subroutine that can be called from xIREST ;or from xIGO, output countdown wait. If a packet is ignored ;and xPNTR is zero, xIFRST loops back and waits for more ;packets, else it returns to the countdown code. ;Ether Branch Function - EBFCT ;NEXT7 = IDL % OCMD % ICMD % OUTGONE % INGONE (also known as POST) ;NEXT6 = COLLision - Can't happen during input xIFRST: T← 22; MAR← 630+T; (EHLOC) Get Ethernet address T← 377,EBFCT; What's happening? L← MD AND T,BUS=0,:xIFOK;[xIFOK,xIFBAD] promiscuous? xIFOK: MTEMP← LLCY8,:xIFCHK; [xIFCHK,xIFPRM] Data wakeup xIFBAD: ERBFCT,TASK,:xIFB1; [xIFB1] POST wakeup; xCMD FF set? xIFB1: :xIFB00; [xIFB00,xIFB01,xIFB10,xIFB11] xIFB00: :xIFIGN; IDL or INGONE, restart rcvr xIFB01: L← ESABRT,:xPOST; OCMD, abort xIFB10: L← ESABRT,:xPOST; ICMD, abort xIFB11: L← ESABRT,:xPOST; ICMD and OCMD, abort xIFPRM: TASK,:xIFBC; Promiscuous. Accept ;Ether Look Function - EILFCT. Gate the first word of the ;data buffer to the bus, but do not increment the read pointer. xIFCHK: L← T← 177400,EILFCT; Mask off src addr byte (BUS AND) L← MTEMP-T,SH=0; Broadcast? SH=0,TASK,:xIFNBC; [xIFNBC,xIFBC] Our Address? xIFNBC: :xIFIGN; [xIFIGN,xISET] xIFBC: :xISET; [xISET] Enter input main loop ;Ether Input Start Function - EISFCT. Start receiver. Interface ;will generate a data wakeup when the first word of the next ;packet arrives, ignoring any packet currently passing. xIFIGN: SINK← xPNTR,BUS=0,EPFCT;Reset; Called from output? EISFCT,TASK,:xOCDWX; [xOCDWX,xIGO] Restart rcvr xOCDWX: EWFCT,:xOCDWT; Return to countdown wait loop xISET: T← 16; MAR← 630+T,:xSETUP; (EICLOC) Double word reference ;Input Main Loop ;Ether Input Data Function - EIDFCT. Gate a word of data to ;the bus from the interface data buffer, increment the read ptr. ; * * * * * W A R N I N G * * * * * ;The delay from decoding EIDFCT to gating data to the bus is ;marginal. Some logic in the interface detects the situation ;(which only happens occasionally) and stops SysClk for one cycle. ;Since memory data must be available during cycle 4, and SysClk ;may stop for one cycle, this means that the MD← EIDFCT must ;happen in cycle 3. There is a bug in this logic which occasionally ;stops the clock in the instruction following the EIDFCT, so ;the EIDFCT instruction should not be the last one of the task, ;or it may screw up someone else (such as RDRAM). ;xIDOK, xIDMOR, and xIDPST must have address bits in the pattern: ;xxx1 xxx4 xxx5 ;ECBFCT is used to force an unconditional branch on NEXT7 xIDATA: T← xCNTR-1, BUS=0; MAR← L← xPNTR+1, EBFCT; [xIDMOR,xIDPST] What's happening xIDMOR: xPNTR← L, L← T, ECBFCT; [xIDOK,xIDPST] Guaranteed to branch xIDOK: MD← EIDFCT, TASK; [xIDZ4] Read a word from the interface xIDZ4: xCNTR← L, :xIDATA; ; We get to xIDPST for one of two reasons: ; (1) The buffer is full. In this case, an EBFCT (NEXT[7]) is pending. ; We want to post "full" if this is a normal data wakeup (no branch) ; but just "input done" if hardware input terminated (branch). ; (2) Hardware input terminated while the buffer was not full. ; In this case, an unconditional branch on NEXT[7] is pending, so ; we always terminate with "input done". xIDPST: L← ESIDON, :xIDFUL; [xIDFUL,xPOST] Presumed to be INGONE xIDFUL: L← ESIFUL, :xPOST; Input buffer overrun ;Ethernet output ;It is possible to get here due to a collision. If a collision ;happened, the interface was reset (EPFCT) to shut off the ;transmitter. EOSFCT is issued to guarantee more wakeups while ;generating the countdown. When this is done, the interface is ;again reset, without really doing an output. xOREST: T← 15; MAR← 630+T; (ELLOC) Get load L← R37; Use clock as random # gen xPNTR← LRSH1; Use bits [6:13] L← MD,EOSFCT; L← current load SH<0,xCNTR← L; Overflowed? MTEMP← LLSH1,:xOLDOK; [xOLDOK,xOLDBD] xOLDBD: L← ESLOAD,:xPOST; Load overlow xOLDOK: L← MTEMP+1; Write updated load MAR← 630+T; (ELLOC); MTEMP← L,TASK; MD← MTEMP,:xORST1; New load = (old lshift 1) + 1 xORST1: L← xPNTR; Continue making random # xPNTR← LRSH1; T← 377; L← xPNTR AND T,TASK; xPNTR← L,:xORST2; ;At this point, xPNTR has 0,,random number, xNCTR has old load. xORST2: T← 16; MAR← 630+T; (EICLOC) Has an input buffer been set up? T← xCNTR; L← xPNTR AND T; L← Random & Load SINK← MD,BUS=0; xCNTR← L,SH=0,EPFCT,:xOINPR;[xOINPR,xOINPN] xOINPR: EISFCT,:xOCDWT; [xOCDWT,xOCDGO] Enable in under out xOINPN: :xOCDWT; [xOCDWT,xOCDGO] No input. ;Countdown wait loop. MRT will generate a wakeup every ;37 usec which will decrement xCNTR. When it is zero, start ;the transmitter. ;Ether Wake Function - EWFCT. Sets a flip flop which will cause ;a wakeup to this task the next time MRT wakes up (every 37 usec). ;Wakeup is cleared when Ether task next runs. EWFCT must be ;issued in the instruction AFTER a task. xOCDWT: L← 177400,EBFCT; What's happening? xPNTR← L,ECBFCT,:xOCDW0;[xOCDW0,xOCDRS] Packet coming in? xOCDW0: L← xCNTR-1,BUS=0,TASK,:xOCDW1; [xOCDW1,xIGO] xOCDW1: xCNTR← L,EWFCT,:xOCDWT; [xOCDWT,xOCDGO] xOCDRS: L← ESABRT,:xPOST; [xPOST] POST event xIGO: :xIFRST; [xIFRST] Input under output ;Output main loop setup xOCDGO: T← 20; MAR← 630+T; (EOCLOC) Double word reference EPFCT; Reset interface EOSFCT,:xSETUP; Start Transmitter ;Ether Output Start Function - EOSFCT. The interface will generate ;a burst of data requests until the interface buffer is full or the ;memory buffer is empty, wait for silence on the Ether, and begin ;transmitting. Thereafter it will request a word every 5.44 us. ;Ether Output Data Function - EODFCT. Copy the bus into the ;interface data buffer, increment the write pointer, clears wakeup ;request if the buffer is now nearly full (one slot available). ;Output main loop xODATA: L← MAR← xPNTR+1,EBFCT; What's happening? T← xCNTR-1,BUS=0,:xODOK; [xODOK,xODPST,xODCOL,xODUGH] xODOK: xPNTR← L,L← T,:xODMOR; [xODMOR,xODEND] xODMOR: xCNTR← L,TASK; EODFCT← MD,:xODATA; Output word to transmitter xODPST: L← ESABRT,:xPOST; [xPOST] POST event xODCOL: EPFCT,:xOREST; [xOREST] Collision xODUGH: L← ESABRT,:xPOST; [xPOST] POST + Collision ;Ether EOT Function - EEFCT. Stop generating output data wakeups, ;the interface has all of the packet. When the data buffer runs ;dry, the interface will append the CRC and then generate an ;OUTGONE post wakeup. xODEND: EEFCT; Disable data wakeups TASK; Wait for EEFCT to take :xOEOT; Wait for Outgone ;Output completion. We are waiting for the interface buffer to ;empty, and the interface to generate an OUTGONE Post wakeup. xOEOT: EBFCT; What's happening? :xOEOK; [xOEOK,xOEPST,xOECOL,xOEUGH] xOEOK: L← ESNEVR,:xPOST; Runaway Transmitter. Never Never. xOEPST: L← ESODON,:xPOST; POST event. Output done xOECOL: EPFCT,:xOREST; Collision xOEUGH: L← ESABRT,:xPOST; POST + Collision