// DLSDriverInit.bcpl -- Alto DLS Driver initiailization
// This code may be thrown away after use

// Last modified April 25, 1982  4:49 PM by Taft
// Last modified October 20, 1983  1:31 PM by Diebert

get "DLSDriver.decl"

external
[
// outgoing procedures
InitializeDLS; CreateDCLM

// incoming procedures -- DLSDriver
DLSInput7; DLSOutput7; DLSResetInput; DLSResetOutput; DLSOutputTI
UpdateCarrierOn; SetDLSLineSpeed
TurnOnDLS; TurnOffDLS; DLSInputInterrupt; DLSOutputInterrupt; DLSErrorInterrupt;
DLSUserFinishProc; DLSSwatContextProc; DLSInputEmpty; DLSOutputFull

// incoming procedures -- OS and packages
InitRingBuffer; InitializeInterrupt; SetBlock; Zero; Dvec;
Allocate; CallSwat; SysErr; Noop

// outgoing statics
lbTable; dlsInputOverflows; dlsErrorInterrupts; dlsOnVector;
dlsSavedUserFinish; dlsSavedSwatContext;
baseIGCB; inputRBCB; outputRBCB; parityTable

// incoming statics
lvUserFinishProc; lvSwatContextProc; 
]

static
[
@baseIGCB		// Base of input group control blocks
iLCBTable		// Table of pointers to input LCBs
oLCBTable		// Table of pointers to output LCBs
@inputRBCB		// Input ring buffer control block
@outputRBCB		// Output ring buffer control block
inputRB			// Input ring buffer
outputRB		// Output ring buffer
dlsTimerQueue		// Timer queue table
dlsOnVector = 0		// Argument for DLSON instruction
@lbTable = 0		// Table of pointers to line blocks
dlsInputOverflows = 0	// Line input buffer overflow count
dlsErrorInterrupts = 0	// DLS error interrupt count
dlsSavedUserFinish	// Places to save old contents
dlsSavedSwatContext	//   of special os dispatches
parityTable		// Table of Ascii chars with even parity
]

// ---------------------------------------------------------------------------
let InitializeDLS(zone, lct) be
// ---------------------------------------------------------------------------
// zone is where to get space from.
// lct is a Line Configuration Table of length numLines.
[
TurnOffDLS()  // Make sure DLS is off
SetBlock(dlsOutBase, 1, numLines)  // Set output lines marking
let firstTime = dlsOnVector eq 0  // True only on first call
if firstTime then  // Allocate storage only once
   [
   // Storage that need not be aligned in any special way
   iLCBTable = Allocate(zone, numLines)  // Input LCB table
   oLCBTable = Allocate(zone, numLines)  // Output LCB table
   lbTable = Allocate(zone, numLines)  // Line data block table
   dlsTimerQueue = Allocate(zone, 256)  // Timer queue for microcode
   inputRB = Allocate(zone, lenDLSIRB)  // Input ring buffer
   outputRB = Allocate(zone, lenDLSORB)  // Output ring buffer

   // IGCBs must start on #20-word boundary
   baseIGCB = (Allocate(zone, numLines+#17)+#17)&#177760

   // DLBs must be aligned so that the ILCBs and OLCBs
   // contained within them are on even word boundaries.
   // CLBs need not be aligned.
   let unusedLB = Allocate(zone, lenLBH)
   for line = 0 to numLines-1 do
      [
      let lb = nil
      switchon lct>>LCT↑line.lineType into
         [
         case ltControl: case ltDialler:
            [
            lb = Allocate(zone, lenCLB)
            endcase
            ]
         case ltUnused:
            [
            lb = unusedLB
            endcase
            ]
         default:
            [  // Data line
            lb = Allocate(zone, lenDLB+1)
            let lcb = lv lb>>DLB.iLCB.link
            if (lcb&1) ne 0 then lb = lb+1
            Zero(lb, lenDLB)
            iLCBTable!line = lv lb>>DLB.iLCB.line
            oLCBTable!line = lv lb>>DLB.oLCB.line
            endcase
            ]
         ]
      lbTable!line = lb
      lb>>LBH.line = line
      lb>>LBH.lineType = lct>>LCT↑line.lineType
      lb>>LBH.otherLine = lct>>LCT↑line.otherLine
      ]

   // RBCBs and DLSOnVector must be aligned on even words
   // Nestle them into the space wasted in the IGCBs
   inputRBCB = baseIGCB+#10+1  // +1 since RBCB pointers are to
   outputRBCB = baseIGCB+#14+1  //  second word of block
   dlsOnVector = baseIGCB+#30
   ]

// InitializeDLS (cont'd)

Zero(dlsTimerQueue, 256)  // Initialize the timer queue

// Initialize the microcode ring buffers
inputRBCB!-1 = inputRB  // Reset input pointer
inputRBCB!1 = inputRB  // Reset output pointer
inputRBCB!0 = inputRB+lenDLSIRB-1  // Set limit pointer
@inputRBCB!0 = inputRB  // Make last word point to first
outputRBCB!-1 = outputRB  // Reset input pointer
outputRBCB!1 = outputRB  // Reset output pointer
outputRBCB!0 = outputRB+lenDLSORB-1  // Set limit pointer
@outputRBCB!0 = outputRB  // Make last word point to first

// Initialize the vector for DLSON
dlsOnVector!0 = iLCBTable
dlsOnVector!1 = baseIGCB
dlsOnVector!2 = inputRBCB
dlsOnVector!3 = outputRBCB
dlsOnVector!4 = dlsTimerQueue

// Initialize the input group control blocks
for line = 0 to numLines-1 by 16 do
   [
   let lcb = baseIGCB+line
   lcb>>IGCB.link = 0
   lcb>>IGCB.type = typeIGCB
   lcb>>IGCB.line = line  // First line number in group
   lcb>>IGCB.idle = 0  // All lines not being sampled
   lcb>>IGCB.interval = defaultSampleInterval
   lcb>>IGCB.active = 0  // All lines turned off
   dlsTimerQueue!(line/16) = lcb+1  // Spread IGCBs along queue
   ]

// Initialize DLS interrupts
if firstTime then  // Do this stuff only once
   [
   InitializeInterrupt(Allocate(zone, 30), 30,
      inputIntMask, DLSInputInterrupt)
   InitializeInterrupt(Allocate(zone, 30), 30,
      outputIntMask, DLSOutputInterrupt)
   InitializeInterrupt(Allocate(zone, 30), 30,
      errorIntMask, DLSErrorInterrupt)

// Set dispatches for eventual cleanup
   dlsSavedUserFinish = @lvUserFinishProc
   @lvUserFinishProc = DLSUserFinishProc
   dlsSavedSwatContext = @lvSwatContextProc
   @lvSwatContextProc = DLSSwatContextProc

// Initialize table for parity computation
   parityTable = Allocate(zone, 128)
   for char = 0 to 127 do
      [
      parityTable!char = char
      for j = 0 to 6 do if ((char rshift j)&1) ne 0 then
        parityTable!char = parityTable!char xor #200
      ]
   ]

// InitializeDLS (cont'd)

// Initialize the line blocks
for line = 0 to numLines-1 do
   [
   let lb = lbTable!line
   let mask = 100000B rshift (line&#17)
   switchon lb>>LBH.lineType into
      [
      case ltControl: case ltDialler:  // Control line
         [
         lb>>CLB.lastState = -1  // Force an update the first time
         // Raise DTR if dataset control, drop control signal if dialler
         dlsOutBase!line = lb>>LBH.lineType eq ltDialler
         endcase
         ]
      case ltUnused: // Unused line or speed status line
         endcase
      default:  // Data line
         [
         // Initialize stream dispatches
         lb>>DLB.gets = DLSInput7
         lb>>DLB.reset = DLSResetInput
         lb>>DLB.endof = DLSInputEmpty
         lb>>DLB.error = Noop
         lb>>DLB.puts = DLSOutputTI
      
         // Undefined entries to SysErr
         lb>>DLB.close = SysErr
         lb>>DLB.putback = SysErr
         lb>>DLB.stateof = SysErr
      
         // Initialize status and ring buffers
         lb>>DLB.statusA = 0
         lb>>DLB.statusB = 0
         lb>>DLB.position = 0
         lb>>DLB.outActive = false
         lb>>DLB.timeout = -1
         InitRingBuffer(lv lb>>DLB.iRBD, lv lb>>DLB.iBuf, lenIRB)
         InitRingBuffer(lv lb>>DLB.oRBD, lv lb>>DLB.oBuf, lenORB)
      
         // Initialize input line control block
         lb>>DLB.iLCB.link = 0
         lb>>DLB.iLCB.type = typeILCB
         lb>>DLB.iLCB.line = line
         lb>>DLB.iLCB.char = 0
         lb>>DLB.iLCB.sdMode = 0  // Not speed determination mode
         lb>>DLB.iLCB.bitsPerChar = 2  // 8 bits per char
         lb>>DLB.iLCB.sdLink = 0
      
         // Initialize output line control block
         lb>>DLB.oLCB.link = 0  // Clear link
         lb>>DLB.oLCB.type = typeOLCB
         lb>>DLB.oLCB.line = line
         lb>>DLB.oLCB.char = 0

         // Set sample intervals, bitsPerChar, puts routine
         SetDLSLineSpeed(lb, defaultBaud)

         // Enable sampling of this line in the GCB
         let lcb = baseIGCB + (line&#177760)
         lcb>>IGCB.idle = lcb>>IGCB.idle % mask
         lcb>>IGCB.active = lcb>>IGCB.active % mask
         endcase
         ]
      ]
   ]

// Get current control state of lines, then turn on DLS microcode
UpdateCarrierOn(Noop)
TurnOnDLS()
]

// ---------------------------------------------------------------------------
and CreateDCLM(zone, lineVec) = valof
// ---------------------------------------------------------------------------
// Creates and returns a Dialler Control Line Map.
// lineVec is a vector of six dialler control line numbers, which must
// be mentioned in the order CRQ/PND, DPR/ACR, DSS/NB1, NB2, NB4, NB8.
[
for i = 0 to 5 do
   if (lbTable!(lineVec!i))>>LBH.lineType ne ltDialler then CallSwat()
let dclm = Allocate(zone, lenDCLM)
dclm>>DCLM.inUse = 0
dclm>>DCLM.lineCRQ = lineVec!0
dclm>>DCLM.lineDPR = lineVec!1
dclm>>DCLM.lineNB↑0 = lineVec!2
dclm>>DCLM.lineNB↑1 = lineVec!3
dclm>>DCLM.lineNB↑2 = lineVec!4
dclm>>DCLM.lineNB↑3 = lineVec!5
dclm>>DCLM.diallerType = lineVec!6
resultis dclm
]