// AltIOImp.bcpl -- Alto Imp driver for Maxc2 // This version supports 96-bit leaders, NCP, Internet/TCP, Pup // Last modified December 13, 1982 9:35 AM get "AltIO.decl" get "Pup0.decl" get "AltIOImp.decl" external [ // Outgoing procedures ImpReset; EncapsulateImpPup; SendImpPacket ImpInputInterrupt; ImpOutputInterrupt; ImpProcess // Incoming procedures Enqueue; Dequeue; CauseInterrupt; DisableInterrupts; EnableInterrupts MemReadRelative; MemWriteRelative; MemReadBlock40A; MemWriteBlock40A MemReadBlock32A; MemWriteBlock32A; RMWBitAbsolute; SignalMaxc ConvertNBP; NBBlockTransfer; NBReadWord; NBWriteWord ImpConvTo32; ImpConvTo36; ImpConvFrom32; ImpConvFrom36 SetTimer; TimerHasExpired; Dismiss; Block CallSwat; Usc; StartIO; MoveBlock; Zero; Min // Outgoing statics impNDB; impMaxcBuf; impInputRequest; impOutputRequest; icb // Incoming statics pbiFreeQ; pbiIQ; lenPup ] static [ impNDB; impMaxcBuf; icb impInputRequest = false impOutputRequest = false lastStatus ] //---------------------------------------------------------------------------- let ImpReset() be //---------------------------------------------------------------------------- // Called when an I/O Reset is done [ ResetImpInterface(true) impInputRequest = false impOutputRequest = false // Set "power on" bit in status word if an interface seems to be installed. icb = impNDB>>ImpNDB.icb impNDB>>ImpNDB.powerOn = icb>>ICB.controlPost ne 0 lastStatus = -1 // force status update in Maxc ] //---------------------------------------------------------------------------- and ResetImpInterface(dropHostReady) be //---------------------------------------------------------------------------- // Shuts off the interface and flushes all buffers. [ IssueControlCommand(icMasterReset) if dropHostReady then IssueControlCommand(icHostReadyOff) IssueControlCommand(icLoopBackOff) UpdateStatus(impNDB>>ImpNDB.icb>>ICB.controlPost) FlushImpInput() FlushImpOutput() impNDB>>ImpNDB.oActive = false ] // Pup interface procedures //---------------------------------------------------------------------------- and 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.pupHost = pdh // just remember this for later ] //---------------------------------------------------------------------------- and SendImpPacket(pbi) be //---------------------------------------------------------------------------- [ test pbi>>ImpPBI.pupHost eq 0 % // For now, discard broadcast impNDB>>ImpNDB.hostNotReady ne 0 % // Discard if host not ready impNDB>>ImpNDB.impNotReady ne 0 // Discard if Imp not ready ifso Enqueue(pbi>>PBI.queue, pbi) ifnot [ Enqueue(lv impNDB>>ImpNDB.pupOQ, pbi) unless impNDB>>ImpNDB.oActive do CauseInterrupt(impNDB>>ImpNDB.icb>>ICB.outputChanMask) ] ] //---------------------------------------------------------------------------- and ImpProcess() be //---------------------------------------------------------------------------- // Process that transfers Imp messages to and from Maxc main memory. // Also watches status and maintains various timers. [ let maxcWord = vec 2 let maxcNBP = vec 2 let nbp = vec lenNBP let icb = impNDB>>ImpNDB.icb let statusTimer, maxcInputTimer = nil, nil SetTimer(lv statusTimer, 0) // The main loop of the process [ Block() if impNDB>>ImpNDB.pupInputIMBQ.head ne 0 then [ // Transfer received Pup to a normal Pup PBI and hand it to AltIOPup let imb = Dequeue(lv impNDB>>ImpNDB.pupInputIMBQ) // Accept only if source address will fit in a Pup host number if Usc(imb>>IMB.imp, 64) ls 0 & imb>>IMB.host ls 4 then [ let host = imb>>IMB.host lshift 6 + imb>>IMB.imp let pbi = Dequeue(pbiFreeQ) if pbi ne 0 then [ pbi>>PBI.packetLength = 2 + (imb>>IMB.pup.length+1) rshift 1 pbi>>ImpPBI.pupHost = host // "encapsulation" MoveBlock(lv pbi>>PBI.pup, lv imb>>IMB.pup, pbi>>PBI.packetLength-2) pbi>>PBI.ndb = impNDB Enqueue(pbiIQ, pbi) ] ] Enqueue(lv impNDB>>ImpNDB.freeInputIMBQ, imb) ] // ImpProcess (cont'd) if impInputRequest & impNDB>>ImpNDB.hostInputIMBQ.head ne 0 then [ // Ready to hand received Host-Host protocol message to Maxc impInputRequest = false // Read the NBP from XIMPIB and see if Maxc is really ready MemReadRelative(xIMPIB, maxcNBP) unless ConvertNBP(maxcNBP, nbp) loop SetTimer(lv maxcInputTimer, 1500) // Convert the Imp leader (96 bits) to 36-bit Maxc memory format let imb = Dequeue(lv impNDB>>ImpNDB.hostInputIMBQ) let format = MessageFormat(imb) let message = lv imb>>IMB.message impMaxcBuf!0 = message!0 // 0-15 impMaxcBuf!1 = message!1 // 16-31 impMaxcBuf!2 = message!2 // 32-35 impMaxcBuf!3 = message!2 lshift 4 + message!3 rshift 12 // 36-51 impMaxcBuf!4 = message!3 lshift 4 + message!4 rshift 12 // 52-67 impMaxcBuf!5 = message!4 lshift 4 // 68-71 impMaxcBuf!6 = message!4 lshift 8 + message!5 rshift 8 // 72-87 impMaxcBuf!7 = message!5 lshift 8 // 88-95 // Low-order 12 bits of last Maxc word get garbage // Convert Host-Host protocol leader if appropriate, and // compute parameters for body transfer. let nMaxcWordsLeader, nMaxcWordsData = nil, nil switchon format into [ case mfHost36: nMaxcWordsData = (16*imb>>IMB.length - size HostLeader +35)/36 docase -1 case mfHost32: nMaxcWordsData = (16*imb>>IMB.length - size HostLeader +31)/32 docase -1 case -1: // tail of cases mfHost36 and mfHost32 // Convert 40-bit Host-Host leader by discarding first 4 bits // and putting the other 36 in one Maxc word. impMaxcBuf!9 = message!6 lshift 4 + message!7 rshift 12 impMaxcBuf!10 = message!7 lshift 4 + message!8 rshift 12 impMaxcBuf!11 = message!8 lshift 4 nMaxcWordsLeader = 4 endcase case mfRaw32: nMaxcWordsData = (16*imb>>IMB.length - size ImpLeader +31)/32 nMaxcWordsLeader = 3 endcase default: CallSwat("[ImpProcess] Wrong-format message on hostInputIMBQ") ] if nMaxcWordsLeader+nMaxcWordsData gr maxMaxcWords then CallSwat("[ImpProcess] Incoming message too large") // ImpProcess (cont'd) // Imp input to Maxc (cont'd) // Transfer leader(s) to Maxc // Must put message length (Maxc words) in bits 0-15 of word 0 of // the Maxc buffer (without disturbing the rest of the word) // and the message itself starting at word 1. NBReadWord(nbp, 0, maxcWord) maxcWord!0 = nMaxcWordsLeader+nMaxcWordsData NBWriteWord(nbp, 0, maxcWord) NBBlockTransfer(MemWriteBlock40A, nbp, 1, impMaxcBuf, nMaxcWordsLeader, 3) // Convert body to appropriate Maxc memory format (if necessary) // and transfer it to Maxc. if nMaxcWordsData gr 0 then switchon format into [ case mfHost36: ImpConvTo36(impMaxcBuf, message+8, nMaxcWordsData) NBBlockTransfer(MemWriteBlock40A, nbp, 5, impMaxcBuf, nMaxcWordsData, 3) endcase case mfHost32: ImpConvTo32(impMaxcBuf, message+8, nMaxcWordsData) NBBlockTransfer(MemWriteBlock40A, nbp, 5, impMaxcBuf, nMaxcWordsData, 3) endcase case mfRaw32: NBBlockTransfer(MemWriteBlock32A, nbp, 4, message+6, nMaxcWordsData, 2) endcase ] // Don't need the IMB any more. Free it and restart input if // it was stopped due to lack of buffers. Enqueue(lv impNDB>>ImpNDB.freeInputIMBQ, imb) if impNDB>>ImpNDB.inputIMB eq 0 then CauseInterrupt(icb>>ICB.inputChanMask) // Signal Maxc that we have given it an Imp message MemWriteRelative(xIMPIB, maxcNBP) //set use flag RMWBitAbsolute(aNVMAX, nmIMPIDN) SignalMaxc() ] // ImpProcess (cont'd) if impOutputRequest & impNDB>>ImpNDB.freeOutputIMBQ.head ne 0 then [ // Ready to get Host-Host protocol message from Maxc and send it impOutputRequest = false // Read the NBP from XIMPOB and see if Maxc is really ready MemReadRelative(xIMPOB, maxcNBP) unless ConvertNBP(maxcNBP, nbp) loop // If host is now down, raise it if impNDB>>ImpNDB.hostNotReady then [ IssueControlCommand(icHostReadyOn) Dismiss(50) IssueControlCommand(icNoop) // just get updated status UpdateStatus(icb>>ICB.controlPost) ] // Copy the overhead word and the leader(s) from Maxc memory. // Copy enough words to encompass both Imp and Host-Host leaders, // though the latter may not actually be needed. // Word 0 has the message length (Maxc words) in bits 0-15. // The message itself starts in word 1. NBReadWord(nbp, 0, maxcWord) let nMaxcWords = maxcWord!0 if Usc(nMaxcWords, maxMaxcWords) gr 0 then CallSwat("[ImpProcess] Maxc Imp message too large") NBBlockTransfer(MemReadBlock40A, nbp, 1, impMaxcBuf, Min(nMaxcWords, 4), 3) // Convert the Imp leader (96 bits) from 36-bit Maxc memory format. let imb = Dequeue(lv impNDB>>ImpNDB.freeOutputIMBQ) let message = lv imb>>IMB.message message!0 = impMaxcBuf!0 // 0-15 message!1 = impMaxcBuf!1 // 16-31 message!2 = (impMaxcBuf!2 & #170000) + impMaxcBuf!3 rshift 4 // 32-47 message!3 = impMaxcBuf!3 lshift 12 + impMaxcBuf!4 rshift 4 // 48-63 message!4 = impMaxcBuf!4 lshift 12 + (impMaxcBuf!5 & #170000) rshift 4 + impMaxcBuf!6 rshift 8 // 64-79 message!5 = impMaxcBuf!6 lshift 8 + impMaxcBuf!7 rshift 8 // 80-95 // Discard 12 garbage bits of Maxc data // Convert the 36-bit Maxc host leader to a 40-bit Host-Host leader // by prefixing 4 zero bits. Do this whether or not it's // appropriate to do so, because we don't yet know whether or not // MessageFormat will need to look at the byte size. message!6 = impMaxcBuf!9 rshift 4 message!7 = impMaxcBuf!9 lshift 12 + impMaxcBuf!10 rshift 4 message!8 = impMaxcBuf!10 lshift 12 + impMaxcBuf!11 rshift 4 // ImpProcess (cont'd) // Imp output from Maxc (cont'd) // Compute parameters for body transfer let format = MessageFormat(imb) let nAltoWords, nMaxcWordsData = nil, nil switchon format into [ case mfHost36: nMaxcWordsData = nMaxcWords-4 nAltoWords = (size HostLeader + 36*nMaxcWordsData +15)/16 endcase case mfHost32: nMaxcWordsData = nMaxcWords-4 nAltoWords = (size HostLeader + 32*nMaxcWordsData +15)/16 endcase case mfRaw32: case mfPup: nMaxcWordsData = nMaxcWords-3 nAltoWords = (size ImpLeader + 32*nMaxcWordsData +15)/16 endcase ] if nAltoWords gr maxImpMessWords then // Consistency check CallSwat("[ImpProcess] Outgoing message too large") // Transfer body from Maxc and // convert it from the appropriate Maxc memory format (if necessary) if nMaxcWordsData gr 0 then switchon MessageFormat(imb) into [ case mfHost36: NBBlockTransfer(MemReadBlock40A, nbp, 5, impMaxcBuf, nMaxcWordsData, 3) ImpConvFrom36(message+8, impMaxcBuf, nMaxcWordsData+1) endcase case mfHost32: NBBlockTransfer(MemReadBlock40A, nbp, 5, impMaxcBuf, nMaxcWordsData, 3) ImpConvFrom32(message+8, impMaxcBuf, nMaxcWordsData+1) endcase case mfRaw32: case mfPup: // Someone trying to send a Pup thru IMPDV?? NBBlockTransfer(MemReadBlock32A, nbp, 4, message+6, nMaxcWordsData, 2) endcase ] // Queue message for transmission to Imp imb>>IMB.length = nAltoWords Enqueue(lv impNDB>>ImpNDB.hostOutputIMBQ, imb) unless impNDB>>ImpNDB.oActive do CauseInterrupt(impNDB>>ImpNDB.icb>>ICB.outputChanMask) // Signal Maxc that we have taken the Imp message MemWriteRelative(xIMPOB, maxcNBP) //set use flag RMWBitAbsolute(aNVMAX, nmIMPODN) SignalMaxc() ] // ImpProcess (cont'd) if TimerHasExpired(lv statusTimer) then [ // Do once-per-second housekeeping SetTimer(lv statusTimer, 100) IssueControlCommand(icNoop) // Noop, just to get updated status UpdateStatus(icb>>ICB.controlPost) // If Maxc fails to accept an input Host-Host protocol packet // within 15 seconds, drop the Host ready line and flush all buffers. test impNDB>>ImpNDB.hostInputIMBQ.head eq 0 ifso SetTimer(lv maxcInputTimer, 1500) ifnot if TimerHasExpired(lv maxcInputTimer) then ResetImpInterface(true) // If status has changed, update status word in Maxc memory if impNDB>>ImpNDB.status ne lastStatus then [ lastStatus = impNDB>>ImpNDB.status maxcWord!0, maxcWord!1, maxcWord!2 = 0, lastStatus, 0 MemWriteRelative(xIMPSI, maxcWord) // If Imp has gone off, flap the interface and flush buffers. // This is to recover from lost interrupts and such. if impNDB>>ImpNDB.impNotReady then ResetImpInterface(false) ] // If Imp and Host are now up and there is no input buffer set up, // attempt to start input. if impNDB>>ImpNDB.impNotReady eq 0 & impNDB>>ImpNDB.hostNotReady eq 0 & impNDB>>ImpNDB.inputIMB eq 0 & impNDB>>ImpNDB.freeInputIMBQ.head ne 0 then CauseInterrupt(icb>>ICB.inputChanMask) ] ] repeat ] //---------------------------------------------------------------------------- and MessageFormat(imb) = valof //---------------------------------------------------------------------------- // Returns the appropriate message format (mfHost36, mfHost32, mfPup) for // supplied Imp Message Buffer. [ let type = imb>>IMB.messageType // Link number may or may not be used for addressing the message, // depending on message type manifest [ y = true; n = false ] let linkAddressed = type gr 9? true, (table [ y; n; n; n; n; y; n; y; y; y ])!type // All non-link-addressed messages are handled as 36-bit Host-Host unless linkAddressed resultis mfHost36 // Link-addressed messages on the Pup link are given to the Pup handler. // Note that this does not necessarily mean that they are Pups -- further // discrimination is required. let link = imb>>IMB.link if link eq linkPup resultis mfPup // Not on the Pup link. // All irregular messages, regular messages on non-NCP links, and // regular messages to/from fake hosts are handled as raw 32-bit messages. if type ne 0 % link gr maxLinkNCP % imb>>IMB.host ge firstFakeHost resultis mfRaw32 // Regular messages on Host-Host protocol links and with byte size 36 // are handled as 36-bit Host-Host, all others as 32-bit Host-Host resultis imb>>IMB.byteSize eq 36? mfHost36, mfHost32 ] //---------------------------------------------------------------------------- and ImpInputInterrupt() be //---------------------------------------------------------------------------- [ let icb = impNDB>>ImpNDB.icb let imb = impNDB>>ImpNDB.inputIMB if imb ne 0 then [ // Dispose of incoming message let queue = lv impNDB>>ImpNDB.freeInputIMBQ UpdateStatus(icb>>ICB.inputPost) switchon icb>>ICB.inputPost.microcode into [ case isDone: [ // Normal completion if impNDB>>ImpNDB.iError then // Clear error, discard this message [ impNDB>>ImpNDB.iError = false; endcase ] if imb>>IMB.format ne 17B then endcase // Discard if not new format imb>>IMB.length = icb>>ICB.inputPointer - lv imb>>IMB.message test MessageFormat(imb) eq mfPup ifso // Pups must also be type 0 (regular) if imb>>IMB.messageType eq 0 & imb>>IMB.length ge (size ImpLeader/16)+pupOvWords & imb>>IMB.pup.length le imb>>IMB.length lshift 1 & imb>>IMB.pup.length le lenPup lshift 1 then queue = lv impNDB>>ImpNDB.pupInputIMBQ ifnot // Host-host or irregular queue = lv impNDB>>ImpNDB.hostInputIMBQ endcase ] case isInputOverflow: [ // Input overflow, cause remainder of message to be discarded impNDB>>ImpNDB.iError = true endcase ] default: CallSwat("[ImpInputInterrupt] Improper microcode status") ] Enqueue(queue, imb) imb = 0 ] unless impNDB>>ImpNDB.impNotReady % impNDB>>ImpNDB.hostNotReady do [ // Try to start up input imb = Dequeue(lv impNDB>>ImpNDB.freeInputIMBQ) if imb ne 0 then [ icb>>ICB.inputPointer = lv imb>>IMB.message icb>>ICB.inputEnd = imb+lenIMB icb>>ICB.inputPost = 0 StartIO(impStartInput) ] ] impNDB>>ImpNDB.inputIMB = imb ] //---------------------------------------------------------------------------- and FlushImpInput() be //---------------------------------------------------------------------------- [ [ // repeat let imb = Dequeue(lv impNDB>>ImpNDB.hostInputIMBQ) if imb eq 0 break Enqueue(lv impNDB>>ImpNDB.freeInputIMBQ, imb) ] repeat if impNDB>>ImpNDB.inputIMB ne 0 then [ Enqueue(lv impNDB>>ImpNDB.freeInputIMBQ, impNDB>>ImpNDB.inputIMB) impNDB>>ImpNDB.inputIMB = 0 ] ] //---------------------------------------------------------------------------- and ImpOutputInterrupt() be //---------------------------------------------------------------------------- [ let icb = impNDB>>ImpNDB.icb if impNDB>>ImpNDB.oActive then [ // Dispose of completed output message unless icb>>ICB.outputPost.microcode eq isDone do CallSwat("[ImpOutputInterrupt] Improper microcode status") 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.impNotReady then [ FlushImpOutput(); return ] test impNDB>>ImpNDB.oError ifso [ // Error flop was set. Send a Nop impNDB>>ImpNDB.oError = false impNDB>>ImpNDB.sendingNop = true icb>>ICB.outputPointer = table [ // Nop message 17B lshift 8; 4; 0; 0; 0; 0 ] icb>>ICB.outputEnd = icb>>ICB.outputPointer+6 ] ifnot test SetupImpOutput() ifnot return ifso // IMB pending [ let imb = impNDB>>ImpNDB.outputIMB icb>>ICB.outputPointer = lv imb>>IMB.message icb>>ICB.outputEnd = icb>>ICB.outputPointer+imb>>IMB.length ] StartIO(impStartOutput) impNDB>>ImpNDB.oActive = true ] //---------------------------------------------------------------------------- and ImpOutputDispose() be //---------------------------------------------------------------------------- [ if impNDB>>ImpNDB.outputIMB ne 0 then [ Enqueue(lv impNDB>>ImpNDB.freeOutputIMBQ, impNDB>>ImpNDB.outputIMB) impNDB>>ImpNDB.outputIMB = 0 ] ] //---------------------------------------------------------------------------- and SetupImpOutput() = valof //---------------------------------------------------------------------------- // Returns true iff ready to send an IMB. [ if impNDB>>ImpNDB.outputIMB eq 0 then impNDB>>ImpNDB.outputIMB = Dequeue(lv impNDB>>ImpNDB.hostOutputIMBQ) if impNDB>>ImpNDB.outputIMB eq 0 & impNDB>>ImpNDB.pupOQ.head ne 0 then [ let pbi = Dequeue(lv impNDB>>ImpNDB.pupOQ) let imb = Dequeue(lv impNDB>>ImpNDB.freeOutputIMBQ) if imb eq 0 then CallSwat("[SetupImpOutput] Output idle but freeOutputIMBQ empty") MoveBlock(lv imb>>IMB.pup, lv pbi>>PBI.pup, (pbi>>PBI.pup.length +1) rshift 1) Zero(lv imb>>IMB.message, size ImpLeader/16) imb>>IMB.format = 17B imb>>IMB.host = pbi>>ImpPBI.pupHost rshift 6 imb>>IMB.imp = pbi>>ImpPBI.pupHost & 77B imb>>IMB.link = linkPup imb>>IMB.length = (size ImpLeader/16)+(pbi>>PBI.pup.length +1) rshift 1 impNDB>>ImpNDB.outputIMB = imb Enqueue(pbi>>PBI.queue, pbi) ] resultis impNDB>>ImpNDB.outputIMB ne 0 ] //---------------------------------------------------------------------------- and FlushImpOutput() be while SetupImpOutput() do ImpOutputDispose() //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- and UpdateStatus(status) be //---------------------------------------------------------------------------- // Update software status from hardware. [ impNDB>>ImpNDB.impNotReady = status<<ImpStatus.impNotReady impNDB>>ImpNDB.hostNotReady = status<<ImpStatus.hostNotReady 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 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() ]