// 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
]