/*       AutomaLab         
             by            
       Michael Creutz      
   creutz@wind.phy.bnl.gov         */

/* 9 September 1991 */
/* Program to simulate totalistic cellular automata.  
   Each cell is updated according to the number of    
   active neighbors.  The number of neighbors for a   
   birth or for survival are set with corresponding   
   gadgets.  Either a 4 or 8 neighborhood can     
   be selected. Conway's life uses an eight cell      
   neighborhood with births on 3 and survival on      
   2 or 3.  New live cells can be added by sketching 
   with the mouse.  Menu items select periodic, dead,
   or alive boundaries as well as the size of the blocks.

   If the "Xpast" gadget is activated the result is also
   XOR'ed with the generation one further step back.
   This gives rise to reversable rules; switching present
   with past reverses the sequence of steps.  This is
   accomplished by hitting the "Rev." gadget.    

   The program directly accesses the blitter 
   for speed.  It compiles with PDC.  */

/* these are the standard Commodore include files,
   obtainable from CATS */
#include <intuition/intuition.h>
#include <graphics/gfx.h>
#include <graphics/gfxmacros.h>
#include <hardware/custom.h>
#include <hardware/blit.h>
#include <exec/memory.h>
#include <stdio.h> 

/* initial configuration; start with life on a periodic lattice
   with small blocks */
short birth=1<<3, 
      survivor=((1<<2)|(1<<3)), 
      neighborhood=8,
      boundary=0, /* periodic */
      block=1;    /* block size */ 

/* size parameters: lattice is SX by SY, SX a multiple of 16
   cell size is "block" by "block"
   all these variables should be divisible by "block" */
#define XMIN 16L
#define YMIN 16L
#define SX 192L 
#define SY 180L

    /* IDCMPFlags */
#define windowflags=VANILLAKEY|MOUSEBUTTONS|MENUVERIFY|GADGETDOWN|MENUPICK
#define safeflags=VANILLAKEY|MOUSEBUTTONS|GADGETDOWN|MENUPICK

/* positions for gadgets */
#define NGTOP 46
#define RGTOP 62

USHORT XPointer[]= /* image of custom pointer */
{0,0,
 0xff80,0,
 0x8080,0x0800,
 0x8080,0x0800,
 0x8080,0x0000,
 0x8080,0x6300,
 0x8080,0x0000,
 0x8080,0x0800,
 0x8080,0x0800,
 0xff80,0,
 0,0
};
# define MYPOINTERSIZE 44
char * XPointerchip=NULL;

/* custom chip registers and parameters for blitter */
struct Custom *custom = (struct Custom *) 0xdff000; 
# define MODULO (40-SX/8)
short blitsize,  
      backshift; /* used for descending mode */

/* plane pointers */
char *bplane, *historyplane, 
  *workplane[]={NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};

/* other structure pointers */
struct IntuitionBase *IntuitionBase=NULL;
struct GfxBase       *GfxBase      =NULL;
struct Screen        *screen       =NULL;
struct Window        *window       =NULL;
struct RastPort      *rastport;
struct MsgPort       *userport;
struct IntuiMessage  *message;
struct TextAttr topaz=
{(STRPTR)"topaz.font",TOPAZ_EIGHTY,0,0};

/* a custom screen */
struct NewScreen newscreen =
{0,0,320,200,        /* dimensions */
 3,                  /* depth */
 5,6,                /* pens */
 0,                  /* viewmodes */
 CUSTOMSCREEN,       /* type */
 &topaz,              /* font */
 "  AutomaLab",      /* title */
 0,                  /* gadgets */
 0                   /* bitmap */
};

/* and a custom window */
struct NewWindow newwindow =
{0,0,320,200,        /* dimensions */
 4,2,                /* pens */ 
 windowflags,        /* IDCMPFlags */
 ACTIVATE|SMART_REFRESH|NOCAREREFRESH
   |BACKDROP|BORDERLESS, /* Flags */
 0,                  /* *FirstGadget */
 0,                  /* *CheckMark */
 "AutomaLab",        /* *Title */
 0,                  /* Screen */
 0,                  /* BitMap */ 
 80,24,-1,-1,        /* min dimensions */
 CUSTOMSCREEN        /* Type */ 
};

/* vectors for pause and clear boxes */
short vectors[]=
{41,0, 41,10, 0,10, 0,0, 41,0},
/* vectors for a small box */
smallvectors[]=
{9,0,9,9,0,9,0,0,9,0};

struct Border moreborder =
{-1,-1,
  1,0,JAM1,
  3,
  &(vectors[4]),
  NULL
},
myborder =
{-1,-1,
 2,0,JAM1,
 3,
 vectors,
 &moreborder
},
moresmallborder =
{-1,-1,
  1,0,JAM1,
  3,
  &(smallvectors[4]),
  NULL
},
smallbox=
{-1,-1,
 2,0,JAM1,
 3,
 smallvectors,
 &moresmallborder
};

/* text attached to gadgets and menus */
struct IntuiText pausetext=
 {2,6,JAM1,0,1,&topaz,"Pause",0},
cleartext = 
 {2,6,JAM1,0,1,&topaz,"Clear",0},
xorpasttext = 
 {2,6,JAM1,0,1,&topaz,"Xpast",0},
reversetext = 
 {2,6,JAM1,4,1,&topaz,"Rev.",0},
speedtext=
 {1,0,JAM1,-12,2,&topaz,"S          F",0},
projecttext[]=
 {{2,1,JAM2,0,0,&topaz,"  Author ",NULL},
  {2,1,JAM2,0,0,&topaz,"  Quit   ",NULL}
},
boundarytext[]=
{{2,1,JAM2,CHECKWIDTH,0,&topaz,"Periodic",NULL},
 {2,1,JAM2,CHECKWIDTH,0,&topaz,"Dead    ",NULL},
 {2,1,JAM2,CHECKWIDTH,0,&topaz,"Live    ",NULL}
},
blocktext[]=
{{2,1,JAM2,CHECKWIDTH,0,&topaz,"tiny    ",NULL},
 {2,1,JAM2,CHECKWIDTH,0,&topaz,"medium  ",NULL},
 {2,1,JAM2,CHECKWIDTH,0,&topaz,"big     ",NULL}
},
requesttext[]=
{{2,1,JAM2,4,4,&topaz,"continue",NULL},
 {2,1,JAM2,4,8 ,&topaz,"Totalistic Cellular Automaton",&requesttext[2]},
 {2,1,JAM2,4,20,&topaz,"         Simulator           ",&requesttext[3]},
 {2,1,JAM2,4,30,&topaz,"            by               ",&requesttext[4]},
 {2,1,JAM2,4,40,&topaz,"       Michael Creutz        ",&requesttext[5]},
 {2,1,JAM2,4,50,&topaz,"          BNL 510A           ",&requesttext[6]},
 {2,1,JAM2,4,60,&topaz,"       Upton, NY 11973       ",&requesttext[7]},
 {2,1,JAM2,4,70,&topaz,"   creutz@wind.phy.bnl.gov   ",NULL}
};

#define IWIDTH 82
#define IHEIGHT 8
struct MenuItem 
 projectitem[]=
  {{&projectitem[1],0,0,IWIDTH,IHEIGHT,
    ITEMTEXT|ITEMENABLED|HIGHCOMP,0,
    (APTR) &projecttext[0],NULL,NULL,NULL,MENUNULL
   },  
   {NULL,0,8,IWIDTH,IHEIGHT,
    ITEMTEXT|ITEMENABLED|HIGHCOMP,0,
    (APTR) &projecttext[1],NULL,NULL,NULL,MENUNULL
   }
  },  
 boundaryitem[]=
  {{&boundaryitem[1],0,0,IWIDTH,IHEIGHT,
    ITEMTEXT|ITEMENABLED|HIGHCOMP|CHECKIT|CHECKED,2|4,
    (APTR) &boundarytext[0],NULL,NULL,NULL,MENUNULL
   },  
   {&boundaryitem[2],0,8,IWIDTH,IHEIGHT,
    ITEMTEXT|ITEMENABLED|HIGHCOMP|CHECKIT,1|4,
    (APTR) &boundarytext[1],NULL,NULL,NULL,MENUNULL
   },
   {NULL,0,16,IWIDTH,IHEIGHT,
    ITEMTEXT|ITEMENABLED|HIGHCOMP|CHECKIT,1|2,
    (APTR) &boundarytext[2],NULL,NULL,NULL,MENUNULL
   }
  },  
 blockitem[]=
  {{&blockitem[1],0,0,IWIDTH,IHEIGHT,
    ITEMTEXT|ITEMENABLED|HIGHCOMP|CHECKIT|CHECKED,2|4,
    (APTR) &blocktext[0],NULL,NULL,NULL,MENUNULL
   },  
   {&blockitem[2],0,8,IWIDTH,IHEIGHT,
    ITEMTEXT|ITEMENABLED|HIGHCOMP|CHECKIT,1|4,
    (APTR) &blocktext[1],NULL,NULL,NULL,MENUNULL
   },
   {NULL,0,16,IWIDTH,IHEIGHT,
    ITEMTEXT|ITEMENABLED|HIGHCOMP|CHECKIT,1|2,
    (APTR) &blocktext[2],NULL,NULL,NULL,MENUNULL
   }
  };  

struct Menu mymenus[]=
{
 {&mymenus[1], 10,0,63,8,MENUENABLED,"Project",&(projectitem[0])},
 {&mymenus[2],100,0,63,8,MENUENABLED,"Boundary",&(boundaryitem[0])},
 {NULL,       200,0,63,8,MENUENABLED,"Block",&(blockitem[0])}
};

/* needed for speed gadget */
struct PropInfo speedinfo =
{AUTOKNOB|FREEHORIZ, /*Flags*/
 0xffff, /* HorizPot */
 0xffff, /* VertPot */
 50, /* HorizBody */
 0, /* VertBody */
 0,0,0,0,0,0
}; 
struct Image image;

/* gadget structures */
struct Gadget pausegadget =
 {0,  /* next gadget in the list */
  223,33,40,9, /* position, size */
  GADGHCOMP,     /* Flags */
  GADGIMMEDIATE|TOGGLESELECT,   /* Activation */
  BOOLGADGET, /* GadgetType */
  &myborder,  /* GadgetRender */
  0,  /* SelectRender */
  &pausetext,  /* GadgetText */
  0,  /* MutualExclude */
  0,0,0
 },
xorgadget =
 {0,  /* next gadget in the list */
  223,RGTOP+100,40,9,
  GADGHCOMP,     /* Flags */
  GADGIMMEDIATE|TOGGLESELECT,   /* Activation */
  BOOLGADGET, /* GadgetType */
  &myborder,  /* GadgetRender */
  0,  /* SelectRender */
  &xorpasttext,  /* GadgetText */
  0,  /* MutualExclude */
  0,5,0
 },
reversegadget =
 {0,     /* next gadget in the list */
  270,RGTOP+100,40,9,
  GADGHCOMP,     /* Flags */
  RELVERIFY|GADGIMMEDIATE, /* Activation */
  BOOLGADGET, /* GadgetType */
  &myborder,  /* GadgetRender */
  0,  /* SelectRender */
  &reversetext,  /* GadgetText */
  0,  /* MutualExclude */
  0,6,0
 },
cleargadget =
 {0,     /* next gadget in the list */
  270,33,40,9,
  GADGHCOMP,     /* Flags */
  RELVERIFY|GADGIMMEDIATE, /* Activation */
  BOOLGADGET, /* GadgetType */
  &myborder,  /* GadgetRender */
  0,  /* SelectRender */
  &cleartext,  /* GadgetText */
  0,  /* MutualExclude */
  0,1,0
 },
speedgadget =
 {NULL,	/* next gadget in the list */
  230,15,70,10,
  GADGHNONE,     /* Flags */
  RELVERIFY|GADGIMMEDIATE,   /* Activation */
  PROPGADGET, /* GadgetType */
  &image,  /* GadgetRender */
  0,  /* SelectRender */
  &speedtext,  /* GadgetText */
  0,  /* MutualExclude */
  &speedinfo,  /* SpecialInfo */
  3,0
 },
neighborgadget =
 {NULL, /* next gadget in the list */
  304,NGTOP,8,8,
  GADGHCOMP,     /* Flags */
  GADGIMMEDIATE,   /* Activation */
  BOOLGADGET, /* GadgetType */
  &smallbox,  /* GadgetRender */
  0,  /* SelectRender */
  0,  /* GadgetText */
  0,  /* MutualExclude */
  0,  /* SpecialInfo */
  4,0
 },
birthgadgets[9],
survivorgadgets[9];

main()
{ULONG class;
 USHORT code;
 short i,g,speed,menu,item,menuwaiting;
 void *iaddress;
 long mx,my,oldmx,oldmy;
 char label;
 menuwaiting=0;
 openup();
 while (1) /* main loop */
  {message=GetMsg(userport); /* check for messages */
   if (message)  
    {class   =message-> Class;
     code    =message-> Code;
     iaddress=message-> IAddress;
     mx      =message-> MouseX;
     my      =message-> MouseY;
     ReplyMsg(message);
     switch (class) /* what kind of message */
      {case VANILLAKEY  : /* quit if <esc> or a control character */
        if (code<32) cleanup(" all done");
        break;
       case MENUVERIFY:
        menuwaiting=-1;
        break;
       case MENUPICK:
        menuwaiting=0;
        while ( (code&0xffff) != MENUNULL) 
         {item=ITEMNUM(code);
          menu=MENUNUM(code);
          switch (menu)
           {case 0:
             if (item==1) cleanup (" all done");
             if (item==0) /* show credits */
              {ModifyIDCMP(window,safeflags);
               AutoRequest(window,&requesttext[1],
                           NULL,&requesttext[0],
                           0,0,260,120);  
               ModifyIDCMP(window,windowflags);
              };   
             break;
            case 1: /* boundary menu */
             if (item>=0 && item<=2)
              {boundary=item;
               fixboundary();
              } 
             break;
            case 2: /* block menu */
             if (item>=0 && item<=2)
              {if (item>0) block=1<<item;
               else block=1;
               setsize();
               clearfield();
              }
             break;
           }; 
          code=( (struct MenuItem *) ItemAddress
               (&(mymenus[0]),(long) code) ) -> NextSelect;
         }; /* end menu searching loop */
        break;
       case GADGETDOWN  :
        switch ( g=(((struct Gadget *) iaddress)->GadgetID) )
         {case 0 :    /* pause gadget */
           WaitPort(userport);
           break; 
          case 1 :    /* clear gadget */ 
           clearfield();
           break;
          case 4 :    /* neighborhood gadget */
           neighborhood=12-neighborhood;
           label=neighborhood+'0';
           Move(rastport,304L,NGTOP+6L);
           SetAPen(rastport,7);
           SetBPen(rastport,0);
           SetDrMd(rastport,JAM2);
           Text(rastport,&label,1); 
           break;
          case 6 : /* reverse gadget; swaps present and past */
           OwnBlitter();
           myblit(bplane,historyplane,
                  0,historyplane,
                  0,ANBC|NABC|ANBNC|NABNC);
           myblit(historyplane,bplane,
                  0,bplane,
                  0,ANBC|NABC|ANBNC|NABNC);
           myblit(bplane,historyplane,
                  0,historyplane,
                  0,ANBC|NABC|ANBNC|NABNC);
           DisownBlitter();
           fixboundary();
           break;
          default :   /* check for rule gadget */
           if ( (g<=18) && (g>=10))
            {if (birthgadgets[i=g-10].Flags & SELECTED)
              birth |= (1<<i);
             else
              birth &= (~(1<<i));
            }
           if ( (g<=28) && (g>=20))
            {if (survivorgadgets[i=g-20].Flags & SELECTED)
              survivor |= (1<<i);
             else
              survivor &= (~(1<<i));
            }
           break;             
         } 
        break;
       case MOUSEBUTTONS:   /* sketch if in box */
        if (code==SELECTDOWN)
         if( (mx>=XMIN)&&(my>=YMIN)
           &&(mx<XMIN+SX)&&(my<YMIN+SY)) 
            {SetWrMsk(rastport,1);
             SetDrMd(rastport,COMPLEMENT);
             Move(rastport,mx,my);
             if (block==1)
              while(GetMsg(userport)==0) 
               {mx=(*window).MouseX;
                my=(*window).MouseY;
                if( (mx>=XMIN)&&(my>=YMIN)
                  &&(mx<XMIN+SX)&&(my<YMIN+SY)) 
                    Draw(rastport,mx,my);
               }
             else /* draw on coarse lattice */
              {oldmx=0;
               oldmy=0; 
               while(GetMsg(userport)==0) 
                {mx=(*window).MouseX;
                 my=(*window).MouseY;
                 mx=mx-(mx%block);
                 my=my-(my%block); 
                 if ((mx!=oldmx) || (my!=oldmy))
                  if( (mx>=XMIN)&&(my>=YMIN)
                    &&(mx<XMIN+SX)&&(my<YMIN+SY))
                     {RectFill(rastport,mx,my,mx+block-1,my+block-1);
                      oldmx=mx;
                      oldmy=my;
                     }      
                }
              }
             SetDrMd(rastport,JAM1);
             SetWrMsk(rastport,7);
            }
        break; 
      }
    } /* end  of if (message) */

if ( ((pausegadget.Flags)&SELECTED)||menuwaiting ) 
 WaitPort(userport);
else 
 {update(); /* update lattice */
  speed=(0xffff-speedinfo.HorizPot)/0x100;
   /* nonlinear delay looks better */
  speed=(speed*speed)/0x400;
  if (speed) Delay(speed);
 }
}/* end of while */
}/* end of main */

update()
{short i,BorS,b40;
b40=block*40;
BorS=birth|survivor;
OwnBlitter();
if (8==neighborhood)
{  /* add up, middle, down to work planes 0,1 */
 myblit(bplane,bplane-b40,
       bplane+b40,workplane[0],
       0,ANBNC|NABNC|NANBC|ABC);
 myblit(bplane,bplane-b40,
       bplane+b40,workplane[1],
       0,ABNC|NABC|ANBC|ABC);
  /* add up, down to 3 west neighbors; result in 5,6,7 */
 myblit(workplane[0],bplane-b40,
       bplane+b40,workplane[5],
       1,ANBNC|NABNC|NANBC|ABC);
 myblit(workplane[0],bplane-b40,
       bplane+b40,workplane[6],
       1,ABNC|ANBC|NABC|ABC);
  /* add in workplane 1 */
 myblit(workplane[1],workplane[6],
       0,workplane[7],
       1,ABNC|ABC);
 myblit(workplane[1],workplane[6],
       0,workplane[6],
       1,NABC|ANBC|NABNC|ANBNC);
  /* add in 3 east neighbors; result in 5,6,7,8 */
  /* plane 1, cannot carry into 8 yet */
 myblit(workplane[1],workplane[6],
       workplane[7],workplane[7],
       -1,ABNC|ANBC|NABC|NANBC);
  myblit(workplane[1],workplane[6],
       0,workplane[6],
       -1,ANBC|NABC|ANBNC|NABNC);
  /* add in workplane 0; use 9 for carry  to 7 */
 myblit(workplane[0],workplane[5],
       workplane[6],workplane[9],
       -1,ABC); /* carry */
 myblit(workplane[0],workplane[5],
       workplane[6],workplane[6],
       -1,NABC|ANBC|NANBC|ABNC);
 myblit(workplane[0],workplane[5],
       0,workplane[5],
       -1,NABC|ANBC|NABNC|ANBNC);
   /* add in carry */
 myblit(workplane[9],workplane[7],
       0,workplane[8],
       0,ABC|ABNC);
 myblit(workplane[9],workplane[7],
       0,workplane[7],
       0,NABC|ANBC|NABNC|ANBNC);
/* sum of eight neighbors now in 5,6,7,8 */
/* now set workplane[i] where i neighbors */
if ( BorS&1) /* 0 neighbors */ 
 { myblit(workplane[5],workplane[6],
       workplane[7],workplane[0],
       0,NANBNC);
   myblit(workplane[0],workplane[8],
       0,workplane[0],
       0,ANBC|ANBNC);
 }
if ( BorS&((1<<1)|(1<<5))) /* 1 neighbor */
  myblit(workplane[5],workplane[6],
       workplane[7],workplane[1],
       0,ANBNC);
if ( BorS&((1<<2)|(1<<6)) ) /* 2 neighbors */
  myblit(workplane[5],workplane[6],
       workplane[7],workplane[2],
       0,NABNC);
if ( BorS&((1<<3)|(1<<6)) ) /* 3 neighbors */
  myblit(workplane[5],workplane[6],
       workplane[7],workplane[3],
       0,ABNC);
if ( BorS&(1<<4))          /* 4 neighbors */
  myblit(workplane[5],workplane[6],
       workplane[7],workplane[4],
       0,NANBC);
if ( BorS&((1<<7)|(1<<6))) /* 7 neighbors */
  myblit(workplane[5],workplane[6],
       workplane[7],workplane[7],
       0,ABC);
if ( BorS&(1<<5))          /* 5 neighbors */
  myblit(workplane[5],workplane[6],
       workplane[1],workplane[5],
       0,ANBNC);
if ( BorS&(1<<6))          /* 6 neighbors */
 { myblit(workplane[6],workplane[2],
       workplane[3],workplane[6],
       0,ANBNC);
   myblit(workplane[6],workplane[7],
       0,workplane[6],
       0,ANBC|ANBNC);
 }
} /* end of if 8 */
 
if (4==neighborhood) /* less work if only 4 neighbors to count */
{
/* add left, top and bottom neighbors to planes 5,6,4 */
/* first bit (plane 0)*/
  myblit(bplane,bplane-b40,
         bplane+b40,workplane[5],
         1,ANBNC|NABNC|NANBC|ABC);
/* second bit */
  myblit(bplane,bplane-b40,
         bplane+b40,workplane[6],
         1,ABNC|ANBC|NABC|ABC);
/* add in right neighbor */
/* calculate plane 2 */
if ( BorS&((1<<4)|(1)))
  myblit(bplane,workplane[5],
         workplane[6],workplane[4],
         -1,ABC);
/* calculate plane 1 */
  myblit(bplane,workplane[5],
         workplane[6],workplane[6],
         -1,ABNC|ANBC|NABC|NANBC);
/* calculate plane 0 */
  myblit(bplane,workplane[5],
         0,workplane[5],
         -1,NABC|NABNC|ANBC|ANBNC);
/* sum of four nearest neighbors now in planes 5,6,4 */
/* put in respective planes */
if ( BorS&1)
  myblit(workplane[5],workplane[6],
         workplane[4],workplane[0],
         0,NANBNC);
if ( BorS&(1<<1))
  myblit(workplane[5],workplane[6],
         0,workplane[1],
         0,ANBC|ANBNC);
if ( BorS&(1<<2))
  myblit(workplane[5],workplane[6],
         0,workplane[2],
         0,NABC|NABNC);
if ( BorS&(1<<3))
  myblit(workplane[5],workplane[6],
         0,workplane[3],
         0,ABC|ABNC);
} /* end of if 4 */

/* accumulate births in plane 9 */
if (birth & 1) /* copy 0 to 9 */
   myblit(workplane[0],0,
       0,workplane[9],
       0,ABC|ABNC|ANBC|ANBNC);
else /* clear 9 */
   myblit(0,0,
       0,workplane[9],
       0,0);
for (i=1;i<=neighborhood;i++)
 if ( (birth & (1<<i) )!=0) /* add in more births */
   myblit(workplane[i],workplane[9],
       0,workplane[9],
       0,ANBC|NABC|ABC|ANBNC|NABNC|ABNC);

/* survivors to plane 0 */
if (0==(survivor&1)) /* clear plane 0, else already taken care of */
   myblit(0,0,
       0,workplane[0],
       0,0);
for (i=1;i<=neighborhood;i++)
 if ( (survivor & (1<<i) )!=0) /* add in more survivors */
   myblit(workplane[i],workplane[0],
       0,workplane[0],
       0,ANBC|NABC|ABC|ANBNC|NABNC|ABNC);

/* final result */
if  ((xorgadget.Flags)&SELECTED) 
 {/* save old history in plane 1 */
  myblit(historyplane,0,
       0,workplane[1],
       0,ABC|ABNC|ANBC|ANBNC); 
  /* new plane in workplane 2 */
  myblit(bplane,workplane[0],
       workplane[9],workplane[2],
       0,ABNC|ABC|NANBC|NABC);
  WaitBOVP((*screen).ViewPort); 
  /* save history */
  myblit(bplane,0,
       0,historyplane,
       0,ABC|ABNC|ANBC|ANBNC);
  /* xor new plane with old history */
  myblit(workplane[1],workplane[2],
       0,bplane,
       0,ANBC|NABC|ANBNC|NABNC);
 }
else
 {WaitBOVP((*screen).ViewPort); 
  /* save history */
  myblit(bplane,0,
       0,historyplane,
       0,ABC|ABNC|ANBC|ANBNC);
  /* new bplane */
  myblit(bplane,workplane[0],
       workplane[9],bplane,
       0,ABNC|ABC|NANBC|NABC);
 }
DisownBlitter();
fixboundary();
}

myblit(a,b,c,d,shift,minterm)
/* This blits from sources a,b,c to destination d         */
/* with a shifted by block*shift bits.  Size and modulo's fixed */
/* by global variables. Make sure blitter is owned before calling. */
 UBYTE *a,*b,*c,*d;
 int shift,minterm; 
{int con1=0,lwm=0xffff;
 if (a) minterm|=SRCA;
 if (b) minterm|=SRCB;
 if (c) minterm|=SRCC;
 if (d) minterm|=DEST;
 if (shift==-1) 
   {minterm|=block*0x1000;  /* shift for a */
    con1=2;           /* descending mode */
    lwm=0xffff>>block;
    a+=backshift;
    b+=backshift;
    c+=backshift;
    d+=backshift;
   }
 if (shift==1) 
   {minterm|=block*0x1000;
    lwm=0xffff<<block;
   }
 WaitBlit();
 custom->bltamod=MODULO;
 custom->bltbmod=MODULO;
 custom->bltcmod=MODULO;
 custom->bltdmod=MODULO;
 custom->bltafwm=0xffff;
 custom->bltalwm=lwm;
 custom->bltdpt=d;
 custom->bltapt=a;
 custom->bltbpt=b;
 custom->bltcpt=c;
 custom->bltcon1=con1;
 custom->bltcon0=minterm; 
 custom->bltsize=blitsize; /* start blit */
}

fixboundary()
/* fixes boundary conditions by copying 
from one line inside edge to other edge. */
{SetWrMsk(rastport,5); /* don't mess with plane 2 */
if (boundary==0)
{ClipBlit(rastport,XMIN+block     ,YMIN,
          rastport,XMIN+SX-block  ,YMIN           ,block,SY,0xc0);
 ClipBlit(rastport,XMIN+SX-2*block,YMIN,
          rastport,XMIN           ,YMIN           ,block,SY,0xc0);
 ClipBlit(rastport,XMIN           ,YMIN+block,
          rastport,XMIN           ,YMIN+SY-block  ,SX,block,0xc0);
 ClipBlit(rastport,XMIN           ,YMIN+SY-2*block,
          rastport,XMIN           ,YMIN           ,SX,block,0xc0);
}
else 
{SetAPen(rastport,boundary-1);
 RectFill(rastport,XMIN         ,YMIN         ,XMIN+block-1,YMIN+SY-1);
 RectFill(rastport,XMIN         ,YMIN         ,XMIN+SX-1   ,YMIN+block-1);
 RectFill(rastport,XMIN         ,YMIN+SY-block,XMIN+SX-1   ,YMIN+SY-1);
 RectFill(rastport,XMIN+SX-block,YMIN         ,XMIN+SX-1   ,YMIN+SY-1);
 SetAPen(rastport,1);
}
 SetWrMsk(rastport,7);
return;
}

openup()
/* this opens screen, window, and draws things */
{int i;
 char label;
 short colormap[8];
 colormap[0]=0x00f; /* screen background  blue */
 colormap[1]=0xfff; /* screen foreground  white */
 colormap[2]=0x000; /* field background=black */
 colormap[3]=0xff0; /* newborn=yellow */
 colormap[4]=0x0aa; /* dark cyan */
 colormap[5]=0x440; /* brown */
 colormap[6]=0xf0f; /* moribund=magenta */
 colormap[7]=0xf00; /* living=red */
 IntuitionBase=(struct IntuitionBase *) OpenLibrary("intuition.library",33);
  if (IntuitionBase==NULL) cleanup("trouble opening intuition");
 GfxBase=(struct GfxBase *) OpenLibrary("graphics.library",33);
  if (GfxBase==NULL) cleanup("trouble opening graphics");
 screen=OpenScreen(&newscreen);
  if (screen==NULL) cleanup("trouble opening screen"); 
 LoadRGB4(&((*screen).ViewPort),colormap,8L);
 newwindow.Screen=screen;
 window=OpenWindow(&newwindow);
  if (window==NULL) cleanup("trouble opening window");
 userport=(*window).UserPort;
 rastport=(*window).RPort;
  /* set up custom pointer */
 if (0==(XPointerchip=AllocMem(MYPOINTERSIZE,MEMF_CHIP))) 
  cleanup("trouble with custom pointer");
 CopyMem(XPointer,XPointerchip,MYPOINTERSIZE);
 SetPointer(window,XPointerchip,9,9,-5,-4);
 SetRGB4(&((*screen).ViewPort),17,-1,-1,-1);
 SetRGB4(&((*screen).ViewPort),18,0,-1,0);
  /* put down gadgets */
 AddGadget(window,&pausegadget,-1);
 AddGadget(window,&cleargadget,-1);
 AddGadget(window,&xorgadget,-1);
 AddGadget(window,&reversegadget,-1);
 AddGadget(window,&speedgadget,-1);
  /* set up neighborhood gadget */
 Move(rastport,304L,NGTOP+6L);
 SetAPen(rastport,7);
 SetDrMd(rastport,JAM1);
 label=neighborhood+'0';
 Text(rastport,&label,1); 
 AddGadget(window,&neighborgadget,-1);
 SetAPen(rastport,2);
 Move(rastport,221,NGTOP+7);
 Text(rastport,"Neighbors:",10);
  /* set up birth and survivor gadgets */
 Move(rastport,220,RGTOP);
 Text(rastport,"born|survive",12);
 SetAPen(rastport,2);
 for (i=0;i<9;i++)
  {label='0'+i;
   Move(rastport,257,RGTOP+11+10*i);
   Text(rastport,&label,1);
   birthgadgets[i].LeftEdge=235;
   birthgadgets[i].TopEdge=RGTOP+5+10*i;
   birthgadgets[i].Width=8;
   birthgadgets[i].Height=8;
   birthgadgets[i].Flags=GADGHCOMP;
   birthgadgets[i].Activation=GADGIMMEDIATE|TOGGLESELECT;
   birthgadgets[i].GadgetType=BOOLGADGET;
   birthgadgets[i].GadgetRender=&smallbox;
   birthgadgets[i].SelectRender=NULL;
   birthgadgets[i].GadgetText=NULL;
   birthgadgets[i].MutualExclude=NULL;
   birthgadgets[i].GadgetID=10+i;
   if (birth & (1<<i)) birthgadgets[i].Flags|=SELECTED;
   AddGadget(window,&(birthgadgets[i]),-1);
   survivorgadgets[i].LeftEdge=280;
   survivorgadgets[i].TopEdge=RGTOP+5+10*i;
   survivorgadgets[i].Width=8;
   survivorgadgets[i].Height=8;
   survivorgadgets[i].Flags=GADGHCOMP;
   survivorgadgets[i].Activation=GADGIMMEDIATE|TOGGLESELECT;
   survivorgadgets[i].GadgetType=BOOLGADGET;
   survivorgadgets[i].GadgetRender=&smallbox;
   survivorgadgets[i].SelectRender=NULL;
   survivorgadgets[i].GadgetText=NULL;
   survivorgadgets[i].MutualExclude=NULL;
   survivorgadgets[i].GadgetID=20+i;
   if (survivor & (1<<i)) survivorgadgets[i].Flags|=SELECTED;
   AddGadget(window,&(survivorgadgets[i]),-1);
  }
 RefreshGadgets(&pausegadget,window,0);
  /* get working planes */
 for (i=0;i<10;i++)
  {workplane[i]=AllocMem(SY*40,MEMF_CHIP);
   if (workplane[i]==0) cleanup("trouble getting memory"); 
  }
  /* clear initial working region */
 clearfield();
  /* draw an active box to get started */
 SetAPen(rastport,3);
 RectFill(rastport,XMIN+SX/4,YMIN+SY/4-(YMIN+SY/4)%block,
         XMIN+SX-1-SX/4,YMIN+SY-1-SY/4-(YMIN+SY-SY/4)%block); 
  /* draw a nice border */
 for (i=0;i<4;i++)
  {SetAPen(rastport,4);
   Move(rastport,XMIN+SX+i, YMIN-1-i);
   Draw(rastport,XMIN+SX+i,YMIN+SY+i);
   Draw(rastport,XMIN-1-i ,YMIN+SY+i);
   SetAPen(rastport,1);
   Draw(rastport,XMIN-1-i , YMIN-1-i);
   Draw(rastport,XMIN+SX+i, YMIN-1-i);
  };
   /* show meaning of colors */ 
 Move(rastport,218,184);
 SetAPen(rastport,3);
 SetBPen(rastport,2);
 SetDrMd(rastport,JAM2);
 Text(rastport," Young",6);
 SetAPen(rastport,7);
 Text(rastport,"  Old ",6);
 Move(rastport,218,192);  
 SetAPen(rastport,6);
 Text(rastport,"  Moribund  ",12);
 for (i=1;i<3;i++)
  {SetAPen(rastport,4);
   Move(rastport,218+8*12+i-1, 178-i);
   Draw(rastport,218+8*12+i-1, 193+i);
   Draw(rastport,218-i  , 193+i);
   SetAPen(rastport,1);
   Draw(rastport,218-i , 178-i);
   Draw(rastport,218+8*12+i-1, 178-i);
  } 
/* turn on menus */
 SetMenuStrip(window,&(mymenus[0]));   
/* set blitter parameters */
 setsize();
 return;
}/* end of openup */

cleanup(char *closing)
/* release memory and close everything */
{int i;
  /* get rid of any remaining messages */
 while (message=GetMsg(userport)) ReplyMsg(message);
  /* free allocated memory */
 WaitBlit(); /* make sure any leftover blitting is done */
 for (i=0;i<10;i++)
  if (workplane[i]) FreeMem(workplane[i],SY*40);
 if (XPointerchip) FreeMem(XPointerchip,MYPOINTERSIZE);
  /* close up */
 if (window) 
  {if (window->MenuStrip) ClearMenuStrip(window);
   CloseWindow(window);
  }
 if (screen) CloseScreen(screen);
 if (GfxBase) CloseLibrary((struct Library *) GfxBase);
 if (IntuitionBase) CloseLibrary((struct Library *) IntuitionBase);
 printf("%s\n",closing);
 exit(0);
}

clearfield() /* clears the working region */
{SetAPen(rastport,2);
 RectFill(rastport,XMIN,YMIN,XMIN+SX-1,YMIN+SY-1);
 SetAPen(rastport,1);
 fixboundary();
 return;
}

setsize()
{/* set blitter parameters */
  short displacement;
  blitsize=(SY-2*block)*64+SX/16; /* size parameter for blitter */ 
  displacement=40*(YMIN+block)+XMIN/8; /* shift from start of bitplane to
   start of field */
  backshift=(SY-2*block-1)*40+((SX-16)/8); /* shift to end of field */
  bplane=((*(*rastport).BitMap).Planes)[0]+displacement;
  historyplane=((*(*rastport).BitMap).Planes)[2]+displacement;
  return;
}
