rokicki@navajo.STANFORD.EDU (Tomas Rokicki) (08/07/86)
[ Blitter away your life ] Here's some code I wrote to access and test the blitter. The blit routines are isolated in a short file; the test routines, which execute John Conway's cellular automata game of LIFE, follow. Read the comments for more information. About the game of LIFE: This program executes LIFE in a 318 by 188 display at 19.8 generations per second. It starts with an `r' pentimino in the center of the screen, and adds a cell at a random location for each generation. To run, just type life. With no arguments, it runs in one bit plane; otherwise, the argument is the number of bit planes to use. (The top bit plane is the most recent generation; the next is the next most recent generation, etc.) To exit (and this is a kludge), move the mouse to the left of the screen and slowly drag it around the vertical center (you need to hit coordinates (0,100) exactly). The program is entertaining if unusable; if anyone adds user interaction, please let me know. The following are the four files, separated by ------cut here------. They compile under Manx 3.20; simple mods for Lattice. ------cut here------ (makefile) life: life.o blit.o ln -o life life.o blit.o -lc life.o: life.c structures.b cc -b +istructures.b life.c structures.b: structures.h cc -b -a +hstructures.b structures.h blit.o: blit.c cc -b +istructures.b blit.c ------cut here------ (structures.h) /* * Structures include file. */ #include "exec/exec.h" #include "intuition/intuition.h" #include "functions.h" #include "graphics/display.h" #include "graphics/gfx.h" #include "graphics/gfxmacros.h" #include "stdio.h" ------cut here------ (blit.c) /* * This include file includes the defines for all the blitter functions. * It only allows use of the `blit' operations; for area fills or line * drawing, it will need to be extended. * * Information gleaned from the Hardware Reference Manual. */ #define BLTADD (0xdff040L) /* * This structure contains everything we need to know. * Do not do a structure copy into this! Instead, assign * each field. The last field assigned must be bltsize; that * starts up the blitter. Also note that all of these are * write only, and you can't read them. */ struct bltstruct { short con0 ; short con1 ; short afwm ; short alwm ; short *csource, *bsource, *asource, *dsource ; short bltsize ; short dmy1, dmy2, dmy3 ; short cmod, bmod, amod, dmod ; } *blitter = BLTADD ; /* * We need an array which tells what to use for all 256 possible operations. */ #define USEA (0x8) #define USEB (0x4) #define USEC (0x2) #define USED (0x1) char touse[256] ; char fwma[16] ; char lwma[16] ; /* * Call initblitdata() once on startup before you ever call blit. */ initblitdata() { register int i ; register int s ; for (i=0; i<256; i++) { s = USED ; if ((i >> 4) != (i & 15)) s += USEA ; if (((i >> 2) & 51) != (i & 51)) s += USEB ; if (((i >> 1) & 85) != (i & 85)) s += USEC ; touse[i] = s ; } s = 0xffff ; for (i=0; i<16; i++) { fwma[i] = s ; s = (s >> 1) & ~0x8000 ; lwma[i] = 0xffff - s ; } } /* * This is the major user interface. Takes addresses and offsets for * all four locations, a modulo and sizes, and a function. * Assumes the modulos for all sources and destination are the same. * You might want to add some arguments or delete some. * * All arguments are in pixels (except the addresses and function.) * * Before you call this routine, call OwnBlitter(); after you have * called it as many times as you need, call DisownBlitter(). Remember * that you cannot do any printf's or anything else which requires the * blitter when you own the blitter, so be careful with the debug code. * The machine will lock but will not crash. */ blit(aaddress, ax, ay, baddress, bx, by, caddress, cx, cy, daddress, dx, dy, modulo, xsize, ysize, function) short *aaddress, *baddress, *caddress, *daddress ; int ax, ay, bx, by, cx, cy, dx, dy, modulo, xsize, ysize, function ; { short *t ; /* * Divide the modulo by 16 because we need words. */ modulo >>= 4 ; /* * Wait for the blitter to finish whatever it needs to do. */ WaitBlit() ; /* * Calculate the real addresses for d and c. */ blitter->dsource = daddress + modulo * dy + (dx >> 4) ; blitter->csource = caddress + modulo * cy + (cx >> 4) ; /* * Mask out the low order bits of dx; add these to the xsize. (The * first bits will be masked using the first word mask.) */ dx &= 15 ; xsize += dx ; blitter->afwm = fwma[dx] ; blitter->alwm = lwma[(xsize - 1) & 15] ; /* * 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. */ t = aaddress + modulo * ay + (ax >> 4) ; ax = dx - (ax & 15) ; if (ax < 0) { t++ ; ax += 16 ; } blitter->asource = t ; t = baddress + modulo * by + (bx >> 4) ; bx = dx - (bx & 15) ; if (bx < 0) { t++ ; bx += 16 ; } blitter->bsource = t ; /* * Now calculate the two control words. If you want to do * the addresses in reverse order, set the appropriate bit in con1. */ blitter->con0 = (ax << 12) + (touse[function] << 8) + function ; blitter->con1 = (bx << 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 ; blitter->amod = blitter->bmod = blitter->cmod = blitter->dmod = 2 * (modulo - xsize) ; /* * This last assignment starts up the blitter. */ blitter->bltsize = (ysize << 6) + xsize ; } ------cut here------ (life.c) /* * 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() ; } ------cut here------ (that's all, folks!)