/*
 *   Here is a simple LIFE program which tests the blitter operations.
 *   It does not extensively test the shifts or anything, but it makes
 *   sure that the basic interface is correct.
 */
#define HSIZE (320)
#define MODULO ((HSIZE + 15)/16)*16
#define VSIZE (190)
#define RASTSIZE (MODULO / 16 * VSIZE)
#include "structures.h"
short *a, *b, *c, *d, *e, *t1=NULL, *t2=NULL, *t3=NULL, *t4=NULL, *t5=NULL ;
short noplanes ;
struct GfxBase *GfxBase = NULL ;     /* the GfxBase */
struct IntuitionBase *IntuitionBase = NULL ; /* the IntuitionBase */
struct Screen *myscreen = NULL ;
struct NewScreen mynewscreen = {
   0,                                         /* left edge */
   0,                                         /* top edge */
   320,                                       /* width */
   200,                                       /* height */
   2,                                         /* depth (change for color?)*/
   1,                                         /* detail pen */
   2,                                         /* block pen */
   0,                                         /* screen mode */
   CUSTOMSCREEN,                              /* type */
   NULL,                                      /* use default font */
   (UBYTE *)"LIFE by Tomas Rokicki",          /* title */
   NULL,                                      /* initialize this gadget field */
   NULL } ;                                   /* no bitmap supplied */
/*
 *   This routine gets a raster for temporary storage.
 */
short *myalloc() {
   void *AllocMem() ;
   void *p ;

   if ((p=AllocMem(2L*RASTSIZE, MEMF_CHIP | MEMF_CLEAR))==NULL) {
      printf("Could not allocate raster data\n") ;
      cleanup() ;
   }
   return(p) ;
}
/*
 *   Here we set things up.
 */
initialize() {
   initblitdata() ;
   if ((IntuitionBase = (struct IntuitionBase *)OpenLibrary(
      "intuition.library",0L))==NULL ||
       (GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",0L))
      ==NULL) {
      printf("Couldn't open libraries.\n") ;
      cleanup() ;
   }
   if ((myscreen = OpenScreen(&mynewscreen))==NULL) {
      printf("Couldn't open screen.\n") ;
      cleanup() ;
   }
   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 ;
   t1 = myalloc() ;
   t2 = myalloc() ;
   t3 = myalloc() ;
   t4 = myalloc() ;
   t5 = myalloc() ;
}
/*
 *   Exit routine.
 */
cleanup() {
   if (myscreen != NULL)
      CloseScreen(myscreen) ;
   myscreen = NULL ;
   if (IntuitionBase)
      CloseLibrary(IntuitionBase) ;
   IntuitionBase = NULL ;
   if (GfxBase)
      CloseLibrary(GfxBase) ;
   GfxBase = NULL ;
   if (t1)
      FreeMem(t1, 2L*RASTSIZE) ;
   if (t2)
      FreeMem(t2, 2L*RASTSIZE) ;
   if (t3)
      FreeMem(t3, 2L*RASTSIZE) ;
   if (t4)
      FreeMem(t4, 2L*RASTSIZE) ;
   if (t5)
      FreeMem(t5, 2L*RASTSIZE) ;
   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) ;
}
/*
 *   Main routine.  If called with no arguments, makes 1 bit plane screen.
 *   Otherwise, first argument is used as the number of bit planes.
 */
main (argc, argv)
int argc ;
char *argv[] ;
{
   register int i ;
   register int x, y ;
   long t1[3], t2[3] ;

   if (argc < 2 || sscanf(argv[1], "%d", &noplanes) < 1 || noplanes < 1
                || noplanes > 5)
      noplanes = 1 ;
   mynewscreen.Depth = noplanes ;
   initialize() ;
   for (i=0; i<RASTSIZE; i++)
      a[i] = 0 ;
   a[2010] = 0x0060 ;
   a[2030] = 0x00c0 ;
   a[2050] = 0x0040 ;
   i = 0 ;
   DateStamp(t1) ;
   x = 12 ;
   while (x != 0 || y != 100) {
      x = rnd(HSIZE-2) + 1 ;
      y = rnd(VSIZE-2) + 1 ;
      a[y*20+(x>>4)] |= 1 << (15 - (x & 15)) ;
      dogeneration() ;
      i++ ;
      x = myscreen->MouseX ;
      y = myscreen->MouseY ;
   }
   DateStamp(t2) ;
   printf("%ld ticks %d generations\n",
      t2[2]-t1[2] +
      (t2[1]-t1[1]) * 3000L +
      (t2[0]-t1[0]) * 4320000L, i) ;
   printf("Done.\n") ;
   cleanup() ;
}
