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