/*******************************************************************************
*   BlitDemons by Walter Strickler
*   This program and all its source code are in the public domain and are
* freely distributable and usable for any purpose, private or commercial.
 ******************************************************************************/
#include "bdemon.h"
#include <hardware/custom.h>
#include <hardware/blit.h>


/*  This is what it's all about:  THE ALGORITHM without the Intuition fluff */

/*******************************************************************************
 *  Local prototypes...
 ******************************************************************************/
void BlitIncrement(struct DoBlitNode *, struct BDMainStruct *);
void BlitCompare(struct DoBlitNode *, struct BDMainStruct *, int, int);
void UpdateEqual(struct DoBlitNode *, struct BDMainStruct *);
void BlitOutput(struct DoBlitNode *, struct BDMainStruct *);
void InitBlitData();
void Blit(struct DoBlitNode *,
          WORD *, int, int, 
          WORD *, int, int, 
          WORD *, int,
          WORD *, int,
          int, int, int, int, int);
void DoBlit(struct DoBlitNode *);


/*******************************************************************************
 *   These defines will be used in the Blit() calls in this file.
 ******************************************************************************/
#define NEG_A        NANBNC | NANBC | NABNC | NABC   /* Negate A */
#define A_XOR_B      NABC | NABNC | ANBC | ANBNC     /* A ^ B */
#define A_XOR_BANDC  ANBNC | ANBC | NABC | ABNC      /* A ^ (B&C) */ 
#define A_EQUIV_B    NANBNC | NANBC | ABNC | ABC     /* ~(A^B) */
#define COMPARE      NANBC | ABC
#define OUTPUT_FUNC  ABC | NABC | NANBC | ABNC


/*******************************************************************************
 *   Define the number of blits in each step.
 ******************************************************************************/
#define INCR_BLITS          5
#define COMPARE_BLITS       4
#define UPDATE_BLITS        1     
#define OUTPUT_BLITS        4
#define TOTAL_BLITS     (INCR_BLITS + COMPARE_BLITS * 4 + OUTPUT_BLITS + \
                        UPDATE_BLITS * 3)


/**************************************************************************
 *  OneGen() performs one demons generation on the planes in PlaneData,
 * using the DoBlitNode pointed at by BlitNodes.  Performs TOTAL_BLITS
 * blits.
 *************************************************************************/
void OneGen(BDSchtoff)
    struct BDMainStruct *BDSchtoff;

    {
    int i;
    struct DoBlitNode *BlitNodes;

    OwnBlitter();
    BlitNodes = BDSchtoff -> BDBlitNodes;
    for (i = 0; i < TOTAL_BLITS; i++)
        {
        DoBlit(&(BlitNodes[i])); 
        }
    DisownBlitter();
    }


/**************************************************************************
 *   InitBlits() allocates an array of DoBlitNodes, then fills it so that
 * when OneGen() is run on this array, it does a demons generation on
 * the given bitplanes.
 *   The actual blitter algorithm came from the Apex version, written by
 * Loren Blaney.
 *************************************************************************/
struct DoBlitNode *InitBlits (PlaneData)
    struct BDMainStruct *PlaneData;
    
    {    
    struct DoBlitNode *BlitData;
    int                NodeCount;

    InitBlitData();   /* Need to do this somewhere */
    BlitData = (struct DoBlitNode *) malloc(sizeof(struct DoBlitNode) * 
     TOTAL_BLITS);
    if (BlitData != NULL)
        {
        NodeCount = 0;
        /* Step 1:  Increment Display[] into Incr[] 
         *          Incr[] = Display[] + 1
         */
        BlitIncrement(&(BlitData[NodeCount]), PlaneData);
        NodeCount = NodeCount + INCR_BLITS;
    
        /* Step 2:  Compare a cell's incremented value (Incr[]) 
         *         with its 4 nearest neighbors.  If any are equal, then set
         *         Equal[] flag.
         */
        BlitCompare(&(BlitData[NodeCount]), PlaneData, TO_EQUAL, UP_NEIGHBOR);
        NodeCount = NodeCount + COMPARE_BLITS;
       /*  Now Equal = (Incr == Up) */
 
        BlitCompare(&(BlitData[NodeCount]), PlaneData, TO_TEMP, LEFT_NEIGHBOR);
        NodeCount = NodeCount + COMPARE_BLITS;
        UpdateEqual(&(BlitData[NodeCount]), PlaneData);
        NodeCount = NodeCount + UPDATE_BLITS;
        /*  Now Equal = (Incr == Up) | (Incr == Left) */
   
        BlitCompare(&(BlitData[NodeCount]), PlaneData, TO_TEMP, RIGHT_NEIGHBOR);
        NodeCount = NodeCount + COMPARE_BLITS;
        UpdateEqual(&(BlitData[NodeCount]), PlaneData);
        NodeCount = NodeCount + UPDATE_BLITS;
        /*  Now Equal = (Incr == Up) | (Incr == Left) | (Incr == Right) */
  
        BlitCompare(&(BlitData[NodeCount]), PlaneData, TO_TEMP, DOWN_NEIGHBOR);
        NodeCount = NodeCount + COMPARE_BLITS;
        UpdateEqual(&(BlitData[NodeCount]), PlaneData);
        NodeCount = NodeCount + UPDATE_BLITS;
        /* Now Equal = 1 if ANY neighbor(s) is/are equal to Incr */
   
        /* Step 3:  Blit the results (Equal) to the display as follows:
         *     if (Equal == 0) { Display[] = Display[]; }
         *     else            { Display[] = Incr[]; }
         */
        BlitOutput(&(BlitData[NodeCount]), PlaneData);
        /* Bimbo! */
        }
    else
        {
        BlitData = NULL;   /* Error return */
        }	
    return BlitData;
    }


/*******************************************************************************
 *  BlitIncrement() increments the bitplane array pointed at by
 * Planes -> Display, and stores the result in Planes -> Incr.  Uses the 
 * blitter.  Make sure you own the blitter before calling this.
*******************************************************************************/
void BlitIncrement(BlitData, Planes)
    struct DoBlitNode   *BlitData;
    struct BDMainStruct *Planes;

    { 
    WORD  **Disp,
          **Incr,
           *Temp;       

    Disp = Planes -> Display;
    Incr = Planes -> Incr;
    Temp = Planes -> Temp;

    /* Increment Blit #1.
     * Incr[0] = ~Disp[0]
     * D = ~A
     */
    Blit(&(BlitData[0]),
         Disp[0], 0, 0,
         NULL,   0, 0,
         NULL, 0,
         Incr[0], 0,
         0, Planes -> Mod, Planes -> XSize, Planes -> YSize, NEG_A);

    /* Increment Blit #2.
     * Incr[1] = Disp1 ^ Disp0 
     * D = A ^ B
     */
    Blit(&(BlitData[1]),
         Disp[1], 0, 0,
         Disp[0], 0, 0,
         NULL, 0,
         Incr[1], 0,
         0, Planes -> Mod, Planes -> XSize, Planes -> YSize, A_XOR_B);

    /* Increment Blit #3.
     * Incr[2] = Disp[2] ^ (Disp[1] & Disp[0]) 
     * D = A ^ (B & C)
     */
    Blit(&(BlitData[2]),
         Disp[2], 0, 0,
         Disp[1], 0, 0,
         Disp[0], 0,
         Incr[2], 0,
         0, Planes -> Mod, Planes -> XSize, Planes -> YSize, A_XOR_BANDC);

    /* Increment Blit #4.    Determine if low 3 bits are set.
     * Temp = (Disp[0] & Disp[1] & Disp[2])
     * D = A & B & C
     */
    Blit(&(BlitData[3]),
         Disp[0], 0, 0,
         Disp[1], 0, 0,
         Disp[2], 0,
         Temp, 0,
         0, Planes -> Mod, Planes -> XSize, Planes -> YSize, ABC);

    /* Blit 5.   Add in 3-bit carry.
     * Incr[3] = (Disp3 ^ Temp)
     * D = A ^ B 
     */
    Blit(&(BlitData[4]),
         Disp[3], 0, 0,
         Temp, 0, 0,
         NULL, 0,
         Incr[3], 0,
         0, Planes -> Mod, Planes -> XSize, Planes -> YSize, A_XOR_B);
    }
 


/*******************************************************************************
 *   BlitCompare() compares, using the blitter, the 4 bitplane number at
 * each pixel location in Incr with the 4 bitplanes in Neighbor.  Neighbor
 * is one of 4 nearest neighbors (WhichNeighbor = UP_NEIGHBOR, DOWN_NEIGHBOR, 
 * LEFT_NEIGHBOR, or RIGHT_NEIGHBOR) of pixels in Planes -> Display.  The 
 * result is written to a bitplane (Temp or Equal) specified by WhereTo.
 * A pixel is set in the result if Incr is equal to the neighbor, otherwise
 * is is cleared.
*******************************************************************************/
void BlitCompare(BlitData, Planes, WhereTo, WhichNeighbor)
    struct DoBlitNode   *BlitData;
    struct BDMainStruct *Planes;
    int                  WhereTo,
                         WhichNeighbor;

    {
    WORD **Source,
         **Neighbor,
          *Dest;
    int    i,
           ShiftX,
           ShiftY;

    switch (WhichNeighbor)
        {
        case UP_NEIGHBOR:
            ShiftX = 0;
            ShiftY = -1;
            break;
        case LEFT_NEIGHBOR:
            ShiftX = -1;
            ShiftY = 0;
            break;
        case RIGHT_NEIGHBOR:
            ShiftX = 1;
            ShiftY = 0;
            break;
        case DOWN_NEIGHBOR:
            ShiftX = 0;
            ShiftY = 1;
            break;
        default:
            assert(FALSE);       /* Shouldn't be here */
            break;
        }
    Source = Planes -> Incr;
    Neighbor = Planes -> Display;
    switch (WhereTo)
        {
        case TO_EQUAL:
            Dest = Planes -> Equal;
            break;
        case TO_TEMP:
            Dest = Planes -> Temp;
            break;
        default:
            assert(FALSE);       /* Shouldn't be here */
            break;
        }

    /* Compare Blit #1.  Set Dest if Neighbor[0] = Source[0]
     * Dest = ~(Source[0] ^ Neighbor[0])
     * D = ~(A ^ B) 
     */
    Blit(&(BlitData[0]),
         Source[0], 0, 0,
         Neighbor[0], ShiftX, ShiftY,   
         NULL, 0,
         Dest, 0,
         0, Planes -> Mod, Planes -> XSize, Planes -> YSize, A_EQUIV_B);

    /* Compare Blit #2-4.  Compare bits 1-3 as in prev blit, but include Dest
     * Dest = ~(Source[i] ^ Neighbor[i]) & Dest
     * D = ~(A ^ B) & C
     */
    /* Do next blit on the upper 3 bitplanes */
    for (i = 1; i <= 3; i++)
        {
        Blit(&(BlitData[i]),
             Source[i], 0, 0,
             Neighbor[i], ShiftX, ShiftY,
             Dest, 0,
             Dest, 0,
             0, Planes -> Mod, Planes -> XSize, Planes -> YSize, COMPARE);
        }
     /*  NOW:  Dest = 1 if (Source[] == Neighbor[]) */
    }


/*******************************************************************************
 *   UpdateEqual() is a quicky function whose entire purpose was to clean up
 * OneGen().  It OR's the bitplanes (Planes -> Equal) and (Planes -> Temp)
 * and stores the result in (Planes -> Equal).
*******************************************************************************/
void UpdateEqual(BlitData, Planes)
    struct DoBlitNode   *BlitData;
    struct BDMainStruct *Planes;

    {
    Blit(BlitData,
         Planes -> Equal, 0, 0,
         Planes -> Temp, 0, 0,
         NULL, 0,
         Planes -> Equal, 0,
         0, Planes -> Mod, Planes -> XSize, Planes -> YSize, A_OR_B);
    }

/*******************************************************************************
 *   Output to the display planes the final result, as follows:
 *          if (Equal == 0) { Display[] = Display[]; }
 *          else            { Display[] = Incr[]; }
 *   By moving Equal to A and changing the function accordingly, we can mask
 * a border out of A and leave it unchanged.  Intuition Borders have a width
 * of two, but we'll use LRBorder just in case something changes.
*******************************************************************************/
void BlitOutput(BlitData, Planes)
    struct DoBlitNode   *BlitData;
    struct BDMainStruct *Planes;
    {
    int i;

    /* Display[i] = 
     *  Equal&Incr[i]&Dis[i] | ~Equal&Incr[i]&Dis[i] | ~Equal&~Incr[i]&Dis[i] | 
     *  Equal&Incr[i]&~Dis[i]
     * Determined using a truth table.
     */
    /* Output displayed bitplane 0 */
    for (i = 0; i <= 3; i++)
        {
        Blit(&(BlitData[i]),
             Planes -> Equal, 0, 0,
             Planes -> Incr[i], 0, 0,
             Planes -> Display[i], 0, 
             Planes -> Display[i], 0,
             Planes -> LRBorder, Planes -> Mod, 
             Planes -> XSize, Planes -> YSize, OUTPUT_FUNC);
        }
    }


/**************************************************************************    
 *   Blit() and InitBlitData() are based loosely on the ones by the same names
 * from PopLife, written by Tomas Rockkiki(?) and/or Olaf Seibert.  I saw no 
 * copyright notice or anything similar in that code, so I presume it's safe 
 * to use.  
 *   I simplified Blit() and changed InitBlitData() to suit.  
 *                                                         -wss
**************************************************************************/

/* --------------------------------------------------------------------- *
 *
 *   Static data for the BLITTER routine.
 *
 *   We need an array which tells what to use
 *   for all 256 possible operations.
 *
 * --------------------------------------------------------------------- */

UBYTE ToUse[256];	/* Which DMA channels we need per minterm */
UWORD FwmA[16];		/* First word mask for A source */
UWORD LwmA[16];		/* Last word mask for A source */

/*
 *   Call InitBlitData() once on startup before you ever call Blit().
 *   Have a look in the Hardware Reference Manual for the mysterious
 *   bit patterns. They are quite obvious then!
 */

void InitBlitData()
    {
    register WORD i;
    register UWORD s,
                   First,
                   Last;

    /*  I sure hope this works!  I have no idea what's going on here! -wss */
    for (i=0; i<256; i++) 
        {
        s = DEST >> 8;
        if ((i >> 4) != (i & 15))	  /* 15 is 0000 1111 */
            {
            s += SRCA >> 8;
            }
        if (((i >> 2) & 51) != (i & 51))  /* 51 is 0011 0011 */
            {
            s += SRCB >> 8;
            }
        if (((i >> 1) & 85) != (i & 85))  /* 85 is 0101 0101 */
            {
            s += SRCC >> 8;
            }
        ToUse[i] = s;
	}
        /* 
         *  I like the concept of having a FwmA[] & LwmA[], but not the 
         * implementation.  This will define a left and right border
         * for A.  (This just happens to be what I want.)
         * Example:  Border = 2, then mask out 2 bits on the left 
         * and 2 bits on the right of A. -wss
         */
        First = 0xFFFF;
        Last  = 0xFFFF;
	for (i=0; i<16; i++) {
		FwmA[i] = First;
		LwmA[i] = Last;
		First = First >> 1;
		Last = Last << 1;
	}
}

/* --------------------------------------------------------------------- *
 *   Arguments:
 *     BNode:      Pointer to a DoBlitNode structure to fill.
 *     AAddress:   The address of the A source before shifting.
 *     Ax:         Number of BITS to shift A left (-) or right (+).
 *     Ay:         Number of LINES to shift A up (-) or down (+).
 *     BAddress:   The address of the A source before shifting.
 *     Ax:         Number of BITS to shift B left (-) or right (+).
 *     By:         Number of LINES to shift B up (-) or down (+).
 *     CAddress:   The address of the C source (no pun) before shifting.
 *           WSS: Took out cx due to blitter limitations (no C shifting).
 *               Actually, I could shift Cx/16 words.  If we need it...
 *     Cy:         Number of LINES to shift C up (-) or down (+).
 *     DAddress:   The address of the dest. before shifting.
 *           WSS: Took out dx due to blitter limitations (no D shifting)
 *               Actually, I could shift Cx/16 words.  If we need it...
 *     Dy:         Number of LINES to shift D up (-) or down (+).
 *     Border:(WSS)The number of pixels (0-15) to mask on either side of A.
 *     Modulo:     The REAL modulo (see HRM) in bits.
 *     XSize:      Horizontal size of the blit in pixels.
 *     YSize:      Vertical size of the blit in pixels.
 *     Function:   The function to perform (BLTLFx)
 *
 *   Blit() has changed quite alot since its PopLife days.  It now fills
 * up one DoBlitNode (pointed at by BNode) structure with blitter register
 * values, to be used by QBlit.
 *                                                            -wss
 * --------------------------------------------------------------------- */

void Blit      (BNode,
                AAddress, Ax, Ay,
		BAddress, Bx, By,
		CAddress,     Cy,
		DAddress,     Dy,
                Border,
		Modulo, XSize, YSize, Function)
    struct DoBlitNode *BNode;
    WORD *AAddress, *BAddress, *CAddress, *DAddress;
    int Ax, Ay, Bx, By,  Cy,  Dy, Border, Modulo, XSize, YSize, Function;

    {
    WORD *t,
          AShiftBits,
          BShiftBits;

    Modulo >>= 4;    /*   Divide the Modulo By 16 because we need words. */
    /*   Poke the  addresses for D and C:   */
    BNode -> dsource = DAddress;
    BNode -> csource = CAddress;
    /* WSS:  Create a nice little border around A:  */
    BNode -> afwm = FwmA[Border];
    BNode -> alwm = LwmA[Border];
  /*
   *   Now calculate the shifts for the A and B operands.  The barrel
   *   shifter counts appear to be to the left instead of the more
   *   intuitive to the right.  Note that I take Dx into account.
   *     WSS:  No ya don't.  
   *           Note that shift right = positive (relative to parameters
   *         passed in).  Thus, word shift is "+= Ax / 16", and  bitshifting
   *         requires negation of Ax, since pos. is to left for blitter.
   */ 

    /* Gross shift of A by whole WORDS: */
    t = AAddress + (Modulo + (XSize / 16)) * Ay + (Ax / 16);  
    AShiftBits = (-Ax) & 0xf;  /* Too clever:  see big comment below */
    if (Ax > 0)  /* Right shift */ 
        {
        t++;   /*  Add 16 bits to the right, then shift AShiftBits to the left.
                * Note that (X & 0xf) = 16 - (abs(X) & 0xf), if X is negative.
                * This is what we want in AShiftBits for right shifting!
                */
	}
    BNode -> asource = t;

    /* Repeat for B source: */
    t = BAddress + (Modulo + (XSize / 16)) * By + (Bx / 16); 
    BShiftBits = (-Bx) & 0xf; 
    if (Bx > 0)    
        {
        t++;
	}
    BNode -> bsource = t;
/*
 *   Now calculate the two control words.  If you want to do
 *   the addresses in reverse order, set the appropriate bit in con1.
 */
    BNode -> con0 = (AShiftBits << 12) + (ToUse[Function] << 8) + Function;
    BNode -> con1 = (BShiftBits << 12);
/*
 *   Calculate the final total XSize in words, and the modulos.  The
 *   modulos are in Bytes when written from the 68000.
 */
    XSize = (XSize + 15) >> 4;  /* Round up to next word */
    BNode -> amod = BNode -> bmod = BNode -> cmod = BNode -> dmod =
	    2 * Modulo;       
/*
 *   This last assignment merely assigns the size value in the DoBlitNode.
 */
    BNode -> bltsize = (YSize << 6) + XSize;
    }  /* End of Blit() */


extern struct Custom custom;  /* I guess this comes from Amiga.lib... */
struct Custom *HWRegs = &custom;  /* Alas:  must be external for Blink */
/*******************************************************************************
 *   DoBlit routine is quite simple.  It merely takes the register contents in 
 * the DoBlitNode struct pointed at by BlitInfo and stuffs it into the blitter
 * registers, kicking the blitter off with a poke to bltsize.
 ******************************************************************************/
void DoBlit(BlitInfo) 
    struct DoBlitNode  *BlitInfo;

    {
    WaitBlit();   /* Wait for the previous blit */
    HWRegs -> bltcon0 = BlitInfo -> con0;
    HWRegs -> bltcon1 = BlitInfo -> con1;
    HWRegs -> bltafwm = BlitInfo -> afwm;
    HWRegs -> bltalwm = BlitInfo -> alwm;
    HWRegs -> bltcpt  = (APTR) BlitInfo -> csource;
    HWRegs -> bltbpt  = (APTR) BlitInfo -> bsource;
    HWRegs -> bltapt  = (APTR) BlitInfo -> asource;
    HWRegs -> bltdpt  = (APTR) BlitInfo -> dsource;
    HWRegs -> bltcmod  = BlitInfo -> cmod;
    HWRegs -> bltbmod  = BlitInfo -> bmod;
    HWRegs -> bltamod  = BlitInfo -> amod;
    HWRegs -> bltdmod  = BlitInfo -> dmod;

    HWRegs -> bltsize  = BlitInfo -> bltsize;  /* This sets the blitter off */
    }  
