// PupAlEthb.bcpl - Pup Alto Ethernet driver (level 0), BCPL portion // Copyright Xerox Corporation 1979, 1980 // Last modified September 26, 1980 3:24 PM by Boggs get "Pup0.decl" get "PupAlEth.decl" external [ // outgoing procedures SendEtherPacket; EncapsulateEtherPup; SendEtherStats FeedEther; @EtherInterrupt; EtherFinish // incoming procedures StartIO; StartEther; DoubleIncrement Enqueue; Dequeue; SysErr; MoveBlock SetTimer; TimerHasExpired; Dismiss CauseInterrupt; DestroyInterrupt DisableInterrupts; EnableInterrupts // incoming statics pbiIQ; pbiFreeQ; ndbQ; lvUserFinishProc savedEtherUFP; lenEtherPacket ] // Note: if pupDebug is true, then code will be compiled which // calls DoubleIncrement, which is in PupAl1A.asm -- above level 0! //---------------------------------------------------------------------------- let EncapsulateEtherPup(pbi, pdh) be //---------------------------------------------------------------------------- // performs Ethernet dependent encapsulation // pbi -> a PBI // pdh is physical destination host for Pup [ pbi>>EtherPBI.dest = pdh pbi>>EtherPBI.src = pbi>>PBI.ndb>>NDB.localHost pbi>>EtherPBI.type = typePup pbi>>PBI.packetLength = (pbi>>PBI.pup.length+5) rshift 1 ] //---------------------------------------------------------------------------- and SendEtherPacket(pbi) be //---------------------------------------------------------------------------- [ let ndb = pbi>>PBI.ndb if pbi>>EtherPBI.dest eq @(ndb>>EtherNDB.eHLoc) % pbi>>EtherPBI.dest eq 0 then // sending to ourself or bcst [ // fake reception of packet let ipbi = Dequeue(pbiFreeQ) if ipbi ne 0 then [ MoveBlock(ipbi+1, pbi+1, (offset EtherPBI.dest/16-1)+pbi>>PBI.packetLength) let q = pbiFreeQ let pf = ndb>>NDB.pfQ.head while pf ne 0 do [ if (pf>>PF.predicate)(ipbi) then [ q = pf>>PF.queue; break ] pf = pf>>PF.link ] Enqueue(q, ipbi) ] ] // transmit the packet Enqueue(lv ndb>>EtherNDB.oQ, pbi) if ndb>>EtherNDB.eOB eq 0 then CauseInterrupt(ndb>>EtherNDB.mask) compileif pupDebug then [ DoubleIncrement(lv ndb>>EtherNDB.packetsSent) ] ] //---------------------------------------------------------------------------- and SendEtherStats(pbi, ndb) = valof //---------------------------------------------------------------------------- [ compileif pupDebug then [ MoveBlock(lv pbi>>PBI.pup.words, lv ndb>>EtherNDB.stats, lenEtherStats) pbi>>PBI.pup.length = pupOvBytes + lenEtherStats ] resultis pupDebug ] //---------------------------------------------------------------------------- and EtherInterrupt(ndb) be //---------------------------------------------------------------------------- // Control comes here when an Ethernet interrupt occurs. // The contents of ePLoc at the time of the interrupt is in // lastEPLoc (zero means that the interrupt was manually initiated). // If an input done interrupt has occurred, lastEIB holds the pbi // just received and lastEELoc holds the ending word count. // In any event, if eState is nonzero, input has been restarted. [ // if the interface has posted, dispose of the packet appropriately let lastEPLoc = ndb>>EtherNDB.lastEPLoc if lastEPLoc ne 0 then switchon lastEPLoc rshift 8 into [ case 0: // good input microcode status [ let q, lastEIB = pbiFreeQ, ndb>>EtherNDB.lastEIB test lastEPLoc eq 377b // hardware status ifso // good packet, put on input queue [ lastEIB>>PBI.packetLength = lenEtherPacket-ndb>>EtherNDB.lastEELoc lastEIB>>PBI.ndb = ndb let pf = ndb>>NDB.pfQ.head while pf ne 0 do [ if (pf>>PF.predicate)(lastEIB) then [ q = pf>>PF.queue; break ] pf = pf>>PF.link ] compileif pupDebug then [ DoubleIncrement(lv ndb>>EtherNDB.packetsRcvd) ] ] ifnot // bad packet, throw it away [ compileif pupDebug then [ DoubleIncrement(lv ndb>>EtherNDB.numBadRcvStatus) ] ] Enqueue(q, lastEIB) endcase ] case 1: // good output microcode status [ if lastEPLoc eq #777 then //hardware status [ Enqueue(ndb>>EtherNDB.eOB>>PBI.queue, ndb>>EtherNDB.eOB) ndb>>EtherNDB.eOB, @(ndb>>EtherNDB.eOCLoc) = 0, 0 compileif pupDebug then [ let load, i = @(ndb>>EtherNDB.eLLoc), -1 [ load = load rshift 1; i = i+1 ] repeatuntil load eq 0 DoubleIncrement(lv ndb>>EtherNDB.loadTable↑i) endcase ] ] compileif pupDebug then [ DoubleIncrement(lv ndb>>EtherNDB.numBadXmtStatus) ] endcase ] // EtherInterrupt (cont'd) case 3: // load overflow, or software-induced timeout [ if ndb>>EtherNDB.eOB ne 0 then Enqueue(ndb>>EtherNDB.eOB>>PBI.queue, ndb>>EtherNDB.eOB) ndb>>EtherNDB.eOB, @(ndb>>EtherNDB.eOCLoc) = 0, 0 compileif pupDebug then [ DoubleIncrement(lv ndb>>EtherNDB.loadTable↑16) ] ] case 2: // input buffer overrun case 5: // reset of some kind endcase case 4: // zero length buffer default: // impossible microcode branch SysErr(lastEPLoc, ecBadEtherStatus) ] compileif pupDebug then [ if ndb>>EtherNDB.eState eq 0 then DoubleIncrement(lv ndb>>EtherNDB.inputOff) ] // set up new input and output buffers if appropriate if ndb>>EtherNDB.eIB eq 0 then [ ndb>>EtherNDB.eIB = Dequeue(pbiFreeQ) if ndb>>EtherNDB.eIB ne 0 then [ @(ndb>>EtherNDB.eIPLoc) = lv ndb>>EtherNDB.eIB>>EtherPBI.dest @(ndb>>EtherNDB.eICLoc) = lenEtherPacket ] ] if ndb>>EtherNDB.eOB eq 0 then [ ndb>>EtherNDB.eOB = Dequeue(lv ndb>>EtherNDB.oQ) if ndb>>EtherNDB.eOB ne 0 then [ @(ndb>>EtherNDB.eOPLoc) = lv ndb>>EtherNDB.eOB>>EtherPBI.dest @(ndb>>EtherNDB.eOCLoc) = ndb>>EtherNDB.eOB>>PBI.packetLength SetTimer(lv ndb>>EtherNDB.tTimer, 10) //time out in 100 ms ] ] // restart the interface if it is off or output is now ready StartEther(ndb) ] //---------------------------------------------------------------------------- // and EtherPupFilter(pbi) = // hand-coded in PupAlEtha.asm //---------------------------------------------------------------------------- // (pbi>>PBI.pup.length+5) rshift 1 eq pbi>>PBI.packetLength & // pbi>>EtherPBI.type eq typePup //---------------------------------------------------------------------------- and FeedEther(ctx) be //---------------------------------------------------------------------------- // This background process performs two tasks: // (1) if the interface is turned off (presumably for lack of input // buffers) and buffers are now availble, start the receiver. // (2) if the interface is transmitting and has timed out, reset it // and fake a load overflow indication to unhang the software. [ let ndb = ctx!3 if ndb>>EtherNDB.eState eq 0 & pbiFreeQ!0 ne 0 then [ @(ndb>>EtherNDB.ePLoc) = 0; CauseInterrupt(ndb>>EtherNDB.mask) ] if ndb>>EtherNDB.eState ls 0 & TimerHasExpired(lv ndb>>EtherNDB.tTimer) then [ DisableInterrupts() StartIO(ndb>>EtherNDB.resetCmd) // causes pending interrupt @(ndb>>EtherNDB.ePLoc) = 3 lshift 8 // fake a load overflow EnableInterrupts() ] Dismiss(4) ] repeat //---------------------------------------------------------------------------- and EtherFinish(code) be //---------------------------------------------------------------------------- // Turns off all Ethernet interfaces. [ if ndbQ ne 0 then [ let ndb = ndbQ!0 while ndb ne 0 do [ if ndb>>NDB.netType eq netTypeEther then [ @(ndb>>EtherNDB.eBLoc) = 0 StartIO(ndb>>EtherNDB.resetCmd) DestroyInterrupt(ndb>>EtherNDB.mask) ] ndb = ndb!0 ] ] @lvUserFinishProc = savedEtherUFP savedEtherUFP = -1 ]