//PupAlEIAInit.bcpl -  Alto initialization for EIA interface
//	Code can be thrown away after use
//	Companion to PupAlEIAb.bcpl and PupAlEIAa.bcpl
// Copyright Xerox Corporation 1979, 1980

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

get "Pup0.decl"
get "PupAlEIA.decl"

external
[
//outgoing procedures
InitAltoEIA

//incoming procedures
EncapsulateEIAPup; SendEIAPacket; SendEIAStats
EIAPupFilter; EIARoutingFilter; StartEIAR
EIAInterrupt; EIAProcess; EIAFinishProc; EIASwatContextProc

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

//outgoing statics
eiaNDB; eiaLT; savedEIAFinishProc; eiaMcLoaded

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

static [ eiaNDB; eiaLT; savedEIAFinishProc; eiaMcLoaded ]

//----------------------------------------------------------------------------
let InitAltoEIA(zone, ctxQ) be
//----------------------------------------------------------------------------
//zone is where to get storage from
//ctxQ is the context list on which to queue the interface process
[
unless eiaMcLoaded return  //no microcode
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
@eiaControl = swi; if @eiaStatus eq 0 return  //no EIA board
let foo = @eiaData  //re sync the interface

savedEIAFinishProc = @lvUserFinishProc
@lvUserFinishProc = EIAFinishProc

//build a Network Data Block for this network, whatever it is.
eiaNDB = Allocate(zone, lenEIANDB)
Zero(eiaNDB, lenEIANDB)

//standard part of NDB
eiaNDB>>NDB.encapsulatePup = EncapsulateEIAPup
eiaNDB>>NDB.level0Transmit = SendEIAPacket
eiaNDB>>NDB.level0Stats = SendEIAStats
eiaNDB>>NDB.destroy = Noop  // not implemented yet
eiaNDB>>NDB.netType = netTypeSLA
eiaNDB>>NDB.deviceNum = 0
eiaNDB>>NDB.pupPF.predicate = EIAPupFilter
eiaNDB>>NDB.pupPF.queue = pbiIQ
Enqueue(lv eiaNDB>>NDB.pfQ, lv eiaNDB>>NDB.pupPF)

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

//internal routing table
SetBlock(lv eiaNDB>>EIANDB.rt, -1, maxHost)
eiaNDB>>EIANDB.rPF.predicate = EIARoutingFilter
eiaNDB>>EIANDB.rPF.queue = lv eiaNDB>>EIANDB.rIQ
Enqueue(lv eiaNDB>>NDB.pfQ, lv eiaNDB>>EIANDB.rPF)

Enqueue(ndbQ, eiaNDB)

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

//EIA interface microcode Line Table
// the +2 is for the CRC table pointer and a blank to pad to an even address
eiaLT = Allocate(zone, lenEIALT+2, false, true)
Zero(eiaLT, lenEIALT+2)  //in particular all states = throwAway
eiaLT = eiaLT+2
EIASwatContextProc()	//must wait until eiaLT has been allocated

//CRC table
let crcTable = Allocate(zone, 256)
eiaLT!-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), EIAInterrupt)

//Enable the EIA microcode
(table [ mcEnableEIA; #1401 ])(eiaLT)

//Discover how many EIA boards are really plugged in.
//We can handle up to maxLine lines, but if there are fewer boards
// we can avoid wasting a pbi under receivers that don't exist.
for line = 0 to maxLine do @eiaControl = swi % line lshift 9
//Wait a wile for the interrupts to settle out...
Dismiss(1)	//waits for 40 ms.
//eiaLT>>EIALT.eiaLTE↑line.status will be non zero for boards that exist.

//EIA Line Command Blocks
for line = 0 to maxLine do
   [
   let lcb = lv eiaNDB>>EIANDB.lcb↑line

   if eiaLT>>EIALT.eiaLTE↑line.status eq 0 then
      [
      lcb>>LCB.state = slaLineMissing
      loop
      ]
   eiaLT>>EIALT.eiaLTE↑line.exists = true

   let eiaLCB = Allocate(zone, lenEIALCB, false, true)
   Zero(eiaLCB, lenEIALCB)
   lcb>>LCB.iEIALCB = eiaLCB
   eiaLCB>>EIALCB.intMask = intMask

   eiaLCB = Allocate(zone, lenEIALCB, false, true)
   Zero(eiaLCB, lenEIALCB)
   lcb>>LCB.oEIALCB = eiaLCB
   eiaLCB>>EIALCB.intMask = intMask

   //Set up the hardware for reception.
   let line000 = line lshift 9
   @eiaControl = initIF % 410B % line000  //talking to data set, fdx, (9600)
   @eiaControl = reset % #200 % line000   //clear rx & tx
   @eiaControl = initRT % #700 % line000  //8 bits, no parity, synchronous
   @eiaControl = initRG % 26B % line000   //syn character is 26b
   @eiaControl = initRG % 777B % line000  //fill character is 377b
   @eiaControl = reset % #100 % line000   //start rx
   
   //Put the EIA microcode into BiSync state.
   let eiaLTE = lv eiaLT>>EIALT.eiaLTE↑line
   eiaLTE>>EIALTE.iState = stateBiSyncInput
   eiaLTE>>EIALTE.oState = stateBiSyncOutput
   
   //Start the receiver.
   lcb>>LCB.iPBI = Dequeue(pbiFreeQ)
   if lcb>>LCB.iPBI ne 0 then StartEIAR(lcb)
   ]
]