// IfsSequinUtil.bcpl - Sequin Utilities
// Copyright Xerox Corporation 1979, 1980, 1981

// Last modified by November 25, 1981  2:29 PM by Taft

get ecOutputVecFull, ecOutOfSequence, ecVPBIBackChain
 from "IfsLeafErrors.decl";
get "IfsLeaf.decl"; get "IfsSequin.decl";

external
[
//outgoing procedures
FindLockedSequin; NextLockedSequin; SequinAllocate; SequinFromPBI;
SequinInput; SequinKickReaper; SequinPut; SequinStartOutput; SequinReadPBI;
SequinReleasePBI; SequinRetransmit; SequinUnlockPBI; LockSequin; UnlockSequin;

//incoming procedures
SysAllocate; Dequeue; Enqueue; IFSError; Max; MultEq; SetPupDPort; GetVPBI;
MoveBlock; ReadVPBI; ReleaseVPBI; ShortenVPBI; UnlockVPBI; SequenceCompare;

//incoming statics
scb; socLeaf;
]

//----------------------------------------------------------------------------
let SequinAllocate(sequin) = valof
//----------------------------------------------------------------------------
[
if sequin>>Sequin.retransmitting resultis 0;
if Max(1, sequin>>Sequin.allocated) -
 (sequin>>Sequin.allocateSequence - sequin>>Sequin.ackedState.current) le 0 %
  sequin>>Sequin.freeOutputSlot eq maxOutputVPBIs then resultis 0;
let vpbi = GetVPBI(lv scb>>SCB.outputAllocInfo);
if vpbi eq 0 resultis 0; vpbi>>VPBI.output = true;
resultis lv vpbi>>VPBI.pbi;
]

//----------------------------------------------------------------------------
and SequinPut(sequin, pbi) be
//----------------------------------------------------------------------------
[
let vpbi = pbi - pbiOffset; let vpbiID = vpbi>>VPBI.vpbiID;
let pbiID = lv vpbi>>VPBI.pbi.pup.id; vpbi>>VPBI.sequin = sequin;
ShortenVPBI(vpbi);    // This optimizes VPBI page packing.
let sequence = sequin>>Sequin.allocateSequence;  // 16-bit sequence number.
sequin>>Sequin.allocateSequence = sequence + 1;
sequin>>Sequin.ancientVPBIs = vpbi>>VPBI.ancient;
vpbi>>VPBI.backChain = sequin>>Sequin.lastPutVPBIID;
vpbi>>VPBI.sequence = sequence; sequin>>Sequin.lastPutVPBIID = vpbiID;
pbiID>>SequinID.sendSequence = sequence;
pbiID>>SequinID.control = sequin>>Sequin.control;
pbi>>PBI.queue = lv scb>>SCB.returnPBIQ;
pbi>>PBI.socket = socLeaf; SetPupDPort(pbi, lv sequin>>Sequin.port);
// Now put the vpbiID into the sequin output vector.
let freeSlot = sequin>>Sequin.freeOutputSlot;
if freeSlot eq maxOutputVPBIs then IFSError(ecOutputVecFull);
sequin>>Sequin.outputVec↑freeSlot = vpbiID;
sequin>>Sequin.freeOutputSlot = freeSlot + 1; UnlockVPBI(vpbi);
SequinStartOutput(sequin);
]

//----------------------------------------------------------------------------
and SequinStartOutput(sequin) be
//----------------------------------------------------------------------------
[
manifest linkOffset = offset Sequin.outputLink/16;
if sequin>>Sequin.outputLink eq -1 then
   [
   Enqueue(lv scb>>SCB.outputSequinQ, sequin + linkOffset);   
   LockSequin(sequin);
   ]
]

//----------------------------------------------------------------------------
and SequinInput(sequin, pbi) be
//----------------------------------------------------------------------------
[
if sequin>>Sequin.freeInputSlot eq maxInputVPBIs then return; //Drop on floor!
let vpbi = GetVPBI(lv scb>>SCB.inputAllocInfo);
vpbi>>VPBI.backChain = sequin>>Sequin.lastInputVPBIID;
vpbi>>VPBI.sequin = sequin;
sequin>>Sequin.lastInputVPBIID = vpbi>>VPBI.vpbiID;
vpbi>>VPBI.sequence = sequin>>Sequin.receiveSequence;
sequin>>Sequin.receiveSequence = sequin>>Sequin.receiveSequence + 1;
MoveBlock(lv vpbi>>VPBI.pbi, pbi, lenPBIOverhead +
 pbi>>PBI.pup.length rshift 1); ShortenVPBI(vpbi);
let freeSlot = sequin>>Sequin.freeInputSlot;
sequin>>Sequin.inputVec↑freeSlot = vpbi>>VPBI.vpbiID;
sequin>>Sequin.freeInputSlot = freeSlot + 1; UnlockVPBI(vpbi);
if sequin>>Sequin.inputLink eq -1 then
   [
   manifest linkOffset = offset Sequin.inputLink/16;
   Enqueue(lv scb>>SCB.inputSequinQ, sequin + linkOffset);   
   LockSequin(sequin);
   ]
]

//----------------------------------------------------------------------------
and SequinReadPBI(vpbiID, dirty, inCore, sequence; numargs na) = valof
//----------------------------------------------------------------------------
[
let vpbi = ReadVPBI(vpbiID,(dirty? dirtyPage,cleanPage) + (inCoreOp & inCore))
if vpbi eq 0 resultis 0;
unless na ls 4 do if sequence ne vpbi>>VPBI.sequence then
 IFSError(ecOutOfSequence);
resultis lv vpbi>>VPBI.pbi;
]

//----------------------------------------------------------------------------
and SequinReleasePBI(pbi) be
//----------------------------------------------------------------------------
[
let vpbi = pbi - pbiOffset;
let sequin = vpbi>>VPBI.sequin;
if sequin ne 0 then SequinKickReaper(sequin);
ReleaseVPBI(vpbi); UnlockVPBI(vpbi);
]

//----------------------------------------------------------------------------
and SequinUnlockPBI(pbi) be UnlockVPBI(pbi - pbiOffset)
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
and SequinFromPBI(pbi) = (pbi - pbiOffset)>>VPBI.sequin
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
and SequinRetransmit(sequin) = valof
//----------------------------------------------------------------------------
[
let sequence = sequin>>Sequin.sendSequence;
let vecLength = sequence - sequin>>Sequin.ackedState.current;
if vecLength eq 0 resultis 0;
let id = sequin>>Sequin.lastSendVPBIID;
let retransmitVec = SysAllocate(vecLength);
let vecIndex = 0;
   [ // Scan the backward chain here.
   sequence = sequence - 1;
   if vecIndex ge vecLength then IFSError(ecVPBIBackChain);   
   retransmitVec!vecIndex = id; vecIndex = vecIndex + 1;
   switchon SequenceCompare(sequence, sequin>>Sequin.ackedState.current) into
      [
      case duplicate:
         vecIndex = vecIndex + sequence - sequin>>Sequin.ackedState.current;
      case equal: break;
      case outOfRange: IFSError(ecVPBIBackChain);
      ]
   let vpbi = ReadVPBI(id, cleanPage, true);  //Read with no lock.
   if sequence ne vpbi>>VPBI.sequence then IFSError(ecOutOfSequence);
   id = vpbi>>VPBI.backChain;
   ] repeat
sequin>>Sequin.retransmitVec = retransmitVec;
sequin>>Sequin.retransmitIndex = vecIndex;
resultis retransmitVec;
]

//----------------------------------------------------------------------------
and FindLockedSequin(port) = valof
//----------------------------------------------------------------------------
[
let sequin = 0;
sequin = NextLockedSequin(sequin)
 repeatuntil sequin eq 0 % MultEq(lv sequin>>Sequin.port, port, lenPort)
resultis sequin;
]

//----------------------------------------------------------------------------
and NextLockedSequin(sequin) = valof
//----------------------------------------------------------------------------
[
test sequin eq 0
   ifso sequin = scb>>SCB.sequinQ.head;
   ifnot [ UnlockSequin(sequin); sequin = sequin>>Sequin.link; ]
if sequin ne 0 then LockSequin(sequin);
resultis sequin;
]

//----------------------------------------------------------------------------
and LockSequin(sequin) be sequin>>Sequin.lock = sequin>>Sequin.lock + 1
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
and UnlockSequin(sequin) be sequin>>Sequin.lock = sequin>>Sequin.lock - 1
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
and SequinKickReaper(sequin) be
//----------------------------------------------------------------------------
[
if sequin>>Sequin.ancientVPBIs then
 scb>>SCB.ancientScan = true;
scb>>SCB.normalScan = true;
]