; PeekEther2Mc.mu -- special ALTO II Ether microcode for Peek ; Accepts packets to c(610b), 0, or 376b ; Copyright Xerox Corporation 1979 ; Last modified February 11, 1979 5:37 PM by Boggs ;Get the symbol and constant definitions #AltoConsts23.mu; ;The reset locations of the tasks: !17,20,LOC0,LOC1,,,,,,EREST,,,,,,,,; ;some global R-Registers $AC0 $R3; Nova AC0 $NWW $R4; State of interrupt system $R37 $R37; Used by MRT, interval timer, and EIA $MTEMP $R25; Public temporary R-Register $START $L004020,0,0; Nova emulator start address LOC0: SWMODE; Silent boot code :START; LOC1: RMR← AC0,:LOC0; Set Boot Locus Vector from AC0 ;Alto Ethernet Microcode, Version III, Boggs and Metcalfe ;4-way branches using NEXT6 and NEXT7 !17,20,EIFB00,EODOK,EOEOK,ENOCMD,EIFB01,EODPST,EOEPST,EOREST,EIFB10,EODCOL,EOECOL,EIREST,EIFB11,EODUGH,EOEUGH,ERBRES; ;2-way branches using NEXT7 ;EOCDW1, EOCDWX, and EIGO are all related. Be careful! !7,10,,EIFOK,,EOCDW1,,EIFBAD,EOCDWX,EIGO; ;Miscellaneous address constraints !7,10,,EOCDW0,EODATA,EIDFUL,EIDZ4,EOCDRS,EIDATA,EPOST; !7,10,,EIDOK,,,EIDMOR,EIDPST; !1,1,EIFB1; !1,1,EIFRST; ;2-way branches using NEXT9 !1,2,EOINPR,EOINPN; !1,2,EODMOR,EODEND; !1,2,EOLDOK,EOLDBD; !1,2,EIFCHK,EIFPRM; !1,2,EOCDWT,EOCDGO; !1,2,ECNTOK,ECNTZR; !1,2,EIFIGN,EISET; !1,2,EIFNBC,EIFBC; !1,2,EIPEEK,EI610; ;R Memory Locations $ECNTR $R12; Remaining words in buffer $EPNTR $R13; points BEFORE next word in buffer ;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 $ESABRT $2777; Abort - usually caused by reset command $ESNEVR $3377; Never Happen - Very bad if it does ;Main memory locations in page 1 reserved for Ethernet $EPLOC $600; Post location $EBLOC $601; Interrupt bit mask $EELOC $602; Ending count location $ELLOC $603; Load location $EICLOC $604; Input buffer Count $EIPLOC $605; Input buffer Pointer $EOCLOC $606; Output buffer Count $EOPLOC $607; Output buffer Pointer $EHLOC $610; Host Address ;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 ; - 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 ;in preparation for a hack at EIREST, zero EPNTR EREST: L← 0,ERBFCT; What's happening ? EPNTR← L,:ENOCMD; [ENOCMD,EOREST,EIREST,ERBRES] ENOCMD: L← ESNEVR,:EPOST; Shouldn't happen ERBRES: L← ESABRT,:EPOST; 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. EPOST: MAR← EELOC; EPNTR← L,TASK; Save microcode status in EPNTR MD← ECNTR; Save ending count MAR← EPLOC; double word reference T← NWW; MD← EPNTR,EPFCT; BUS AND EPNTR with Status L← MD OR T,TASK; NWW OR c(EBLOC) NWW← L,:EREST; Done. Wait for next command ;This is a subroutine called from both input and output (EOCDGO ;and EISET). 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. ESETUP: NOP; L← MD,BUS=0; check for zero length T← MD-1,:ECNTOK; [ECNTOK,ECNTZR] start-1 ECNTZR: L← ESCZER,:EPOST; Zero word count. Abort ;Ether Countdown Branch Function - ECBFCT. ;NEXT7 = Interface buffer not empty. ECNTOK: ECNTR← L,L← T,ECBFCT,TASK; EPNTR← L,:EODATA; [EODATA,EIDATA] ;Ethernet Input ;It turns out that starting the receiver for the first time and ;restarting it after ignoring a packet do the same things. EIREST: :EIFIGN; 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. ;if destination byte equals 376b, it is a peek report, accept. ;EIFRST is really a subroutine that can be called from EIREST ;or from EIGO, output countdown wait. If a packet is ignored ;and EPNTR is zero, EIFRST 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 EIFRST: MAR← EHLOC; Get Ethernet address T← 377,EBFCT; What's happening? L← MD AND T,BUS=0,:EIFOK;[EIFOK,EIFBAD] promiscuous? EIFOK: MTEMP← LLCY8,:EIFCHK; [EIFCHK,EIFPRM] Data wakeup EIFBAD: ERBFCT,TASK,:EIFB1; [EIFB1] POST wakeup; xCMD FF set? EIFB1: :EIFB00; [EIFB00,EIFB01,EIFB10,EIFB11] EIFB00: :EIFIGN; IDL or INGONE, restart rcvr EIFB01: L← ESABRT,:EPOST; OCMD, abort EIFB10: L← ESABRT,:EPOST; ICMD, abort EIFB11: L← ESABRT,:EPOST; ICMD and OCMD, abort EIFPRM: TASK,:EI610; Promiscuous. Accept ;Ether Look Function - EILFCT. Gate the first word of the ;data buffer to the bus, but do not increment the read pointer. EIFCHK: L← T← 177400,EILFCT; Mask off src addr byte (BUS AND) L← MTEMP-T,SH=0; Dest eq 0? L← 177000-T,SH=0,:EIFNBC; [EIFNBC,EIFBC] Dest eq c(610b)? EIFNBC: SH=0,TASK,:EIPEEK; [EIPEEK,EI610] Dest eq 376b? EIFBC: TASK,:EI610; [EI610] Broadcast packet EI610: :EISET; [EISET] Dest = us EIPEEK: :EIFIGN; [EIFIGN,EISET] ;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. EIFIGN: SINK← EPNTR,BUS=0,EPFCT;Reset; Called from output? EISFCT,TASK,:EOCDWX; [EOCDWX,EIGO] Restart rcvr EOCDWX: EWFCT,:EOCDWT; Return to countdown wait loop EISET: MAR← EICLOC,:ESETUP; 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). ;EIDOK, EIDMOR, and EIDPST must have address bits in the pattern: ;xxx1 xxx4 xxx5 ;ECBFCT is used to force an unconditional branch on NEXT7 EIDATA: T← ECNTR-1, BUS=0; MAR← L← EPNTR+1, EBFCT; [EIDMOR,EIDPST] What's happening EIDMOR: EPNTR← L, L← T, ECBFCT; [EIDOK,EIDPST] Guaranteed to branch EIDOK: MD← EIDFCT, TASK; [EIDZ4] Read a word from the interface EIDZ4: ECNTR← L, :EIDATA; ; We get to EIDPST 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". EIDPST: L← ESIDON, :EIDFUL; [EIDFUL,EPOST] Presumed to be INGONE EIDFUL: L← ESIFUL, :EPOST; 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. EOREST: MAR← ELLOC; Get load L← R37; Use clock as random # gen EPNTR← LRSH1; Use bits [6:13] L← MD,EOSFCT; L← current load SH<0,ECNTR← L; Overflowed? MTEMP← LLSH1,:EOLDOK; [EOLDOK,EOLDBD] EOLDBD: L← ESLOAD,:EPOST; Load overlow EOLDOK: L← MTEMP+1; Write updated load MAR← ELLOC; MTEMP← L,TASK; MD← MTEMP,:EORST1; New load = (old lshift 1) + 1 EORST1: L← EPNTR; Continue making random # EPNTR← LRSH1; T← 377; L← EPNTR AND T,TASK; EPNTR← L,:EORST2; ;At this point, EPNTR has 0,,random number, ENCTR has old load. EORST2: MAR← EICLOC; Has an input buffer been set up? T← ECNTR; L← EPNTR AND T; L← Random & Load SINK← MD,BUS=0; ECNTR← L,SH=0,EPFCT,:EOINPR;[EOINPR,EOINPN] EOINPR: EISFCT,:EOCDWT; [EOCDWT,EOCDGO] Enable in under out EOINPN: :EOCDWT; [EOCDWT,EOCDGO] No input. ;Countdown wait loop. MRT will generate a wakeup every ;37 usec which will decrement ECNTR. 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. EOCDWT: L← 177400,EBFCT; What's happening? EPNTR← L,ECBFCT,:EOCDW0;[EOCDW0,EOCDRS] Packet coming in? EOCDW0: L← ECNTR-1,BUS=0,TASK,:EOCDW1; [EOCDW1,EIGO] EOCDW1: ECNTR← L,EWFCT,:EOCDWT; [EOCDWT,EOCDGO] EOCDRS: L← ESABRT,:EPOST; [EPOST] POST event EIGO: :EIFRST; [EIFRST] Input under output ;Output main loop setup EOCDGO: MAR← EOCLOC; Double word reference EPFCT; Reset interface EOSFCT,:ESETUP; 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 EODATA: L← MAR← EPNTR+1,EBFCT; What's happening? T← ECNTR-1,BUS=0,:EODOK; [EODOK,EODPST,EODCOL,EODUGH] EODOK: EPNTR← L,L← T,:EODMOR; [EODMOR,EODEND] EODMOR: ECNTR← L,TASK; EODFCT← MD,:EODATA; Output word to transmitter EODPST: L← ESABRT,:EPOST; [EPOST] POST event EODCOL: EPFCT,:EOREST; [EOREST] Collision EODUGH: L← ESABRT,:EPOST; [EPOST] 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. EODEND: EEFCT; Disable data wakeups TASK; Wait for EEFCT to take :EOEOT; Wait for Outgone ;Output completion. We are waiting for the interface buffer to ;empty, and the interface to generate an OUTGONE Post wakeup. EOEOT: EBFCT; What's happening? :EOEOK; [EOEOK,EOEPST,EOECOL,EOEUGH] EOEOK: L← ESNEVR,:EPOST; Runaway Transmitter. Never Never. EOEPST: L← ESODON,:EPOST; POST event. Output done EOECOL: EPFCT,:EOREST; Collision EOEUGH: L← ESABRT,:EPOST; POST + Collision