// PupAlEthInit.bcpl -- Alto Ethernet Initialization // Code may be thrown away after use // companion to PupAlEthb.bcpl and PupAlEtha.asm // Copyright Xerox Corporation 1979, 1980, 1982 // Last modified September 27, 1982 2:02 PM by Taft get "Pup0.decl" get "PupAlEth.decl" external [ // outgoing procedures InitAltoEther; DestroyAltoEther // incoming procedures SendEtherPacket; EncapsulateEtherPup; SendEtherStats EtherFinish; EtherPupFilter InitializeInterrupt; FindInterruptMask; DestroyInterrupt InitializeContext; FeedEther Enqueue; Dequeue; Unqueue; Zero; MoveBlock; Allocate; Free; StartIO; SysErr // outgoing statics savedEtherUFP; lenEtherPacket; etherMcLoaded // incoming statics ndbQ; pbiIQ; pbiFreeQ; lenPup; etherProlog lvUserFinishProc ] static [ savedEtherUFP = -1; lenEtherPacket; etherMcLoaded ] manifest [ eISS = 50 //Ether Interrupt Stack Size eFSS = 40 //Ether Feeder Stack Size interruptVector = #501 etherOutputCommand = 1 etherInputCommand = 2 etherResetCommand = 3 ] //---------------------------------------------------------------------------- let InitAltoEther(zone, ctxQ, code) be //---------------------------------------------------------------------------- //zone is where to get storage from //ctxQ is the context list on which to queue the feeder process //EtherInterrupt should have a high priority mask bit //The low two bits of etherMcLoaded are flags. Bit 15 means the first // ExtraEther1.mu is loaded, Bit 14 means ExtraEther2.mu is loaded. [ if code ne 0 then if (code & etherMcLoaded) eq 0 return //no microcode let loc, sio = nil, nil switchon code into [ case 0: [ loc = 600b; sio = 0; endcase ] case 1: [ loc = 630b; sio = 2; endcase ] case 2: [ loc = 642b; sio = 4; endcase ] ] loc!0 = 0 //ePLoc loc!1 = 0 //eBLoc let host = StartIO(etherResetCommand lshift sio) & 377b //reset if loc!0 eq 0 return //interface didn't respond - must not be one if host eq 377b then SysErr(nil, ecNoEthernetInterface) if host eq 0 then SysErr(nil, ecZeroHostAddress) loc!8 = host //eHLoc // standard part of NDB let ndb = Allocate(zone, lenEtherNDB+eISS) Zero(ndb, lenEtherNDB+eISS) ndb>>NDB.localHost = host //local host number ndb>>NDB.encapsulatePup = EncapsulateEtherPup ndb>>NDB.level0Transmit = SendEtherPacket //send pbi routine ndb>>NDB.level0Stats = SendEtherStats ndb>>NDB.destroy = DestroyAltoEther ndb>>NDB.netType = netTypeEther ndb>>NDB.deviceNum = code ndb>>NDB.pupPF.predicate = EtherPupFilter ndb>>NDB.pupPF.queue = pbiIQ Enqueue(lv ndb>>NDB.pfQ, lv ndb>>NDB.pupPF) Enqueue(ndbQ, ndb) // Ether specific part of NDB let t = lv ndb>>EtherNDB.ePLoc for i = 0 to 8 do t!i = loc+i ndb>>EtherNDB.outCmd = etherOutputCommand lshift sio ndb>>EtherNDB.inCmd = etherInputCommand lshift sio ndb>>EtherNDB.resetCmd = etherResetCommand lshift sio ndb>>EtherNDB.statsType = netTypeEther ndb>>EtherNDB.statsVersion = etherStatsVersion ndb>>EtherNDB.zone = zone ndb>>EtherNDB.ctxQ = ctxQ // handy constant for driver lenEtherPacket = lenPup+2 // InitAltoEther (cont'd) // This rather complex interrupt mechanism works as follows: // Control goes through the interrupt vector to asmProlog // which saves ac3, and loads ac3 with the ndb corresponding // to the interrupting interface. It then calls EtherInterruptEntry. // EtherInterruptEntry tries to get the receiver going as fast // as possible, and then jumps through EtherNDB.savedID to the // normal bcpl interrupt handler. // The interrupt handler sets up the stack and calls bcplProlog // which calls EtherInterrupt with the ndb as argument. // EtherInterrupt finishes processing the interrupt and returns // back into the interrupt handler which dismisses the interrupt MoveBlock(lv ndb>>EtherNDB.prolog, etherProlog, (size EtherNDB.prolog/16)) ndb>>EtherNDB.ndb = ndb ndb>>EtherNDB.mask = FindInterruptMask(1) //high priority @(ndb>>EtherNDB.eBLoc) = ndb>>EtherNDB.mask InitializeInterrupt(ndb+lenEtherNDB, eISS, ndb>>EtherNDB.mask, lv ndb>>EtherNDB.bcplProlog) let i = 0 //discover interrupt channel number while ((1 lshift i) & ndb>>EtherNDB.mask) eq 0 do i = i +1 ndb>>EtherNDB.savedID = interruptVector!i interruptVector!i = lv ndb>>EtherNDB.asmProlog // interface housekeeping process let ctx = InitializeContext(Allocate(zone, eFSS), eFSS, FeedEther, 1) ctx!3 = ndb Enqueue(ctxQ, ctx) ndb>>EtherNDB.feederCtx = ctx if savedEtherUFP eq -1 then [ savedEtherUFP = @lvUserFinishProc @lvUserFinishProc = EtherFinish ] ] //---------------------------------------------------------------------------- and DestroyAltoEther(ndb) be //---------------------------------------------------------------------------- // Shuts off the Ethernet interface and returns all allocated storage. [ @(ndb>>EtherNDB.eBLoc) = 0 StartIO(ndb>>EtherNDB.resetCmd) DestroyInterrupt(ndb>>EtherNDB.mask) if ndb>>EtherNDB.eIB ne 0 then Enqueue(pbiFreeQ, ndb>>EtherNDB.eIB) if ndb>>EtherNDB.eOB ne 0 then Enqueue(pbiFreeQ, ndb>>EtherNDB.eOB) while ndb>>EtherNDB.oQ.head ne 0 do Enqueue(pbiFreeQ, Dequeue(lv ndb>>EtherNDB.oQ)) Unqueue(ndb>>EtherNDB.ctxQ, ndb>>EtherNDB.feederCtx) Free(ndb>>EtherNDB.zone, ndb>>EtherNDB.feederCtx) Unqueue(ndbQ, ndb) Free(ndb>>EtherNDB.zone, ndb) // try to get rid of EtherFinish -- this is possible only if EtherFinish // was the last userFinishProc queued. if @lvUserFinishProc eq EtherFinish then [ @lvUserFinishProc = savedEtherUFP; savedEtherUFP = -1 ] ]