the famous "crabs" program ported
Bill Carpenter
wjc at ho5cad.ATT.COM
Tue Nov 14 05:22:24 AEST 1989
Attached below is the source for the famous "crabs" program, ported to
run on the UNIXpc (with or without the Botton "vidpal" video ram
access hardware mod). See the simple compile instructions in the
comments near the top of the code.
If you have the video access hardware mod (or are thinking of getting
it), you will also want the source for "blitlib.a" to do direct memory
raster operations (instead of via a system call). Watch for that in a
separate posting in these groups within a day or two.
BTW, this was posted to comp.sources.games earlier this year. The
code still works on 5620 and 630 terminals. The email address of the
original author is "gwyn at smoke.brl.mil". I also recommend that you
find the cited article in Scientific American (*after* you've seen
this running).
--
Bill Carpenter att!ho5cad!wjc or attmail!bill
-------------------------------< crabs.c >---------------------------------
/*
crabs -- see September 1985 Scientific American pages 18..23
last edit: 89/02/25 D A Gwyn
SCCS ID: @(#)crabs.c 1.3 for 5620 DMD and 630 MTG
To compile:
$ dmdcc -o crabs.m crabs.c # -g -O also recommended
To run:
$ dmdld crabs.m # runs only in mpx mode
or
$ dmdld crabs.m - # for invisible crabs
*/
#if defined(unixpc)
/*
**
** UNIXpc version, mods by Bill Carpenter, Oct/Nov '89
** att!ho5cad!wjc william_j_carpenter at att.com
** attmail!bill
**
** cc -O -c -Dunixpc crabs.c
** ld -s /lib/crt0s.o /lib/shlib.ifile -o crabs crabs.o
**
** cc -O -c -Dunixpc -Dvidpal crabs.c
** ld -s /lib/crt0s.o /lib/shlib.ifile -o crabs crabs.o blitlib.a
**
** The second version works with both normal machines and those with the
** "vidpal" video ram access modification. It determines at run time if
** you have it. If you have it, it will run on screen memory directly;
** else, it will operate within the confines of the window from which it
** is called using the kernel-based raster stuff.
**
** For the second form, you need blitlib.a. That's the bitblit library
** from the "mgr" window manager. Posted a while back to
** comp.sources.unix. You also need bitmap.h from that library.
**
** There are at least a couple variations on blitlib.a. In the "mgr"
** source posted to c.s.unix, there were 68020 assembler and
** machine-independent C versions. The latter runs on the UNIXpc, but it
** draws things incorrectly (I think due to byte ordering and alignment
** differences from sun2's). In later versions of "mgr" (which include a
** port to DEC3100), there is a new portable version of blitlib.a. Out
** of the box, it needs some configuration for the UNIXpc (and perhaps a
** few minor bugfixes?).
**
** If you have some other bitblit routine available to you, you shouldn't
** have too much trouble getting this code to work with it. There are
** only a couple different of bitblit operations used in this program.
**
** All of the code specific to the UNIXpc port is identified by
**
** #if defined(unixpc)
**
** Code specific to use of the VIDPAL hardware mod (including the
** autodetection that you don't have it) if identified by
**
** #if defined(vidpal)
**
** and is a subset of the UNIXpc mods.
*/
#endif /* unixpc */
#ifndef lint
static char SCCS_ID[] = "@(#)crabs.c 1.3 89/02/25"; /* for "what"
utility */
#endif
#ifdef unixpc
# include <stdio.h>
# include <sys/signal.h>
# include <sys/window.h>
# ifdef vidpal
# include "bitmap.h"
# endif
# define XMIN 0
# define YMIN 12 /* to keep from being completely annoying */
#else
# define XMIN 0
# define YMIN 0
#endif
short xmin = XMIN;
short ymin = YMIN;
#ifdef unixpc
# define XMAX 720
# define YMAX 348
/*
** The scheme for detecting if you have the vidpal board is to try to
** access video memory. This forces either a bus error or a segmentation
** violation (not sure which) if you don't have it, since it's a
** privileged address. But, since you might want to compile without the
** vidpal option even if you have the board, I #ifdef'd it to another
** address that is equally illegal.
*/
# ifdef vidpal
# define UNIXPC_VIDEO ((unsigned short *)0x420000)
# else
# define UNIXPC_VIDEO ((unsigned short *)0x41ffff)
# endif
int single_window_fd = -1;
typedef struct {unsigned short us[16];} Texture16;
typedef unsigned short Word;
typedef struct {short x; short y} Point;
typedef struct {Point origin; Point corner} Rectangle;
typedef struct
{
Word *base;
unsigned short width;
Rectangle rect;
} Bitmap;
/* warning "Pt" and "Rect" use overlaid static space in these definitions */
Point
Pt(x,y)
short x,y;
{
static Point scratch;
scratch.x = x;
scratch.y = y;
return (scratch);
};
Rectangle
Rect(ox,oy,cx,cy)
short ox,oy,cx,cy;
{
static Rectangle scratch;
scratch.origin.x = ox;
scratch.origin.y = oy;
scratch.corner.x = cx;
scratch.corner.y = cy;
return (scratch);
};
# define F_STORE DSTSRC
# define F_XOR DSTXOR
# define WORDSIZE 16
# define display crabs_physical
#else /* below is !unixpc */
# include <dmd.h>
#endif
#ifdef DMD630
#define texture16 texture
#define crabs_T_background T_background
#define crabs_physical physical
#else
static Texture16 crabs_T_background = /* background texture */
{
#if defined(unixpc) /* this difference due to bit order within words */
0x2222, 0x8888, 0x2222, 0x8888,
0x2222, 0x8888, 0x2222, 0x8888,
0x2222, 0x8888, 0x2222, 0x8888,
0x2222, 0x8888, 0x2222, 0x8888
#else
0x1111, 0x4444, 0x1111, 0x4444,
0x1111, 0x4444, 0x1111, 0x4444,
0x1111, 0x4444, 0x1111, 0x4444,
0x1111, 0x4444, 0x1111, 0x4444
#endif
};
static Bitmap crabs_physical = /* full screen definition */
{
#ifdef unixpc
(Word *)UNIXPC_VIDEO, /* without hardware mod, this dumps core */
(XMAX + 15) / 16, /* bitmap width in 16-bit Words */
#else
(Word *)0x700000L, /* DMD screen image base address */
(XMAX + 31) / 32, /* bitmap width in 32-bit Words */
#endif
0, 0, XMAX, YMAX /* screen rectangle within bitmap */
};
#endif
#define NCRABS 32 /* total number of crabs (1..32) */
#define MAXVEL 8 /* abs. bound on velocity component */
#define PERIOD 2 /* sleep time (ticks) per cycle */
typedef int bool; /* Boolean data type */
#define false 0
#define true 1
static bool visible; /* true if crabs are to be shown */
static struct
{
Point ulc; /* upper left corner screen coords */
Point vel; /* velocity (pixels/cycle) */
} crab[NCRABS]; /* keeps track of crabs' state */
/* We rely on this forcing the following arrays to be Word-aligned! */
/* There are 4 possible crab orientations, each of which
has 4 possible relationships with the grey background.
(Scientific American article says 8, but it's wrong.) */
/* Crab images XORed with grey background texture at various offsets: */
static short upcrab[] = /* facing up */
{
0x6E4C, 0x2A66,
0xB377, 0xE6D5,
0x8081, 0x8101,
0xB935, 0xAC9D,
0x6E5C, 0x3A76,
0x3A76, 0x6E5C,
0xAC9D, 0xB935,
0x0242, 0x4240
};
static short downcrab[] = /* facing down */
{
0x4240, 0x0242,
0xB935, 0xAC9D,
0x6E5C, 0x3A76,
0x3A76, 0x6E5C,
0xAC9D, 0xB935,
0x8101, 0x8081,
0xEECD, 0xAB67,
0x3276, 0x6654
};
static short rightcrab[] = /* facing right */
{
0x4E4C, 0x0A46,
0xB333, 0xA291,
0x6A59, 0x3B73,
0x3A72, 0x6A58,
0x6859, 0x3971,
0x3B73, 0x6A59,
0xA291, 0xB333,
0x0A46, 0x4E4C
};
static short leftcrab[] = /* facing left */
{
0x6250, 0x3272,
0x8945, 0xCCCD,
0xCEDC, 0x9A56,
0x9A16, 0x8E9C,
0x4E5C, 0x1A56,
0x9A56, 0xCEDC,
0xCCCD, 0x8945,
0x3272, 0x6250
};
/* The bitmaps for the four orientations: */
static Bitmap upmap = { (Word *)upcrab, 32/WORDSIZE, 0, 0, 32, 8 };
static Bitmap downmap = { (Word *)downcrab, 32/WORDSIZE, 0, 0, 32, 8 };
static Bitmap rightmap = { (Word *)rightcrab, 32/WORDSIZE, 0, 0, 32, 8 };
static Bitmap leftmap = { (Word *)leftcrab, 32/WORDSIZE, 0, 0, 32, 8 };
/* Crab "vicinities" are recorded in the following
global map; see Collide() and Draw() for details: */
static long vicinity[(XMAX + 31) / 32 + 2][(YMAX + 31) / 32 + 2];
/* includes margins all around */
static void Cycle(), DrawCrab(), HideCrabs(), Init(), ModVel(), NewVel();
static int RandInt();
static long Collide();
#ifdef unixpc
core_dump_catcher(ziggy)
int ziggy;
{
/*
** Caught what would normally be a core cump due to an invalid memory
** reference. That means we should revert to single-window mode and
** use ioctl(WIOCRASTOP) for bitblits.
**
** Set crabs_physical.base = 0 to make blits window-relative. Set ymin = 0
** to start with the very top scan line (instead of specially
** protecting the very top line of characters [usually done in
** full-screen mode].)
**
** Set the file descriptor to use standard input as the window
** descriptor for the ioctl(). Having this variable non-negative is
** also the runtime flag that that's what we want instead of direct
** memory writes.
**
** We leave the XMAX,YMAX to be the size of the full display. We
** could make them variables (they are #define constants now) and
** reset them here to the bounds of the actual windows. We could
** also catch SIGWIND and change them accordingly on window reshapes.
** I didn't do it because single-window mode isn't nearly as exciting
** as full-screen mode in the first place.
*/
single_window_fd = 0;
crabs_physical.base = 0;
ymin = 0;
signal(ziggy, core_dump_catcher);
}
#endif
main( argc, argv )
int argc;
char *argv[];
{
#ifdef unixpc
/*
** Force autodetection of vidpal board by referencing video
** memory. Notice that if your compiler optimizes out this
** otherwise pointless reference, the reset won't take place
** until the first round of legit bitblits. I'm not sure what
** kind of mischief that causes. If you haven't used the
** -Dvidpal compile option, we just unconditionally force
** detection of a standard machine.
*/
int fdex;
#ifdef vidpal
signal(SIGSEGV, core_dump_catcher);
signal(SIGBUS, core_dump_catcher);
fdex = *UNIXPC_VIDEO; /* don't optimize me out of existence */
#else
core_dump_catcher(SIGBUS);
#endif
/*
** Close all the file descriptors so that you can run this in the
** background and exit without leaving the window hanging around.
*/
for (fdex=80; fdex<80; ++fdex)
{
if (fdex != single_window_fd) close(fdex);
}
nice(19);
#endif
#ifdef DMD630
local();
#endif
Init( argc, argv ); /* set up initial grey crab layer */
for ( ; ; ) /* no way out! */
{
#ifndef unixpc
/*
** On the UNIXpc, sleep is in units of one second, much too big
** for crabs. Also, we don't have the resource contention
** situation that the 5620/630's have. So, skip the sleep and
** just "nice()" it (see above).
*/
sleep( PERIOD ); /* relinquish the processor */
#endif
Cycle(); /* move the crabs */
}
/*NOTREACHED*/
}
static void
Init( argc, argv ) /* set up initial crab layer */
int argc;
char *argv[];
{
int i; /* crab # */
visible = argc <= 1; /* default is to show crabs */
#ifndef unixpc
/*
** This pre-eats the window from which the crabs are downloaded on
** the 5620/630. Since we're treating that as the whole screen or
** the whole single-window for the UNIXpc, that means that the entire
** interesting area is done at startup. Much less interesting than
** on the DMDs.
*/
texture16( &display, display.rect, &crabs_T_background, F_STORE );
/* crab layer */
#endif
/* Create initial set of crabs: */
for ( i = 0; i < NCRABS; ++i )
{
/* Assign random position within "crabs" layer: */
crab[i].ulc.x = RandInt( display.rect.origin.x,
display.rect.corner.x - 8
);
crab[i].ulc.y = RandInt( display.rect.origin.y,
display.rect.corner.y - 8
);
/* Assign random velocity: */
NewVel( i );
/* Draw crab at initial position (within "crabs" layer): */
if ( visible )
DrawCrab( i );
}
#ifndef unixpc
/*
** For the equivalent on the UNIXpc, just run nohup'd and in the
** background.
*/
display.rect.corner = display.rect.origin; /* make unpickable */
#endif
}
static void
Cycle() /* one motion cycle for all crabs */
{
static long old[8]; /* old contents of new crab position */
static Bitmap oldmap = { (Word *)old, 32/WORDSIZE, 0, 0, 8, 8 };
Point p; /* new crab upper left corner */
Rectangle r; /* new crab area */
long syndrome; /* crab collision mask */
int i; /* crab # */
int w; /* index for old[.] */
for ( i = 0; i < NCRABS; ++i )
{
DrawCrab( i ); /* erase crab from previous position */
for ( ; ; ) /* determine a new position */
{
p.x = crab[i].ulc.x + crab[i].vel.x; /* motion */
p.y = crab[i].ulc.y + crab[i].vel.y;
if ( p.x >= xmin && p.x < XMAX - 8
&& p.y >= ymin && p.y < YMAX - 8
)
break; /* on-screen, proceed */
/* Bounce off edge of screen: */
NewVel( i ); /* assign new velocity */
}
r.origin = p;
r.corner.x = p.x + 8;
r.corner.y = p.y + 8;
/* Check for collision with other crabs;
if you don't worry about this, you get
crud left behind from crab collisions
(visible in Scientific American article).
(Note that crab # i has been removed.) */
/* The strategy is: only undraw possibly colliding crabs.
The obvious alternative, not showing any crabs until
all locations have been painted, would probably cause
the set of crabs to flicker or to appear too faint. */
if ( (syndrome = Collide( p )) != 0L )
HideCrabs( syndrome ); /* save from following code */
/* Save old contents of new crab location: */
bitblt( &crabs_physical, r, &oldmap, Pt( 0, 0 ), F_STORE );
/* Paint the new location grey: */
texture16( &crabs_physical, r, &crabs_T_background, F_STORE );
/* Determine if new location used to be grey: */
bitblt( &crabs_physical, r, &oldmap, Pt( 0, 0 ), F_XOR );
for ( w = 0; w < 8; ++w )
if ( old[w] != 0L )
{ /* this location has been nibbled */
p = crab[i].ulc; /* reset position */
NewVel( i ); /* bounce away from bite */
break;
}
if ( syndrome != 0L )
HideCrabs( syndrome ); /* bring them back */
/* Draw the crab in its new position: */
crab[i].ulc = p;
ModVel( i ); /* randomly alter crab velocity */
DrawCrab( i );
}
}
static long
Collide( p ) /* return syndrome for crab collision */
Point p; /* crab upper left corner */
{
long syndrome; /* accumulate syndrome here */
bool right = p.x % 32 > 32 - 8,
down = p.y % 32 > 32 - 8; /* more than one vicinity? */
int x32 = p.x / 32,
y32 = p.y / 32; /* vicinity array indices */
/* "Or" in crabs from overlapping vicinities: */
syndrome = vicinity[x32 + 1][y32 + 1];
if ( right )
syndrome |= vicinity[x32 + 1 + 1][y32 + 1];
if ( down )
syndrome |= vicinity[x32 + 1][y32 + 1 + 1];
if ( right && down )
syndrome |= vicinity[x32 + 1 + 1][y32 + 1 + 1];
return syndrome;
}
static void
HideCrabs( syndrome ) /* draw crabs contained in syndrome */
long syndrome; /* syndrome (crab bit flags) */
{
int i; /* indexes crab[.] */
long m; /* bit mask for crab # i */
for ( m = 1L, i = 0; i < NCRABS; m <<= 1, ++i )
if ( (m & syndrome) != 0L ) /* crab contained in syndrome */
DrawCrab( i ); /* toggle crab */
}
static void
DrawCrab( i ) /* draw specified crab */
int i; /* crab # (0..NCRABS-1) */
{
Point p; /* upper left corner for crab image */
Point v; /* crab velocity */
Bitmap *whichmap; /* -> 1 of 4 possible orientations */
int index; /* selects 1 of 4 offsets wrt grey */
int x32, y32; /* vicinity array indices */
bool right, down; /* more than one vicinity? */
long syn_bit; /* crab possible-occupancy bit */
if ( visible )
{
p = crab[i].ulc;
v = crab[i].vel;
if ( abs( v.x ) >= abs( v.y ) )
if ( v.x > 0 )
whichmap = &upmap;
else
whichmap = &downmap;
else
if ( v.y > 0 )
whichmap = &rightmap;
else
whichmap = &leftmap;
index = (p.x + p.y * 2) % 4 * 8;
bitblt( whichmap,
Rect( index, 0, index + 8, 8 ),
&crabs_physical,
p,
F_XOR
);
/* A crab's vicinities are the disjoint 32x32 regions
that contain any piece of the crab's 8x8 square.
On the average, 9 out of 16 crabs occupy just 1
vicinity; 6 out of 16 crabs occupy 2 vicinities,
and 1 out of every 16 crabs occupies 4 vicinities. */
x32 = p.x / 32;
y32 = p.y / 32; /* coords for upper left vicinity */
right = p.x % 32 > 32 - 8; /* also next vicinity right? */
down = p.y % 32 > 32 - 8; /* also next vicinty down? */
/* Toggle crab's occupancy bit in all occupied vicinities: */
syn_bit = i == 0 ? 1L : 1L << i;
vicinity[x32 + 1][y32 + 1] ^= syn_bit;
if ( right )
vicinity[x32 + 1 + 1][y32 + 1] ^= syn_bit;
if ( down )
vicinity[x32 + 1][y32 + 1 + 1] ^= syn_bit;
if ( right && down )
vicinity[x32 + 1 + 1][y32 + 1 + 1] ^= syn_bit;
}
/* else nibble away but don't show crabs */
}
static void
NewVel( i ) /* assign new velocity to crab */
int i; /* crab # */
{
crab[i].vel.x = RandInt( 1 - MAXVEL, MAXVEL );
crab[i].vel.y = RandInt( 1 - MAXVEL, MAXVEL );
/* Velocity (0,0) is okay since we repeatedly modify all velocities. */
}
static void
ModVel( i ) /* randomly modify crab velocity */
int i; /* crab # */
{
int d; /* increment */
if ( crab[i].vel.x >= MAXVEL - 1 )
d = RandInt( -1, 1 );
else if ( crab[i].vel.x <= 1 - MAXVEL )
d = RandInt( 0, 2 );
else
d = RandInt( -1, 2 );
crab[i].vel.x += d;
if ( crab[i].vel.y >= MAXVEL - 1 )
d = RandInt( -1, 1 );
else if ( crab[i].vel.y <= 1 - MAXVEL )
d = RandInt( 0, 2 );
else
d = RandInt( -1, 2 );
crab[i].vel.y += d;
}
static int
RandInt( lo, hi ) /* generate random integer in range */
int lo, hi; /* range lo..hi-1 */
{
return lo + (int)((long)(hi - lo) * (long)(rand() & 0x7FFF) / 32768L);
}
#ifdef unixpc
int
texture16(dest, inner, pattern, operation)
Bitmap *dest;
Rectangle inner;
Texture16 *pattern;
int operation;
{
/*
** Paint the inner rectangle with the pattern. We take advantage of
** the fact that we know the operation will always be a straight
** STORE.
*/
if (single_window_fd >= 0)
{
struct urdata ww;
ww.ur_srcbase = dest->base;
ww.ur_srcwidth = dest->width * 2;
ww.ur_dstbase = dest->base;
ww.ur_dstwidth = dest->width * 2;
ww.ur_srcx = inner.origin.x;
ww.ur_srcy = inner.origin.y;
ww.ur_dstx = inner.origin.x;
ww.ur_dstx = inner.origin.x;
ww.ur_dsty = inner.origin.y;
ww.ur_width = inner.corner.x - inner.origin.x;
ww.ur_height = inner.corner.y - inner.origin.y;
ww.ur_srcop = SRCPAT;
ww.ur_dstop = DSTSRC;
ww.ur_pattern = pattern->us;
ioctl(single_window_fd, WIOCRASTOP, &ww);
}
#ifdef vidpal
else
{
/*
** These mod 8's work because we need alignment of the pattern
** stuff (which repeats %4 horizontal and %2 veritical). We know
** from the semantics of the program that the rectangle we're
** moving is always 8x8. Using %8 gives the least multiword
** blits while leaving some flexibility to modify the pattern
** later without clobbering this.
*/
int ax = inner.origin.x % 8;
int ay = inner.origin.y % 8;
int mgrblit_operation;
BITMAP mgrblit_s, mgrblit_d;
if (operation == F_STORE)
{
mgrblit_operation = BIT_SRC;
}
else if (operation == F_XOR)
{
mgrblit_operation = BIT_XOR;
}
mgrblit_s.data = pattern->us;
mgrblit_d.data = dest->base;
mgrblit_s.primary = &mgrblit_s;
mgrblit_d.primary = &mgrblit_d;
mgrblit_s.x0 = 0;
mgrblit_d.x0 = 0;
mgrblit_s.y0 = 0;
mgrblit_d.y0 = 0;
mgrblit_s.wide = 16;
mgrblit_d.wide = dest->width * 16;
mgrblit_s.high = 16;
mgrblit_d.high = dest->rect.corner.y - dest->rect.origin.y;
mgrblit_s.type = _STATIC;
mgrblit_d.type = _STATIC;
bit_blit(&mgrblit_d, inner.origin.x, inner.origin.y,
inner.corner.x - inner.origin.x, inner.corner.y - inner.origin.y,
mgrblit_operation,
&mgrblit_s, ax, ay);
}
#endif
return (0);
}
int
bitblt(source, mvrect, dest, destorigin, operation)
Bitmap *source;
Bitmap *dest;
Point destorigin;
int operation;
Rectangle mvrect;
{
if (single_window_fd >= 0)
{
struct urdata ww;
ww.ur_srcbase = source->base;
ww.ur_srcwidth = source->width * 2;
ww.ur_dstbase = dest->base;
ww.ur_dstwidth = dest->width * 2;
ww.ur_srcx = mvrect.origin.x;
ww.ur_srcy = mvrect.origin.y;
ww.ur_dstx = destorigin.x;
ww.ur_dsty = destorigin.y;
ww.ur_width = mvrect.corner.x - mvrect.origin.x;
ww.ur_height = mvrect.corner.y - mvrect.origin.y;
ww.ur_srcop = SRCSRC;
ww.ur_dstop = operation;
ww.ur_pattern = 0;
ioctl(single_window_fd, WIOCRASTOP, &ww);
}
#ifdef vidpal
else
{
int mgrblit_operation;
BITMAP mgrblit_s, mgrblit_d;
if (operation == F_STORE)
{
mgrblit_operation = BIT_SRC;
}
else if (operation == F_XOR)
{
mgrblit_operation = BIT_XOR;
}
mgrblit_s.data = source->base;
mgrblit_d.data = dest->base;
mgrblit_s.primary = &mgrblit_s;
mgrblit_d.primary = &mgrblit_d;
mgrblit_s.x0 = 0;
mgrblit_d.x0 = 0;
mgrblit_s.y0 = 0;
mgrblit_d.y0 = 0;
mgrblit_s.wide = source->rect.corner.x - source->rect.origin.x;
mgrblit_d.wide = dest->rect.corner.x - dest->rect.origin.x;
mgrblit_s.wide = source->width * 16;
mgrblit_d.wide = dest->width * 16;
mgrblit_s.high = source->rect.corner.y - source->rect.origin.y;
mgrblit_d.high = dest->rect.corner.y - dest->rect.origin.y;
mgrblit_s.type = _STATIC;
mgrblit_d.type = _STATIC;
bit_blit(&mgrblit_d, destorigin.x, destorigin.y,
mvrect.corner.x - mvrect.origin.x, mvrect.corner.y - mvrect.origin.y,
mgrblit_operation,
&mgrblit_s, mvrect.origin.x, mvrect.origin.y);
}
#endif
return (0);
}
#endif
More information about the Unix-pc.sources
mailing list