{ CCOTCIO.TEXT --------------------------------------------------------}
{
{       CCOTCIO -- OMNINET Commands Unit for Corvus CONCEPT
{
{       (c) Copyright 1983 Corvus Systems, Inc.
{                          San Jose, California
{
{       All Rights Reserved
{
{       v 1.0  06-20-83  LEF  Original unit (from PB prototype)
{       v 1.1  09-14-83  LEF  Add uPntr to TCparmBlk
{                             Modifications to TCgetCounts
{       v 1.2  02-02-84  KB   Changes to not busy wait on interrupt
{                             routine in echo and poke.
{
{       This UNIT contains functions and procedures which construct
{       Omninet commands and send them to the Omninet Transporter
{       driver.  It also defines constants and data structures which
{       are useful when programming an Omninet application.  Hopefully,
{       a Pascal programmer can use this UNIT without knowing the
{       details of the Transporter interface ...
{
{!CC}{ Corvus CONCEPT version
{----------------------------------------------------------------------}
      {$R-}

UNIT ccOTCio;

INTERFACE
  
CONST
      TCvers    = '1.2';
      TCvrs64   = 100; { $64 which is one of the Transporter versions  }
      TCvrs8A   = 138; { $8A which is one of the Transporter versions  }
{!CC} TCbyteLO  = -128;  TCbyteHI = 127; { define byte value limits    }

      { Transporter Driver Return Codes                                }
      TCnotRdy  = 21;  { Transporter not ready                         }
      TCqueued  = 30;  { driver queued command warning                 }
      TCentUse  = 52;  { entry in use error                            }
      TCinvFnc  = 56;  { invalid function code error                   }

      { Indexes into Transporter counters                              }
      TCCmiss   = 1;   { missed packets (number of ADLC interrrupts)   }
      TCCcoll   = 2;   { collision AVOIDANCE interrrupts               }
      TCCintErr = 3;   { unknown interrupts inside Transporter         }
      TCCrcvErr = 4;   { ADLC receive errors (CRC, overrun, etc.       }
      TCCmaxCnt = 4;   { number of Omninet transporter counters        }

{$P}
      { Transporter Return Codes                                       }
      Waiting   = 255; { $FF                                           }
      CmdAcpt   = 254; { $FE command accepted                          }
      Echoed    = 192; { $C0 echo command was successful               }

      GaveUp    = 128; { $80 aborted send command after MaxRetry tries }
      TooLong   = 129; { $81 last message sent too long for receiver   }
      NoSockt   = 130; { $82 sent to an uninitialized socket           }
      HdrErr    = 131; { $83 sender hdr length did not match receiver  }
      BadSock   = 132; { $84 invalid socket number                     }
      Inuse     = 133; { $85 tried to setup receive on active socket   }
      BadDest   = 134; { $86 sent to an invalid host number            }

      OkCode    = 0;   { success!!!                                    }

      NoTrans   = -1;  { indicates that we are unable to communicate   }
                       { with Transporter - StrobeCmd failed           }

TYPE
{!CC} TCcntBuf   = array [1..TCCmaxCnt] of longint;
      TChosts    = 0..63;
      TChostSet  = set of TChosts;

      pTCbuffer  = ^TCbuffer;
{!CC} TCbuffer   = array [0..32765] of TCbyteLO..TCbyteHI;

      pTCrsltRcd = ^TCrsltRcd;
      TCrsltRcd  = record
                   Rcode: TCbyteLO..TCbyteHI;
                   Sourc: TCbyteLO..TCbyteHI;
                   Len:   integer;
{!CC}              UCdta: array [0..255] of TCbyteLO..TCbyteHI;
                   end;

      pTComniCmd = ^TComniCmd;
      TComniCmd  = record case integer of
                     1: (p: record
                            RP: pTCrsltRcd;
                            DP: pTCbuffer;
                            LN: integer;
                            HL: integer;
                            end);
{!CC}                2: (a: array [1..12] of TCbyteLO..TCbyteHI);
                   end;

{$P}
      pTCparmBlk = ^TCparmBlk;
      TCparmBlk  = record
{DRV.OMNINET}      pComd: pTComniCmd; { Omninet command pointer   unit }
{DRV.OMNINET}      pProc: pTCbuffer;  { interrupt procedure ptr   user }
{DRV.OMNINET}      pPblk: pTCparmBlk; { parameter block pointer   both }
                   pBuff: pTCbuffer;  { data buffer pointer       user }
                   pRslt: pTCrsltRcd; { result record pointer     both }
                   oComd: TComniCmd;  { Omninet command           unit }
                   rDone: boolean;    { request complete of TRUE  both }
                   rStat: integer;    { request status            both }
                   rRslt: integer;    { request result code       both }
                   {                                               {1.1}
                   { uPntr is not used by this unit and may be     {1.1}
                   { used for any purpose by the user.  uPntr is   {1.1}
                   { not documented in 09-83 documentation.        {1.1}
                   {                                               {1.1}
                   uPntr: longint;    { user pointer              user }
                   end;

VAR
      TCtrnVrsn:  integer;   { Transporter version number              }
      TCcounts:   TCcntBuf;  { Transporter counters                    }
      TCadlc:     integer;   { status of ADLC at last receive error    }
      TCintPtr:   pTCbuffer; { interrupt processing routine pointer    }
{!CC} TChaveDrv:  boolean;   { TRUE if using Omninet driver            }

{----------------------------------------------------------------------}
{----------------------------------------------------------------------}
PROCEDURE ccOTCioInit;
{----------------------------------------------------------------------}
PROCEDURE ccOTCioTerm;
{----------------------------------------------------------------------}
PROCEDURE TCinitBlk
                  (var p: TCparmBlk;    { request parameter block      }
                       r: pTCrsltRcd;   { result record pointer        }
                       b: pTCbuffer;    { data buffer pointer          }
                       i: pTCbuffer);   { interrupt processing pointer }
{----------------------------------------------------------------------}
PROCEDURE TCinterrupt
                      (q: integer;      { queue flag                   }
                       s: integer;      { driver status                }
                       r: pTCrsltRcd;   { result record pointer        }
                       b: pTCbuffer;    { data buffer pointer          }
                       p: pTCparmBlk);  { request parameter block ptr  }
{----------------------------------------------------------------------}
FUNCTION  TCsetRecv
                (var tcp: TCparmBlk;    { request parameter block      }
                     sck: integer;      { socket number                }
                     dln: integer;      { data length                  }
                     hln: integer):     { message header length        }
                          integer;      { returns driver status        }
{----------------------------------------------------------------------}
{$P}
{----------------------------------------------------------------------}
FUNCTION  TCsndMesg
                (var tcp: TCparmBlk;    { request parameter block      }
                     sck: integer;      { socket number                }
                     dln: integer;      { data length                  }
                     hln: integer;      { message header length        }
                     dst: integer):     { destination host number      }
                          integer;      { returns driver status        }
{----------------------------------------------------------------------}
FUNCTION  TCendRecv
                    (sck: integer;      { socket number                }
                 var res: integer):     { result code                  }
                          integer;      { returns driver status        }
{----------------------------------------------------------------------}
FUNCTION  TCinitTrans
                (var hst: integer):     { our host number              }
                          integer;      { returns driver status        }
{----------------------------------------------------------------------}
FUNCTION  TCwhoAmI
                (var hst: integer):     { our host number              }
                          integer;      { returns driver status        }
{----------------------------------------------------------------------}
FUNCTION  TCechoTrans
                    (dst: integer;      { destination host number      }
                 var res: integer):     { command result status        }
                          integer;      { returns driver status        }
{----------------------------------------------------------------------}
FUNCTION  TCpeekTrans
                    (adr: integer;      { transporter RAM address      }
                 var val: integer):     { transporter RAM value        }
                          integer;      { returns driver status        }
{----------------------------------------------------------------------}
FUNCTION  TCpokeTrans
                    (adr: integer;      { transporter RAM address      }
                     val: integer;      { transporter RAM value        }
                 var res: integer):     { command result status        }
                          integer;      { returns driver status        }
{----------------------------------------------------------------------}
FUNCTION  TCsetRetry
                    (cnt: integer):     { retry count                  }
                          integer;      { returns driver status        }
{----------------------------------------------------------------------}
FUNCTION  TCnetMap
                (var map: TChostSet):   { set of host numbers          }
                          integer;      { returns driver status        }
{----------------------------------------------------------------------}
PROCEDURE TCgetCounts;
{----------------------------------------------------------------------}

IMPLEMENTATION

{$P}
CONST
{!CC} RdyAdr   = $30F7F; { addr of VIA register A, for OMNINET ready   }
{!CC} StrAdr   = $30FA1; { addr of Transporter register                }
      MaxRetry = 10;     { default number of retries                   }

      { Transporter Opcodes }
      RecvOp = 240;      { $F0 - SETUPRECV opcode }
      SendOp = 064;      { $40 - SENDMSG opcode   }
      InitOp = 032;      { $20 - INIT opcode      }
      EndOp  = 016;      { $10 - ENDRECV opcode   }
      DebOp  = 008;      { $08 - PEEK/POKE opcode }
      EchoOp = 002;      { $02 - ECHOCMD opcode   }
      WhoOp  = 001;      { $01 - WHOAMI opcode    }

      PeekOp = 000;      { $00 }
      PokeOp = 255;      { $FF }

      { offsets into command record for byte fields }
      crOPCD = 1;        { opcode }
      crSOCK = 5;        { socket number }
      crEDST = 5;        { destination for echo commands }
      crPAD1 = 5;        { Transporter address to peek or poke - MSB }
      crPAD2 = 6;        { Transporter address to peek or poke - LSB }
      crPEPO = 7;        { peek/poke designator for Deb commands }
      crPVAL = 8;        { poke value }
      crHLEN = 11;       { header length }
      crDEST = 12;       { destination for sends }

      { Hexadecimal equivalents }
      h80    = 128;    {   $80 }
      h90    = 144;    {   $90 }
      hA0    = 160;    {   $A0 }
      hB0    = 176;    {   $B0 }
      hE1    = 225;    {   $E1 }
      hF800  = -2048;  { $F800 }

TYPE  byte   = TCbyteLO..TCbyteHI;

VAR   RecvSet:  array [1..4] of boolean; { active sockets for receive  }
      otcPB:    TCparmBlk;  { request parameter block for this unit    }
      otcRR:    TCrsltRcd;  { result record for this unit              }
      RRptr:    pTCrsltRcd; { result record pointer for this unit      }
{!CC} OmniUnit: integer;    { Omninet transporter driver unit number   }

{$P}
      FUNCTION OSomniDv: integer;                              EXTERNAL;


{ TranSock ------------------------------------------------------------}
{ Force a socket number to be in the proper format for the Transporter
{----------------------------------------------------------------------}
FUNCTION TranSock (sck: integer): integer;
    begin
    case sck of
        1,h80: TranSock := ord (h80); 2,h90: TranSock := ord (h90);
        3,hA0: TranSock := ord (hA0); 4,hB0: TranSock := ord (hB0);
          end; {case}
    end;

{ DrvrSock ------------------------------------------------------------}
{ Force a socket number to be in the proper format for the driver
{----------------------------------------------------------------------}
FUNCTION DrvrSock (sck: integer): integer;
    begin
    case sck of
        1,h80: DrvrSock := 1;         2,h90: DrvrSock := 2;
        3,hA0: DrvrSock := 3;         4,hB0: DrvrSock := 4;
          end; {case}
    end;

{$P}
{ ExecCmd -------------------------------------------------------------}
{ ExecCmd attempts to invoke a transporter command by calling the
{ Transporter driver (if available) ....
{----------------------------------------------------------------------}
FUNCTION ExecCmd (cmd: integer;
              var tcp: TCparmBlk;
                  fnc: integer):
                       integer;
      var sr,b,i,j: integer;                                       {1.2}

{ ExecCmd.StrobeCmd ---------------------------------------------------}
{ Strobe command address to Transporter
{ (used if OMNINET driver not available)
{----------------------------------------------------------------------}
{!CC} FUNCTION StrobeCmd (CmdAdr: pTCbuffer): boolean;
{!CC}     var i: integer; isready: boolean; StrAddr: ^byte;

{ ExecCmd.StrobeCmd.Ready ---------------------------------------------}
{----------------------------------------------------------------------}
{!CC} FUNCTION Ready: boolean;
{!CC}     var i: byte; j: integer; RdyAddr: ^byte;
{!CC}     begin
{!CC}     RdyAddr := pointer (RdyAdr); j := 10000;
{!CC}     repeat
{!CC}         i := RdyAddr^; j := j-1;
{!CC}         until (j = 0) or (ODD (i));
{!CC}     Ready := ODD (i);
{!CC}     end;

{ ExecCmd.StrobeCmd ---------------------------------------------------}
{----------------------------------------------------------------------}
{!CC}     begin
{!CC}     StrAddr := pointer (StrAdr); i := 1;
{!CC}     repeat
{!CC}         isready := ready;
{!CC}         if isready then StrAddr^ := CmdAdr^[i];
{!CC}         i := i + 1
{!CC}         until (i > 3) or (NOT isready);
{!CC}     StrobeCmd := isready;
{!CC}     end;

{$P}
{ ExecCmd -------------------------------------------------------------}
{----------------------------------------------------------------------}
      begin
      tcp.rDone := FALSE;
      with tcp.pComd^ do begin
          p.RP := tcp.pRslt;       { set command record result pointer }
          a[crOPCD] := ord(cmd);   { set command record command        }
          p.RP^.Rcode := -1;       { set waiting status            {1.2}
          end;                                                     {1.2}
{!CC} if TChaveDrv
{!CC}     then begin
{!CC}         UnitStatus (OmniUnit,tcp,fnc);
{!CC}         ExecCmd := IOresult;
{!CC}         end
{!CC}     else begin
{!CC}         if StrobeCmd (@tcp.pComd)
                  then begin

                      repeat until (tcp.pRslt^.Rcode <> -1);       {1.2}

                  {   j := 10000;                              }
                  {   repeat                                   }
                  {       j := j - 1                           }
                  {       until (tcp.pComd^.p.RP^.Rcode <> -1) }
                  {          or (j = 0);                       }

                      end;
              tcp.rDone := TRUE;
              tcp.rStat := 0;
              tcp.rRslt := tcp.pRslt^.Rcode;                       {1.2}
{!CC}         if tcp.rRslt < 0 then tcp.rRslt := tcp.rRslt+256;
              ExecCmd := 0;
{!CC}         end;
      end;

{$P}
{ BusyWait ------------------------------------------------------------}
{ Wait until the transporter command is complete
{ Returns status from driver interrupt handler
{----------------------------------------------------------------------}
FUNCTION BusyWait (var tcp: TCparmBlk): integer;
      begin
      with tcp do begin
          while NOT rDone do {nothing}; BusyWait := rStat;
          end;
      end;

{ RCbusy ----------------------------------------------------------{1.2}
{ Wait until the transporter command is complete                   {1.2}
{ Returns status from driver and transporter result code           {1.2}
{------------------------------------------------------------------{1.2}
FUNCTION RCbusy (var tcp: TCparmBlk; var r : integer): integer;    {1.2}
      begin                                                        {1.2}
      with tcp do begin                                            {1.2}
          rStat := r;                                              {1.2}
          if (r = okcode) or (r = TCqueued)                        {1.2}
              then begin                                           {1.2}
                  r := okcode; {if queued then is ok}              {1.2}
                  while pRslt^.rCode = -1 do;                      {1.2}
                  rRslt := pRslt^.Rcode;                           {1.2}
{!CC}             if rRslt < 0 then rRslt := rRslt + 256;          {1.2}
                  rDone := TRUE;                                   {1.2}
                  end                                              {1.2}
              else rDone := TRUE;                                  {1.2}
          RCbusy := rRslt;                                         {1.2}
          end;                                                     {1.2}
      end;                                                         {1.2}

{ GetResult -----------------------------------------------------------}
{----------------------------------------------------------------------}
FUNCTION GetResult (var tcp: TCparmBlk; ior: integer): integer;
      begin
      if (ior = 0) OR (ior = TCqueued)
          then GetResult := BusyWait (tcp)
          else GetResult := ior;
      end;

{$P}
{ TCinterrupt ---------------------------------------------------------}
{ Interrupt procedure used by this UNIT
{----------------------------------------------------------------------}
PROCEDURE TCinterrupt {(q: integer;      { queue flag                  )
                        s: integer;      { driver status               )
                        r: pTCrsltRcd;   { result record pointer       )
                        b: pTCbuffer;    { data buffer pointer         )
                        p: pTCparmBlk)}; { request parameter block     }
      begin
      with p^ do begin
          rStat := s;
          if (q = 0) OR (s <> 0) then begin
              rDone := TRUE;
              rRslt := r^.Rcode;                                   {1.2}
{!CC}         if rRslt < 0 then rRslt := rRslt+256;
              end;
          end;
      end;


{ TCinitBlk -----------------------------------------------------------}
{ Initialize request control block
{----------------------------------------------------------------------}
PROCEDURE TCinitBlk {(var p: TCparmBlk;  { request parameter block     )
                          r: pTCrsltRcd; { result record pointer       )
                          b: pTCbuffer;  { data buffer pointer         )
                          i: pTCbuffer)};{ interrupt processing ptr    }
      var n: integer;
      begin
      with p do begin
{!CC}     pComd := @oComd;
{!CC}     pProc := i;
{!CC}     pPblk := @p;
{!CC}     pBuff := b;
{!CC}     pRslt := r;
          rDone := FALSE;
          rStat := 0;
          rRslt := 255;
          for n := 1 to 12 do oComd.a[n] := 0;
          end;
      end;

{$P}
{ TCsetRetry ----------------------------------------------------------}
{----------------------------------------------------------------------}
FUNCTION TCsetRetry {(cnt: integer):    { retry count                  )
                           integer};    { returns driver status        }
      var res: integer;
      begin
      TCsetRetry := TCpokeTrans (hE1,cnt,res);
      end;


{ TCnetMap ------------------------------------------------------------}
{ Returns map which is a set of all of the active hosts on the local
{ Omninet.  If the integer returned is not zero, a driver error
{ occurred while attempting to echo.  The Max retry parameter inside
{ the Transporter is set to 1 to minimize the impact of all of the
{ unsuccessful echoes.
{----------------------------------------------------------------------}
FUNCTION TCnetMap {(var map: TChostSet):{ set of host numbers          )
                             integer};  { returns driver status        }
      var i,x,res: integer;
      begin
      x := TCsetRetry (1);
      x := 0;
      map := [];
      for i := 0 to 63 do
          if x = 0 then begin
              x := TCechoTrans (i,res);
              if res = Echoed then map := map+[i];
              end;
      TCnetMap := x;
      x := TCsetRetry (MaxRetry);
      end;

{$P}
{ TCgetCounts ---------------------------------------------------------}
{ Reads the Transporter counters and sets them to 0
{----------------------------------------------------------------------}
PROCEDURE TCgetCounts;
      var r,x,i,val: integer;
      begin
      if TCtrnVrsn < TCvrs8A
      then begin { get counters from T6.4 locations... }
          for i := 1 to 3 do begin
              x := TCpeekTrans (229+i,val);
              TCcounts[i] := TCcounts[i] + val;
              x := TCpokeTrans (229+i,0,r);
              end;
          x := TCpeekTrans (233,TCadlc);
          x := TCpeekTrans (234,val);
          TCcounts[TCCrcvErr] := TCcounts[TCCrcvErr] + val;
          x := TCpokeTrans (234,0,r);
          end
      else begin { get counters from T8.A/T9.B locations... }      {1.2}
          x := TCpeekTrans (229,val);                              {1.2}
          TCcounts[TCCmiss] := TCcounts[TCCmiss] + val;            {1.2}
          x := TCpokeTrans (229,0,r);                              {1.2}
          x := TCpeekTrans (230,val);
          TCcounts[TCCcoll] := TCcounts[TCCcoll] + val;
          x := TCpokeTrans (230,0,r);
          x := TCpeekTrans (231,TCadlc);
          if TCadlc <> 0 then begin
              TCcounts[TCCrcvErr] := TCcounts[TCCrcvErr]+1;
              x := TCpokeTrans (231,0,r);
              end;
          end;
      end;

{$P}
{----------------------------------------------------------------------}
{
{ The following procedures construct Omninet commands and sends
{ them to the Transporter.
{
{----------------------------------------------------------------------}
    
    
{ TCinitTrans ---------------------------------------------------------}
{ Initialize Transporter and determine our host number
{ Note: TCinitTrans must not be called from an interrupt routine.  {1.2}
{----------------------------------------------------------------------}
FUNCTION TCinitTrans {(var hst: integer):  { our host number           )
                                integer};  { returns driver status     }
      var p: pTCbuffer; r: integer;
      begin
      TCinitBlk (otcPB,RRptr,NIL,TCintPtr);
{!CC} p := NIL;
{!CC} if TChaveDrv then     { clear out driver interrupt service table }
{!CC}     for r := 1 to 4 do UnitStatus (OmniUnit,p,128+r);
      r := ExecCmd (InitOp,otcPB,0);
      r := GetResult (otcPB,r);
      hst := otcPB.rRslt;
      TCinitTrans := r;
      end;


{ TCwhoAmI ------------------------------------------------------------}
{ Find out what our host number is
{ Note: TCwhoAmI must not be called from an interrupt routine.     {1.2}
{----------------------------------------------------------------------}
FUNCTION TCwhoAmI {(var hst: integer):     { our host number           )
                             integer};     { returns driver status     }
      var r: integer;
      begin
      TCinitBlk (otcPB,RRptr,NIL,TCintPtr);
      r := ExecCmd (WhoOp,otcPB,0);
      r := GetResult (otcPB,r);
      hst := otcPB.rRslt;
      TCwhoAmI := r;
      end;

{$P}
{ TCechoTrans ---------------------------------------------------------}
{ Echo to specified Transporter
{----------------------------------------------------------------------}
FUNCTION TCechoTrans {(dst: integer;    { destination host number      )
                   var res: integer):   { command result status        )
                            integer};   { returns driver status        }
      var r: integer;
      begin
      TCinitBlk (otcPB,RRptr,NIL,NIL);                             {1.2}
      otcPB.pComd^.a[crEDST] := dst;
      r := ExecCmd (EchoOp,otcPB,0);
      res := RCbusy (otcPB,r);                                     {1.2}
      TCechoTrans := r;
      end;


{ TCsndMesg -----------------------------------------------------------}
{ Send a message to another host...
{----------------------------------------------------------------------}
FUNCTION TCsndMesg {(var tcp: TCparmBlk;  { request parameter block    )
                         sck: integer;    { socket number              )
                         dln: integer;    { data length                )
                         hln: integer;    { message header length      )
                         dst: integer):   { destination host number    )
                              integer};   { returns driver status      }
      var WaitForComp: boolean; r: integer;
      begin
      tcp.rDone := FALSE;
      tcp.rRslt := 255;
      WaitForComp := (tcp.pProc = NIL);                            {1.2}
      with tcp.pComd^ do begin
          p.DP := tcp.pBuff;
          p.RP := tcp.pRslt;
          p.LN := dln;
          a[crSOCK] := ord(TranSock(sck));
          a[crHLEN] := hln;
          a[crDEST] := dst;
          end;
      r := ExecCmd (SendOp,tcp,0);
      if WaitForComp then tcp.rRslt := RCbusy (tcp,r);             {1.2}
      TCsndMesg := r;
      end;

{$P}
{ TCsetRecv -----------------------------------------------------------}
{ Assembles a receive command and sends it to the transporter.  Will
{ not return until the command has been accepted...
{----------------------------------------------------------------------}
FUNCTION TCsetRecv {(var tcp: TCparmBlk;  { request parameter block    )
                         sck: integer;    { socket number              )
                         dln: integer;    { data length                )
                         hln: integer):   { message header length      )
                              integer};   { returns driver status      }
      var r: integer;
      begin
      with tcp.pComd^ do begin
          p.DP := tcp.pBuff;
          p.RP := tcp.pRslt;
          p.LN := dln;
          a[crSOCK] := ord(TranSock(sck));
          a[crHLEN] := hln;
          end;

      { driver always waits for the result to change from FF to FE     }
      { before returning.  This was done because T6.4 transporters     }
      { do not generate that interrupt.                                }

      r := ExecCmd (RecvOp,tcp,DrvrSock(sck));
      with tcp do begin
          rStat := r;
          rRslt := pRslt^.Rcode;                                   {1.2}
{!CC}     if rRslt < 0 then rRslt := rRslt+256;
          if rRslt = CmdAcpt
              then begin
                  rDone := FALSE;
                  RecvSet[DrvrSock(sck)] := TRUE;
                  end
              else rDone := TRUE;
          end;
      TCsetRecv := r;
      end;

{ TCendRecv -----------------------------------------------------------}
{ reset a setup receive
{----------------------------------------------------------------------}
FUNCTION TCendRecv {(sck: integer;      { socket number                )
                 var res: integer):     { result code                  )
                          integer};     { returns driver status        }
      var r: integer;
      begin
      TCinitBlk (otcPB,RRptr,NIL,NIL);                             {1.2}
      otcPB.pComd^.a[crSOCK] := ord(TranSock(sck));
      r := ExecCmd (EndOp,otcPB,128+DrvrSock(sck));
      res := RCbusy (otcPB,r);                                     {1.2}
      RecvSet[DrvrSock(sck)] := FALSE;
      TCendRecv := r;
      end;

{$P}
{ TCpeekTrans ---------------------------------------------------------}
{ Read from the RAM inside the Transporter
{----------------------------------------------------------------------}
FUNCTION TCpeekTrans {(adr: integer;    { transporter RAM address      )
                   var val: integer):   { transporter RAM value        )
                            integer};   { returns driver status        }
      var r: integer;
      begin
      TCinitBlk (otcPB,RRptr,NIL,NIL);                             {1.2}
      with otcPB.pComd^ do begin
          a[crPEPO] := ord (PeekOp);
          a[crPAD1] := ord (adr DIV 256);
          a[crPAD2] := ord (adr MOD 256);
          end;
      r := ExecCmd (DebOp,otcPB,0);
      with otcPB do begin
          rStat := r;
          rRslt := pRslt^.Rcode;                                   {1.2}
{!CC}     if rRslt < 0 then rRslt := rRslt+256;
          end;
      val := otcPB.rRslt;
      TCpeekTrans := r;
      end;


{ TCpokeTrans ---------------------------------------------------------}
{ Write into the Transporter's RAM
{----------------------------------------------------------------------}
FUNCTION TCpokeTrans {(adr: integer;    { transporter RAM address      )
                       val: integer;    { transporter RAM value        )
                   var res: integer):   { command result status        )
                            integer};   { returns driver status        }
      var r: integer;
      begin
      TCinitBlk (otcPB,RRptr,NIL,NIL);                             {1.2}
      with otcPB.pComd^ do begin
          a[crPAD1] := ord (adr DIV 256);
          a[crPAD2] := ord (adr MOD 256);
          a[crPEPO] := ord (PokeOp);
          a[crPVAL] := ord (val);
          end;
      r := ExecCmd (DebOp,otcPB,0);                                {1.2}
      res := RCbusy (otcPB,r);                                     {1.2}
      TCpokeTrans := r;
      end;

{$P}
{ ccOTCioInit ---------------------------------------------------------}
{ Initialize ccOTCio unit.  This procedure could also initialize the
{ Transporter and poke the proper values for the Transporter parameters
{ which have values other than the default..................
{----------------------------------------------------------------------}
PROCEDURE ccOTCioInit;
      var i: integer; b: boolean;
      begin
{!CC} TCintPtr := @TCinterrupt; RRptr := @otcRR; OmniUnit := OSomniDv;
{!CC} b := UnitBusy (OmniUnit); TChaveDrv := IOresult <> 0;
      for i := 1 to 4 do RecvSet[i] := FALSE;
      for i := 1 to TCCmaxCnt do TCcounts[i] := 0;
      i := TCpeekTrans (hF800,TCtrnVrsn);
      end;


{ ccOTCioTerm ---------------------------------------------------------}
{----------------------------------------------------------------------}
PROCEDURE ccOTCioTerm;
      var i,r,s: integer;
      begin
      for i := 1 to 4 do
          if RecvSet[i] then r := TCendRecv (i,s);
      end;

end.

