//PupAlComProcInit.bcpl -  Alto SLA initialization for ComProc interface
//	Code can be thrown away after use
//	Companion to PupAlComProcb.bcpl and PupAlComProca.bcpl
// Copyright Xerox Corporation 1979, 1980

// Last modified March 9, 1980  6:21 PM by Taft

get "Pup0.decl"
get "PupAlComProc.decl"

external
[
//outgoing procedures
InitAltoComProc

//incoming procedures
EncapsulateCPPup; SendCPPacket; SendCPStats
CPPupFilter; CPRoutingFilter; StartCPR
CPInterrupt; CPProcess; CPFinishProc; CPSwatContextProc

InitializeContext; InitializeInterrupt; FindInterruptMask
Allocate; Dequeue; Enqueue; SetBlock; Zero; SetTimer; Dismiss; Noop

//outgoing statics
cpNDB; cpLT; savedCPFinishProc; cpMcLoaded

//incoming statics
ndbQ; pbiIQ; pbiFreeQ; lenPup
lvUserFinishProc
]

static [ cpNDB; cpLT; savedCPFinishProc; cpMcLoaded ]

//----------------------------------------------------------------------------
let InitAltoComProc(zone, ctxQ) be
//----------------------------------------------------------------------------
//zone is where to get storage from
//ctxQ is the context list on which to queue the interface process
[
unless cpMcLoaded return  //no microcode
let atLeastOneLIM = false
for line = 0 to maxLine do
   [
   @(cpBase + 4*line + cpControl0) = 1
   if @(cpBase + 4*line + cpControl0) eq 1 then
      [ atLeastOneLIM = true; break ]
   ]
structure Vers: [ eng bit 4; build bit 4; mc bit 8 ]
if (table [ #61014; #1401 ])()<<Vers.eng ne 3 return  //must be Alto II XM

savedCPFinishProc = @lvUserFinishProc
@lvUserFinishProc = CPFinishProc
CPSwatContextProc()	//doesn't do what you think...look at it

//build a Network Data Block for this network
cpNDB = Allocate(zone, lenCPNDB)
Zero(cpNDB, lenCPNDB)

//standard part of NDB
cpNDB>>NDB.encapsulatePup = EncapsulateCPPup
cpNDB>>NDB.level0Transmit = SendCPPacket
cpNDB>>NDB.level0Stats = SendCPStats
cpNDB>>NDB.destroy = Noop  // not implemented yet
cpNDB>>NDB.netType = netTypeSLA
cpNDB>>NDB.deviceNum = 0
cpNDB>>NDB.pupPF.predicate = CPPupFilter
cpNDB>>NDB.pupPF.queue = pbiIQ
Enqueue(lv cpNDB>>NDB.pfQ, lv cpNDB>>NDB.pupPF)

//InitAltoComProc (cont'd)

//ComProc line control blocks
for line = 0 to maxLine do
   [
   let lcb = lv cpNDB>>CPNDB.lcb↑line
   lcb>>LCB.line = line
   SetTimer(lv lcb>>LCB.rTimer, 0)
   ]

//internal routing table
SetBlock(lv cpNDB>>CPNDB.rt, -1, maxHost)
cpNDB>>CPNDB.rPF.predicate = CPRoutingFilter
cpNDB>>CPNDB.rPF.queue = lv cpNDB>>CPNDB.rIQ
Enqueue(lv cpNDB>>NDB.pfQ, lv cpNDB>>CPNDB.rPF)

Enqueue(ndbQ, cpNDB)

//set up interface process
Enqueue(ctxQ, InitializeContext(Allocate(zone, 150), 150, CPProcess))

//microcode Line Table
// the +2 is for the CRC table pointer and a blank to pad to an even address
cpLT = Allocate(zone, lenCPLT+2, false, true)
Zero(cpLT, lenCPLT+2)  //in particular all states = throwAway
cpLT = cpLT+2

//CRC table
let crcTable = Allocate(zone, 256)
cpLT!-1 = crcTable
for entry = 0 to 255 do
   [
   let crc = 0
   let value = entry
   for power = 0 to 7 do
      test (value & 1) eq 0
         ifso value = value rshift 1
         ifnot
            [
            crc = crc xor (120001B rshift (7 - power))
            value = (value rshift 1) xor 120001B
            ]
   crcTable!entry = crc
   ]

//Assign an interrupt channel
let intMask = InitializeInterrupt(Allocate(zone, 120), 120,
 FindInterruptMask(1), CPInterrupt)

//Enable the microcode
(table [ mcSetLineTab; #1401 ])(cpLT)

//InitAltoComProc (cont'd)

//Line Command Blocks
for line = 0 to maxLine do
   [
   let lcb = lv cpNDB>>CPNDB.lcb↑line

   //Discover whether a LIM is plugged in.
   //We can handle up to maxLine lines, but if there are fewer LIMs
   // we can avoid wasting a pbi under receivers that don't exist.
   @(cpBase + 4*line + cpControl0) = 1
   if @(cpBase + 4*line + cpControl0) ne 1 then  //not there
      [
      lcb>>LCB.state = slaLineMissing
      loop
      ]
   cpLT>>CPLT.cpLTE↑line.exists = true

   let cpLCB = Allocate(zone, lenCPLCB, false, true)
   Zero(cpLCB, lenCPLCB)
   lcb>>LCB.iCPLCB = cpLCB
   cpLCB>>CPLCB.intMask = intMask

   cpLCB = Allocate(zone, lenCPLCB, false, true)
   Zero(cpLCB, lenCPLCB)
   lcb>>LCB.oCPLCB = cpLCB
   cpLCB>>CPLCB.intMask = intMask

   //Set up the hardware for reception.
   @(cpBase + 4*line + cpControl0) = 60B   //DTR, RTS
// @(cpBase + 4*line + cpControl0) = 64B   //DTR, RTS, Wrap
   @(cpBase + 4*line + cpControl1) = 302B  //8 bits, clock 0, synchronous
   @(cpBase + 4*line + cpSync) = 26B       //SYN character
   
   //Put the microcode into BiSync state.
   let cpLTE = lv cpLT>>CPLT.cpLTE↑line
   cpLTE>>CPLTE.iState = stateBiSyncInput
   cpLTE>>CPLTE.oState = stateBiSyncOutput
   
   //Start the receiver.
   lcb>>LCB.iPBI = Dequeue(pbiFreeQ)
   if lcb>>LCB.iPBI ne 0 then StartCPR(lcb)
   (table [ mcChangeControlReg; #1401 ])(4*line, #100200)  //set rcvEnbl
   ]
]