/* :ts=2 */
/*
:.,/^}/s/;/;/g
 */
#include "include/debug.h"
/*   LIFE.  Original Life program, with blit funcition and 10-blit
 *   Life algorithm by Tomas Rokicki.
 *   User interface by David Storer,  Cedar Rapids, Iowa.
 *                     (Compuserve ID 71420,2672)
 *   February, 1988.
 *   Compiled with Manx Aztec C, 3.4a
 */
#include "life.h"
#include "structures.h"
#include "getfile.h"
short *a, *b, *c, *d, *e, *t1=NULL, *t2=NULL, *t3=NULL, *t4=NULL, *t5=NULL ;
short noplanes ;
short running = FALSE ;              /* loop control flag */
short singlestep = FALSE ;           /* single step control flag */
short tilemode = FALSE ;        /* tile mode flag when name starts w/ '*' */ 
short startpat, rr;
struct GfxBase *GfxBase = NULL ;     /* the GfxBase */
struct IntuitionBase *IntuitionBase = NULL ; /* the IntuitionBase */

struct IntuiMessage *message ;
struct IntuiMessage messagecopy ;
struct Screen *myscreen = NULL ;
struct Window *mywin = NULL ;
struct RastPort *rp = NULL ;
struct ViewPort *vp = NULL ;

struct NewScreen mynewscreen = {
   0,                                         /* left edge */
   0,                                         /* top edge */
   320,                                       /* width */
   200,                                       /* height */
   4,                                         /* depth */
   1,                                         /* detail pen */
   2,                                         /* block pen */
   0,                                         /* screen mode */
   CUSTOMSCREEN,                              /* type */
   NULL,                                      /* use default font */
   (UBYTE *)"LIFE by Tomas Rokicki & DWS",    /* title */
   NULL,                                      /* initialize this gadget field */
   NULL } ;                                   /* no bitmap supplied */

struct NewWindow mynewwin = {
		0,				/* Left */
		0,				/* Top */
		HSIZE,		/* Width */
		VSIZE+10,	/* Height */
		1,				/* DPen */
	  2,				/* BPen */
	  MOUSEBUTTONS|	/* IDCMP */
		VANILLAKEY  |
	  CLOSEWINDOW,

 /* BACKDROP   |  */
		BORDERLESS |
    ACTIVATE   |    
		RMBTRAP    |
		WINDOWCLOSE,	/* Flags */

		0,				/* Gadgets */
		0,				/* Check */
		(UBYTE *)"Life by Rokicki & Storer",		/* Title */
		0,				/* Screen */
		0,				/* Bitmap */
		0,				/* MinWidth */
		0,				/* MinHeight */
		0,				/* MaxWidth */
		0,				/* MaxHeight */
		CUSTOMSCREEN	/* Screen type */
};

UWORD colors[16] = {
                     0x011c,     /*  0 0000         */
                     0x0fff,     /*  1 0001         */
                     0x011c,     /*  2 0010         */
                     0x0fff,     /*  3 0011         */
                     0x011c,     /*  4 0100         */
                     0x0fff,     /*  5 0101         */
                     0x011c,     /*  6 0110         */
                     0x0fff,     /*  7 0111         */
                     0x077a,     /*  8 1000         */
                     0x0fff,     /*  9 1001         */
                     0x077a,     /* 10 1010         */
                     0x0fff,     /* 11 1011         */
                     0x077a,     /* 12 1100         */
                     0x0fff,     /* 13 1101         */
                     0x077a,     /* 14 1110         */
                     0x0fff };   /* 15 1111         */
struct	spriteimage {		/* for defining a variable cursor */ 
  UWORD posctl[2];
  UWORD sprdata[32];
  UWORD reserved[2];
};
struct spriteimage *ptr ;  /* pointer to new mouse pointer image data */

struct colonycursor {
    UWORD cldata[16];
};
struct colonycursor *ptr2;  /* pointer to DrawImage version of colony  */

struct colonydata {
    UBYTE cldata[16][16];
};

struct colony {
  char   keyassign;
	USHORT width, height;
	UBYTE  cldata[16][16];
};
#define MAXCOLONY 100
/* The form in slot 0 is for setting up the background grid. */
struct colony form[MAXCOLONY]  = {{'\0', 10, 10, { {1,1,0,0,0,1,0,0,0,0},
                                                   {1,0,0,0,0,0,0,0,0,0},
                                                   {0,0,0,0,0,0,0,0,0,0},
                                                   {0,0,0,0,0,0,0,0,0,0},
                                                   {0,0,0,0,0,0,0,0,0,0},
                                                   {1,0,0,0,0,1,0,0,0,0},
                                                   {0,0,0,0,0,0,0,0,0,0},
                                                   {0,0,0,0,0,0,0,0,0,0},
                                                   {0,0,0,0,0,0,0,0,0,0},
                                                   {0,0,0,0,0,0,0,0,0,0}}}};
short   erasemode = FALSE ;
#define MAXNAME 19
char names[MAXCOLONY][MAXNAME+1];  /* Here's a name for a pre-inited pat.*/
                                   /*  = { "1-dot Eraser" } ;  */

char *name[MAXCOLONY];
int  lastcolony ;
char wintitle[40];

int 		current = 1 ;		/* index to current form data */
int     oldcurr ;       /* holds old value of current during erasemode */

struct Image cursorimage = {
	0, 10,		/* LeftEdge, TopEdge 	*/
	0, 0,     /* Width, Height (set before use by DrawImage)	*/
	1,				/* Depth				*/
  NULL,     /* *ImageData   assigned later */
	1,				/* PlanePick 			*/
	0,				/* PlaneOnOff			*/
	NULL  		/* *NextImage			*/
};

struct Image       *ptr3;  /* pointer to new cursorimage */

int     rotmir  = 0 ;   /* current state of rotation-mirror fn */

  /* present rotmir = 0  1  2  3  4  5  6  7  */
	/*    after   */
UBYTE   rot90r[8] = { 3, 2, 7, 6, 1, 0, 5, 4 };   /* new rotmir */
/*YTE   rot90r[8] = { 5, 2, 7, 0, 1, 6, 3, 4 };   /* new rotmir */
UBYTE   mirva[8]  = { 2, 5, 0, 7, 6, 1, 4, 3 };   


int     ireverse, jreverse; /* used to cause subscripts to run hi to low */
int     imax, jmax ;        /* set from either height or width of current */
                            /* form, depending on bit 0 of rotmir */
printhelp()
{
  int i ;
	char ctrl[4];

  printf("\14");  /* form feed to clear screen */
	printf("Mouse Buttons:  ");
	printf(
	"    Left -- Place colony on display.  Right -- Rotate cursor.\n");
	printf("Keyboard controls:");
	printf(
	"  DEL --  Back up one generation, if Depth is 2 or 3.\n");
	printf(
	"  Spacebar -- start/stop.           Backspace -- single generation.\n");
	printf(
	"  Return, Tab -- Reverse cursor.    ESC  -- End program.\n");
	printf("Keypad controls:  ");
	printf("RightShift-zero: Clear display.         '6' -- Random.\n");
	printf(
	"  Enter: Read/Save pattern on disk. '-' -- Erase mode.");
	printf("    '.' -- Tile mode.\n");
	printf(
	"  '1', '2', '3' -- Set Depth.       '4' -- Refresh Grid.");
	printf("  '5' -- Remove Grid.\n");
	printf(
	"  '7' -- Quick restore.             '8' -- Quick save.\n");
	printf("Key assignments from file 'LIFE.KEY'.  ");
	printf("Use keys to change cursor.\n");

	for ( i = 1; i < lastcolony; i++) {
	  if ( form[i].keyassign < ' ') {
       ctrl[0] = '\'';
       ctrl[1] = '^';
			 ctrl[2] = form[i].keyassign + '@';
			 ctrl[3] = '\0';
		}
		else {
       ctrl[0] = ' ';
       ctrl[1] = '\'';
			 ctrl[2] = form[i].keyassign;
			 ctrl[3] = '\0';
		}
    printf(" %s'=%-19s", ctrl, name[i] );
	  if ( i % 3 == 0)  printf("\n");
  }
	if (i % 3 != 1) printf("\n");
} /* printhelp */

printheader()
{
  printf("\14\n\n\n\n\n\n\n\n");  /* form feed to clear screen */
	printf("                             LIFE\n\n\n");
	printf("                 Blit algorithm by Tomas Rokicki.\n");
	printf("                 Interaction by David Storer.\n");
	printf("\n\n\n\n\n");
	printf(
	"Use <left-Amiga-n and -m> to switch between Life display and Help.\n");
	printf("Press RETURN key to begin.") ;
	getchar(); /* Wait for user to acknowledge. */
} /* printheader */
/*
 *   This routine takes present value of rotmir and from its bit pattern
 *   determines the settings for imax, jmax, and ireverse, jreverse.
 */
next(cur)
int cur;   /* current form slot */
{
#define ICTL 4
#define JCTL 2
#define MIR  1

  if ( rotmir & MIR ) {             /* determine if rotation has reversed */
	  imax = form[cur].width;     /* role of width and height */
		jmax = form[cur].height;
  }
	else {
	  imax = form[cur].height;
		jmax = form[cur].width;
	}

	ireverse = ( rotmir & ICTL ) ?  imax-1  :  0 ;
	jreverse = ( rotmir & JCTL ) ?  jmax-1  :  0 ;
	/**debug*?
	printf("curr=%d, imax=%d, jmax=%d, irev=%d, jrev=%d, rotmir=%d \n",
	        cur, imax,    jmax,    ireverse,jreverse,rotmir);
  /**gubed */
} /* next */

/*
 *   getcolonies reads in the definitions of the colonies from the 
 *   file LIFE.KEY in the current directory.  The subscript of the last
 *   colony read is returned in lastcolony.
 */ 
getcolonies()
{
FILE   *fp ;
int w, h, col ;
int temp ;
int i, j ;
char key[2];

  for( i = 0; i < MAXCOLONY; i++) {
	   name[i] = &names[i][0];
	};

  if( NULL == (fp = fopen("LIFE.KEY", "r") ))
    cleanup("Can't open LIFE.KEY for input.");

	for(col = 1; col < MAXCOLONY; col++) {
	  if(EOF == fscanf(fp, "%20[~#]#", name[col] ) )
		  break;
		if( 3 != fscanf(fp, "%1s%d%d", key, &w, &h) ) {
		  printf("Malformed LIFE.KEY input, Colony %d\n", col);
			fclose(fp);
			cleanup("Input terminated.");
		};
		form[col].keyassign = key[0];
		if( (w > 16) || (w < 0) || (h > 16) || (h < 0) ) {
		  printf("Width and Height must be in range 1 to 16.");
			fclose(fp);
			cleanup("Input terminated.");
		};
		for (i = 0; i < h; i++) {
		  for (j = 0; j < w; j++) {
			  if( 1 != fscanf(fp, "%d", &temp) ) {
					  printf("Malformed LIFE.KEY data, Colony %d cell %d,%d\n",
						                                        col,     i, j);
						fclose(fp);
						cleanup("Input terminated.");
				};
				form[col].cldata[i][j] = temp;
			}
		}
		form[col].width = w;
		form[col].height = h;
	} /* outer for */
	lastcolony = col;
	fclose(fp);
} /* getcolonies */

/* 
 *   setcurr function converts a key value to a current form number 
 */
int setcurr(key)
char key;
{
  int i, found;
	found = FALSE ;
	for( i = 0; i < lastcolony; i++)
	  if ( key == form[i].keyassign ) { 
		  found = TRUE ;
			break ;
		};
	if( !found)
	  DisplayBeep(myscreen);
	return( found ? i : -1);
} /* setcur */

/*
 *   This routine gets a raster for temporary storage.
 */
short *myalloc() 
{
   void *AllocMem() ;
   void *p ;

   if ((p=AllocMem(2L*RASTSIZE, MEMF_CHIP | MEMF_CLEAR))==NULL) {
      cleanup("Could not allocate raster data\n") ;
   }
   return(p);
} /* myalloc */

#define ABS(a) ( (a>0) ? (a) : (-(a)) )
setcolony(cur)
int    cur;
{ UWORD row, bitmask;
  int   i, j, k;
  long  xoff, yoff ;

	next(cur);
	for( k = 0, i = 0; i < imax; k++, i++) {
	  row = 0;
		bitmask = 0x8000;
	  for( j = 0; j < jmax; j++) {
		  if ( rotmir & MIR )
			  row |= ( form[cur].cldata[ABS(j - jreverse)][ABS(i - ireverse)] ) ?
               bitmask : 0 ;
			else
			  row |= ( form[cur].cldata[ABS(i - ireverse)][ABS(j - jreverse)] ) ?
               bitmask : 0 ;
			bitmask >>= 1;
			} /* inner for */
		  ptr2 -> cldata[k] = row; 

			/* The data needed for SetPointer is in a different format from that */
			/* needed for DrawImage.  Here we take the DrawImage format and copy */
			/* the relevant part to the memory allocated for the pointer image.  */
			ptr -> sprdata[2 + k * 2] = row;

		}; /* outer for */
		
		xoff = yoff = -1L;
		SetPointer(mywin, ptr, (long)(imax + 1),
	                      		(long)jmax,
	         									xoff,  /* x-offset */
														yoff); /* y-offset */
} /* setcolony */

/* drawgrid sets up a tickmark every eighth pixel vert. & horiz. in     */
/* in bitplane 4.  The color table set set so that any dead cells will */
/* show either blue (background) or gridtick in blue-grey */
drawgrid()
{
	register int x, y ;
	for (y=0; y<VSIZE; y+=8)
	  for ( x=0; x<HWORDS; x++)
	    d[y*HWORDS+x] = 0x8080;
} /*drawgrid */

/*
 *   Here we set things up.
 */
initialize() 
{
   int cc;
   int i;
   void *AllocMem() ;

   initblitdata() ;
   if ((IntuitionBase =
	 		  (struct IntuitionBase *)OpenLibrary("intuition.library",0L))==NULL ||
       (GfxBase = 
			  (struct GfxBase *)OpenLibrary("graphics.library",0L)) ==NULL) {
      cleanup("Couldn't open libraries.") ;
   };

   if ((myscreen = (struct Screen *)OpenScreen(&mynewscreen))==NULL) {
      cleanup("Couldn't open screen.") ;
   };

   a = ((short *)(myscreen->BitMap.Planes[0])) + 200;
   b = ((short *)(myscreen->BitMap.Planes[1])) + 200;
   c = ((short *)(myscreen->BitMap.Planes[2])) + 200;
   d = ((short *)(myscreen->BitMap.Planes[3])) + 200;
   /* e = ((short *)(myscreen->BitMap.Planes[4])) + 200; */
	 e  = myalloc() ;       /* temp space for saving gen zero. */
   t1 = myalloc() ;				/* temporary space for blitter Life Algorithm */
   t2 = myalloc() ;				/* The fact that there are five temps is related*/
   t3 = myalloc() ;				/* to the algorithm, not the number of bit planes*/
   t4 = myalloc() ;
   t5 = myalloc() ;

		mynewwin.Screen = myscreen;

		if ((mywin = (struct Window *)OpenWindow(&mynewwin)) == NULL)
	   	cleanup("Can't open window");

		rp = mywin->RPort;
		vp = &myscreen->ViewPort;
	
    if ((ptr=AllocMem(128L, MEMF_CHIP | MEMF_CLEAR))==NULL) 
	    cleanup("Could not allocate pointer data\n") ;

	  if ((ptr2 = AllocMem(128L, MEMF_CHIP | MEMF_CLEAR))==NULL) 
	    cleanup("Could not allocate cursor data\n") ;

    if ((ptr3 = AllocMem(128L, MEMF_CHIP | MEMF_CLEAR))==NULL) 
	    cleanup("Could not allocate cursor image data\n") ;
	   
		LoadRGB4(vp, colors, 16L);
	  *ptr3 = cursorimage;
	  ptr3->ImageData = (USHORT *)ptr2;
		
		rotmir = 0;
		drawgrid();
	  current = oldcurr = 1;  /* start with form[1] */ 
		setcolony(current);


		 /**DEBUG**?
	   printf("ptr2=%lx, ptr3=%lx\n", ptr2, ptr3);
	   printf("Formimage: TopEdge=%x, LeftEdge=%x\n", ptr3->TopEdge,
	                  								ptr3->LeftEdge);
	   printf("Width=%x, Height=%x\n", ptr3->Width,
	                                   ptr3->Height);
	   printf("ImageData=%lx\n", ptr3->ImageData);
	   printf("End of initialize\n");
         cc = getchar();     
         if (cc== 'q' || cc == 'Q') cleanup("Abort");
		 /**DEBUG**/
} /* initialize */

	/*
	 *   Exit routine.
	 */
cleanup(s)
  char *s;
{
   if (mywin)         { ClearPointer(mywin);          CloseWindow(mywin) ;
	                                                    mywin = NULL ;        }
   if (myscreen)			{ CloseScreen(myscreen) ;		  	myscreen = NULL ;     }
   if (IntuitionBase) { CloseLibrary(IntuitionBase) ; IntuitionBase = NULL ;}
   if (GfxBase)       { CloseLibrary(GfxBase) ; 			GfxBase = NULL ;			}
   if (ptr)  					{ FreeMem(ptr, 128L);						ptr = NULL ;					}
   if (ptr2) 					{ FreeMem(ptr2, 128L);					ptr2 = NULL ;	  			}
   if (ptr3)					{ FreeMem(ptr3, 128L);					ptr3 = NULL ;         }
   if (e) 						{ FreeMem(e,  2L*RASTSIZE) ;		e  = NULL;						}
   if (t1) 						{ FreeMem(t1, 2L*RASTSIZE) ;		t1 = NULL;						}
	 if (t2) 						{ FreeMem(t2, 2L*RASTSIZE) ;		t2 = NULL;						}
	 if (t3) 						{ FreeMem(t3, 2L*RASTSIZE) ;		t3 = NULL;						}
   if (t4) 						{ FreeMem(t4, 2L*RASTSIZE) ;		t4 = NULL;						}
   if (t5) 						{ FreeMem(t5, 2L*RASTSIZE) ;		t5 = NULL;						}
   printf("%s\n",s);
   exit(0) ;
}

#define PARITY (0x96)
#define CARRY (0xe8)
#define PARITY2 (0x3c)
#define CARRY2 (0xc0)
#define SPECIAL1 (0x12)
#define SPECIAL2 (0xe0)
#define COPY (0xf0)
/*
 *   Does one LIFE generation.  Fancy algorithm uses only 10 blits.  If
 *   anyone can improve this, please let me know.
 */
dogeneration() 
{
   OwnBlitter() ;
/*
 *   Take horizontal sums.
 */
   blit(a, 0, 1,
        a, 2, 1,
        a, 1, 1,
        t1, 1, 1,
     		MODULO, HSIZE-2, VSIZE-2, PARITY) ;
   blit(a, 0, 1,
        a, 2, 1,
        a, 1, 1,
        t2, 1, 1,
     		MODULO, HSIZE-2, VSIZE-2, CARRY) ;
/*
 *   Take sums for middle row.
 */
   blit(a, 0, 1, 
        a, 2, 1,
        a, 1, 1,
        t3, 1, 1,
     		MODULO, HSIZE-2, VSIZE-2, PARITY2) ;
   blit(a, 0, 1, 
        a, 2, 1,
        a, 1, 1,
        t4, 1, 1,
     		MODULO, HSIZE-2, VSIZE-2, CARRY2) ;
/*
 *   Now, sum each of the three columns.
 */
   blit(t1, 1, 0,
        t1, 1, 2,
        t3, 1, 1,
        t5, 1, 1,
     		MODULO, HSIZE-2, VSIZE-2, PARITY) ;
   blit(t1, 1, 0,
        t1, 1, 2,
        t3, 1, 1,
        t3, 1, 1,
     		MODULO, HSIZE-2, VSIZE-2, CARRY) ;
   blit(t2, 1, 0,
        t2, 1, 2,
     		t4, 1, 1,
        t1, 1, 1,
     		MODULO, HSIZE-2, VSIZE-2, PARITY) ;
   blit(t2, 1, 0,
        t2, 1, 2,
        t4, 1, 1,
        t4, 1, 1,
     		MODULO, HSIZE-2, VSIZE-2, CARRY) ;
/*
 *   Now, check high two order bits, then combine with original and
 *   low order bit.
 */
   blit(t1, 1, 1,
        t4, 1, 1,
        t3, 1, 1,
        t2, 1, 1,
     		MODULO, HSIZE-2, VSIZE-2, SPECIAL1) ;
/*
 *   Before we do the final write, we copy bits down one generation.
 */
   switch (noplanes) {
case 5:
   blit(d, 1, 1,
        d, 1, 1, 
        d, 1, 1, 
        e, 1, 1,
     		MODULO, HSIZE-2, VSIZE-2, COPY) ;
case 4:
   blit(c, 1, 1,
        c, 1, 1,
        c, 1, 1,
        d, 1, 1,
     		MODULO, HSIZE-2, VSIZE-2, COPY) ;
case 3:
   blit(b, 1, 1,
        b, 1, 1, 
        b, 1, 1, 
        c, 1, 1,
     		MODULO, HSIZE-2, VSIZE-2, COPY) ;
case 2:
   blit(a, 1, 1,
        a, 1, 1,
        a, 1, 1,
        b, 1, 1,
     		MODULO, HSIZE-2, VSIZE-2, COPY) ;
default: ;
}
   blit(t2, 1, 1,
        t5, 1, 1,
        a, 1, 1, 
        a, 1, 1,
     		MODULO, HSIZE-2, VSIZE-2, SPECIAL2) ;
   DisownBlitter() ;
}
/*
 *   Random number generator; probably not a very good one.
 */
int rnd(i)
int i ;
{
   static long seed = 323214521 ;
   long rval ;

   seed = seed * 123213 + 121 ;
   rval = (seed >> 5) & 65535 ;
   return ((i * rval) >> 16) ;
}

randompat()
{
	register int x, y, i;

		BltClear(a, 2L * RASTSIZE, 0);

		for (i=0; i<10000; i++) {
				x = rnd(HSIZE-4) + 2;
				y = rnd(VSIZE-4) + 2;
				a[y*HWORDS+(x>>4)] |= 1 << (15 - (x & 15)) ;
				}
} /* randompat */


previousgen()
{
	if ( noplanes > 1)     /* move previous generation back to 'a' raster */
    blit(b, 1, 1,
         b, 1, 1,
         b, 1, 1,
         a, 1, 1,
      		MODULO, HSIZE-2, VSIZE-2, COPY) ;
	if ( noplanes > 2)     /* if 3 gens kept, move third back to second  */
    blit(c, 1, 1,
         c, 1, 1,
         c, 1, 1,
         b, 1, 1,
      		MODULO, HSIZE-2, VSIZE-2, COPY) ;

} /* previousgen */

changetitle(runflag, eraseflag, tileflag, gen)
 short  runflag, eraseflag, tileflag ;
 short  gen;
{
  int t ;
  static char *title[] = {"Stop, Space to start.                    ",
                          "Run,  Space to stop",
													"Tile mode.                               ",
													"Erase mode.  Keypad '-' exits"};
	static char *temp = "            ";

	if (tileflag) {
	  title[2][12] = '\0';
		strcat(title[2], name[current]);
	  t = 2;
	}
	else if (eraseflag)
		t = 3;
	else
	  t = (runflag) ? 1 : 0 ;
	if (!runflag) {
	  title[0][22] = '\0';
		sprintf(temp, "Gen. %d", gen);
		strcat(title[0], temp);
	}
	SetWindowTitles(mywin, title[t], -1L);
} /* changetitle */

domessage(x, y, gen) 
  int x, y, gen;
{
#define KEYPAD (0x0100)   /* Qual bit set if key is on keypad */ 
#define RTSHIFT (0x0002)   /* Qual bit set if right shift key down */
	register int i, j ;
	char key ;
  int found;
	BOOL keypad;		/* true if vanillakey was from keypad */

	switch(messagecopy.Class)
	{
		case MOUSEBUTTONS:
			if (messagecopy.Code == SELECTDOWN )
			{
	      ptr3->Width  = jmax;
				ptr3->Height = imax;
				ptr3 -> PlanePick = !erasemode;
				if (tilemode) {
					BltClear(a, 2L * RASTSIZE, 0);
				  for( i = imax+2; i < VSIZE-imax-2; i+=imax)
					  for( j = jmax+2; j < HSIZE-jmax-2; j+=jmax)
							DrawImage(rp, ptr3, (long)j, (long)i);
					tilemode = FALSE;
					movmem(a, e, 2*RASTSIZE);    /* save it.*/
					gen = 0;
					drawgrid();				/* redraw the grid. */
					changetitle(running, erasemode, tilemode, gen);
				}
				else
				  {
				  DrawImage(rp, ptr3, (long)x, (long)y);
					}
			}
			else if (messagecopy.Code == MENUDOWN )
				{
				  rotmir = rot90r[rotmir] ; /* rotate 90 deg to right */
		      setcolony(current);
				}
			break;
		case VANILLAKEY:
	    key = (char) messagecopy.Code ;
			keypad = (messagecopy.Qualifier & KEYPAD);
			if (keypad) 
			  switch(key) {
				  case '\r':    /* Enter key on keypad allows file input or output */
						if (dofile(myscreen, mywin, a)) /* solicit a file name and */
						  gen = 0;                      /* read or write it */
							movmem(a, e, 2*RASTSIZE);    /* reset gen if read */
						break;
					case '0' :   /* Keypad zero plus right shift key erases display */
					  if (messagecopy.Qualifier & RTSHIFT) {
					    BltClear(a, 2L * RASTSIZE, 0);
						  gen = 0 ;
						}
				 	  break ;
					case '-': 		/* Keypad '-' toggles erase mode */
						erasemode = !erasemode;
						tilemode = FALSE;     /* force to NOT tilemode */
						break;
					case '.' :   /* Keypad '.' toggles Tile Mode */
					  tilemode = !tilemode;
						erasemode = FALSE;     /* force to NOT erasemode */
						break;
					case '1':
					case '2':
					case '3':
					  noplanes = key - '0';
						break;
					case '4':    /* refresh grid */
					  drawgrid();
						break;
					case '5':   /* erase grid */
						BltClear(d, 2L * RASTSIZE, 0);
					  break;
					case '6':   /* generate random pattern */
					  randompat();
						movmem(a, e, 2*RASTSIZE);    /* save it.*/
						gen = 0;
						break;
					case '7':
					  movmem(e, a, 2*RASTSIZE);    /* restore it */
						gen = 0;
						break;
					case '9':
					  movmem(a, e, 2*RASTSIZE);    /* save it */
						break;
					default:
				        ;
				}
			else /* not keypad */
			switch(key) {
			  case '\r':			/* Return key does the mirror */
				case '\t':  		/* Tab key also */
					rotmir = mirva[rotmir];  /* Do mirror about vert axis */
					break;
			  case ' ':       /* space-bar starts and stops */
					running = !running ;  /* toggle run mode */
					singlestep = FALSE ;
				  break ;
			  case '\b':      /* back space single steps the display */
					running = TRUE ;         /* force run mode */
	        singlestep = TRUE ;      /* for one generation */
				  break ;
				case '\177':    /* DEL key backs up, if possible to prev gen. */
					previousgen();
					gen--;
				  break;
				case '\33':      /* Escape terminates program */
				  cleanup("");
			  default : 
				  found = setcurr( key );  /* set up for colony defined by 'key' */
					if (found != -1) {
						current = found;
						rotmir = 0;
					}
			} /* switch */
			
			/* set title according to mode */
		  changetitle(running, erasemode, tilemode, gen); 
	    setcolony(current);
			break;
		case CLOSEWINDOW:
				cleanup("") ;
		default:
			;
	}; /* switch */
	return(gen) ;
} /* domessage */


/*
 *   Main routine.
 */
main ()
{
	int cc;
	register int i ;
	register int gen ; 
	register int x, y ;

	printheader();
	getcolonies();
  initialize() ;
  printhelp();	 

  gen = 0 ;        /* generation counter */
  noplanes = 3;    /* init to three so user can back up. */
	                 /* keypad '1', '2', and '3' will change this */
    
	running = FALSE;            /* begin in not-running mode */
	for(;;){
	changetitle(running, erasemode, tilemode, gen);
	while(!running)   /*  initial pattern setup */
	{ 
		if((message = (struct IntuiMessage *)GetMsg(mywin->UserPort)) != 0L ) {
			messagecopy = *message;			/* make local copy for our use */
			ReplyMsg(message);
			gen = domessage(x, y, gen);
	  } /* if */
		x = myscreen->MouseX ;
		y = myscreen->MouseY - 10;
		y = (y < 1) ? 1 : y;
	} /* while for setup */ 

	do
	{
		if((message = (struct IntuiMessage *)GetMsg(mywin->UserPort)) != 0L )
		{
			messagecopy = *message;			/* make local copy for our use */

	      /***DEBUG***?
		    printf("Before ReplyMsg.  x=%x  y=%x \n", x, y);
			/**?  cc = getchar();
	        if (cc== 'q' || cc == 'Q') cleanup("Abort");   /** debug **?
		    printf("class=%lx   code=%lx \n", messagecopy.Class, 
				                                messagecopy.Code);
				printf("\n");
			/**?  cc = getchar();
	        if (cc== 'q' || cc == 'Q') cleanup("Abort");   /** debug **?
		  /***DEBUG***/

			ReplyMsg(message);
			gen = domessage(x, y, gen);
	  } /* if */
		x = myscreen->MouseX ;
		y = myscreen->MouseY - 10;
		y = (y < 1) ? 1 : y;

		dogeneration() ;
		gen++ ;
	} while (running && !singlestep ); 
	running = FALSE;
	} /* for(;;) */

} /* main */
