[net.micro.amiga] Direct blitter access

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!)