unit ProtoDispat;

{**********************************************************}
{                                                          }
{ Development program for Protocol Dispatcher.             }
{ file : prtcldsp.text                                     }
{ date : 18-August-1983  kb                                }
{                                                          }
{ PROBLEMS :                                               }
{   1) What to do if setup recv fails?  Especially in      }
{      interrupt routine.                                  }
{   2) What to do if get dequeue error?                    }
{   3) Do I need User Control data in result vector?       }
{   4) Need to disable/enable level 3 ints when changing   }
{      entries in table.                                   }
{   5) Instead of having a max buffer size based on        }
{      protocols max use dispatchers max buffer size until }
{      can dynamically allocate buffers.                   }
{   6) What to do if end recv fails????.  could just be    }
{      inuse of curcmd entry??????                         }
{                                                          }
{**********************************************************}
Interface

Uses
    {$U /CCOS/OS.GLOBALS} Globals,
    {$U       NETGLOBALS} NetGlobals;

Var
    IOerror: Integer;        {temp for development}
    TRCsave: Byte;           {temp for development}

{Functions and Procedures}

{direct user call}
function PDuser(ScktNmbr: TCsockets;   {socket number}
                FncCode,               {function code}
                PID,                   {protocol id  }
                MaxBfSz : Integer;     {max buffer size}
                Address,               {pointer to int rtn or buffer}
                UserData: Longint)     {user specified data}
                                  : Integer;  {returns IOresult}

{initialization}
procedure PDinit;
{$P}
Implementation

Const
    PDtblLOC   = $1B0;   {save of PDControl address}

    {68000 status register values}
    SRNoOmni  =  $2300;  {disable omninet interrupts}
    SRAll     =  $2000;  {enable all interrrupts}

Var
    PDControl: PDCtlBlk;
    PD80Buffers,PD90Buffers: PDBufList;

{Procedures and Functions}

    procedure getA4A5(var A4,A5: Longint); external;

    procedure CallUserInt(var PIDent: PIDentry; var BCtl: BCentry); external;

    function DisInts(SR: Integer) : Integer; external;

    procedure EnbInts(SR: Integer); external;

    function GetPDtable : pPDCtlBlk;

    Var
       ppPDtbl: ^pPDCtlBlk;

    begin
    {get PD table pointer from SysCom}
    ppPDtbl := pointer(PDtblLOC);
    GetPDtable := ppPDtbl^;
    end; {GetPDtable}
{$P}
    procedure FillTCmd(var TC   : PDtrnpCMD;
                           pRslt: pPDresult;
                           pData: pPD1Buffer;
                           DLen : Integer;
                           cmd  : Byte;
                           sckt : TCsockets);
    begin
    pRslt^.ResultCode := TRCwait;
    with TC do
      begin
      p.RP := pRslt;
      p.DP := pData;
      p.LN := DLen;
      p.HL := 0;     {$80 and $90 have no user control}
      a[crOPCD] := cmd;
      case sckt of
        Sckt80: a[crSOCK] := TC80;
        Sckt90: a[crSOCK] := TC90;
        ScktA0: a[crSOCK] := TCA0;
        ScktB0: a[crSOCK] := TCB0;
        end;{case}
      end; {with}
    end;  {FillTCmd}

    procedure FillTPBlk(var TPB : PDtranpPB;
                        pTCmd   : pPDtrnpCMD;
                        pRtn    : pBytes);
    begin
    with TPB do
      begin
      pComd :=  pTCmd;
      pProc :=  pRtn;
      pUser :=  ord4(GetPDtable);
      end; {with}
    end;  {FillTPBlk}
{$P}
    function PDrecvCMD(pCtlBlk: pPDBufCtl; Index: Integer;
                       sckt: TCsockets) : Integer;
    Var TCcmdBlk : PDtrnpCMD;
        TDparmBlk: PDtranpPB;

    begin
    {setup transporter command block}
    with pCtlBlk^ do
      FillTCmd(TCcmdBlk, @BC[Index].Result, BC[Index].BufferAdr,
                  MaxBufSize, TCrcvOP, sckt);

    {setup driver parameter block}
    FillTPBlk(TDparmblk, @TCcmdBlk, pCtlblk^.IntrptRtn);

    UnitStatus( 33,TDparmblk,(ord(sckt)+1) ); {call driver}
    PDrecvCMD := IOresult;
    end; {PDrecvCMD}


    function PDendRecv(pCtlBlk: pPDBufCtl; Index: Integer;
                       sckt: TCsockets) : Integer;
    Var TCcmdBlk : PDtrnpCMD;
        TDparmBlk: PDtranpPB;
        ior,i: Integer;

    begin
    {setup transporter command block}
    with pCtlBlk^ do
      FillTCmd(TCcmdBlk, @BC[Index].Result, NIL, 0, TCendOP, sckt);
      {has no data buffer or buffer size}

    {setup driver parameter block - no interrupt procedure}
    FillTPBlk(TDparmblk, @TCcmdBlk, NIL);

    {call driver to clear socket and end receive}
    UnitStatus( 33,TDparmblk,(128 + (ord(sckt) + 1)) ); ior := IOresult;

    if (ior <> 0) then
       with pCtlBlk^.BC[Index].Result do
         begin
{????}   i := 40000; {time out counter}
         while ((ResultCode = TRCwait) AND (i>0)) do i := i - 1;
         if (ResultCode = TRCwait) then ior := 22; {IOEtimot}
         end; {with}

    PDendRecv := ior;
    end; {PDendRecv}
{$P}
    procedure PDsetRecv(pPDCtl: pPDBufCtl; sckt: TCsockets);
    Var i,ior,SR: Integer;
        Found: Boolean;

    begin
    SR := DisInts(SRNoOmni); {turn of net ints}
    with pPDCtl^ do
       begin
       {get another buffer}
       i := 1; Found := FALSE;
       while ( (i <= PDmaxBuffs) AND (NOT Found) ) do
          if (NOT BC[i].InUse) then
             Found := TRUE
          else
             i := i + 1;

       {if no buffer then set cur buf index = 0 then return}
       if NOT Found then
          begin
          CurrentBuff := 0;
          EnbInts(SR);
          exit(PDsetRecv);
          end
       else BC[i].InUse := TRUE;
       end; {with}

    EnbInts(SR);
    ior := PDrecvCMD(pPDCtl,i,sckt); {call transporter driver to setup rcv}

    with pPDCtl^.BC[i].Result do
      {if queued or (ok and result = $FE) then exit}
      if (ior = 30) OR ((ResultCode = TRCrcvset) AND (ior = 0)) then
         IOerror := ior
      else {error}
         {if sckt inuse do clear entry w/ endreceive then setup rcv}
         if (ior = 52) OR (ResultCode = TRCinuse) then
            begin
{????}      while (PDendRecv(pPDCtl,i,sckt) <> 0) do;
            ior := PDrecvCMD(pPDCtl,i,sckt)
            end
         else
            begin
{????}      IOerror := ior; {if other error???}
{????}      TRCsave := ResultCode;
            end;
    end; {PDsetRecv}
{$P}
    procedure IntRoutine(Dque, Error: Integer;   {dequeue flag & err code}
                         BufferPtr: pPD1Buffer;  {data buffer}
                         CtlBuf: pPDCtlBlk;      {user data= cntrl blk}
                         pPDBC: pPDBufCtl;       {ptr to BufCtl}
                         Sckt: TCsockets);       {socket number}

    Var  i,curbuff: Integer;
         Found: Boolean;

    begin
    with CtlBuf^ do
      if Dque = 0 then
         begin
         {current buf ctl entry is indexed by CurrentBuff field}
         curbuff := pPDBC^.CurrentBuff;
         PDsetRecv(pPDBC,Sckt); {setup another receive on socket $80}

         {get PID table entry}
         i := 1; Found := FALSE;
         while ( (i <= MAXPIDentries) AND (NOT Found) ) do
           with PIDtable[i] do
             if (ProtoID = BufferPtr^[1]) AND (SocketNum = Sckt) then
                Found := TRUE
             else
                i := i + 1;

         {if is a known protocol then call proto mgr else ignore}
         if (Found) then
            CallUserInt( PIDtable[i],pPDBC^.BC[ curbuff ] );

         end
      else { if bad then reset up command. }
           { unless is bad transporter ????}
         if Error <> 0 then PDsetRecv(pPDBC,Sckt);
    end; {IntRoutine}

    procedure PD90intrpt(Dque, Error: Integer;   {dequeue flag & err code}
                         RsltPtr: pPDresult;     {result array}
                         BufferPtr: pPD1Buffer;  {data buffer}
                         CtlBuf: pPDCtlBlk);     {user data= cntrl blk}

    begin
    IntRoutine(Dque, Error, BufferPtr, CtlBuf, @CtlBuf^.PD90Ctl, Sckt90);
    end; {PD90intrpt}

    procedure PD80intrpt(Dque, Error: Integer;   {dequeue flag & err code}
                         RsltPtr: pPDresult;     {result array}
                         BufferPtr: pPD1Buffer;  {data buffer}
                         CtlBuf: pPDCtlBlk);     {user data= cntrl blk}

    begin
    IntRoutine(Dque, Error, BufferPtr, CtlBuf, @CtlBuf^.PD80Ctl, Sckt80);
    end; {PD80intrpt}
{$P}
    function PDuser;{(ScktNmbr: TCsockets;
                      FncCode, PID, MaxBfSz: Integer;
                      Address, UserData: Longint) : Integer;}

    Var  pPDCB: pPDCtlBlk;
         pBufCtl: pPDBufCtl;
         A4,A5: Longint;
         ior,BufSize: Integer;

      procedure Deactivate;  {remove and deactivate entry}
      { Uses global variables: pPDCB and pBufCtl }

      Var count,i: Integer;

      begin
      { remove PID from PIDtable and count active entries}
      count := 0;
      for i := 1 to MAXPIDentries do
        with pPDCB^.PIDtable[i] do
          if Active then
             begin
             if (ProtoID = PID) AND (ScktNmbr = SocketNum) then
                Active := FALSE
             else
                count := count + 1;
             end;

      { if was last one then end recv }
      if (count = 0) then
         with pBufCtl^ do
           begin
{????}     while (PDendRecv(pBufCtl,CurrentBuff,ScktNmbr) <> 0) do;
           MaxBufSize := 0;  {to restart dispatcher new max size >}
           CurrentBuff := 0; {and no buffer setup. curbuf = 0     }
           end;
      end; {Deactivate}
{$P}
      procedure Activate;    {create and activate new entry}
      { Uses global variables: pPDCB and pBufCtl }

      Var i,SR: Integer;
          Found: Boolean;

      begin
      {if pmgr buf size is bigger than buffers then error}
      if (MaxBfSz > BufSize) then ior := 69 {IOEbszerr}
      else
        begin
        {find free PIDtable entry}
        i := 1;  Found := FALSE;
        while (i <= MAXPIDentries) AND (NOT Found) do
          if (pPDCB^.PIDtable[i].Active) then i := i + 1
                                         else Found := TRUE;

        {exit if no entry, give error, else setup entry}
        if Found then
           begin
           with pPDCB^.PIDtable[i] do
             begin
             SocketNum  := ScktNmbr;
             ProtoID    := PID;
             MaxBufSize := MaxBfSz;
             PMgrData   := UserData;
             PMgrRcvRtn := Address;
             RegA4      := A4;
             RegA5      := A5;
             end; {with}

           {if this protocol max buffer size is > current}
           {max buffer size then reset up receive with   }
           {this buffer size as new max.                 }
           with pBufCtl^ do
             if (MaxBfSz > MaxBufSize) then
                begin
                SR := DisInts(SRNoOmni); {turn of net ints}
                if CurrentBuff <> 0 then {else no rcv setup}
                   begin
{????}             while (PDendRecv(pBufCtl,CurrentBuff,ScktNmbr) <> 0) do;
                   BC[CurrentBuff].InUse := FALSE;
                   end;
                EnbInts(SR);
                MaxBufSize := MaxBfSz;
                PDsetRecv(pBufCtl,ScktNmbr);
                end;

           pPDCB^.PIDtable[i].Active := TRUE; {last thing is make it active}
           end {Found then}
        else ior := 51; {IOEtblfl}
        end; {else}
      end; {Activate}
{$P}
      procedure RestorBuf;
      { Uses global variables: pPDCB and pBufCtl }

      Var count,i: Integer;
          pPDbuf: pPD1Buffer;

      begin
      pPDbuf := pointer(Address);
      for i := 1 to PDmaxBuffs do
         with pBufCtl^.BC[i] do
           if (pPDbuf = BufferAdr) then InUse := FALSE;

      {count active entries}
      count := 0;
      for i := 1 to MAXPIDentries do
        if pPDCB^.PIDtable[i].Active then count := count + 1;

      {check if need to do setup receive because}
      {ran out of buffers in interrupt routine. }
      if (pBufCtl^.CurrentBuff = 0) AND (count <> 0) then
         PDsetRecv(pBufCtl,ScktNmbr);
      end;

    begin {PDuser}
      ior := 0;  {assume no error}
      getA4A5(A4,A5);        {save user's A4 and A5 registers}
      pPDCB := GetPDtable;   {get ptr to PD table}
      case ScktNmbr of
        Sckt80: begin
                BufSize := PD80BfSz;
                pBufCtl := @pPDCB^.PD80Ctl;
                end;
        Sckt90: begin
                BufSize := PD90BfSz;
                pBufCtl := @pPDCB^.PD90Ctl;
                end;
        ScktA0,
        ScktB0: begin
                PDuser := 3; {IOEioreq}
                exit(PDuser);
                end;
        end; {case}

      Case FncCode of
         PDFCactv: Activate;
         PDFCdact: Deactivate;
         PDFCrstr: RestorBuf;
         Otherwise: ior := 56; {IOEfnccd}
