
/*   RamTest
**
**   von  Meinhard Ullrich                  
**
**   (C) Kickstart (Maxon Computer GmbH) 1988    
*/



#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 )
     /****************************/

   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)
     /****************************/

   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)
       /************************/

   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)
     /******************************************************************/


   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()
     /******************/

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

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


     /********************************/
VOID  Gebrauchsanweisung(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 )
     /****************************************/

 
   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)
     /************************/

   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()
     /**************/

{
   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)
     /****************/

   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");

}



