/*****************************************************/
/*                                                   */
/*                     RamTest                       */
/*                                                   */
/* Geschrieben von Meinhard Ullrich                  */
/*                 Hauptstraße 44                    */
/*                 8771 Urspringen                   */
/*                                                   */
/* Copyright Kickstart (Maxon Computer GmbH) 1988    */
/*                                                   */
/*****************************************************/


/*  Mit diesem Programm können Sie das Chipmem und das Fastmem Ihres
**  Amiga testen.
**
**  Folgende Optionen werden unterstützt:
**   -f Fastmem, -c Chipmem;                  Standardeinstellung: -cf
**   -p Drucker, -b Bildschirm, -d Datei      Standardeinstellung: -b
**   -a ausführliche, -k kurze Informationen  Standardeinstellung: -k
**  Aufruf: RamTest {-{C|c|F|f|P|p|B|b|A|a|K|k}{[D|d] <Dateiname>}}
**
**  Beispiel: RamTest -fPa  gibt einen ausführlichen Test des 
**                          Fastmems auf dem Drucker aus.
**
**            RamTest -dc df1:test.out
**                          testet das Chipmem und gibt Status- und 
**                          Fehlermeldungen auf die Datei df1:test.out aus.
**
**  Sie sollten die Ausgabe nur dann in eine Ramdatei umleiten, wenn sie
**  genug an Chipmem und Fastmem haben. Hat das Programm das ganze Chip- oder
**  Fastmem alloziert und vom anderen Speicherbereich ist nicht genug da, 
**  dann schlagen einige Schreibversuche in die Ramdatei fehl, sodaß diese
**  danach etwas verkrüppelt sein kann. 
**
**  Wurde bei den Optionen sowohl "Ausführlich" als auch "Kurz" gewählt,
**  so verwendet das Programm die Option, die als letzte in der Optionen-
**  zeichenkette vorkommt.
**
**  Übrigens sollten Sie - sofern Sie sich die Ergebnisse am Bildschirm
**  anschauen wollen - darauf achten, daß in den Preferences 80 Zeichen
**  pro Zeile eingestellt sind. Ansonsten bekommen Sie eine sehr häßliche
**  Bildschirmdarstellung, aber was red' ich...
**
**  Zur Funktion:
**  Das Programm beschreibt nacheinander den gesamten freien Speicher mit 
**  den Testmustern 0000, 1010, 0101 und 1111 und überprüft danach, ob der
**  Speicher noch diese Muster enthält.
**
**  Verwendeter Compiler:
**  Das Programm ist für den Aztec-Compiler geschrieben.
**
**  Ich schlage vor, die Objektdatei mit der Aztec-Linker-Option +C 
**  zu binden, weil damit Programm und Daten zur Laufzeit ins Chipmem
**  geladen werden, welchem ich mehr traue als dem Fastmem (vor allem
**  den Ram-Erweiterungen). Besteht besonderer Anlaß, dem Chipmem zu miß-
**  trauen, so sollten Sie eine Version mit der +F(astmem)-Option erstellen.
*/


#include "libraries/dosextens.h"
#include "exec/types.h"
#include "exec/memory.h"
#include "clib/macros.h"
#include "functions.h"


#define        MAX_SEGMENTE 100
#define        BIN 1
#define        DEZ 2
#define        HEX 3

typedef struct 
{
   ULONG Erster_Fehler;
   ULONG Letzter_Fehler;
   ULONG Fehlerzaehler;
} Fehlerrapport;

typedef struct                      /* Bitfeld */
   {                               
      unsigned Chipmem      : 1;
      unsigned Fastmem      : 1;
      unsigned Drucker      : 1;
      unsigned Bildschirm   : 1;
      unsigned Datei        : 1;
      unsigned Ausfuehrlich : 1;
   } Optionstyp; 

/***************************************************/
/***              Globale Variablen              ***/
/***************************************************/

BOOL                          Geschwaetzig;
char                          Textpuffer[80];

extern   struct FileHandle    *Open();
         struct FileHandle    *Bildschirm, *Drucker, *Datei;        

struct                     /* belegt 8*MAX_SEGMENTE Byte */
{
   ULONG * Tab_Speicheranfang;
   ULONG Tab_Speicherlaenge;
}                             Tabelle[MAX_SEGMENTE];    

LONG                          Gesamtfehler = 0;

Optionstyp                    Option = {0,0,0,0,0,0};
               
ULONG                         Testzahl[4] =
{ 
   0x00000000,             /* 00000000000000000000000000000000 */
   0xaaaaaaaa,             /* 10101010101010101010101010101010 */
   0x55555555,             /* 01010101010101010101010101010101 */
   0xffffffff              /* 11111111111111111111111111111111 */
};



/***************************************************/
/***          Prozeduren und Funktionen          ***/
/***************************************************/



     /****************************/
VOID  Speicherfreigabe( Segmente )
     /****************************/

 /****************************************************************
 Eingabe:     (unsigned long) Segmente
 Funktionen:  FreeMem  [Amiga-Standardfunktion]

 Effekt: Die Funktion gibt belegte Speichersegmente frei, deren Adressen
         und Längen in der globalen Variablen Tabelle gespeichert sind.
         <Segmente> bezeichnet die Anzahl der freizugebenden Segmente.
 ****************************************************************/

   ULONG  Segmente;

{
   /* Global: Tabelle[] */
   SHORT i;

   for (i = 1; i <= Segmente; i++)
      FreeMem( Tabelle[i-1].Tab_Speicheranfang,
               Tabelle[i-1].Tab_Speicherlaenge);
}

 

     /****************************/
void  SchreibeMedien(Zeichenkette)
     /****************************/

 /****************************************************************
 Eingabe:     (char *) Zeichenkette  
 Funktionen:  Write             [Amiga-Standardfunktion]
              strlen            [Aztec-Bibliothek]
              Speicherfreigabe  [interne Funktion]

 Effekt: Die Prozedur gibt die übergebene <Zeichenkette> auf alle
         geöffnete Kanäle aus. Diese können sein: Bildschirm,
         Drucker oder eine Datei.
 ****************************************************************/

   STRPTR  Zeichenkette;

{
   /* Global: Bildschirm, Drucker, Datei */
   STATIC BOOL  MissErfolg;
  
   if (Bildschirm)
      Write(Bildschirm, Zeichenkette, (LONG)strlen(Zeichenkette));

   if (Drucker && !MissErfolg)
   {
      MissErfolg = 
         (Write(Drucker, Zeichenkette, (LONG)strlen(Zeichenkette)) == -1);

      if (MissErfolg && !Bildschirm)     /* Sag uns, daß Du aussteigst */
         printf("Ab hier also ohne Drucker...\n");
   }   

   if (Datei)
      Write(Datei, Zeichenkette, (LONG)strlen(Zeichenkette));
}



       /************************/
STRPTR  Konvertiere(Zahl, Modus)
       /************************/

 /****************************************************************
 Eingabe:     (unsigned long) Zahl,
              (int)           Modus
 Ausgabe:     (char *)        Textpuffer
 Funktionen:  sprintf  [Aztec-Bibliothek]
              strcpy   [Aztec-Bibliothek]

 Effekt: Die Funktion erhält eine <Zahl> und wandelt sie dem <Modus>
         entsprechend in eine Zeichenkette vom Typ HEX, BIN oder DEZ um.
 ****************************************************************/

   ULONG  Zahl;
   int    Modus;

{
   /* Global: Textpuffer[] */
   SHORT i;

   if      (Modus == HEX)
      sprintf(Textpuffer, "%lx",Zahl);     
   else if (Modus == DEZ)                     
      sprintf(Textpuffer, "%ld", Zahl);  
   else if (Modus == BIN)
   {
      /* Bilde Maske mit einer 1 an der entsprechenden Stelle, vergleiche **
      ** mit der auszuwertenden Zahl und setze im Textpuffer an der ent-  **
      ** sprechenden Stelle eine '1' bzw. eine '0'.                       */
      for (i=0; i<32; i++)
         Textpuffer[i] = (((0x80000000 >> i) & Zahl) == 0) ? '0' : '1';
      Textpuffer[32] = '\0';
   }
   else
      strcpy(Textpuffer, "!!! Falscher Modus !!!");

   return (Textpuffer);   /* Rückgabe eines Funktionswertes nur aus       **
                          ** ästhetischen Gründen; eigentlich ist die     **
                          ** Adresse der globalen Variablen Textpuffer    **
                          ** auch global bekannt (vgl. Aufrufe der Fkt.). */
}


    
     /******************************************************************/
VOID  Teste_Speicherblock(Speicheranfang, Speicherlaenge, Sammelmeldung)
     /******************************************************************/

 /****************************************************************
 Eingabe:     (unsigned long *) Speicheranfang,
              (unsigned long)   Speicherlaenge
 Ausgabe:     (unsigned long)   ErsterFehler,
                                LetzterFehler,
                                Fehlerzaehler
 Funktionen:  SchreibeMedien  [interne Funktion]
              Konvertiere     [interne Funktion]
              MIN, MAX        [Makros, def. in clib/macros.h]

 Effekt: Die Funktion bekommt mit <Speicheranfang> einen Zeiger auf einen 
         Speicherblock und in <Speicherlaenge> die Länge des Blocks.
         Sie füllt den Speicherblock jeweils ganz mit einem der vier 
         Testmuster und prüft ihn danach wieder durch. 
         Sie gibt dabei Statusmeldungen auf alle geöffnete Kanäle aus.
         Die Ausgabeparameter werden in eine Struktur vom Typ Fehlerrapport
         geschrieben, deren Adresse in <Sammelmeldung> übergeben wurde.
 ****************************************************************/  

   ULONG         *  Speicheranfang;
   ULONG            Speicherlaenge; 
   Fehlerrapport *  Sammelmeldung;

{
   /* Global: Geschwaetzig, Testzahl[] */
   UCOUNT j;
   LONG lokaler_Fehler;
   REGISTER LONG Testmuster;   
   REGISTER ULONG * Speicherzeiger;
   REGISTER ULONG * Speicherende;
   REGISTER ULONG Erster_Fehler  = 0xffffffff;
   REGISTER ULONG Letzter_Fehler = 0x00000000;
   REGISTER ULONG Fehlerzaehler  = 0L;

   Speicherende = Speicheranfang + (Speicherlaenge/4);  
       /* Ein Langwort = 4 Bytes */

   for (j = 0; j <= 3; j++)      /* 4 Testmuster durchprobieren */
   {
      Testmuster = Testzahl[j];
      if(Geschwaetzig)
      {
         SchreibeMedien("  Beschreibe den Speicher mit  %");
         SchreibeMedien(Konvertiere(Testmuster, BIN));
         SchreibeMedien("\n");
      }

      /* Ein Dank an Claus Brod, der durch einen wertvollen Tip */
      /* zur Optimierung dieser Funktion beigetragen hat:       */
      /* Speicherzeiger statt Speicherarithmetik!               */

      /* Wir kleistern zuerst den ganzen reservierten Speicher voll ... */
      Speicherzeiger = Speicheranfang;
      while (Speicherzeiger < Speicherende)    
      {  
         *Speicherzeiger = Testmuster;   /* etwas günstiger als:           */
         Speicherzeiger++;               /* *Speicherzeiger++ = Testmuster */
      }            

      /* ... Und lesen ihn erst dann wieder aus */
      Speicherzeiger = Speicheranfang;
      lokaler_Fehler = 0L;
      while (Speicherzeiger < Speicherende)
      {
         if (*Speicherzeiger != Testmuster)
         {
            if(Geschwaetzig)
            {
               SchreibeMedien("     Adresse $");
               SchreibeMedien(Konvertiere((ULONG)Speicherzeiger, HEX));
               SchreibeMedien(", gelesen  %");
               SchreibeMedien(Konvertiere(*Speicherzeiger, BIN));      
               SchreibeMedien("\n");
               lokaler_Fehler++;
            }

            Erster_Fehler  = MIN(Erster_Fehler, (ULONG)Speicherzeiger);
            Letzter_Fehler = MAX(Letzter_Fehler, (ULONG)Speicherzeiger);
            Fehlerzaehler++;
         }
         Speicherzeiger++;
      }
      if (Geschwaetzig && (lokaler_Fehler != 0))
         SchreibeMedien("\n");   
   }

   /* Warum nicht gleich mit folgender Struktur arbeiten? **
   ** 1. ist das Arbeiten mit Registervariablen schneller,**
   ** 2. ist es so etwas übersichtlicher:                 */
   Sammelmeldung->Erster_Fehler  = Erster_Fehler; 
   Sammelmeldung->Letzter_Fehler = Letzter_Fehler;
   Sammelmeldung->Fehlerzaehler  = Fehlerzaehler;

}


     /******************/
VOID  Schliesse_Medien()
     /******************/

 /****************************************************************
 Funktionen:  Close  [Amiga-Standardfunktion]

 Effekt: Alle offenen Kanäle werden wieder geschlossen.
 ****************************************************************/

{
   /* Global: Bildschirm, Drucker, Datei */

   if (Bildschirm)
      Close(Bildschirm);
   if (Drucker)
      Close(Drucker);
   if (Datei)
      Close(Datei);
}


     /********************************/
VOID  Gebrauchsanweisung(Programmname)
     /********************************/

 /****************************************************************
 Eingabe:     (char *) Programmname
 Funktionen:  printf 

 Effekt: Gibt Gebrauchsanweisung für das Programm ins aktuelle
         CLI-Fenster aus. Es wird dabei berücksichtigt, ob der
         Benutzer das Programm umgetauft hat ( <Programmname> ).
 ****************************************************************/

   STRPTR  Programmname; 

{
   printf("\n\n");
   printf("Schreibweise: %s ", Programmname);
   printf("{-{C|c|F|f|P|p|B|b|A|a|K|k}{[D|d] <Dateiname>}}\n\n");
        
   printf("Die Optionen haben folgende Bedeutung:\n");

   printf("  C,c :  Das Chipmem wird getestet.\n");
   printf("  F,f :  Das Fastmem wird getestet.\n");
   /* Standard: Chipmem und Fastmem */

   printf("  P,p :  Die Ausgabe erfolgt auf den Drucker.\n");
   printf("  B,b :  Die Ausgabe erfolgt auf dem Bildschirm.\n");
   printf("  D,d :  Die Ausgabe erfolgt in eine Datei, die erfragt wird.\n");
   /* Standard: Bildschirmausgabe  */

   printf("  A,a :  Ausführliche Fehlermeldungen.\n");
   printf("  K,k :  Kurzer Fehlerbericht.\n");
   /* Standard: Kurzausführung                                   **
   ** Falls beide Optionen gewählt wurden, kommt das zum Tragen, **
   ** was zuletzt spezifiziert wurde.                            */
}




     /****************************************/ 
VOID  Setze_Optionen( Spezifikation, Vorgabe )
     /****************************************/

 /****************************************************************
 Eingabe:     (char *) Spezifikation,
              (BOOL)   Vorgabe
 Funktionen:  strlen  [Aztec-Bibliothek]

 Effekt: Ist <Vorgabe> wahr, wird das globale Bitfeld Optionen 
         entsprechend der Optionenzeichenkette <Spezifikation> 
         - andernfalls mit den Standardoptionen - besetzt.
 ****************************************************************/
 
   STRPTR  Spezifikation;
   BOOL    Vorgabe;

{
   /* Global: Option, Geschwaetzig */  
   int i;

   if (Vorgabe)   
      for (i = 1; i < strlen(Spezifikation); i++)
         switch(*(Spezifikation + i)) 
         {
            case 'c':
            case 'C':
               Option.Chipmem = 1;
               break;

            case 'f':
            case 'F':
               Option.Fastmem = 1;
               break;

            case 'p':
            case 'P':
               Option.Drucker = 1;
               break;

            case 'b':
            case 'B':
               Option.Bildschirm  = 1;
               break;

            case 'd':
            case 'D':
               Option.Datei = 1;
               break;

            case 'a':
            case 'A':
               Option.Ausfuehrlich = 1;
               break;

            case 'k':
            case 'K':
               Option.Ausfuehrlich = 0;
               break;

            default:
               printf("Option %c nicht vorgesehen!\n", *(Spezifikation + i));
               exit(FALSE);
               break;
         }

   if ((Option.Datei == 0) && (Option.Bildschirm == 0) && (Option.Drucker == 0))
      Option.Bildschirm = 1;                 /* Standardeinstellung */

   if ((Option.Chipmem == 0) && (Option.Fastmem == 0))
      Option.Chipmem = Option.Fastmem = 1;   /* Standardeinstellung */

   Geschwaetzig = (BOOL)Option.Ausfuehrlich;
}

     /************************/ 
VOID  Oeffne_Medien(Dateiname)
     /************************/

 /****************************************************************
 Funktionen:  printf,
              Open              [Amiga-Standardfunktion],
              Exit              [Amiga-Standardfunktion],
              Schliesse_Medien  [interne Funktion]

 Effekt: Entsprechend den gewählten Optionen bzw. der Standardeinstellung
         werden die Kanäle Datei, Drucker und Bildschirm für die Ausgabe
         geöffnet. Soll auch eine Datei geöffnet werden, verwendet die
         Funktion den Namen, der in der Kommandozeile eingegeben wurde.
 ****************************************************************/

   STRPTR  Dateiname;

{
   /* Global: Option, Bildschirm, Drucker, Datei */ 

   if (Option.Datei == 1)
   {
      Datei = Open(Dateiname, MODE_NEWFILE);
      if (!Datei)
      {
         printf("Leider kann ich diese Datei nicht öffnen. Schade, schade...\n");
         exit(FALSE);
      }
   }

   if (Option.Bildschirm == 1)
   {
      Bildschirm = Open("RAW:1/1/639/199/RamTest", MODE_NEWFILE);
      if (!Bildschirm)
      {
         printf("CLI-Fenster läßt sich nicht öffnen. Tut mir leid...\n");
         Schliesse_Medien();
         exit(FALSE);
      }

   }

   if (Option.Drucker == 1)
   {
      Drucker = Open("PRT:", MODE_OLDFILE);
      if ((Write(Drucker, "\0", 1L)) == -1L)
      {
         printf("Sind Sie sicher, daß Sie überhaupt einen Drucker haben?\n");
         Schliesse_Medien();
         exit(FALSE);
      }
   }
}

     /**************/ 
VOID  Speichertest()
     /**************/

 /****************************************************************
 Funktionen:  SchreibeMedien       [interne Funktion],
              AvailMem             [Amiga-Standardfunktion],
              AllocMem             [Amiga-Standardfunktion],
              Konvertiere          [interne Funktion],
              Teste_Speicherblock  [interne Funktion],
              Schliesse_Medien     [interne Funktion],
              printf,
              exit                 [Amiga-Standardfunktion],
              Speicherfreigabe     [interne Funktion]

 Effekt: In dieser Prozedur wird die meiste Arbeit getan: Es wird das
         Chipmem und/oder das Fastmem getestet. Dabei werden Status-
         und Fehlermeldungen auf die offenen Kanäle ausgegeben.
 ****************************************************************/

{
   STRPTR  Speicherbereich;
   LONG  Fehlerzaehler, Speicherart, getesteter_Speicher, Laenge;
   LONG  Belegte_Segmente;
   LONG  * Speicheranfang;
   Fehlerrapport  Sammelmeldung;   
   
   SchreibeMedien("                   ******************** \n");
   SchreibeMedien("                   *** Speichertest *** \n");   
   SchreibeMedien("                   ******************** \n\n\n");

   while ((Option.Chipmem == 1) || (Option.Fastmem == 1))
   {
      if (Option.Chipmem == 1)
      {
         Speicherbereich = "Chipmem";
         Speicherart = MEMF_CHIP | MEMF_PUBLIC;
         Option.Chipmem = 0;
      }
      else
      {
         Speicherbereich = "Fastmem";
         Speicherart = MEMF_FAST | MEMF_PUBLIC;
         Option.Fastmem = 0;
      }

      SchreibeMedien("=============================\n");
      SchreibeMedien(" Ich teste jetzt das ");
      SchreibeMedien(Speicherbereich);  
      SchreibeMedien("\n");
      SchreibeMedien("=============================\n\n");

      Belegte_Segmente = Fehlerzaehler = getesteter_Speicher = 0L;
   
      while ((Laenge = AvailMem(Speicherart | MEMF_LARGEST)) != NULL)
         if ((Speicheranfang = AllocMem(Laenge, Speicherart)) != NULL)
         {
            if (Belegte_Segmente < MAX_SEGMENTE)
            {
               Tabelle[Belegte_Segmente].Tab_Speicheranfang = Speicheranfang;
               Tabelle[Belegte_Segmente].Tab_Speicherlaenge = Laenge;
            }
            Belegte_Segmente++;

            SchreibeMedien("\nTeste den Speicher von $");
            SchreibeMedien(Konvertiere((ULONG)Speicheranfang, HEX ));
            SchreibeMedien(" bis $");
            SchreibeMedien(Konvertiere((ULONG)(Speicheranfang + Laenge/4), HEX ));
            SchreibeMedien(" (Länge: ");
            SchreibeMedien(Konvertiere((ULONG)Laenge, DEZ));
            SchreibeMedien(" Bytes)\n");

            Teste_Speicherblock(Speicheranfang, Laenge, &Sammelmeldung);
            /* Das letzte Argument bezeichnet ein Feld, **
            ** indem ich danach die Ergebnisse erwarte. */

            Fehlerzaehler += Sammelmeldung.Fehlerzaehler;
            getesteter_Speicher += Laenge;

            SchreibeMedien("  In diesem Segment befanden sich ");
            SchreibeMedien(Konvertiere(Sammelmeldung.Fehlerzaehler, DEZ ));
            SchreibeMedien(" Fehler.\n");
            
            if (Sammelmeldung.Fehlerzaehler != 0L)
            {
               SchreibeMedien("  Untere Fehlergrenze:   $");
               SchreibeMedien(Konvertiere(Sammelmeldung.Erster_Fehler, HEX ));
               SchreibeMedien("\n");
               SchreibeMedien("  Obere Fehlergrenze:    $");
               SchreibeMedien(Konvertiere(Sammelmeldung.Letzter_Fehler, HEX ));
               SchreibeMedien("\n");
               SchreibeMedien("  Länge dieses Segments: ");
               SchreibeMedien(Konvertiere(Sammelmeldung.Letzter_Fehler -
                                          Sammelmeldung.Erster_Fehler, DEZ));
               SchreibeMedien(" Bytes\n");
             }

         }
         else
         {
            Schliesse_Medien();
            printf("System gibt mir freien Speicher nicht!\n");
            exit(FALSE);
         } 

      SchreibeMedien("\n\n Im ");
      SchreibeMedien(Speicherbereich);
      SchreibeMedien(" traten insgesamt (bei vier Testmustern) ");
      SchreibeMedien(Konvertiere(Fehlerzaehler, DEZ));
      SchreibeMedien(" Fehler auf.\n");
      
      SchreibeMedien(" Es wurden insgesamt ");
      SchreibeMedien(Konvertiere(getesteter_Speicher, DEZ));
      SchreibeMedien(" ($");
      SchreibeMedien(Konvertiere(getesteter_Speicher, HEX));
      SchreibeMedien(") Bytes getestet.\n");

      SchreibeMedien(" Der Speicher war auf ");
      SchreibeMedien(Konvertiere(Belegte_Segmente, DEZ));
      SchreibeMedien(" Segmente verteilt.\n\n\n\n");

      Gesamtfehler += Fehlerzaehler;

      Speicherfreigabe(MIN(Belegte_Segmente, MAX_SEGMENTE));

   }   
}

     /****************/
VOID  main(argc, argv)
     /****************/

 /****************************************************************
 Funktionen:  index              [Aztec-Bibliothek],
              Gebrauchsanweisung [interne Funktion],
              Setze_Optionen     [interne Funktion],
              Oeffne_Medien      [interne Funktion],
              Speichertest       [interne Funktion],
              Read               [Amiga-Standardfunktion],
              Schliesse_Medien   [interne Funktion]

 Effekt: "Und hurre hurre, hopp, hopp, hopp,
          Gings fort in sausendem Galopp,
          Daß Roß und Reiter schnoben
          Und Kies und Funken stoben..." 
         [aus: Lenore, Gottfried August Bürger]
 ****************************************************************/

   int     argc;
   STRPTR  argv[];


{
   STRPTR Spezifikation, Dateiname;
   char  Puffer[2];     /* Es gibt Tasten, die zwei Werte liefern */


   /* Falls Optionen spezifiziert wurden, muß genau dann ein Dateiname */
   /* angegeben worden sein, wenn die Option d (D) verwendet wurde.    */                  
   if (
         (argc != 1)
         && !((*argv[1] == '-')  
             &&(  ((argc == 2) && (  (index(argv[1], 'd') == 0L) 
                                   &&(index(argv[1], 'D') == 0L)))  
                ||((argc == 3) && (  (index(argv[1], 'd') != 0L)
                                   ||(index(argv[1], 'D') != 0L)))
               )
             )
      )
   {  
      Gebrauchsanweisung(argv[0]);
      exit(FALSE);
   }

   Spezifikation = (argc >= 2) ? argv[1] : "dummy";
   Setze_Optionen(Spezifikation, (argc >= 2));  
 
   Dateiname = (argc == 3) ? argv[2] : "noname";
   Oeffne_Medien(Dateiname);

   Speichertest();

   if (Bildschirm)         /* Warte auf Tastendruck */
      Read(Bildschirm, Puffer, 1L);
   
   Schliesse_Medien();
   
   if (Gesamtfehler != 0L)
      printf ("\nSpeichertest fand Fehler! \n");

}



