-- File: PhoneHeadDicentra.mesa - last edit:
-- AOF                 15-Feb-88  9:12:42
-- HGM                 29-Nov-84 18:06:13
-- Copyright (C) 1983, 1984, 1988 by Xerox Corporation. All rights reserved. 

DIRECTORY
  DicentraInputOutput USING [
    InterruptLevel, IOAddress, Output, SetPhonelineCSB, SetWakeupMask],
  Environment USING [Byte],
  Inline USING [BITAND, BITOR, LowHalf],
  MultibusAddresses USING [
    dialer0, dialer1, dialer2, IOAddress, modem0, scc0, scc1, scc2, scc3],
  NewRS232CFace USING [
    Command, CommandStatus, ControlRecord, DeviceStatus, LineNumber,
    Operation, OperationPtr, ParameterHandle, ParameterStatus, ResetRecord,
    RS232CClientType, TransferStatus],
  PhoneFace USING [Line, Status],
  ResidentHeap USING [Alignment, HeapLocation],
  ResidentMemory USING [Allocate];

PhoneHeadDicentra: PROGRAM
  IMPORTS Inline, DicentraInputOutput, ResidentMemory
  EXPORTS NewRS232CFace, PhoneFace =
  BEGIN
  
  Line: TYPE = PhoneFace.Line;
  Status: TYPE = PhoneFace.Status;
  
  sendTail, recvTail: ARRAY Line OF IOCB;
  
  csb: LONG POINTER TO CSB = AllocateCSB[];
  CSB: TYPE = ARRAY Line OF ControlBlock;
  ControlBlock: TYPE = RECORD [
    tranState: WORD,
    tranIOCB: ShortIOCB,
    tranIOCBMapped: LONG POINTER,
    recvState: WORD,
    recvIOCB: ShortIOCB,
    recvIOCBMapped: LONG POINTER,
    tranUnderruns: CARDINAL,
    tranUnused: ARRAY [9..12) OF WORD,
    recvMissed: CARDINAL,
    recvAborted: CARDINAL,
    recvAbortIdle: CARDINAL,
    recvAbortEx: CARDINAL];
    
  IOCB: TYPE = LONG POINTER TO ControlBlockRecord;
  ShortIOCB: TYPE = POINTER TO ControlBlockRecord;

  offset: CARDINAL = SIZE[NewRS232CFace.Operation];  
  controlBlockSize: PUBLIC <<PhoneFace>> CARDINAL ← SIZE[ControlBlockRecord];
  operationSize: PUBLIC <<NewRS232CFace>> CARDINAL ← 
    SIZE[NewRS232CFace.Operation] + controlBlockSize;
  operationAlignment: PUBLIC <<NewRS232CFace>> ResidentHeap.Alignment ← a1;
  operationLocation: PUBLIC <<NewRS232CFace>> ResidentHeap.HeapLocation ← first64K;
  ControlBlockRecord: PUBLIC TYPE = RECORD [
    next: ShortIOCB,
    status: WORD,
    buffer: LONG POINTER,
    bytes: CARDINAL,
    bytesLeft: CARDINAL,
    unused: ARRAY [6..8) OF WORD,
    
    finger: LONG POINTER,
    mapped: LONG POINTER,
    spare: ARRAY [12..16) OF WORD ];
    
  lines: CARDINAL = 8;
  numberOfLines: PUBLIC NewRS232CFace.LineNumber ← lines;
  NoSuchLine: ERROR = CODE;

  Handle: PUBLIC <<NewRS232CFace>> TYPE = RECORD[line: Line, flag: CARDINAL];
  nilHandle: PUBLIC <<NewRS232CFace>> Handle ← [lines, LAST[CARDINAL]];

  channels: ARRAY [0..lines) OF LONG POINTER TO Words = [
    LOOPHOLE[MultibusAddresses.scc0 + 10H],  -- Chan A
    LOOPHOLE[MultibusAddresses.scc0 + 00H],  -- Chan B
    LOOPHOLE[MultibusAddresses.scc1 + 10H],
    LOOPHOLE[MultibusAddresses.scc1 + 00H],
    LOOPHOLE[MultibusAddresses.scc2 + 10H],  -- Chan A
    LOOPHOLE[MultibusAddresses.scc2 + 00H],  -- Chan B
    LOOPHOLE[MultibusAddresses.scc3 + 10H],
    LOOPHOLE[MultibusAddresses.scc3 + 00H]];
    
  interruptVect: ARRAY [0..lines) OF WORD = [0, 0, 10H, 10H, 20H, 20H, 30H, 30H];
  baudRateNumber: ARRAY [0..lines) OF WORD = [014H, 014H, 077H, 077H, 077H, 077H, 077H, 077H];
  
  Words: TYPE = RECORD [
    r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15: WORD];

  TurnOn, Initialize: PUBLIC <<PhoneFace, NewRS232CFace>> PROC[
    interruptBits: WORD] =
    BEGIN
    SetupTestClocks[];
    csb↑ ← ALL[ [
        tranState: 0,
        tranIOCB: NIL,
        tranIOCBMapped: NIL,
        recvState: 0,
        recvIOCB: NIL,
        recvIOCBMapped: NIL,
        tranUnderruns: 0,
        tranUnused: ALL[0],
        recvMissed: 0,
        recvAborted: 0,
        recvAbortIdle: 0,
        recvAbortEx: 0 ] ];
    FOR line: Line IN [0..lines) DO InitChip[line]; ENDLOOP;
    FOR line: Line IN [0..lines) DO ResetLine[line]; ENDLOOP;
    DicentraInputOutput.SetWakeupMask[interruptBits, phoneLine];
    DicentraInputOutput.SetPhonelineCSB[csb];
    END;

  InitializeCleanup: PUBLIC <<NewRS232CFace>> PROC[] = {};  --InitializeCleanup
  
  SetupTestClocks: PROCEDURE =
    BEGIN
    SetupTestClock[MultibusAddresses.modem0, 00BH];  -- LCa for first 2 lines at 56KB
    SetupTestClock[MultibusAddresses.dialer0, 041H];  -- LCb for next 2 lines at 9600
    SetupTestClock[MultibusAddresses.dialer1, 041H];  -- LCc for next 2 lines at 9600
    SetupTestClock[MultibusAddresses.dialer2, 041H];  -- LCd for next 2 lines at 9600
    END;

  SetupTestClock: PROC[chip: DicentraInputOutput.IOAddress, ticks: CARDINAL] =
    BEGIN
    DicentraInputOutput.Output[001H, chip + 000H];  -- Master Interrupt Control ← Reset
    DicentraInputOutput.Output[000H, chip + 000H];  -- Master Interrupt Control ← No Reset
    DicentraInputOutput.Output[010H, chip + 001H];  -- Master Config Control ← Enable Port C and Cnt3
    DicentraInputOutput.Output[000H, chip + 01AH];  -- Cnt3 MSB
    DicentraInputOutput.Output[ticks, chip + 01BH];  -- Cnt3 LSB
    DicentraInputOutput.Output[0C2H, chip + 01EH];  -- Cnt3 Mode ← Sq Out
    DicentraInputOutput.Output[006H, chip + 00CH];  -- Cnt3 Command ← Go
    END;

  InitChip: PROC[line: Line] =
    BEGIN
    chan: LONG POINTER TO Words = channels[line];
    chanB: LONG POINTER TO Words = channels[Inline.BITOR[line, 1]];
    IF line > lines THEN ERROR NoSuchLine;
    DicentraInputOutput.Output[002H, @chanB.r0];  -- Shift Left (ADR0 is ignored)
    DicentraInputOutput.Output[0C9H, @chan.r9];  -- Hardware Reset, MIE, V, VIS
    DicentraInputOutput.Output[interruptVect[line], @chan.r2];  -- Int Vector Base
    END;

  InitiateCommand: PUBLIC <<NewRS232CFace>> PROC[
    handle: Handle, command: NewRS232CFace.Command]
    RETURNS[commandStatus: NewRS232CFace.CommandStatus] =
    {RETURN[inProgress]};  --InitiateCommand

  InitiateReceive: PUBLIC <<NewRS232CFace>> PROC[
    handle: Handle, operation: NewRS232CFace.OperationPtr]
    RETURNS [transferStatus: NewRS232CFace.TransferStatus] =
    BEGIN
    QueueInput[
      handle.line, LOOPHOLE[operation + offset],
      operation.dataBuffer, operation.bufferByteLength];
    RETURN[inProgress];
    END;  --InitiateReceive

  InitiateResetStatusBits: PUBLIC <<NewRS232CFace>> PROC[
    handle: Handle, resetRecord: NewRS232CFace.ResetRecord]
    RETURNS [commandStatus: NewRS232CFace.CommandStatus] =
    {RETURN[inProgress]};  --InitiateResetStatusBits

  InitiateSetControlBits: PUBLIC <<NewRS232CFace>> PROC[
    handle: Handle, controlRecord: NewRS232CFace.ControlRecord]
    RETURNS [commandStatus: NewRS232CFace.CommandStatus] =
    {RETURN[inProgress]};  --InitiateSetControlBits

  InitiateSetParameters: PUBLIC <<NewRS232CFace>> PROC[
    handle: Handle, parameters: NewRS232CFace.ParameterHandle]
    RETURNS[parameterStatus: NewRS232CFace.ParameterStatus] =
    {RETURN[inProgress]};  --InitiateSetParameters

  InitiateTransmit: PUBLIC <<NewRS232CFace>> PROC[
    handle: Handle, operation: NewRS232CFace.OperationPtr]
    RETURNS [transferStatus: NewRS232CFace.TransferStatus] =
    BEGIN
    QueueOutput[
      handle.line, LOOPHOLE[operation + offset],
      operation.dataBuffer, operation.bufferByteLength];
    RETURN[inProgress];
    END;  --InitiateTransmit

  PollCommand: PUBLIC <<NewRS232CFace>> PROC[handle: Handle]
    RETURNS [commandStatus: NewRS232CFace.CommandStatus] =
    {RETURN[completed]};  --PollCommand

  PollReceiveOrTransmit: PUBLIC <<NewRS232CFace>> PROC[
    handle: Handle, operation: NewRS232CFace.OperationPtr]
    RETURNS[
      bytesTransferred: CARDINAL, transferStatus: NewRS232CFace.TransferStatus] =
    BEGIN
    OPEN iocb: LOOPHOLE[operation + offset, IOCB];
    SELECT TRUE FROM
      (iocb.status = 00000H) =>
        RETURN[0, inProgress];  --either send or receive
      (iocb.status = 0FFFFH) =>
        RETURN[iocb.bytes - iocb.bytesLeft - 1, success];  --send success
      (Inline.BITAND[iocb.status, 0FFH] = 087H) =>
        RETURN[iocb.bytes - iocb.bytesLeft - 1, success]; --receive okay
      (iocb.status = 00001H) =>
        RETURN[0, invalidFrame]; --receive packetToLong
      (Inline.BITAND[iocb.status, 020H] # 0) =>
        RETURN[0, dataLost]; --receive overrun
      (Inline.BITAND[iocb.status, 040H] # 0) =>
        RETURN[0, checksumError]; --crc
      ENDCASE =>
        RETURN[0, deviceError]; --receive or transmit otherError
    END;  --PollReceiveOrTransmit

  PollSetControlBits: PUBLIC <<NewRS232CFace>> PROC[handle: Handle]
    RETURNS [commandStatus: NewRS232CFace.CommandStatus] =
    {RETURN[completed]};  --PollSetControlBits

  PollSetParameters: PUBLIC <<NewRS232CFace>> PROC[handle: Handle]
    RETURNS[parameterStatus: NewRS232CFace.ParameterStatus] =
    {RETURN[completed]};  --PollSetParameters

  ReleaseHandle: PUBLIC <<NewRS232CFace>> PROC[handle: Handle]
    RETURNS [nilHandle: Handle] = {RETURN[nilHandle]};  --ReleaseHandle

  ResetStatusBits: PUBLIC <<NewRS232CFace>> PROC[
    handle: Handle, resetRecord: NewRS232CFace.ResetRecord] = {};

  SetControlBits: PUBLIC <<NewRS232CFace>> PROC[
    handle: Handle, controlRecord: NewRS232CFace.ControlRecord] = {};

  TurnOff: PUBLIC <<PhoneFace>> PROC =
    BEGIN
    DicentraInputOutput.SetPhonelineCSB[NIL];
    END;

  ResetLine: PUBLIC <<PhoneFace>> PROC[line: Line] =
    BEGIN
    chan: LONG POINTER TO Words = channels[line];
    IF line > lines THEN ERROR NoSuchLine;
    DicentraInputOutput.Output[000H, @chan.r1];  -- Disable all ints
    DicentraInputOutput.SetPhonelineCSB[csb];  -- Just in case
    csb[line].tranIOCB ← NIL;
    csb[line].tranState ← 0;
    csb[line].recvIOCB ← NIL;
    csb[line].recvState ← 0;
    DicentraInputOutput.Output[interruptVect[line], @chan.r2];  -- Int Vector Base
    DicentraInputOutput.Output[020H, @chan.r4];  -- SDLC
    DicentraInputOutput.Output[013H, @chan.r1];  -- Rx All Int, Tx Int En, Ext Int En
    DicentraInputOutput.Output[0D9H, @chan.r3];  -- 8bits/char, Hunt, CRC, RxE
    DicentraInputOutput.Output[0EBH, @chan.r5];  -- DTR, 8bits/char, TxE, RTS, CRC
    DicentraInputOutput.Output[07EH, @chan.r7];  -- Flag Char
    DicentraInputOutput.Output[084H, @chan.r10];  -- CRC←1, Abort on Underrun
    DicentraInputOutput.Output[080H, @chan.r15];  -- Enable Ext Int on Abort
    DicentraInputOutput.Output[070H, @chan.r0];  -- Reset Rx CRC, Error Reset
    DicentraInputOutput.Output[090H, @chan.r0];  -- Reset Tx CRC, Reset Ext/Sts Int
    DicentraInputOutput.Output[008H, @chan.r11];  -- External Clocks
    IF FALSE THEN
      BEGIN
      DicentraInputOutput.Output[baudRateNumber[line], @chan.r12];  -- Low byte of time constant
      DicentraInputOutput.Output[000H, @chan.r13];  -- High byte of time constant
      DicentraInputOutput.Output[003H, @chan.r14];  -- Baud Rate Gen from PClk
      DicentraInputOutput.Output[050H, @chan.r11];  -- Clocks from BR Gen
      END;
    END;

  GetPacketsMissed: PUBLIC <<PhoneFace>> PROC[line: Line] RETURNS [CARDINAL] =
    BEGIN
    IF line > lines THEN ERROR NoSuchLine;
    RETURN[csb[line].recvMissed]
    END;

  QueueOutput: PUBLIC <<PhoneFace>> PROC[
    line: Line, iocb: IOCB, buffer: LONG POINTER, bytes: CARDINAL] =
    BEGIN
    chan: LONG POINTER TO Words = channels[line];
    firstByte: LONG POINTER TO PACKED ARRAY [0..0) OF Environment.Byte = buffer;
    IF line > lines THEN ERROR NoSuchLine;
    iocb.status ← 0;
    iocb.next ← NIL;
    iocb.buffer ← buffer;
    iocb.bytes ← bytes;
    IF csb[line].tranIOCB = NIL THEN
      BEGIN
      csb[line].tranIOCB ← Inline.LowHalf[iocb];
      DicentraInputOutput.Output[080H, @chan.r0];  -- Reset Tx CRC
      DicentraInputOutput.Output[firstByte[0], @chan.r8];  -- Send first data byte
      END
    ELSE
      BEGIN
      sendTail[line].next ← Inline.LowHalf[iocb];
      IF csb[line].tranIOCB = NIL AND iocb.status = 0 THEN  -- Rats, lost race
        BEGIN
        csb[line].tranIOCB ← Inline.LowHalf[iocb];
        DicentraInputOutput.Output[080H, @chan.r0];  -- Reset Tx CRC
        DicentraInputOutput.Output[firstByte[0], @chan.r8];  -- Send first data byte
        END;
      END;
    sendTail[line] ← iocb;
    END;

  GetDeviceStatus: PUBLIC <<NewRS232CFace>> PROC[handle: Handle]
    RETURNS[deviceStatus: NewRS232CFace.DeviceStatus] =
    BEGIN
    END;  --GetDeviceStatus

  GetHandle: PUBLIC <<NewRS232CFace>> PROC[
    lineNumber: NewRS232CFace.LineNumber,
    rs232cClient: NewRS232CFace.RS232CClientType ← normalClient]
    RETURNS [handle: Handle] = {RETURN[[lineNumber, LAST[CARDINAL]]]};

  GetNextLine: PUBLIC <<NewRS232CFace>> PROC[
    lineNumber: NewRS232CFace.LineNumber]
    RETURNS [nextLineNumber: NewRS232CFace.LineNumber] =
    {RETURN[IF (lineNumber ← lineNumber.SUCC) < lines THEN lineNumber
      ELSE NewRS232CFace.LineNumber.FIRST]};  --GetNextLine

  GetSendStatus: PUBLIC <<PhoneFace>> PROC[iocb: IOCB] RETURNS [status: Status] =
    BEGIN
    IF iocb.status = 0 THEN RETURN[pending];
    SELECT iocb.status FROM
      0FFFFH => RETURN[ok];
      ENDCASE => RETURN[underrun];
    END;

  QueueInput: PUBLIC <<PhoneFace>> PROC[
    line: Line, iocb: IOCB, buffer: LONG POINTER, bytes: CARDINAL] =
    BEGIN
    iocb.status ← 0;
    iocb.next ← NIL;
    iocb.buffer ← buffer;
    iocb.bytes ← bytes;
    IF line > lines THEN ERROR NoSuchLine;
    IF csb[line].recvIOCB = NIL THEN
      BEGIN
      csb[line].recvIOCB ← Inline.LowHalf[iocb]
      END
    ELSE
      BEGIN
      recvTail[line].next ← Inline.LowHalf[iocb];
      IF csb[line].recvIOCB = NIL AND iocb.status = 0 THEN  -- lost race
        csb[line].recvIOCB ← Inline.LowHalf[iocb];
      END;
    recvTail[line] ← iocb;
    END;

  GetRecvStatus: PUBLIC <<PhoneFace>> PROC[iocb: IOCB] RETURNS [status: Status] =
    BEGIN
    IF iocb.status = 0 THEN RETURN[pending];
    IF iocb.status = 1 THEN RETURN[packetTooLong];
    IF Inline.BITAND[iocb.status, 020H] # 0 THEN RETURN[overrun];
    IF Inline.BITAND[iocb.status, 040H] # 0 THEN RETURN[crc];
    SELECT Inline.BITAND[iocb.status, 0FFH] FROM
      087H => RETURN[ok];
      ENDCASE => RETURN[otherError];
    END;

  GetPacketLength: PUBLIC <<PhoneFace>> PROC[iocb: IOCB] RETURNS [bytes: CARDINAL] =
    BEGIN
    RETURN[iocb.bytes - iocb.bytesLeft - 1];
    END;

  AllocateCSB: PROCEDURE RETURNS [base: LONG POINTER] =
    BEGIN
    pages: CARDINAL = 1;
    base ← ResidentMemory.Allocate[hyperspace, pages];
    END;

  SetupTestClocks[];
  END.