; ChainEther*.mu -- Chained input microcode for the Alto Ethernet ; Last modified HGM December 14, 1978 6:24 PM ; typing in fix for 4,,371 posting again ; Last modified HGM October 18, 1978 8:57 PM ; trying to shrink it a few words ; Last modified HGM October 9, 1978 11:27 PM ; fix for 377B,,0 posting ; Last modified September 23, 1978 5:19 PM ;4-way branches using NEXT6 and NEXT7 !17,20,0EIFB00,0EODOK,0EO0EOK,0ENOCMD,0EIFB01,0EODPST,0EOEPST,0ERESTO,0EIFB10,0EODCOL,0EOECOL,0ERESTI,0EIFB11,0EODUGH,0EOEUGH,0ERBRES; ;2-way branches using NEXT7 ;0EOCDW1, 0EOCDWX, and 0EIGO are all related. Be careful! !7,10,,0EIFOK,,0EOCDW1,,0EIFBAD,0EOCDWX,0EIGO; ;Miscellaneous address constraints !7,10,,0EOCDW0,0EODATA,0EIDFUL,0EIDZ4,0EOCDRS,0EIDATA,0EIPOST; !7,10,,0EIDOK,,,0EIDMOR,0EIDPST,,0EAPOST; !1,1,0EIFB1; !1,1,0EIFRST; !1,1,0ELOOK4; !1,1,0EODCOR; ;2-way branches using NEXT9 !1,2,0EOREST,0ELOOK1; !1,2,0ELOOK3,0ELOOK2; !1,2,0EODDCB,0EIREST; !1,2,0EPNTOK,0EPNTZ; !1,2,0EOINPR,0EOINPN; !1,2,0EODMOR,0EODEND; !1,2,0EOLDOK,0EOLDBD; !1,2,0EIFCHK,0EIFPRM; !1,2,0EOCDWT,0EOCDGO; !1,2,0ECNTOK,0ECNTZR; !1,2,0EIFIGN,0EISET; !1,2,0EIFNBC,0EIFBC; ;Ethernet microcode Status codes $ESIDON $377; Input Done $ESODON $777; Output Done $ESIFUL $1377; Input Buffer full - words lost from tail of packet $ESLOAD $1777; Load location overflowed $ESCZER $2377; Zero word count for input or output command, ; or odd input chain pointer, or zero input pointer $ESABRT $2777; Abort - usually caused by reset command $ESNEVR $3377; Never Happen - Very bad if it does ;Function Definitions $EIDFCT $L000000,014004,000100; BS = 4, Input data $EILFCT $L016013,070013,000100; F1 = 13, Input Look $EPFCT $L016014,070014,000100; F1 = 14, Post $EWFCT $L016015,000000,000000; F1 = 15, Wake-Up $EODFCT $L026010,000000,124000; F2 = 10, Output data $EOSFCT $L024011,000000,000000; F2 = 11, Start output $ERBFCT $L024012,000000,000000; F2 = 12, Rest branch $EEFCT $L024013,000000,000000; F2 = 13, End of output $EBFCT $L024014,000000,000000; F2 = 14, Branch $ECBFCT $L024015,000000,000000; F2 = 15, Countdown branch $EISFCT $L024016,000000,000000; F2 = 16, Start input ;Main memory locations in page 1 reserved for Ethernet. ;There aren't constants for these locations for the second+third ;Ethernet boards, so we manufacture them. To avoid extensive editing, ;the code for the normal ethernet also manufactures them. ;Odd addresses for double word references are not needed, thus "---". ;Constants 630, 631, and 642 do exist. ; R11 is normally used by MRT ; R21 and R26 are normaly used by the display and cursor tasks ;ELOC 600, 630, 642 Base location of Main Control block ;ECNTR R12, R11, R42 Number of words left to transfer ;EPNTR R13, R14, R21 Points before word to transfer ;EPLOC 600, 630, 642 = ELOC Post location ;EBLOC 601, 631, 643 = --- Interrupt bit mask ;EELOC 602, 632, 644 = ELOC+2 Ending count location ;ELLOC 603, 633, 645 = ELOC+3 Load location ;EICLOC 604, 634, 646 = ELOC+4 Input buffer Count ;EIPLOC 605, 635, 647 = --- Input buffer Pointer ;EOCLOC 606, 636, 650 = ELOC+6 Output buffer Count ;EOPLOC 607, 637, 651 = --- Output buffer Pointer ;EHLOC 610, 640, 652 = ELOC+10 Host Address ;ECHLOC 611, 641, 653 = ELOC+11 Chain Pointer for Input Control Block ; This section of code is designed to be included several times in one ; mu file. Thats why all the tags have a 0 in front of them. To make ; a version for another board, let Bravo change all 0E to 1E for you. ; For each instance of this microcode that you wish to include in ; your RAM, you will have to have something like the following: ; $*ECNTR $R12; ; $*EPNTR $R13; ; $*ELOC $600; ; #ChainEther*.mu; ; Don't forget to include *EREST in your PREDEF for the Boot Vector ; - Whenever a label has a pending branch, the list of possible ; destination addresses is shown in brackets in the comment field. ; - Special functions are explained in a comment near their first use. ; - To avoid naming conflicts, all labels and special functions ; have "E" as the first letter. ;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 ; *** Ethernet task is idle -- waiting for StartIO from emulator 0EREST: ERBFCT; What's happening ? :0ENOCMD; [0ENOCMD,0ERESTO,0ERESTI,0ERBRES] 0ENOCMD: L_ ESNEVR,:0EAPOST; Shouldn't happen 0ERESTO: :0ELOOK; Output when no input buffers 0ERESTI: :0ELOOK; Probably new input buffer 0ERBRES: L_ ESABRT,:0EAPOST; 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. ; Get here if we are Aborting ; Next7 must be a one to shake pending branch from EOCRDS 0EAPOST: 0EPNTR_ L,TASK; Save microcode status in 0EPNTR NOP; MAR_ 0ELOC; (EPLOC) double word reference T_ NWW; MD_ 0EPNTR,EPFCT; BUS AND 0EPNTR with Status L_ MD OR T, TASK; NWW OR c(EBLOC) NWW_ L, :0EREST; ; Non-Abort Post from Input routine ; L has microcode status, something is waking us up 0EIPOST: T_ 11; MAR_ 0ELOC+T; (ECHLOC) 0EPNTR_ L; Save microcode status in 0EPNTR T_ MD; MAR_ 2+T; L_ T; MD_ 0ECNTR, TASK; Save ending count 0ECNTR_ L; 0ECNTR Points to Current Input Control Block ; Advance Chain Pointer to next Input Control Block T_ 11; MAR_ 0ECNTR+T; (ECHLOC) of Current Input Control Block L_ MD; MAR_ 0ELOC+T; (ECHLOC) MTEMP_ L, TASK; MTEMP _ new Input Control Block MD_ MTEMP; MAR_ 0ECNTR, :0EWAKE; (EPLOC) double word reference ; Non-Abort Post from Output routine ; L has microcode status, something is waking us up 0EOPOST: T_ 7; MAR_ 0ELOC+T; (EOPLOC) Zero output pointer so we won't send it again 0EPNTR_ L, TASK; MD_ 0; ; don't bother to save ending count on output MAR_ 0ELOC; (EPLOC) double word reference ; Store status and generate interrupt(s) 0EWAKE: T_ NWW; MD_ 0EPNTR,EPFCT; BUS AND 0EPNTR with Status L_ MD OR T; NWW OR c(EBLOC) NWW_ L,TASK; EOSFCT, :0ELOOK; Generate more Wakeups ; Look for something to do, Something must be generating Wakeups 0ELOOK: T_ 7; MAR_ 0ELOC+T; (EOPLOC) Look to see if there is output ready NOP; SINK_ MD,BUS=0; T_ 11, :0EOREST; [0EOREST,0ELOOK1] Check for Input buffer ready 0ELOOK1: MAR_ 0ELOC+T; (ECHLOC) T_ 1; L_ MD AND T, T_ MD, BUS=0; EPFCT, SH=0, :0ELOOK3; [0ELOOK3,0ELOOK2] 0ELOOK2: TASK, :0ELOOK4; [0ELOOK4] Nothing to do 0ELOOK4: :0EREST; 0ELOOK3: 0EPNTR_ L, :0EODDCB; [0EODDCB,0EIREST] ; Interface has been reset, use EOSFCT to generate another wakeup 0EODDCB: EOSFCT,:0ECNTZR; Odd Control Block Pointer ;This is a subroutine called from both input and output (0EOCDGO ;and 0EISET). 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. 0ESETUP: NOP; L_ MD,BUS=0; check for zero length T_ MD-1,:0ECNTOK; [0ECNTOK,0ECNTZR] start-1 0ECNTZR: L_ ESCZER,:0EAPOST; Zero word count. Abort ;Ether Countdown Branch Function - ECBFCT. ;NEXT7 = Interface buffer not empty. 0ECNTOK: 0ECNTR_ L, L_ 0+T+1; SH=0, L_ T; ECBFCT, 0EPNTR_ L, :0EPNTOK; [0EPNTOK,0EPNTZ] 0EPNTZ: L_ ESCZER, :0EAPOST; [0EAPOST] Empty Pointer 0EPNTOK: :0EODATA; [0EODATA,0EIDATA] ;Ethernet Input ;It turns out that starting the receiver for the first time and ;restarting it after ignoring a packet do the same things. 0EIREST: :0EIFIGN; 0EPNTR is 0 to flag input mode ;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. ;0EIFRST is really a subroutine that can be called from 0EIREST ;or from 0EIGO, output countdown wait. If a packet is ignored ;and 0EPNTR is zero, 0EIFRST 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 ; *** Ethernet task may wait for a packet to arrive ; if called from EIGO via EOCDWT, a packet is already arriving ; if called from EIGO via 0EIFIGN, it waits here 0EIFRST: T_ 10; MAR_ 0ELOC+T; (EHLOC) Get Ethernet address T_ 377,EBFCT; What's happening? L_ MD AND T,BUS=0,:0EIFOK;[0EIFOK,0EIFBAD] promiscuous? 0EIFOK: MTEMP_ LLCY8,:0EIFCHK; [0EIFCHK,0EIFPRM] Data wakeup 0EIFBAD: ERBFCT,TASK,:0EIFB1; [0EIFB1] POST wakeup; xCMD FF set? 0EIFB1: :0EIFB00; [0EIFB00,0EIFB01,0EIFB10,0EIFB11] 0EIFB00: :0EIFIGN; IDL or INGONE, restart rcvr 0EIFB01: :0ELOOK; Probably Output to do 0EIFB10: :0ELOOK; Other way to look for Output 0EIFB11: L_ ESABRT,:0EAPOST; ICMD and OCMD, abort 0EIFPRM: TASK,:0EIFBC; Promiscuous. Accept ;Ether Look Function - EILFCT. Gate the first word of the ;data buffer to the bus, but do not increment the read pointer. 0EIFCHK: L_ T_ 177400,EILFCT; Mask off src addr byte (BUS AND) L_ MTEMP-T,SH=0; Broadcast? SH=0,TASK,:0EIFNBC; [0EIFNBC,0EIFBC] Our Address? 0EIFNBC: :0EIFIGN; [0EIFIGN,0EISET] 0EIFBC: :0EISET; [0EISET] 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. 0EIFIGN: SINK_ 0EPNTR,BUS=0,EPFCT;Reset; Called from output? EISFCT,TASK,:0EOCDWX; [0EOCDWX,0EIGO] Restart rcvr 0EOCDWX: EWFCT,:0EOCDWT; Return to countdown wait loop 0EISET: T_ 11; Get Pointer and Count out of Current Input Control Block MAR_ 0ELOC+T; (ECHLOC) NOP; T_ MD; MAR_ 4+T,:0ESETUP; (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). ;0EIDOK, 0EIDMOR, and 0EIDPST must have address bits in the pattern: ;xxx1 xxx4 xxx5 ;ECBFCT is used to force an unconditional branch on NEXT7 ; *** Ethernet task may wait for next input word 0EIDATA: T_ 0ECNTR-1, BUS=0; MAR_ L_ 0EPNTR+1, EBFCT; [0EIDMOR,0EIDPST] What's happening 0EIDMOR: 0EPNTR_ L, L_ T, ECBFCT; [0EIDOK,0EIDPST] Guaranteed to branch 0EIDOK: MD_ EIDFCT, TASK; [0EIDZ4] Read a word from the interface 0EIDZ4: 0ECNTR_ L, :0EIDATA; ; We get to 0EIDPST 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". 0EIDPST: L_ ESIDON, :0EIDFUL; [0EIDFUL,0EIPOST] Presumed to be INGONE 0EIDFUL: L_ ESIFUL, :0EIPOST; 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. 0EOREST: T_ 3; MAR_ 0ELOC+T; (ELLOC) Get load L_ R37; Use clock as random # gen 0EPNTR_ LRSH1; Use bits [6:13] L_ MD,EOSFCT; L_ current load SH<0,0ECNTR_ L; Overflowed? MTEMP_ LLSH1,:0EOLDOK; [0EOLDOK,0EOLDBD] 0EOLDBD: L_ ESLOAD,:0EOPOST; Load overlow 0EOLDOK: L_ MTEMP+1; Write updated load MAR_ 0ELOC+T; (ELLOC); MTEMP_ L,TASK; MD_ MTEMP,:0EORST1; New load = (old lshift 1) + 1 0EORST1: L_ 0EPNTR; Continue making random # 0EPNTR_ LRSH1; T_ 377; L_ 0EPNTR AND T,TASK; 0EPNTR_ L,:0EORST2; ;At this point, 0EPNTR has 0,,random number, ENCTR has old load. 0EORST2: T_ 11; MAR_ 0ELOC+T; (ECHLOC) Has an input buffer been set up? T_ 0ECNTR; L_ 0EPNTR AND T; L_ Random & Load SINK_ MD,BUS=0; 0ECNTR_ L,SH=0,EPFCT,:0EOINPR;[0EOINPR,0EOINPN] 0EOINPR: EISFCT,:0EOCDWT; [0EOCDWT,0EOCDGO] Enable in under out 0EOINPN: :0EOCDWT; [0EOCDWT,0EOCDGO] No input. ;Countdown wait loop. MRT will generate a wakeup every ;37 usec which will decrement 0ECNTR. 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. ; *** Ethernet task is waiting for first input word or retransmission clock 0EOCDWT: L_ 177400,EBFCT; What's happening? 0EPNTR_ L,ECBFCT,:0EOCDW0;[0EOCDW0,0EOCDRS] Packet coming in? 0EOCDW0: L_ 0ECNTR-1,BUS=0,TASK,:0EOCDW1; [0EOCDW1,0EIGO] 0EOCDW1: 0ECNTR_ L,EWFCT,:0EOCDWT; [0EOCDWT,0EOCDGO] ; We have probably been reset 0EOCDRS: L_ ESABRT,:0EAPOST; [0EAPOST] POST event 0EIGO: :0EIFRST; [0EIFRST] Input under output ;Output main loop setup 0EOCDGO: T_ 6; MAR_ 0ELOC+T; (EOCLOC) Double word reference EPFCT; Reset interface EOSFCT,:0ESETUP; 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 ; *** Ethernet task may wait to send next output word 0EODATA: L_ MAR_ 0EPNTR+1,EBFCT; What's happening? T_ 0ECNTR-1,BUS=0,:0EODOK; [0EODOK,0EODPST,0EODCOL,0EODUGH] 0EODOK: 0EPNTR_ L,L_ T,:0EODMOR; [0EODMOR,0EODEND] 0EODMOR: 0ECNTR_ L,TASK; EODFCT_ MD,:0EODATA; Output word to transmitter 0EODPST: L_ ESABRT,:0EAPOST; [0EAPOST] POST event 0EODCOL: EPFCT,:0EODCOR; [0EODCOR] Collision 0EODCOR: :0EOREST; Wait for Outgone 0EODUGH: L_ ESABRT,:0EAPOST; [0EAPOST] POST + Collision ;Ether 0EOT 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. 0EODEND: EEFCT; Disable data wakeups TASK; Wait for EEFCT to take :0EO0EOT; Wait for Outgone ;Output completion. We are waiting for the interface buffer to ;empty, and the interface to generate an OUTGONE Post wakeup. ; *** Ethernet task may wait for output buffer to empty 0EO0EOT: EBFCT; What's happening? :0EO0EOK; [0EO0EOK,0EOEPST,0EOECOL,0EOEUGH] 0EO0EOK: L_ ESNEVR,:0EAPOST; Runaway Transmitter. Never Never. 0EOEPST: L_ ESODON,:0EOPOST; POST event. Output done 0EOECOL: EPFCT,:0EOREST; Collision 0EOEUGH: L_ ESABRT,:0EAPOST; POST + Collision