[comp.sys.atari.st] Munching Squares

minow@decvax.UUCP (06/15/87)

This program puts interesting wallpaper on your monitor.  Have fun.
munchasm.c (which is the central algorithm in assembler optimized
to use the BCHG instruction is left as an exercise for the student.)

Martin Minow
decvax!minow

--------
/*
 * Munching squares.
 * Originally from HAKMEM (MIT AI memo 239).  According to
 * HAKMEM, this was discovered by Jackson Wright on the
 * MIT RLE PDP-1 circa 1962.  The PDP-10 algorithm is
 *	DATAI	2
 *	ADDB	1,2
 *	ROTC	2,-22
 *	XOR	1,2
 *	JRST	.-4
 *
 * This version is copyright 1987 by Martin Minow, Arlington MA.
 * It may be copied and modified without permission, but may not
 * be sold for direct personal gain.
 * 
 */

#include <osbind.h>
#include <ctype.h>
#include <linea.h>
#define	EOS	'\0'
#define TRUE	1
#define FALSE	0
/*
 * USE_ASM is non-zero if compiling with munchasm.c
 */
#ifndef	USE_ASM
#define	USE_ASM	1
#endif

#ifndef BITS
#define	BITS	10			/* Size of display is 1 << BITS	*/
#endif
#define	SIDE	(1 << BITS)

char	bits[] = {			/* Bitmask to XOR to the screen	*/
	1, 2, 4, 8, 16, 32, 64, 128
};

typedef struct seed {
	int	xseed, yseed;
} SEED;

SEED	seeds[] = {
/*	   x    y		Note: X should be non-zero		*/
  {	   1,   0		},
  {	   1,   1		},
  {	  17,  17		},
  {	  73,  18		},
  {	   1, (SIDE / 4)	},
  {	   2,   0		},
  {	  16, (SIDE / 4)	},
  {	   1, (SIDE / 2)	},
  {	   2, (SIDE / 2)	},
  {	   4, (SIDE / 2)	},
  {	   6, (SIDE / 2)	},
  {	   8, (SIDE / 2)	},
  {	  16, (SIDE / 2)	},
  {	   1, (SIDE / 2) - 1	},
  {	   2, (SIDE - 1)	},
  {	   2, (SIDE / 4) * 3	},
  {	   1,   9		},
  {	   3,   0		},
  {	   4,	0		},
  {	  18,  73		},
  {	  15,   0		},
  {	  17,   0		},
  {	   0,   0		}
};

/*
 * Define the physical screen -- the defind values are for the Atari ST
 * Change these and you'll have to hack munchasm.c, too.
 */
#define	Xpixels		640
#define	Ypixels		400

#define MASK		((1 << BITS) - 1)
#define BYTES_PER_ROW	(Xpixels / 8)
#define	xsize	SIDE			/* Pixels in one row		*/
#define	ysize	SIDE			/* Pixels in one column		*/
#define	xorigin	((Xpixels - xsize) / 2)
#define	yorigin	((Ypixels - ysize) / 2)
long		origin;			/* Upper-left-hand corner	*/
long		maxcount;		/* (xsize * ysize) * 2		*/
unsigned long	xseed, yseed;
int		index = 0;
char		*the_screen;

unsigned char	signon[] =
 "\033EMunching squares (from Hackmem)\r\n\
  Copyright \275 1987, Martin Minow, Arlington Mass. 02174, U.S.A.\r\n\
  This program may be freely redistributed, but may not be sold\r\n\
  for direct personal gain.\r\n\
  Portions of this program copyright \275 1984, Mark Williams Company.\r\n\
  Type any character to exit (eventually).  Type 'x' to enter parameters.\r\n\
  \r\n";

main()
{
	register int		c;

	/*
	 * Write the signon message to the console, then stall
	 * for three seconds or so.
 	 */
	hidemouse();
	Cconws(signon);	
	for (index = 0; index < 210; index++)
	    Vsync();
	/*
	 * index points into the [x,y] seeds.
	 * origin is the offset to the image's [0,0] point.
	 * the_screen -> the [0,0] byte of the physical screen.
	 * maxcount is the number of points to plot for a display.
	 */
	index = 0;
	origin = (yorigin * BYTES_PER_ROW) + (xorigin / 8);
	the_screen = Physbase();
	maxcount = ((long) xsize) * ((long) ysize) * 2;
	Cconws("\033E\033f");		/* Clear screen, cursor off	*/
	while (Cconis() == 0) {
	    if (seeds[index].xseed == 0
	     && seeds[index].yseed == 0)
		index = 0;
	    xseed = seeds[index].xseed;	/* Get another seed.		*/
	    yseed = seeds[index].yseed;
	    index++;
	    Cconws("\033E");		/* Clear screen			*/
	    munching();
	}
	Cconws("\033E\033e");		/* Clear screen, cursor on	*/
	/*
	 * Read the character from the console. Maybe interact.
	 */
	if ((c = Cconin()) == 'x' || c == 'X') {
	    for (;;) {
		getseed();
		if (xseed == 0 && yseed == 0)
		    break;
	        Cconws("\033E\033f");
	        munching();
	    }
	}
	Cconws("\033E\033e");		/* Clear screen, cursor on	*/
	showmouse();
}

#if !USE_ASM

munching()
/*
 * Do one munch cycle (section of a 1024x1024 window)
 */
{
	register unsigned long	acc, seed;
	register unsigned short	x, y;
	register long		count;
	short			i;
	register char		*screen = the_screen;

	acc = 0;
	seed = (yseed << BITS) + xseed;
	count = maxcount;
	do {
	    /*
	     * Get the x and y parts of the accumulator.
	     * Then, convert [x,y] into a byte address
	     * If the address is on-screen, use the low-order
	     * 3 bits of x to select the bit to invert and do it.
	     * Finally, update the accumulator.
	     */  
	    x = acc & MASK;
	    y = ((acc >> BITS) & MASK) ^ x;
	    i = (y * BYTES_PER_ROW) + (x >> 3) + origin;
#if BITS > 8
	    if (i >= 0 && i < (Ypixels * BYTES_PER_ROW))
#else
#assert ((1 << BITS) - 1) * ((1 << BITS) - 1) < (Ypixels * BYTES_PER_ROW)
#endif
		screen[i] ^= bits[x & 0x7];
	    acc += seed;
	} while (--count != 0);
}
#endif

getseed()
{
	register char	*bp;
	extern char	*getint();
	char		buf[130];

	/*
	 * Position cursor and turn it on.
	 */
	Cconws("\033E\033eEnter seed, 0,0 to exit: ");
	buf[0] = (sizeof buf) - 2;
	Cconrs(buf);
	buf[buf[1] + 2] = EOS;
	bp = getint(&buf[2], &xseed);
	getint(bp,  &yseed);
}

char *
getint(bp, result)
register char		*bp;
register unsigned long	*result;
{
	*result = 0;
	while (*bp != EOS && !isdigit(*bp))
	    bp++;
	while (isdigit(*bp)) {
	    *result *= 10;
	    *result += (*bp - '0');
	    bp++;
	}
	return (bp);
}