! Self-modifying reproducing Z-code.
!
! Generates random junk and sees how the interpreter behaves.  If it's clever
! it shouldn't die except for @quit and stack overflow.
!
! inform \$MAX_STATIC_DATA=40000 crashme.inf
!
! Written by Evin Robertson 1999.  Placed in public domain.

Array randstuff -> 32767;
Array checkfix  -> 257;

Global replay = 0;

Array filename string "CRASHME.MEM";

#iftrue (#version_number <= 3);
Constant Granularity = 2;
Constant FileMult = 2;
#endif;

#iftrue (#version_number >= 4 && #version_number <= 5);
Constant Granularity = 4;
Constant FileMult = 4;
#endif;

#iftrue (#version_number >= 6 && #version_number <= 7);
Constant Granularity = 4;
Constant FileMult = 8;
#endif;

#iftrue (#version_number == 8);
Constant Granularity = 8;
Constant FileMult = 8;
#endif;


[ Main i a g r c l t game_size checksum;
   game_size = FileMult * (0-->13); ! game size
   
   r = randstuff % Granularity;
   if(r)
      r = Granularity - r;

   a = randstuff + r;
   c = a / Granularity;
   l = 32767 - r;

   if(replay) {
      print "You are running crashme's output. This will repeat the test run which generated this output.^";
   } else {
      print "This program generates random Z-code which is run to test the robustness of your Z-machine interpreter. Most likely this will infinite loop. Do not run if you can't kill your interpreter when it is tightly looping.^Will attempt to write CRASHME.MEM which should be a valid Z-machine game which contains the same code that will be run.^";
   }
   print "Press 'q' to abort.^";
   @read_char 1 -> i;
   if(i == 'Q' or 'q')
      @quit;
   
   replay++;
   

   if(replay == 1) {
      for(i=0: i < l: i++)
	 a->i = random(256) - 1;
      for(i=0: i < 13: i++)
	 a->i = (emptyfunc * Granularity)->i;
      
      checksum = 0;
      for(i=0: i < l: i++)
	 checksum = checksum + a->i;
      g = 0-->6;  ! globals location
      for(i=0: i < 480: i++)
	 checksum = checksum + g->i;
      
      for(i=0: i < 257: i++) {
	 if(-255 <= checksum && checksum <= 0) {
	    checkfix->i = -checksum;
	    break;
	 } else {
	    checkfix->i = 255;
	    checksum = checksum + 255;
	 }
      }
      #IFV5; @save 0 game_size filename -> t; #ENDIF;
   }
   
   ! reduce the chances that it'll attempt to write over the same file
   for(i=0: i <= 11: i++)
      filename->i = 0;
   
   @call_vn c;
   print "^Done.^";
];

[ emptyfunc;
   print "starting...^";
];
