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