; PeekEther1Mc.mu -- special ALTO I 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,EIDOK,EIFOK,,EOCDW1,EIDPST,EIFBAD,EOCDWX,EIGO; ;Miscellaenous address constraints !7,10,,EOCDW0,EODATA,,,EOCDRS,EIDATA,EPOST; !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,EIDMOR,EIDFUL; !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, so this loop causes SysClk to stop for one cycle by ;referencing MD in cycle 4. EIDATA: L_ MAR_ EPNTR+1,EBFCT; What's happening? T_ ECNTR-1,BUS=0,:EIDOK;[EIDOK,EIDPST] word count zero? EIDOK: EPNTR_ L,L_ T,:EIDMOR; [EIDMOR,EIDFUL] EIDMOR: MD_ EIDFCT,TASK; Read a word from interface ECNTR_ L,:EIDATA; EIDPST: L_ ESIDON,:EPOST; [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_ LLSH1; Use bits [2:9] L_ MD,EOSFCT; L_ current load SH<0,ECNTR_ L; Overflowed? MTEMP_ LLSH1,:EOLDOK; [EOLDOK,EOLDBD] EOLDBD: L_ ESLOAD,:EPOST; Load overlow EOLDOK: MAR_ ELLOC; Write updated load L_ MTEMP+1; MTEMP_ L,TASK; MD_ MTEMP,:EORST1; New load = (old lshift 1) + 1 EORST1: L_ EPNTR; Continue making random # EPNTR_ LLSH1; T_ 177400; L_ EPNTR AND T,TASK; EPNTR_ LLCY8,: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 wake 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