-- Copyright (C) 1983, 1984 by Xerox Corporation. All rights reserved. 
-- PhoneHeadDicentra.mesa, HGM, 29-Nov-84 18:06:13

DIRECTORY
  Environment USING [Byte],
  Inline USING [BITAND, BITOR, LowHalf],
  ResidentMemory USING [Allocate],

  DicentraInputOutput USING [
    InterruptLevel, IOAddress, Output, SetPhonelineCSB, SetWakeupMask],
  MultibusAddresses USING [
    dialer0, dialer1, dialer2, modem0, scc0, scc1, scc2, scc3],
  PhoneFace USING [Line, Status];

PhoneHeadDicentra: PROGRAM
  IMPORTS Inline, DicentraInputOutput, ResidentMemory
  EXPORTS 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;
  
  controlBlockSize: PUBLIC CARDINAL ← SIZE[ControlBlockRecord];
  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;
  NoSuchLine: ERROR = CODE;

  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: PUBLIC PROCEDURE [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;
  
  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: PROCEDURE [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: PROCEDURE [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;

  TurnOff: PUBLIC PROCEDURE =
    BEGIN
    DicentraInputOutput.SetPhonelineCSB[NIL];
    END;

  ResetLine: PUBLIC PROCEDURE [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 PROCEDURE [line: Line] RETURNS [CARDINAL] =
    BEGIN
    IF line > lines THEN ERROR NoSuchLine;
    RETURN[csb[line].recvMissed]
    END;

  QueueOutput: PUBLIC PROCEDURE [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;

  GetSendStatus: PUBLIC PROCEDURE [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 PROCEDURE [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 PROCEDURE [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 PROCEDURE [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.