// PupAlImp.bcpl -- Alto Imp driver // Copyright Xerox Corporation 1983 // Last modified August 22, 1983 2:03 PM by Taft // Note: this code requires the special version of the Alto1822 microcode // that does scatter/gather. get "Pup0.decl" get "PupAlImp.decl" external [ // Outgoing procedures EncapsulateImpPup; SendImpPacket; ImpPupFilter; ResetImpInterface ImpInputInterrupt; ImpOutputInterrupt; ImpProcess; ImpFinish // Incoming procedures Enqueue; Dequeue; CauseInterrupt; DisableInterrupts; EnableInterrupts SetTimer; TimerHasExpired; Dismiss; Block CallSwat; StartIO; MoveBlock; Zero // Outgoing statics impNDB; savedImpUFP // Incoming statics pbiFreeQ; pbiIQ; lenPBI; lvUserFinishProc ] static [ impNDB savedImpUFP = -1 ] // Pup interface procedures //---------------------------------------------------------------------------- let EncapsulateImpPup(pbi, pdh) be //---------------------------------------------------------------------------- // Performs Arpanet-dependent encapsulation. // pbi points at a PBI containing a Pup. // pdh is physical destination host for Pup. [ pbi>>ImpPBI.host = pdh rshift 6 pbi>>ImpPBI.imp = pdh & 77B pbi>>ImpPBI.link = linkPup pbi>>PBI.packetLength = (pbi>>PBI.pup.length+1) rshift 1 ] //---------------------------------------------------------------------------- and SendImpPacket(pbi) be //---------------------------------------------------------------------------- // Assumes that the ImpPBI.host, imp, and link fields have been filled in. // Note: contrary to normal practice, the PBI.packetLength refers only to the // encapsulated data. Assumes that it is nonzero. [ test pbi>>ImpPBI.imp eq 0 % // For now, discard broadcast impNDB>>ImpNDB.status.hostNotReady ne 0 % // Discard if host not ready impNDB>>ImpNDB.status.impNotReady ne 0 // Discard if Imp not ready ifso Enqueue(pbi>>PBI.queue, pbi) ifnot [ Enqueue(lv impNDB>>ImpNDB.oQ, pbi) unless impNDB>>ImpNDB.oActive do CauseInterrupt(impNDB>>ImpNDB.icb>>ICB.outputChanMask) ] ] //---------------------------------------------------------------------------- and ImpInputInterrupt() be //---------------------------------------------------------------------------- [ let icb = impNDB>>ImpNDB.icb let pbi = impNDB>>ImpNDB.iPBI if pbi ne 0 then [ // Dispose of incoming message UpdateStatus(icb>>ICB.inputPost) let queue = pbiFreeQ let newError = false switchon icb>>ICB.inputPost.microcode into [ case isInputOverflow: // Input buffer full and not end of message if icb>>ICB.inputPointer - lv impNDB>>ImpNDB.iLeader ule lenImpLeader then [ // end of leader -- set up to read data into PBI icb>>ICB.inputPointer = lv pbi>>ImpPBI.data icb>>ICB.inputEnd = pbi+lenPBI icb>>ICB.inputPost = 0 StartIO(impStartInput) return ] // Discard rest of message; but process whatever was received anyway, // since the overflow may simply be Imp padding. newError = true // fall through case isInputFullAndEnd: // Input buffer full and end of message simultaneously icb>>ICB.inputPointer = icb>>ICB.inputEnd // microcode gets this wrong // fall through case isDone: // Normal completion [ let llen = icb>>ICB.inputPointer - lv impNDB>>ImpNDB.iLeader if impNDB>>ImpNDB.iError % // Discard if previous error llen uls lenImpLeader % // Discard if incomplete leader impNDB>>ImpNDB.iLeader.format ne 17B then endcase // Discard if not new format pbi>>PBI.packetLength = llen eq lenImpLeader? 0, icb>>ICB.inputPointer - lv pbi>>ImpPBI.data pbi>>ImpPBI.host = impNDB>>ImpNDB.iLeader.host // Compress encapsulation into PBI pbi>>ImpPBI.imp = impNDB>>ImpNDB.iLeader.imp pbi>>ImpPBI.link = impNDB>>ImpNDB.iLeader.link switchon impNDB>>ImpNDB.iLeader.messageType into [ case 0: // regular message [ pbi>>PBI.ndb = impNDB let pf = impNDB>>NDB.pfQ.head while pf ne 0 do [ if (pf>>PF.predicate)(pbi) then [ queue = pf>>PF.queue; break ] pf = pf>>PF.link ] endcase ] case 4: // nop -- contains local host address impNDB>>NDB.localHost = (pbi>>ImpPBI.host & 3) lshift 6 + (pbi>>ImpPBI.imp & 77B) endcase ] endcase ] // ImpInputInterrupt (cont'd) default: CallSwat("[ImpInputInterrupt] Bad microcode status") ] Enqueue(queue, pbi) pbi = 0 impNDB>>ImpNDB.iError = newError ] unless impNDB>>ImpNDB.status.impNotReady % impNDB>>ImpNDB.status.hostNotReady do [ // Try to start up input pbi = Dequeue(pbiFreeQ) if pbi ne 0 then [ // Set up to read leader first icb>>ICB.inputPointer = lv impNDB>>ImpNDB.iLeader icb>>ICB.inputEnd = icb>>ICB.inputPointer+lenImpLeader icb>>ICB.inputPost = 0 StartIO(impStartInput) ] ] impNDB>>ImpNDB.iPBI = pbi ] //---------------------------------------------------------------------------- and ImpPupFilter(pbi) = //---------------------------------------------------------------------------- pbi>>ImpPBI.link eq linkPup & (pbi>>ImpPBI.pup.length+1) rshift 1 ule pbi>>PBI.packetLength //---------------------------------------------------------------------------- and FlushImpInput() be //---------------------------------------------------------------------------- // Call this only after having reset the interface [ if impNDB>>ImpNDB.iPBI ne 0 then [ Enqueue(pbiFreeQ, impNDB>>ImpNDB.iPBI) impNDB>>ImpNDB.iPBI = 0 ] ] //---------------------------------------------------------------------------- and ImpOutputInterrupt() be //---------------------------------------------------------------------------- [ let icb = impNDB>>ImpNDB.icb if impNDB>>ImpNDB.oActive then [ unless icb>>ICB.outputPost.microcode eq isDone do CallSwat("[ImpOutputInterrupt] Bad microcode status") if icb>>ICB.outputPointer eq lv impNDB>>ImpNDB.oLeader + lenImpLeader & not impNDB>>ImpNDB.sendingNop then [ // end of leader. Now send data and end of packet let pbi = impNDB>>ImpNDB.oPBI icb>>ICB.outputPointer = lv pbi>>ImpPBI.data icb>>ICB.outputEnd = icb>>ICB.outputPointer+pbi>>PBI.packetLength icb>>ICB.outputPost = 0 IssueControlCommand(icEndPacketOn) StartIO(impStartOutput) return ] // Dispose of completed output message UpdateStatus(icb>>ICB.outputPost) test impNDB>>ImpNDB.sendingNop ifso impNDB>>ImpNDB.sendingNop = false ifnot unless impNDB>>ImpNDB.oError do ImpOutputDispose() ] impNDB>>ImpNDB.oActive = false // If the Imp is down, flush all output if impNDB>>ImpNDB.status.impNotReady then [ FlushImpOutput(); return ] let leader = lv impNDB>>ImpNDB.oLeader Zero(leader, lenImpLeader) leader>>ImpLeader.format = 17B test impNDB>>ImpNDB.oError ifso [ // Error flop was set. Send a Nop impNDB>>ImpNDB.oError = false impNDB>>ImpNDB.sendingNop = true leader>>ImpLeader.messageType = 4 // Set the host address so that the right thing happens if the Imp // interface is looped back. leader>>ImpLeader.host = impNDB>>ImpNDB.localHost rshift 6 leader>>ImpLeader.imp = impNDB>>ImpNDB.localHost & 77B IssueControlCommand(icEndPacketOn) // packet consists of leader only ] ifnot [ unless SetupImpOutput() return let pbi = impNDB>>ImpNDB.oPBI leader>>ImpLeader.host = pbi>>ImpPBI.host leader>>ImpLeader.imp = pbi>>ImpPBI.imp leader>>ImpLeader.link = pbi>>ImpPBI.link IssueControlCommand(icEndPacketOff) // do not end packet after leader ] icb>>ICB.outputPointer = leader icb>>ICB.outputEnd = leader+lenImpLeader icb>>ICB.outputPost = 0 StartIO(impStartOutput) impNDB>>ImpNDB.oActive = true ] //---------------------------------------------------------------------------- and ImpOutputDispose() be //---------------------------------------------------------------------------- [ let pbi = impNDB>>ImpNDB.oPBI if pbi ne 0 then [ Enqueue(pbi>>PBI.queue, pbi) impNDB>>ImpNDB.oPBI = 0 ] ] //---------------------------------------------------------------------------- and SetupImpOutput() = valof //---------------------------------------------------------------------------- // Returns true iff ready to send a PBI. [ if impNDB>>ImpNDB.oPBI eq 0 then impNDB>>ImpNDB.oPBI = Dequeue(lv impNDB>>ImpNDB.oQ) resultis impNDB>>ImpNDB.oPBI ne 0 ] //---------------------------------------------------------------------------- and FlushImpOutput() be while SetupImpOutput() do ImpOutputDispose() //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- and UpdateStatus(status) be //---------------------------------------------------------------------------- // Update software status from hardware. [ impNDB>>ImpNDB.status = status if status<<ImpStatus.impWasDown then [ // Imp was down. Attempt to clear it and record software errors IssueControlCommand(icClearImpWasDown) impNDB>>ImpNDB.iError = true impNDB>>ImpNDB.oError = true ] ] //---------------------------------------------------------------------------- and ResetImpInterface(hostUp) be //---------------------------------------------------------------------------- // Resets the interface and flushes all buffers. Leaves the host up if hostUp is true, // down otherwise. [ let icb = impNDB>>ImpNDB.icb IssueControlCommand(icMasterReset) IssueControlCommand(icLoopBackOff) test hostUp ifso [ IssueControlCommand(icHostReadyOn) Dismiss(50) // wait for relay to close IssueControlCommand(icNoop) // just get updated status ] ifnot IssueControlCommand(icHostReadyOff) UpdateStatus(icb>>ICB.controlPost) FlushImpInput() FlushImpOutput() impNDB>>ImpNDB.oActive = false impNDB>>ImpNDB.iError = true impNDB>>ImpNDB.oError = true ] //---------------------------------------------------------------------------- and IssueControlCommand(command) be //---------------------------------------------------------------------------- // All control commands must be issued via this subroutine, which must // disable interrupts since the control portion of the ICB is shared // between input and output. [ let icb = impNDB>>ImpNDB.icb DisableInterrupts() icb>>ICB.controlPost = 0 icb>>ICB.control = command StartIO(impControlStatus, icb) EnableInterrupts() ] //---------------------------------------------------------------------------- and ImpProcess() be // once-per-second housekeeping //---------------------------------------------------------------------------- [ let icb = impNDB>>ImpNDB.icb IssueControlCommand(icNoop) UpdateStatus(icb>>ICB.controlPost) // If Imp has gone off, flap the interface and flush buffers. // This is to recover from lost interrupts and such. if impNDB>>ImpNDB.status.impNotReady then ResetImpInterface(true) // If Imp and Host are now up and there is no input buffer set up, // attempt to start input. if impNDB>>ImpNDB.iPBI eq 0 & impNDB>>ImpNDB.status.impNotReady eq 0 & impNDB>>ImpNDB.status.hostNotReady eq 0 & pbiFreeQ>>Q.head ne 0 then CauseInterrupt(icb>>ICB.inputChanMask) // let ImpInputInterrupt do it Dismiss(100) ] repeat //---------------------------------------------------------------------------- and ImpFinish(code) be //---------------------------------------------------------------------------- [ ResetImpInterface(false) @lvUserFinishProc = savedImpUFP savedImpUFP = -1 ]