//AltIOPup.bcpl -- AltIO linkage to Pup package
// Last modified March 14, 1981 3:39 PM
get "altio.decl"
get "pup0.decl"
get "pupaleth.decl"
external
[
//outgoing procedures
PupReset; NBReadWord; NBWriteWord; NBBlockTransfer; ConvertNBP
//incoming procedures
InitializeContext; Block
Enqueue; Dequeue; Unqueue; Min
MemReadRelative; MemWriteRelative; RMWBitAbsolute; SignalMaxc
MemReadBlock32A; MemWriteBlock32A; DisplaceMaxcAdr
MoveBlock; CallSwat
//outgoing statics
pupCtx; lenPupCtx; pupInputRequest; pupOutputRequest
pbiIQ; pbiFreeQ; lenPup; lenPBI; ndbQ; etherNDB
//incoming statics
ctxQ; impNDB
]
static
[
pupInputRequest; pupOutputRequest
pbiIQ; pbiFreeQ; lenPup; lenPBI; ndbQ; etherNDB
pupCtx
lenPupCtx = 150
]
// ---------------------------------------------------------------------------
let PupReset() be
// ---------------------------------------------------------------------------
// Called to reinitialize the Pup context when an i/o reset is done
[
Unqueue(ctxQ, pupCtx) //no-op if this is the first time
Enqueue(ctxQ, InitializeContext(pupCtx, lenPupCtx, PupProcess))
pupInputRequest = false
pupOutputRequest = false
]
// ---------------------------------------------------------------------------
and PupProcess() be
// ---------------------------------------------------------------------------
// Process that handles Pup input and output for Maxc
[
until pupInputRequest do
[ //after i/o reset and before Maxc first requests input,
//just discard all packets that are received
while pbiIQ!0 ne 0 do Enqueue(pbiFreeQ, Dequeue(pbiIQ))
Block()
]
let maxcNBP = vec 2
let nbp = vec lenNBP
let pbphys = vec 2
// PupProcess (cont'd)
// The main loop of the process
[
Block()
if pupInputRequest & pbiIQ!0 ne 0 then
[ // Ready to hand received Pup to Maxc
pupInputRequest = false
// Read the NBP from XPUPIB and see if Maxc is really ready
MemReadRelative(xPUPIB, maxcNBP)
unless ConvertNBP(maxcNBP, nbp) loop
// Get the Pup and compute PBPHYS word.
// This is conceptually different for Ethernet and Arpanet Pups,
// but it just so happens that the length of the encapsulation and
// the position of the source host address are the same for both! (yuk)
let pbi = Dequeue(pbiIQ)
pbphys>>PBPHYS.words = (pbi>>PBI.pup.length+3) rshift 2
pbphys>>PBPHYS.net = pbi>>PBI.ndb>>NDB.localNet
pbphys>>PBPHYS.host = pbi>>EtherPBI.src
// Copy the Pup and the PBPHYS word into the Maxc buffer
NBWriteBlock32(nbp, xPBHEAD, lv pbi>>PBI.pup, pbphys>>PBPHYS.words)
NBWriteWord(nbp, xPBPHYS, pbphys)
Enqueue(pbiFreeQ, pbi)
// Signal Maxc that we have given it a Pup
MemWriteRelative(xPUPIB, maxcNBP) //set use flag
RMWBitAbsolute(aNVMAX, nmPUPIDN)
SignalMaxc()
]
if pupOutputRequest & pbiFreeQ!0 ne 0 then
[ // Ready to get Pup from Maxc and send it
pupOutputRequest = false
// Read the NBP from XPUPOB and see if Maxc is really ready
MemReadRelative(xPUPOB, maxcNBP)
unless ConvertNBP(maxcNBP, nbp) loop
// Copy the PBPHYS word and the Pup from the Maxc buffer
let pbi = Dequeue(pbiFreeQ)
if pbi eq 0 then [ pupOutputRequest = true; loop ]
NBReadWord(nbp, xPBPHYS, pbphys)
if pbphys>>PBPHYS.words lshift 1 gr lenPup then
CallSwat("Maxc Pup too large")
NBReadBlock32(nbp, xPBHEAD, lv pbi>>PBI.pup, pbphys>>PBPHYS.words)
// Queue packet for transmission on appropriate network.
// Note that the Ether and Arpa net numbers are wired in. (yuk)
let ndb = selecton pbphys>>PBPHYS.net into
[
case netEther: etherNDB
case netArpa: impNDB
default: CallSwat("Maxc sent Pup to unknown net")
]
pbi>>PBI.ndb = ndb
(ndb>>NDB.encapsulatePup)(pbi, pbphys>>PBPHYS.host)
pbi>>PBI.queue = pbiFreeQ
(ndb>>NDB.level0Transmit)(pbi)
// Signal Maxc that we have taken the Pup
MemWriteRelative(xPUPOB, maxcNBP) //set use flag
RMWBitAbsolute(aNVMAX, nmPUPODN)
SignalMaxc()
]
] repeat
]
// Flavors of Maxc memory operations that perform the necessary
// remap when a page boundary is crossed.
// ---------------------------------------------------------------------------
and NBReadWord(nbp, disp, maxcData) =
// ---------------------------------------------------------------------------
// Performs a one-word read whose Maxc address is
// disp relative to the address denoted by nbp, and whose
// Alto address is maxcData.
disp ls nbp>>NBP.count1?
MemReadRelative(disp, maxcData, lv nbp>>NBP.adr1),
MemReadRelative(disp-nbp>>NBP.count1, maxcData, lv nbp>>NBP.adr2)
// ---------------------------------------------------------------------------
and NBWriteWord(nbp, disp, maxcData) =
// ---------------------------------------------------------------------------
// Performs a one-word write whose Maxc address is
// disp relative to the address denoted by nbp, and whose
// Alto address is maxcData.
disp ls nbp>>NBP.count1?
MemWriteRelative(disp, maxcData, lv nbp>>NBP.adr1),
MemWriteRelative(disp-nbp>>NBP.count1, maxcData, lv nbp>>NBP.adr2)
// ---------------------------------------------------------------------------
and NBReadBlock32(nbp, disp, maxcData, count) =
// ---------------------------------------------------------------------------
NBBlockTransfer(MemReadBlock32A, nbp, disp, maxcData, count, 2)
// ---------------------------------------------------------------------------
and NBWriteBlock32(nbp, disp, maxcData, count) =
// ---------------------------------------------------------------------------
NBBlockTransfer(MemWriteBlock32A, nbp, disp, maxcData, count, 2)
// ---------------------------------------------------------------------------
and NBBlockTransfer(Operation, nbp, disp, maxcData, count, inc) = valof
// ---------------------------------------------------------------------------
// Performs the specified Maxc block operation Op. The Maxc address
// of the start of the block is disp relative to nbp, and the
// Alto address of the block is maxcData. count is the number of
// Maxc words to transfer and inc is the Alto address increment for
// each Maxc word.
[
let maxcAdr = vec 1
test disp ls nbp>>NBP.count1
ifso DisplaceMaxcAdr(disp, maxcAdr, lv nbp>>NBP.adr1)
ifnot DisplaceMaxcAdr(disp-nbp>>NBP.count1, maxcAdr, lv nbp>>NBP.adr2)
let code = nil
while count ne 0 do
[
let remWords1 = nbp>>NBP.count1-disp
let c = Min(count, maxBlt)
if remWords1 gr 0 then c = Min(c, remWords1)
if remWords1 eq 0 then MoveBlock(maxcAdr, lv nbp>>NBP.adr2, 2)
code = Operation(maxcAdr, maxcData, c)
if code ne 0 then break
maxcData = maxcData+c*inc
disp = disp+c
count = count-c
// If we stopped because of the maxBlt limit, give other contexts
// a chance to run.
if c eq maxBlt then Block()
]
resultis code
]
// ---------------------------------------------------------------------------
and ConvertNBP(maxcWord, nbp) = valof
// ---------------------------------------------------------------------------
// Converts a Net Buffer Pointer read from Maxc in maxcWord
// into an NBP structure. The Net Buffer Pointer read from Maxc
// is in the following form (see Maxc document 11):
// B0 use flag
// B1-2 zero
// B3-14 second physical page number, if block crosses
// page boundary; zero otherwise
// B15-26 first physical page number
// B27-35 location in page of start of block
// The use flag (B0) of the Maxc word is set to one in preparation
// for writing it back to Maxc as a completion signal.
// Returns true if the buffer is ready for AltIO to act on it,
// i.e., the use flag is zero and the whole word is nonzero.
[
if maxcWord!1 eq 0 % (maxcWord!0 & #100000) ne 0 resultis false
nbp>>NBP.adr1↑0 = maxcWord!1
nbp>>NBP.adr1↑1 = maxcWord!2
nbp>>NBP.count1 = #1000 - ((maxcWord!1 lshift 4 + maxcWord!2 rshift 12)& #777)
nbp>>NBP.adr2↑0 = (maxcWord!0 & #17776) lshift 4
nbp>>NBP.adr2↑1 = 0
maxcWord!0 = maxcWord!0 % #100000
resultis true
]