/******************************************************************************* * 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 #include /* 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 */ }