UNIT IPX;
(****************************************************************************)
(*                                                                          *)
(*  PROJEKT        : PASCAL Treiber fuer Novell-NetWare                     *)
(*  MODULE         : IPX.PAS                                                *)
(*  VERSION        : 1.10C                                                  *)
(*  COMPILER       : Turbo Pascal V 6.0                                     *)
(*  DATUM          : 13.06.91                                               *)
(*  AUTOR          : R. Gilomen                                             *)
(*  GEPRUEFT       : R. Gilomen                                             *)
(*                                                                          *)
(*--------------------------------------------------------------------------*)
(*                                                                          *)
(*  BESCHREIBUNG   : Bibliothek mit den IPX-Grunfunktionen. Dieses Modul    *)
(*                   wurde mit IPX Version 2.12 getestet.                   *)
(*                                                                          *)
(*--------------------------------------------------------------------------*)
(*                                                                          *)
(*  MODIFIKATIONEN :                                                        *)
(*                                                                          *)
(*  Version  1.00A      20.02.91  R. Gilomen    Initial Version             *)
(*  Version  1.10A      28.02.91  R. Gilomen    Neue Funktionen             *)
(*                                              IPX_To_Addr                 *)
(*                                              IPX_From_Addr               *)
(*                                              IPX_Internetwork_Address    *)
(*  Version  1.10B      07.03.91 R. Gilomen     Fehler in Funktion IPX_Done *)
(*                                              behoben. Bei SEND wurde     *)
(*                                              Source.Socket veraendert.   *)
(*  Version  1.10C      13.06.91 R. Gilomen     Defaultwert fuer Parameter  *)
(*                                              STAY_OPEN auf $FF gesetzt.  *)
(*                                                                          *)
(****************************************************************************)


(*//////////////////////////////////////////////////////////////////////////*)
                                   INTERFACE
(*//////////////////////////////////////////////////////////////////////////*)


(*==========================================================================*)
(*                         DEKLARATIONEN / DEFINITIONEN                     *)
(*==========================================================================*)

CONST

(* Allgemeine Deklarationen *)

         MAX_SOCKETS          = 20;    (* Maximale Anzahl konfigurierte     *)
                                       (* Kommunikationssockel.             *)
         MAX_DATA_SIZE        = 546;   (* Maximale Datenlaenge              *)
         NET_LENGTH           = 4;     (* Laenge Netzwerkadresse            *)
         NODE_LENGTH          = 6;     (* Laenge Knotenadresse              *)
   

(* Code Deklarationen *)

         SEND                  = $10;
         RECEIVE               = $20;


(* Deklaration der Rueckgabewerte *)

         SUCCESS               = $00;
         NOT_ENDED             = $10;
         PARAMETER_ERROR       = $20;
         NO_DESTINATION        = $21;
         DEVICE_SW_ERROR       = $30;
         SOCKET_TABLE_FULL     = $31;
         PACKET_BAD            = $32;
         PACKET_UNDELIVERIABLE = $33;
         PACKET_OVERFLOW       = $34;
         DEVICE_HW_ERROR       = $40;


TYPE   S4Byte          =  ARRAY [1..4]  OF BYTE; (* Datentyp fuer Network   *)
       S6Byte          =  ARRAY [1..6]  OF BYTE; (* Datentyp fuer Node      *)

                                                 (* Datentyp fuer Daten     *)
       Data_Packet     = ARRAY [1..MAX_DATA_SIZE] OF CHAR;

       SData           = RECORD                  (* Daten und Laenge        *)
                           Data   : Data_Packet;
                           Length : WORD;
                          END;

       Network_Address = RECORD                  (* Datentyp fuer NW-Adr.   *)
                           Network     : S4Byte;
                           Node        : S6Byte;
                           Socket      : WORD;
                         END;


(*==========================================================================*)
(*                         PROZEDUREN / FUNKTIONEN                          *)
(*==========================================================================*)


FUNCTION IPX_Setup : BYTE;
(*--------------------------------------------------------------------------*)
(*                                                                          *)
(*  BESCHREIBUNG : Die Routine initialisiert die IPX-Software und deren     *)
(*                 Funktion.                                                *)
(*                                                                          *)
(*                                                                          *)
(*  PARAMETER    :  IN : -                                                  *)
(*                                                                          *)
(*                  OUT: Rueckgabewert = Fehlercode                         *)
(*                                                                          *)
(*--------------------------------------------------------------------------*)



FUNCTION IPX_Open_Socket ( VAR Socket : WORD ) : BYTE;
(*--------------------------------------------------------------------------*)
(*                                                                          *)
(*  BESCHREIBUNG :  Die Routine eroeffnet einen Kommunikationssockel.       *)
(*                                                                          *)
(*                                                                          *)
(*  PARAMETER    :  IN : Socket        = Nummer des Sockels, der eroeffnet  *)
(*                                       werden soll.                       *)
(*                                                                          *)
(*                  OUT: Socket        = Nummer des Sockels, der effektiv   *)
(*                                       geoeffnet wurde.                   *)
(*                                                                          *)
(*                       Rueckgabewert = Fehlercode                         *)
(*                                                                          *)
(*--------------------------------------------------------------------------*)



FUNCTION IPX_Close_Socket ( Socket : WORD ) : BYTE;
(*--------------------------------------------------------------------------*)
(*                                                                          *)
(*  BESCHREIBUNG :  Die Routine schliesst einen Kommunikationssockel.       *)
(*                                                                          *)
(*                                                                          *)
(*  PARAMETER    :  IN : Socket        = Nummer des Sockels, der geschlos-  *)
(*                                       sen werden soll.                   *)
(*                                                                          *)
(*                  OUT: Rueckgabewert = Fehlercode                         *)
(*                                                                          *)
(*--------------------------------------------------------------------------*)



FUNCTION IPX_Send ( Socket    : WORD;
                    Dest_Addr : Network_Address;
                    Buffer    : SData
                  ) : BYTE;
(*--------------------------------------------------------------------------*)
(*                                                                          *)
(*  BESCHREIBUNG :  Die Routine dient zum senden von Daten an eine oder     *)
(*                  mehrere Gegenstationen.                                 *)
(*                                                                          *)
(*                                                                          *)
(*  PARAMETER    :  IN : Socket        = Sockelnummer, auf der gesendet     *)
(*                                       werden soll.                       *)
(*                       Dest_Addr     = Vollstaendige Netwerkadresse der   *)
(*                                       Gegenstation(en).                  *)
(*                       Buffer        = Daten die gesendet werden und      *)
(*                                       dessen Laenge.                     *)
(*                                                                          *)
(*                  OUT: Rueckgabewert = Fehlercode                         *)
(*                                                                          *)
(*--------------------------------------------------------------------------*)



FUNCTION IPX_Receive ( Socket : WORD ) : BYTE;
(*--------------------------------------------------------------------------*)
(*                                                                          *)
(*  BESCHREIBUNG :  Die Routine dient zum Empfangen von Daten einer Gegen-  *)
(*                  station. Die Daten koennen, wenn das Kommando beendet   *)
(*                  ist, mit der Funktion IPX_Done vom Netzwerk abgeholt    *)
(*                  werden.                                                 *)
(*                                                                          *)
(*  PARAMETER    :  IN : Socket        = Sockelnummer, auf der empfangen    *)
(*                                       werden soll.                       *)
(*                                                                          *)
(*                  OUT: Rueckgabewert = Fehlercode                         *)
(*                                                                          *)
(*--------------------------------------------------------------------------*)



FUNCTION IPX_Done ( Socket          : WORD;
                    Code            : BYTE;
                    VAR Source_Addr : Network_Address;
                    VAR Buffer      : SData
                  ) : BYTE;
(*--------------------------------------------------------------------------*)
(*                                                                          *)
(*  BESCHREIBUNG :  Die Funktion liefert den Status einer vorher abgesetz-  *)
(*                  ten Routine. Zurueckgegeben wird, ob die Routine schon  *)
(*                  beendet ist oder nicht sowie eventuelle Daten.          *)
(*                                                                          *)
(*                                                                          *)
(*  PARAMETER    :  IN : Socket        = Sockelnummer, auf der die Funktion *)
(*                                       ausgefuehrt werden soll.           *)
(*                       Code          = Routine, deren Status ueberprueft  *)
(*                                       werden soll.                       *)
(*                                                                          *)
(*                  OUT: Source_Addr   = Vollstaendige Netzwerkadresse der  *)
(*                                       Gegenstation, von der Daten einge- *)
(*                                       troffen sind.                      *)
(*                       Buffer        = Buffer, in dem eventuelle Daten    *)
(*                                       abgelegt werden koennen.           *)
(*                       Rueckgabewert = Fehlercode                         *)
(*                                                                          *)
(*--------------------------------------------------------------------------*)



FUNCTION IPX_Internetwork_Address ( VAR Network : S4Byte;
                                    VAR Node    : S6Byte
                                  ) : BYTE;
(*--------------------------------------------------------------------------*)
(*                                                                          *)
(*  BESCHREIBUNG :  Die Funktion liefert die Internetzwerkadresse der       *)
(*                  jeweiligen Station.                                     *)
(*                                                                          *)
(*                                                                          *)
(*  PARAMETER    :  OUT: Network       = Netzwerkadresse                    *)
(*                       Node          = Knotenadresse                      *)
(*                       Rueckgabewert = Fehlercode                         *)
(*                                                                          *)
(*--------------------------------------------------------------------------*)



FUNCTION IPX_To_Addr ( Network     : String;
                       Node        : String;
                       Socket      : String;
                       VAR Addr    : Network_Address
                     ) : BYTE;
(*--------------------------------------------------------------------------*)
(*                                                                          *)
(*  BESCHREIBUNG : Die Routine konvertiert die Eingabestrings in die Daten- *)
(*                 struktur Network_Address.                                *)
(*                                                                          *)
(*                                                                          *)
(*  PARAMETER    :  IN : Network       = Netzwerkadresse die konvertiert    *)
(*                                       werden soll.                       *)
(*                       Node          = Knotenadresse die konvertiert      *)
(*                                       werden soll.                       *)
(*                       Socket        = Sockelnummer die konvertiert       *)
(*                                       werden soll.                       *)
(*                                                                          *)
(*                  OUT: Addr          = Konvertierte vollsaendige Netz-    *)
(*                                       werkadresse.                       *)
(*                       Rueckgabewert = Fehlercode                         *)
(*                                                                          *)
(*--------------------------------------------------------------------------*)



FUNCTION IPX_From_Addr ( Addr            : Network_Address;
                         VAR Network     : String;
                         VAR Node        : String;
                         VAR Socket      : String
                       ) : BYTE;
(*--------------------------------------------------------------------------*)
(*                                                                          *)
(*  BESCHREIBUNG : Die Routine konvertiert die vollstaendige Netzwerk-      *)
(*                 adresse in String's.                                     *)
(*                                                                          *)
(*                                                                          *)
(*  PARAMETER    :  IN : Addr          = Vollstaendige Netzwerkadresse      *)
(*                                                                          *)
(*                  OUT: Network       = Netzwerkadresse die konvertiert    *)
(*                                       wurde.                             *)
(*                       Node          = Knotenadresse die konvertiert      *)
(*                                       wurde.                             *)
(*                       Socket        = Sockelnummer die konvertiert       *)
(*                                       wurde.                             *)
(*                       Rueckgabewert = Fehlercode                         *)
(*                                                                          *)
(*--------------------------------------------------------------------------*)




(*//////////////////////////////////////////////////////////////////////////*)
                                 IMPLEMENTATION
(*//////////////////////////////////////////////////////////////////////////*)


(*==========================================================================*)
(*                                UNITS IMPORT                              *)
(*==========================================================================*)

USES     Dos;

(*==========================================================================*)
(*                         DEKLARATIONEN / DEFINITIONEN                     *)
(*==========================================================================*)


CONST

(* Allgemeine Definitionen *)

         HEADER       = 30;            (* Groesse IPX-Header                *)
         PACKET_SIZE  = 576;           (* IPX-Paket groesse                 *)


(* Definitionen der IPX-Funktionen *)

         IPX_TST      = $7A00;         (* Vorbereiten fuer IPX Test         *)
         MUX_INTR     = $2F;           (* Multiplex Interrupt               *)
         OPEN_SOCKET  = $0000;         (* Oeffnet einen Sockel              *)
         CLOSE_SOCKET = $0001;         (* Schliesst einen Sockel            *)
         GET_TARGET   = $0002;         (* Pruefe Gegenstation               *)
         DO_SEND      = $0003;         (* Sendet ein Paket                  *)
         DO_RECEIVE   = $0004;         (* Empfaengt Pakete                  *)
         GET_ADDR     = $0009;         (* Bestimmt Internetzwerkadresse     *)


(* Definitionen der IPX-Parameter *)

         STAY_OPEN    = $FF;           (* $00 : Sockel bleibt geoeffnet,    *)
                                       (* bis er explizit geschlossen wird  *)
                                       (* oder das Programm terminiert.     *)
                                       (* $FF : Sockel bleibt geoeffnet,    *)
                                       (* bis er explizit geschlossen wird. *)
                                       (* Wird benoetigt fuer TSR-Programme.*)

(* Definitionen der IPX-Rueckgabewerte *)

         IPX_LOADED   = $FF;           (* IPX ist geladen                   *)
         OPENED       = $00;           (* Sockel erfolgreich geoeffnet      *)
         ALREADY_OPEN = $FF;           (* Sockel ist bereits goeffnet       *)
         TABLE_FULL   = $FE;           (* Sockel Tabelle ist voll           *)
         EXIST        = $00;           (* Weg zu Gegenstation existiert     *)
         NO_SOCKET    = $FF;           (* Sockel existiert nicht            *)
         SEND_OK      = $00;           (* Senden war erfolgreich            *)
         SOCKET_ERROR = $FC;           (* Sockel existiert nicht mehr       *)
         SIZE_ERROR   = $FD;           (* Paketgroesse nicht korrekt        *)
         UNDELIV      = $FE;           (* Paket nicht ausgeliefert          *)
         OVERFLOW     = $FD;           (* Buffer zu klein                   *)
         HW_ERROR     = $FF;           (* Hardware defekt                   *)
         REC_OK       = $00;           (* Paket erfolgreich empfangen       *)


(* Definition der ECB-Parameter *)

         FINISHED     = $00;           (* Routine beendet                   *)
         FRAG_COUNT   = 1;             (* Anzahl Fragmente                  *)
         UNKNOWN      = 0;             (* Unbekannter Paket Typ             *)

(* Deklarationen *)

TYPE     S12Byte      = ARRAY [1..12] OF BYTE;   (* Interner Datentyp       *)

         IPX_Packet   = RECORD         (* IPX-Paket Struktur                *)
                          CheckSum         : WORD;
                          Length           : WORD;
                          TransportControl : BYTE;
                          PacketType       : BYTE;
                          Destination      : Network_Address;
                          Source           : Network_Address;
                          IPX_Data         : Data_Packet;
                        END;

         ECB_Fragment = RECORD         (* Fragment der ECB Struktur         *)
                          Address : ^IPX_Packet;
                          Size    : WORD;
                        END;

         ECB = RECORD                  (* ECB Datenstruktur                 *)
                Link_Adress        : S4Byte;
                ESR_Address        : ^BYTE;
                InUseFlag          : BYTE;
                CompletionCode     : BYTE;
                SocketNumber       : WORD;
                IPX_Workspace      : S4Byte;
                DriverWorkspace    : S12Byte;
                ImmediateAddress   : S6Byte;
                FragmentCount      : WORD;
                FragDescr          : ECB_Fragment;
               END;


         Int_Addr = RECORD             (* Datenstruktur Internetzwerkadr.   *)
                      Network : S4Byte;
                      Node    : S6Byte;
                    END;


VAR      IPX_Location : ARRAY [1..2] OF WORD;    (* Adresse von IPX         *)

                                                 (* Array in dem die ECB's  *)
                                                 (* verwaltet werden.       *)
         ECB_Table    : ARRAY [1..MAX_SOCKETS] OF ^ECB;


(*==========================================================================*)
(*                         PROZEDUREN / FUNKTIONEN                          *)
(*==========================================================================*)


PROCEDURE IPX_Call ( VAR Regs : Registers );
(*--------------------------------------------------------------------------*)
(*                                                                          *)
(*  BESCHREIBUNG :  Diese Prozedur setzt die in Regs spezifizierten         *)
(*                  Register des Prozessors. Anschliessend wird ein IPX-    *)
(*                  Call ausgefuehrt und die Register wieder ausgelesen.    *)
(*                  Es werden nicht alle Register der Datenstruktur         *)
(*                  Regs uebernommen!                                       *)
(*                                                                          *)
(*  PARAMETER    :  IN : Regs          = Register, die gesetzt werden       *)
(*                                       sollen.                            *)
(*                                                                          *)
(*                  OUT: Regs          = Register, die vom IPX gesetzt      *)
(*                                       wurden (Return values).            *)
(*                                                                          *)
(*--------------------------------------------------------------------------*)

VAR      Temp_AX, Temp_BX, Temp_CX, Temp_DX,
         Temp_ES, Temp_SI, Temp_DI               : WORD;

BEGIN
 Temp_AX := Regs.AX;
 Temp_BX := Regs.BX;
 Temp_CX := Regs.CX;
 Temp_DX := Regs.DX;
 Temp_SI := Regs.SI;
 Temp_ES := Regs.ES;
 Temp_DI := Regs.DI;
 ASM
  PUSH BP                              (* Register sichern                  *)
  PUSH SP
  PUSH SS
  PUSH DS
  PUSH AX
  PUSH BX
  PUSH CX
  PUSH DX
  PUSH SI
  PUSH ES
  PUSH DI
  MOV AX, Temp_AX                      (* Register setzen                   *)
  MOV BX, Temp_BX
  MOV CX, Temp_CX
  MOV DX, Temp_DX
  MOV SI, Temp_SI
  MOV ES, Temp_ES
  MOV DI, Temp_DI
  CALL DWORD PTR IPX_Location          (* IPX aufrufen                      *)
  MOV Temp_AX, AX                      (* Register auslesen                 *)
  MOV Temp_BX, BX
  MOV Temp_CX, CX
  MOV Temp_DX, DX
  MOV Temp_SI, SI
  MOV Temp_ES, ES
  MOV Temp_DI, DI
  POP DI
  POP ES                               (* Gesicherte Register wieder        *)
  POP SI                               (* zuruecksetzen.                    *)
  POP DX
  POP CX
  POP BX
  POP AX
  POP DS                               
  POP SS                               
  POP SP
  POP BP
 END;

 Regs.AX := Temp_AX;
 Regs.BX := Temp_BX;
 Regs.CX := Temp_CX;
 Regs.DX := Temp_DX;
 Regs.SI := Temp_SI;
 Regs.ES := Temp_ES;
 Regs.DI := Temp_DI;
END;



FUNCTION IPX_Setup : BYTE;
(*--------------------------------------------------------------------------*)
(*                                                                          *)
(*  BESCHREIBUNG : Die Routine initialisiert die IPX-Software und deren     *)
(*                 Funktion.                                                *)
(*                                                                          *)
(*                                                                          *)
(*  PARAMETER    :  IN : -                                                  *)
(*                                                                          *)
(*                  OUT: Rueckgabewert = Fehlercode                         *)
(*                                                                          *)
(*--------------------------------------------------------------------------*)

VAR      i        : INTEGER;           (* Laufvariable                      *)
         Temp_Reg : Registers;         (* Temporaere Register fuer Int.     *)


BEGIN
  Temp_Reg.AX := IPX_TST;              (* Test ob IPX geladen.              *)
  Intr (MUX_INTR,Temp_Reg);
  IF (Temp_Reg.AL <> IPX_LOADED) THEN
  BEGIN
    IPX_Setup := DEVICE_SW_ERROR;      (* IPX nicht geladen                 *)
    EXIT;
  END;
  Temp_Reg.AX := Temp_Reg.ES;
  IPX_Location[1] := Temp_Reg.DI;      (* Adresse von IPX sichern           *)
  IPX_Location[2] := Temp_Reg.AX;

  FOR i := 1 TO MAX_SOCKETS DO         (* Array fuer ECB init.              *)
    ECB_Table[i] := NIL;

  IPX_Setup := SUCCESS;                (* Initialisierung erfolgreich       *)
END;



FUNCTION IPX_Open_Socket ( VAR Socket : WORD ) : BYTE;
(*--------------------------------------------------------------------------*)
(*                                                                          *)
(*  BESCHREIBUNG :  Die Routine eroeffnet einen Kommunikationssockel.       *)
(*                                                                          *)
(*                                                                          *)
(*  PARAMETER    :  IN : Socket        = Nummer des Sockels, der eroeffnet  *)
(*                                       werden soll.                       *)
(*                                                                          *)
(*                  OUT: Socket        = Nummer des Sockels, der effektiv   *)
(*                                       geoeffnet wurde.                   *)
(*                                                                          *)
(*                       Rueckgabewert = Fehlercode                         *)
(*                                                                          *)
(*--------------------------------------------------------------------------*)

VAR      i        : INTEGER;           (* Laufvariable                      *)
         Index    : INTEGER;           (* Index auf ECB_Table               *)

         Temp_Reg : Registers;         (* Temporaere Register fuer IPX-Call *)


BEGIN
  Socket := Swap(Socket);              (* In Motorola Format konvertieren   *)

  FOR i := 1 TO MAX_SOCKETS DO         (* Pruefen, ob Sockel existiert      *)
    IF ECB_Table[i] <> NIL THEN
      IF Socket = ECB_Table[i]^.SocketNumber THEN
      BEGIN
        IPX_Open_Socket := PARAMETER_ERROR;
        EXIT;
      END;

  Index := 1;
  WHILE (ECB_Table[Index] <> NIL) DO   (* Pruefen, ob alle Sockel belegt    *)
  BEGIN                                (* falls es noch freie ECB hat,      *)
    IF Index >= MAX_SOCKETS THEN       (* steht Index auf einem solchen.    *)
    BEGIN
      IPX_Open_Socket := SOCKET_TABLE_FULL;
      EXIT;
    END;
    Index := Index + 1;
  END;

  Temp_Reg.BX := OPEN_SOCKET;          (* Register fuer Call vorbereiten    *)
  Temp_Reg.AL := STAY_OPEN;
  Temp_Reg.DX := Socket;

  IPX_Call (Temp_Reg);

  Socket := Temp_Reg.DX;               (* Register auslesen                 *)

  IF Temp_Reg.AL <> OPENED THEN        (* IPX nicht i.O.                    *)
  BEGIN
    IPX_Open_Socket := DEVICE_SW_ERROR;
    EXIT;
  END;

  NEW (ECB_Table[Index]);              (* Vollstaendiger ECB erzeugen       *)
  NEW (ECB_Table[Index]^.FragDescr.Address);
  ECB_Table[Index]^.SocketNumber := Socket;

  Socket := Swap(Socket);              (* Zurueck in INTEL Format konv.     *)
  IPX_Open_Socket := SUCCESS;

END;



FUNCTION IPX_Close_Socket ( Socket : WORD ) : BYTE;
(*--------------------------------------------------------------------------*)
(*                                                                          *)
(*  BESCHREIBUNG :  Die Routine schliesset einen Kommunikationssockel.      *)
(*                                                                          *)
(*                                                                          *)
(*  PARAMETER    :  IN : Socket        = Nummer des Sockels, der geschlos-  *)
(*                                       sen werden soll.                   *)
(*                                                                          *)
(*                  OUT: Rueckgabewert = Fehlercode                         *)
(*                                                                          *)
(*--------------------------------------------------------------------------*)

VAR      Index    : INTEGER;           (* Index auf ECB_Table               *)

         Temp_Reg : Registers;         (* Temporaere Register fuer IPX-Call *)


BEGIN
  Socket := Swap(Socket);              (* In Motorola Format konvertieren   *)

  Index := 1;                          (* Sockel suchen                     *)
  WHILE (ECB_Table[Index]^.SocketNumber <> Socket) DO
  BEGIN                               
    IF Index >= MAX_SOCKETS THEN
    BEGIN
      IPX_Close_Socket := PARAMETER_ERROR;       (* Sockel existiert nicht  *)
      EXIT;
    END;
    Index := Index + 1;
  END;

  Temp_Reg.BX := CLOSE_SOCKET;         (* Register fuer Call vorbereiten    *)
  Temp_Reg.DX := Socket;

  IPX_Call (Temp_Reg);

                                       (* Allozierter Speicher freigeben    *)
  DISPOSE (ECB_Table[Index]^.FragDescr.Address);
  ECB_Table[Index]^.FragDescr.Address := NIL;
  DISPOSE (ECB_Table[Index]);
  ECB_Table[Index] := NIL;
 

  IPX_Close_Socket := SUCCESS;

END;



FUNCTION IPX_Send ( Socket    : WORD;
                    Dest_Addr : Network_Address;
                    Buffer    : SData
                  ) : BYTE;
(*--------------------------------------------------------------------------*)
(*                                                                          *)
(*  BESCHREIBUNG :  Die Routine dient zum senden von Daten an eine oder     *)
(*                  mehrere Gegenstation(en).                               *)
(*                                                                          *)
(*                                                                          *)
(*  PARAMETER    :  IN : Socket        = Sockelnummer, auf der gesendet     *)
(*                                       werden soll.                       *)
(*                       Dest_Addr     = Vollstaendige Netwerkadresse der   *)
(*                                       Gegenstation(en).                  *)
(*                       Buffer        = Daten die gesendet werden und      *)
(*                                       dessen Laenge.                     *)
(*                                                                          *)
(*                  OUT: Rueckgabewert = Fehlercode                         *)
(*                                                                          *)
(*--------------------------------------------------------------------------*)

VAR      i         : INTEGER;          (* Laufvariable                      *)
         Index     : INTEGER;          (* Index auf ECB_Table               *)

         Temp_Reg  : Registers;        (* Temporaere Register fuer IPX-Call *)

         Temp_Imm_Addr : S6Byte;       (* Temporaere ImmdediateAddress      *)

         Temp_Addr : S12Byte;          (* Temporaere Internetworkadresse    *)


BEGIN
  Socket := Swap(Socket);              (* In Motorola Format konvertieren   *)
  Dest_Addr.Socket := Swap(Dest_Addr.Socket);

  Index := 1;                          (* Sockel suchen                     *)
  WHILE (ECB_Table[Index]^.SocketNumber <> Socket) DO
  BEGIN
    IF Index >= MAX_SOCKETS THEN
    BEGIN
      IPX_Send := PARAMETER_ERROR;     (* Sockel existiert nicht            *)
      EXIT;
    END;
    Index := Index + 1;
  END;

  IF Buffer.Length > MAX_DATA_SIZE THEN     (* Laenge der Daten pruefen     *)
  BEGIN
    IPX_Send := PARAMETER_ERROR;
    EXIT;
  END;

  WITH Dest_Addr DO                    (* Pruefe ob Gegenstation erreichbar *)
  BEGIN
    FOR i := 1 TO NET_LENGTH DO        (* Internetzwerkadresse zusammenst.  *)
      Temp_Addr[i] := Network[i];
    FOR i := 1 TO NODE_LENGTH DO
      Temp_Addr[i + NET_LENGTH] := Node[i];
    Temp_Addr[11] := Lo(Socket);       (* Low-Byte                          *)
    Temp_Addr[12] := HI(Socket);       (* High-Byte                         *)
  END;

  Temp_Reg.ES := Seg(Temp_Addr);       (* Register fuer Call vorbereiten    *)
  Temp_Reg.SI := Ofs(Temp_Addr);

  Temp_Reg.DI := Ofs(Temp_Imm_Addr);
  Temp_Reg.BX := GET_TARGET;

  IPX_Call (Temp_Reg);

  ECB_Table[Index]^.ImmediateAddress := Temp_Imm_Addr;

  IF Temp_Reg.AL <> EXIST THEN
  BEGIN
    IPX_Send := NO_DESTINATION;        (* Weg nicht verfuegbar              *)
    EXIT;
  END;

  WITH ECB_Table[Index]^ DO            (* ECB mit Parametern fuellen        *)
  BEGIN
    ESR_Address := NIL;
    SocketNumber := Socket;
    InUseFlag := FINISHED;
    FragmentCount := FRAG_COUNT;
    WITH FragDescr.Address^ DO         (* IPX-Header vorbereiten            *)
    BEGIN
      PacketType := UNKNOWN;
      WITH Destination DO
      BEGIN
        Network := Dest_Addr.Network;
        Node := Dest_Addr.Node;
        Socket := Dest_Addr.Socket;
      END;
      IPX_Data := Buffer.Data;
    END;
    FragDescr.Size := Buffer.Length + 30;
  END;

  Temp_Reg.ES := Seg(ECB_Table[Index]^);  (* Register fuer Call vorbereiten *)
  Temp_Reg.SI := Ofs(ECB_Table[Index]^);
  Temp_Reg.BX := DO_SEND;

  IPX_Call (Temp_Reg);

  IPX_Send := SUCCESS;

END;



FUNCTION IPX_Receive ( Socket : WORD ) : BYTE;
(*--------------------------------------------------------------------------*)
(*                                                                          *)
(*  BESCHREIBUNG :  Die Routine dient zum Empfangen von Daten einer Gegen-  *)
(*                  station. Die Daten koennen, wenn das Kommando beendet   *)
(*                  ist, mit der Funktion IPX_Done vom Netzwerk abgeholt    *)
(*                  werden.                                                 *)
(*                                                                          *)
(*  PARAMETER    :  IN : Socket        = Sockelnummer, auf der empfangen    *)
(*                                       werden soll.                       *)
(*                                                                          *)
(*                  OUT: Rueckgabewert = Fehlercode                         *)
(*                                                                          *)
(*--------------------------------------------------------------------------*)

VAR      Index     : INTEGER;          (* Index auf ECB                     *)
         i         : INTEGER;          (* Laufvariable                      *)

         Temp_Reg  : Registers;        (* Temporaere Register fuer IPX-Call *)


BEGIN
  Socket := Swap(Socket);              (* In Motorola Format konvertieren   *)

  Index := 1;                          (* Sockel suchen                     *)
  WHILE (ECB_Table[Index]^.SocketNumber <> Socket) DO
  BEGIN
    IF Index >= MAX_SOCKETS THEN
    BEGIN
      IPX_Receive := PARAMETER_ERROR;  (* Sockel existiert nicht            *)
      EXIT;
    END;
    Index := Index + 1;
  END;

  WITH ECB_Table[Index]^ DO            (* ECB mit Parametern fuellen        *)
  BEGIN
    ESR_Address := NIL;
    FragmentCount := FRAG_COUNT;
    FragDescr.Size := PACKET_SIZE;
    InUseFlag := FINISHED;
  END;

  Temp_Reg.ES := Seg(ECB_Table[Index]^);    (* Register vorbereiten         *)
  Temp_Reg.SI := Ofs(ECB_Table[Index]^);
  Temp_Reg.BX := DO_RECEIVE;

  IPX_Call (Temp_Reg);

  IF Temp_Reg.AL = NO_SOCKET THEN
  BEGIN
    IPX_Receive := DEVICE_SW_ERROR;
    EXIT;
  END;

  IPX_Receive := SUCCESS;

END;




FUNCTION IPX_Done ( Socket          : WORD;
                    Code            : BYTE;
                    VAR Source_Addr : Network_Address;
                    VAR Buffer      : SData
                  ) : BYTE;
(*--------------------------------------------------------------------------*)
(*                                                                          *)
(*  BESCHREIBUNG :  Die Funktion liefert den Status einer vorher abgesetz-  *)
(*                  ten Routine. Zurueckgegeben wird, ob die Routine schon  *)
(*                  beendet ist oder nicht sowie eventuelle Daten.          *)
(*                                                                          *)
(*                                                                          *)
(*  PARAMETER    :  IN : Socket        = Sockelnummer, auf der die Funktion *)
(*                                       ausgefuehrt werden soll.           *)
(*                       Code          = Routine, deren Status ueberprueft  *)
(*                                       werden soll.                       *)
(*                                                                          *)
(*                  OUT: Source_Addr   = Vollstaendige Netzwerkadresse der  *)
(*                                       Gegenstation, von der Daten einge- *)
(*                                       troffen sind.                      *)
(*                       Buffer        = Buffer, in dem eventuelle Daten    *)
(*                                       abgelegt werden koennen.           *)
(*                       Rueckgabewert = Fehlercode                         *)
(*                                                                          *)
(*--------------------------------------------------------------------------*)

VAR      i         : INTEGER;          (* Laufvariable                      *)
         Index     : INTEGER;          (* Index auf ECB_Table               *)

         Temp_Reg  : Registers;        (* Temporaere Register fuer IPX-Call *)


BEGIN
  Socket := Swap(Socket);              (* In Motorola Format konvertieren   *)

  Index := 1;                          (* Sockel suchen                     *)
  WHILE (ECB_Table[Index]^.SocketNumber <> Socket) DO
  BEGIN
    IF Index >= MAX_SOCKETS THEN
    BEGIN
      IPX_Done := PARAMETER_ERROR;     (* Sockel existiert nicht            *)
      EXIT;
    END;
    Index := Index + 1;
  END;
                                       (* Test ob Funktion beendet          *)
  IF ECB_Table[Index]^.InUseFlag <> FINISHED THEN
  BEGIN
     IPX_Done := NOT_ENDED;
     EXIT;
  END;

  CASE Code OF
    SEND :
    BEGIN                              (* Send Completion Code auswerten    *)
      CASE ECB_Table[Index]^.CompletionCode OF
        SEND_OK      : ;
        SOCKET_ERROR : BEGIN
                         IPX_Done := DEVICE_SW_ERROR;
                         EXIT;
                       END;
        SIZE_ERROR   : BEGIN
                         IPX_Done := PACKET_BAD;
                         EXIT;
                       END;
        UNDELIV      : BEGIN
                         IPX_Done := PACKET_UNDELIVERIABLE;
                         EXIT;
                       END;
        HW_ERROR     : BEGIN
                         IPX_Done := DEVICE_HW_ERROR;
                         EXIT;
                       END
        ELSE           BEGIN
                         IPX_Done := DEVICE_SW_ERROR;
                         EXIT;
                       END;
      END;
    END;
    RECEIVE :
    BEGIN                             (* Receive Completion Code auswerten  *)
      CASE ECB_Table[Index]^.CompletionCode OF
        REC_OK : BEGIN                 (* Daten in Benutzerbuffer kopieren  *)
                   WITH ECB_Table[Index]^.FragDescr DO
                   BEGIN
                     Buffer.Data := Address^.IPX_Data;
                     Buffer.Length := Swap(Address^.Length) - HEADER;
                   END;
                                       (* Netzwerkadresse umkopieren        *)
                   WITH ECB_Table[Index]^.FragDescr.Address^.Source DO
                   BEGIN
                     Source_Addr.Network := Network;
                     Source_Addr.Node := Node;
                     Source_Addr.Socket := Swap(Socket);
                   END;
                 END;
        SOCKET_ERROR : BEGIN
                         IPX_Done := DEVICE_SW_ERROR;
                         EXIT;
                       END;
        OVERFLOW     : BEGIN
                         IPX_Done := PACKET_OVERFLOW;
                         EXIT;
                       END;
        NO_SOCKET    : BEGIN
                         IPX_Done := DEVICE_SW_ERROR;
                         EXIT;
                       END
        ELSE           BEGIN
                         IPX_Done := DEVICE_SW_ERROR;
                         EXIT;
                       END;
      END;
    END
    ELSE  BEGIN
            IPX_Done := PARAMETER_ERROR;
          EXIT;
    END;

  END;

  IPX_Done := SUCCESS;

END;



FUNCTION IPX_Internetwork_Address ( VAR Network : S4Byte;
                                    VAR Node    : S6Byte
                                  ) : BYTE;
(*--------------------------------------------------------------------------*)
(*                                                                          *)
(*  BESCHREIBUNG :  Die Funktion liefert die Internetzwerkadresse der       *)
(*                  jeweiligen Station.                                     *)
(*                                                                          *)
(*                                                                          *)
(*  PARAMETER    :  OUT: Network       = Netzwerkadresse                    *)
(*                       Node          = Knotenadresse                      *)
(*                       Rueckgabewert = Fehlercode                         *)
(*                                                                          *)
(*--------------------------------------------------------------------------*)

VAR      Temp_Reg     : Registers;     (* Temporaere Register fuer IPX-Call *)

         Reply_Buffer : Int_Addr;      (* Temporaerer Buffer fuer Adressen  *)

BEGIN

  Temp_Reg.ES := Seg(Reply_Buffer);    (* Register vorbereiten              *)
  Temp_Reg.SI := Ofs(Reply_Buffer);
  Temp_Reg.BX := GET_ADDR;

  IPX_Call (Temp_Reg);

  Network := Reply_Buffer.Network;     (* Daten umkopieren                  *)
  Node := Reply_Buffer.Node;

  IPX_Internetwork_Address := SUCCESS;

END;



FUNCTION IPX_To_Addr ( Network     : String;
                       Node        : String;
                       Socket      : String;
                       VAR Addr    : Network_Address
                     ) : BYTE;
(*--------------------------------------------------------------------------*)
(*                                                                          *)
(*  BESCHREIBUNG : Die Routine konvertiert die Eingabestrings in die Daten- *)
(*                 struktur Network_Address.                                *)
(*                                                                          *)
(*                                                                          *)
(*  PARAMETER    :  IN : Network       = Netzwerkadresse die konvertiert    *)
(*                                       werden soll.                       *)
(*                       Node          = Knotenadresse die konvertiert      *)
(*                                       werden soll.                       *)
(*                       Socket        = Sockelnummer die konvertiert       *)
(*                                       werden soll.                       *)
(*                                                                          *)
(*                  OUT: Addr          = Konvertierte vollsaendige Netz-    *)
(*                                       werkadresse.                       *)
(*                       Rueckgabewert = Fehlercode                         *)
(*                                                                          *)
(*--------------------------------------------------------------------------*)

VAR      i,n,Code  : INTEGER;
         c         : CHAR;
         Temp      : BYTE;

BEGIN

  (* Pruefe Netzwerk und Node Laenge *)
  IF (ORD(Network[0]) <> (2 * NET_LENGTH)) OR
     (ORD(Node[0]) <> (2 * NODE_LENGTH)) THEN
  BEGIN
    IPX_To_Addr := PARAMETER_ERROR;
    EXIT;
  END;

  (* Netzwerkadresse konvertieren *)
  i := 1;
  n := 1;
  WHILE ( i <= (2 * NET_LENGTH)) DO
  BEGIN
    c := UPCASE(Network[i]);
    CASE c OF
      'A'..'F': Addr.Network[n] := ORD(c) - 55;
      '0'..'9': Addr.Network[n] := ORD(c) - 48
    ELSE        BEGIN
                  IPX_To_Addr := PARAMETER_ERROR;
                  EXIT;
                END;
    END;
    Addr.Network[n] := Addr.Network[n] SHL 4;
    c := UPCASE(Network[i + 1]);
    CASE c OF
      'A'..'F': Temp := ORD(c) - 55;
      '0'..'9': Temp := ORD(c) - 48;
    ELSE        BEGIN
                  IPX_To_Addr := PARAMETER_ERROR;
                  EXIT;
                END;
    END;
    Addr.Network[n] := Addr.Network[n] + Temp;
    i := i + 2;
    n := n + 1;
  END;


  (* Node-Adresse konvertieren *)
  i := 1;
  n := 1;
  WHILE ( i <= (2 * NODE_LENGTH)) DO
  BEGIN
    c := UPCASE(Node[i]);
    CASE c OF
      'A'..'F': Addr.Node[n] := ORD(c) - 55;
      '0'..'9': Addr.Node[n] := ORD(c) - 48;
    ELSE        BEGIN
                  IPX_To_Addr := PARAMETER_ERROR;
                  EXIT;
                END;
    END;
    Addr.Node[n] := Addr.Node[n] SHL 4;
    c := UPCASE(Node[i + 1]);
    CASE c OF
      'A'..'F': Temp := ORD(c) - 55;
      '0'..'9': Temp := ORD(c) - 48;
    ELSE        BEGIN
                  IPX_To_Addr := PARAMETER_ERROR;
                  EXIT;
                END;
    END;
    Addr.Node[n] := Addr.Node[n] + Temp;
    i := i + 2;
    n := n + 1;
  END;

  (* Sockelnummer konvertieren *)
  VAL (Socket,Addr.Socket,Code);
  IF Code <> 0 THEN
  BEGIN
    IPX_To_Addr := PARAMETER_ERROR;
    EXIT;
  END;

  IPX_To_Addr := SUCCESS;

END;



FUNCTION IPX_From_Addr ( Addr            : Network_Address;
                         VAR Network     : String;
                         VAR Node        : String;
                         VAR Socket      : String
                       ) : BYTE;
(*--------------------------------------------------------------------------*)
(*                                                                          *)
(*  BESCHREIBUNG : Die Routine konvertiert die vollstaendige Netzwerk-      *)
(*                 adresse in String's.                                     *)
(*                                                                          *)
(*                                                                          *)
(*  PARAMETER    :  IN : Addr          = Vollstaendige Netzwerkadresse      *)
(*                                                                          *)
(*                  OUT: Network       = Netzwerkadresse die konvertiert    *)
(*                                       wurde.                             *)
(*                       Node          = Knotenadresse die konvertiert      *)
(*                                       wurde.                             *)
(*                       Socket        = Sockelnummer die konvertiert       *)
(*                                       wurde.                             *)
(*                       Rueckgabewert = Fehlercode                         *)
(*                                                                          *)
(*--------------------------------------------------------------------------*)

VAR      i,n,Code      : INTEGER;
         c             : CHAR;
         TempHi,TempLo : BYTE;

BEGIN

  (* Netzwerkadresse konvertieren *)
  i := 1;
  n := 1;
  WHILE ( i <= (2 * NET_LENGTH)) DO
  BEGIN
    TempHi := Addr.Network[n] DIV 16;  (* Hi-Nibble                         *)
    CASE TempHi OF
      10..15  : Network[i] := CHR(TempHi + 55);
      0..9    : Network[i] := CHR(TempHi + 48)
    ELSE        BEGIN
                  IPX_From_Addr := PARAMETER_ERROR;
                  EXIT;
                END;
    END;
    i := i + 1;
    TempLo := Addr.Network[n] MOD 16;  (* Lo-Nibble                         *)
    CASE TempLo OF
      10..15  : Network[i] := CHR(TempLo + 55);
      0..9    : Network[i] := CHR(TempLo + 48)
    ELSE        BEGIN
                  IPX_From_Addr := PARAMETER_ERROR;
                  EXIT;
                END;
    END;
    i := i + 1;
    n := n + 1;
  END;
  Network[0] := CHR(i);               (* Laenge Netzwerkadresse fuer String *)


  (* Node-Adresse konvertieren *)
  i := 1;
  n := 1;
  WHILE ( i <= (2 * NODE_LENGTH)) DO
  BEGIN
    TempHi := Addr.Node[n] DIV 16;     (* Hi-Nibble                         *)
    CASE TempHi OF
      10..15  : Node[i] := CHR(TempHi + 55);
      0..9    : Node[i] := CHR(TempHi + 48)
    ELSE        BEGIN
                  IPX_From_Addr := PARAMETER_ERROR;
                  EXIT;
                END;
    END;
    i := i + 1;
    TempLo := Addr.Node[n] MOD 16;     (* Lo-Nibble                         *)
    CASE TempLo OF
      10..15  : Node[i] := CHR(TempLo + 55);
      0..9    : Node[i] := CHR(TempLo + 48)
    ELSE        BEGIN
                  IPX_From_Addr := PARAMETER_ERROR;
                  EXIT;
                END;
    END;
    i := i + 1;
    n := n + 1;
  END;
  Node[0] := CHR(i - 1);              (* Laenge Knotenadr. fuer String     *)


  (* Sockelnummer konvertieren *)
  STR (Addr.Socket,Socket);

  IPX_From_Addr := SUCCESS;
END;

END.