// DLSConInit.bcpl -- Initialization for DLS control program
// Code may be discarded after use

// Last modified June 20, 1982  10:45 AM by Taft
// Last modified January 17, 1985  10:06 AM by Diebert

get "DLSDriver.decl"
get "Pup.decl"
get "DLSControl.decl"
get "DLSConfig.decl"
get "SysDefs.d"
get "AltoDefs.d"
get "DLSUtil.decl"

external
[
// Procedures defined herein
DLSBeforeJuntaInit; DLSAfterJuntaInit;

// Procedures defined elsewhere
DLSAfterJunta; LoadRam; ReadDLSConfig; InitializeDLS; CreateDCLM;
DLSTop; DLSBackground; AltoCommand; StatusBackground; DspPuts;
TelnetRendezvousServer; DLSSocketNotFound; SetDLSLineSpeed;
SetTimer; Enqueue;
InitPupLevel1; SetAllocation; OpenLevel1Socket; SocketNotFound;
Max; InitRingBuffer; InitializeContext; GetFixed; Ws; Wss; Wns;
InitializeZone; Allocate; Zero;
SysErr; SysErrT; MyFrame; CallersFrame; Idle; Block;
CallSwat; Junta; PutTemplate; InitGrapevine; LoginServer

// Statics defined elsewhere
dlsName; dlsRegistry; dlsInList; dlsOutList; dlsWizardList
versionText; endInit; sysZone; mainCtx; ctxTable; @lbTable; crlf;
RamImage; stDis; nPBI; iPBI; dPSIB;
rtpStackSize; strayPupQ; logstream; loginServerCB; dcb
]

static   [ dlsConfig ]
manifest [ linefeedChar = 10 ]


//----------------------------------------------------------------------------
let DLSBeforeJuntaInit() be
//----------------------------------------------------------------------------
// Called before the Junta is done, to do all operations requiring the disk.
[
endInit = @endCode


// Read the DLS configuration file
let fixedZn = vec size ZN/16
fixedZn>>ZN.Allocate = FixedAllocate
fixedZn>>ZN.Free = SysErr
dlsConfig = GetFixed(lenDLSConfig)
ReadDLSConfig(dlsConfig, fixedZn)
dlsName = dlsConfig>>DLSConfig.name
dlsRegistry = dlsConfig>>DLSConfig.thisRegistry
dlsInList = dlsConfig>>DLSConfig.thisInList
dlsOutList = dlsConfig>>DLSConfig.thisOutList
dlsWizardList = dlsConfig>>DLSConfig.thisWizardList

// Link up SysErr with the copy we load
SysErr = SysErrT

// Bye-bye OS
Junta(levBcpl, DLSAfterJunta)
]

//----------------------------------------------------------------------------
and DLSAfterJuntaInit() be
//----------------------------------------------------------------------------
[
// Make a zone out of the (up to) 32K of unused space immediately
// below the main DLSControl procedure's frame
let lenUnusedSpace = MyFrame()-600-@endCode

if lenUnusedSpace ls 0 then lenUnusedSpace = 77777B
sysZone = InitializeZone(@endCode, lenUnusedSpace)
@endCode = @endCode+lenUnusedSpace

dcb = Allocate(sysZone, lDCB, false, true)
Zero(dcb, lDCB)
dcb>>DCB.background = 1
@displayListHead  = dcb


// Initialize data structures for DLS driver and microcode
mainCtx = Allocate(sysZone, 2)  // Q header for context list
mainCtx!0 = 0  // Q initially empty
let lct = vec lenLCT
for line = 0 to numLines-1 do
   [
   lct>>LCT↑line.lineType = dlsConfig>>DLSConfig.lc↑line.lineType
   lct>>LCT↑line.otherLine = dlsConfig>>DLSConfig.lc↑line.otherLine
   ]
unless LoadRam(RamImage, true) eq 0 do
   CallSwat("Failed to load DLS microcode")
(table [ #61010; #1401 ])(nil, #20)  //jump into RAM emulator
InitializeDLS(sysZone, lct)

// Initialize dialler data structures, if any
let haveDialler = false
let haveLog = false
let signalVec = vec 7
for line = 0 to numLines-1 do
   [
   if dlsConfig>>DLSConfig.lc↑line.lineType eq ltLog then haveLog = true
   if dlsConfig>>DLSConfig.lc↑line.lineType eq ltDialler then
      [
      signalVec!6 = dlsConfig>>DLSConfig.lc↑line.data.diallerType
      signalVec!(dlsConfig>>DLSConfig.lc↑line.dialler.signalIndex) = line
      haveDialler = true
      ]
   ]

// DLSAfterJuntaInit() continued

unless haveLog do
   [ CallSwat("Configuration has no log specified."); finish ]
if haveDialler then
   [
   let dclm = CreateDCLM(sysZone, signalVec)
   for line = 0 to numLines-1 do
      if dlsConfig>>DLSConfig.lc↑line.dialOut then
         [
         let dlb = lbTable!line
         dlb>>DLB.dclm = dclm
         dlb>>DLB.modemType = dlsConfig>>DLSConfig.lc↑line.data.modemType
         dlb>>DLB.modemAddress = dlsConfig>>DLSConfig.lc↑line.data.modemAddress
         ]
   ]

Enqueue(mainCtx,
 InitializeContext(Allocate(sysZone, 150), 150, TelnetRendezvousServer))
SocketNotFound = DLSSocketNotFound
strayPupQ = Allocate(sysZone, 2); Zero(strayPupQ, 2)

// Create and initialize a context for each data line
ctxTable = Allocate(sysZone, numLines)  // Table of contexts
Zero(ctxTable, numLines)
let numDataLines = 0
for line = 0 to numLines-1 do
   [
   if (lbTable!line)>>LBH.lineType eq ltLog then logstream = lbTable!line
   if (lbTable!line)>>LBH.lineType gr ltData then // Don't build ctx for Log file.
      [
      let dlb = lbTable!line
      let ctx = Allocate(sysZone, lenCtxRegion)  // Build context
      Zero(ctx, lenCtxRegion)
      InitializeContext(ctx, lenCtxRegion, DLSTop, lenCTX)
      ctxTable!line = ctx
      ctx>>CTX.dlb = dlb
      if dlsConfig>>DLSConfig.lc↑line.data.constantBaud ne 0 then
         [
         ctx>>CTX.constantBaud = true
         SetDLSLineSpeed(dlb, dlsConfig>>DLSConfig.lc↑line.data.constantBaud)
         ]
      ctx>>CTX.terminalType = dlsConfig>>DLSConfig.lc↑line.data.terminalType
      ctx>>CTX.terminalLength = dlsConfig>>DLSConfig.lc↑line.data.terminalLength
      ctx>>CTX.terminalWidth = dlsConfig>>DLSConfig.lc↑line.data.terminalWidth
      ctx>>CTX.dialOutOnly = dlsConfig>>DLSConfig.lc↑line.data.dialOutOnly
      ctx>>CTX.host = dlsConfig>>DLSConfig.lc↑line.data.host
      dlb>>DLB.stopBits = dlsConfig>>DLSConfig.lc↑line.data.stopBits
      ctx>>CTX.noPad = dlsConfig>>DLSConfig.lc↑line.data.noPad
      ctx>>CTX.eightBit = dlsConfig>>DLSConfig.lc↑line.data.eightBit
      Enqueue(mainCtx, ctx)  // Put on main context list
      numDataLines = numDataLines+1
      ]
   ]

// Create contexts for any necessary background tasks
Enqueue(mainCtx, InitializeContext(Allocate(sysZone, 100), 100,
   DLSBackground))

loginServerCB = Allocate(sysZone, lenLSCB)
Zero(loginServerCB, lenLSCB)
Enqueue(mainCtx, InitializeContext(Allocate(sysZone, 400), 400,
   LoginServer))


// InitDLSControl (cont'd)

// Allocate 1 more line worth of PBIs if we have a dailler
if haveDialler then numDataLines = numDataLines + 1

// Determine how much storage is left in sysZone
let unused = nil
Allocate(sysZone, #77777, lv unused)

// Determine how much storage will be available after the
// initialization code is thrown away, and
// decide how much space can be devoted to packet buffers.
// We initially allocate only one socket allocation's worth of
// PBIs (=iPBI).  Funny rshifts are to avoid numbers that look negative.
unused = unused + (endInit-DLSBeforeJuntaInit) + (CallersFrame()-@endCode-100)
nPBI = ((unused - 1000 -  // Slop for breakage
   numDataLines*(rtpStackSize+1)) rshift 1) /  // Reserved for RTP contexts
   ((lenPBIOverhead+(pupOvBytes+lenPupContents)/2+1) rshift 1)

// Determine per-line PBI allocation by dividing the available PBIs equally
// among the lines.  For a large configuration, overcommit the PBIs by a
// factor of two.  For a very small configuration, limit the number of PBIs
// per line to avoid excess buffering.
iPBI = (nPBI-1)/numDataLines
if iPBI ls 6 then iPBI = 2*iPBI
if iPBI gr 15 then
   [ iPBI = 15; nPBI = iPBI*numDataLines+1 ]
if iPBI ls 4 then CallSwat("Not enough PBIs")

// Initialize Pup software packages and allocate initial PBIs
InitPupLevel1(sysZone, mainCtx, iPBI, lenPupContents, 20)
let fakeSoc = dPSIB-offset PupSoc.psib/16
SetAllocation(fakeSoc, iPBI, iPBI-2, iPBI-2)

Idle = Block
InitGrapevine(sysZone)
PutTemplate(logstream, "*n*l*n*l*n*l*n*l*n*l$s in operation.*n*l", versionText)
]


//----------------------------------------------------------------------------
and FixedAllocate(zone, nWords) = GetFixed(nWords)
//----------------------------------------------------------------------------