[comp.sys.amiga] In The Year 1987, Man Perfects The Amiga....

ewhac@well.UUCP (03/21/87)

[ You watch: Reagan will get munched by a line eater. ]

	This is BIG.  A lot bigger than I had originally anticipated or
desired.  Nevertheless, people seem to like it, and I finally "finished" it.
It's my latest display hack.

	It's called "Robotroff".

	By the way, when is someone going to notice all this code I've been
writing and offer me a job?  I can write serious programs too, you know.
While I do enjoy writing this stuff, I'd enjoy it even more if someone were
paying me for it.

	Anyway, I think you'll like this one.  Hope you haven't thrown away
your Manx 3.20a compiler yet.

					Schwab

------------------ Andy Finkel will know what to do here. ----------------
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	README
#	robotroff.doc
#	makefile
#	robotroff.h
#	_main.c
#	functions.c
#	robotroff.c
#	sprites.c
#	rnd.asm
# This archive created: Sat Mar 21 00:39:13 1987
# By:	Leo 'Bols Ewhac' Schwab ()
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f 'README'
then
	echo shar: "will not over-write existing file 'README'"
else
cat << \SHAR_EOF > 'README'
	Hello again.  A friend of mine suggested that I write this.  So I
did.  I'm rather proud of this one.  It's my most ambitious display hack
yet.  It's called "Robotroff".

	I'd tell you what this does, but much of the enjoyment of this
program is derived from its surprise value.  Therefore, I think you'll be
better served if you compile and/or run this program without first trying to
determine what it does.  If your curiosity level won't permit that, then
read the robotroff.doc file for instructions.

MANUFACTURE
	Here's how to make this monstrosity:
1> make

	There.  That wasn't so bad, was it?

USEAGE
	Just type "robotroff" at a CLI prompt.  Trust me.  If you're
absolutely convinced that nothing happened, try again, using "robotroff -i".

CAVEATS
	This program compiles under Manx 3.20a --==>> ONLY. <<==--  I'm
almost positive it won't compile/link under Manx 3.40 without modification
to the _main.c file.  (Aside:  You can safely eliminate the _main.c file,
but you lose one of the major features of the program by doing this.  Check
the source for more information.)  If there's enough demand, and if I ever
get my 3.40 upgrade, I'll create a version that will work with that.
Further, I suspect that this will never work under Lattice without a major
re-write.

	(Postscript:  Jim Goodnow II handed me a copy of 3.40 at the last
BADGE meeting, and I've had a chance to look at it.  It will take me a few
day to get up to speed on 3.40, but I will be posting a 3.40-compatible
version of _main.c.  Look for it in about a week or two (or three...).)

	Also, there apparently is a problem with invoking this program from
a Resident-CLI.  It may or may not crash the system if invoked this way.
Moral:  Invoke from a normal CLI and save your aspirin.

	I hope you like this one.  I do.  I hope my friend does.

_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
 ________		 ___			Leo L. Schwab
	   \		/___--__		The Guy in The Cape
  ___  ___ /\		    ---##\		ihnp4!ptsfa!well!ewhac
      /   X  \_____    |  __ _---))			..or..
     /   /_\--    -----+==____\ // \  _		well ---\
___ (   o---+------------------O/   \/ \	dual ----> !unicom!ewhac
     \     /		    ___ \_  (`o )	hplabs -/       ("AE-wack")
 ____ \___/			     \_/
	      Recumbent Bikes:			"Work FOR?  I don't work FOR
	    The _O_n_l_y Way To Fly!		anybody!  I'm just having fun."
SHAR_EOF
fi
if test -f 'robotroff.doc'
then
	echo shar: "will not over-write existing file 'robotroff.doc'"
else
cat << \SHAR_EOF > 'robotroff.doc'
ROBOTROFF(1)		Amiga User's Manual		ROBOTROFF(1)


NAME
	robotroff - insidious display hack

SYNOPSIS
	robotroff [-h[n]] [-l[n]] [-p[n]] [-i] [-kill]

DESCRIPTION
	Robotroff is yet another display hack from the warped mind
	of Leo Schwab.

	Upon invocation, robotroff installs itself in the background
	and returns your CLI prompt.  It can also be run from the
	WorkBench.  Robotroff continues to run until killed off.
	Robotroff consumes very little CPU, even when active, so it
	may be left running until a reset or power-off.

	Subsequent invocations of robotroff will attempt to locate
	the original invocation, and pass information to it.  Thus,
	it is possible to modify the operating parameters of the
	program while it runs in the background.
	
	The following options are supported:

	-h[n]	Sets the "highwater" time delay length.  The amount
		of time between activity will be no longer than n
		seconds.  If n is not supplied, or is negative or
		zero, a default of two minutes will be used.

	-l[n]	Sets the "lowater" time delay length.  The amount
		of time between activity will be no shorter than n
		seconds.  If n is not supplied, or is negative or
		zero, a default of thirty seconds will be used.

	-p[n]	Sets the task priority.  This permits the user to
		make robotroff consume even less CPU.  If n is
		absent, zero is assumed.

	-i	Force activity to occur right now (immediately).

	-kill	Kills off the original robotroff invocation.
		Robotroff will announce that it has been terminated,
		then exit, freeing all allocated resources.  This
		option is meaningless and therefore ignored if
		supplied when first running robotroff.

	Malformed or unrecognized arguments are silently ignored.

EXAMPLES
	robotroff		Starts background robotroff process.
	robotroff -i		Makes background process active immediately.
	robotroff -h10 -l5	Reconfigures operating parameters.
	robotroff -p-2		Sets priority to -2.
	robotroff -kill		Terminates background process.

AUTHOR
	Leo L. Schwab

BUGS
	Probably.  Report them to the author as they are encountered.
	Note that lack of a desired feature is not considered a bug.
SHAR_EOF
fi
if test -f 'makefile'
then
	echo shar: "will not over-write existing file 'makefile'"
else
cat << \SHAR_EOF > 'makefile'
# :ts=8
# Makefile for robotroff.  I'm not an expert at makefiles,
# so please forgive my stupidity.

OBJECTS =	_main.o robotroff.o functions.o sprites.o rnd.o

robotroff: $(OBJECTS)
	ln $(OBJECTS) +cd -lc -o robotroff

robotroff.o functions.o: robotroff.h
SHAR_EOF
fi
if test -f 'robotroff.h'
then
	echo shar: "will not over-write existing file 'robotroff.h'"
else
cat << \SHAR_EOF > 'robotroff.h'
/* :ts=8 bk=0
 *
 * robotroff.h		Various defines.
 *
 * Leo L. Schwab			8703.16
 */

#define	PORTNAME		"Robotroff Control Port"
#define	CMDSIG			(1L << cmdport -> mp_SigBit)
#define	TIMESIG			(1L << timeport -> mp_SigBit)
#define	DEFAULTHIGHWATER	120
#define DEFAULTLOWATER		30

#define	HULKHEIGHT		16
#define GRUNTHEIGHT		13
#define	NFORCEHEIGHT		13
#define	MINX			-20
#define	MINY			-20

#define	COLLISION		0x0200

#define	collision()		(cstm -> clxdat & COLLISION)
#define getcbase(sn)		(((sn) >> 1 << 2) + 16)
#define getr(c)			(((c) & 0x0f00) >> 8)
#define getg(c)			(((c) & 0x00f0) >> 4)
#define getb(c)			((c) & 0x000f)
#define	abs(n)			((n) < 0 ? -(n) : (n))
#define	sgn(n)			((n) < 0 ? -1 : (n) > 0)

struct control {	/*  Control block to alter operating parameters  */
	struct Message msg;
	UWORD actions;
	int highwater, lowater, newpri;
};
#define	SETHIGH			1
#define	SETLOW			(1<<1)
#define	SETPRI			(1<<2)
#define	DOSOMETHING		(1<<3)
#define	SUICIDE			(1<<4)

extern void	*OpenLibrary(), *OpenWindow(), *ViewPortAddress(),
		*CreateStdIO(), *CreateExtIO(), *CreatePort(), *FindPort(),
		*GetMsg(), *FindTask();
extern long	Wait(), GetSprite(), VBeamPos(), OpenDevice();
extern short	rnd();
SHAR_EOF
fi
if test -f '_main.c'
then
	echo shar: "will not over-write existing file '_main.c'"
else
cat << \SHAR_EOF > '_main.c'
/* :ts=8 bk=0
 *
 * Copyright (C) 1986,1987 by Manx Software Systems, Inc.
 * Modified and made available for general use with permission of
 * Manx Software Systems, Inc.
 *
 *------------------------------------------------------------------------
 *
 * This is special code which can be run from the Workbench or from the
 * CLI.  When run from the CLI, the program detaches itself from the CLI
 * and starts running in the background (giving your CLI prompt back).
 * This means that all I/O from the program must be through windows
 * created by the program.
 *
 * This startup routine correctly handles a start from the WorkBench.
 * If started from the CLI, it will pass the command line arguments to the
 * "child" process.  This routine also handles quoted arguments (in a
 * rather primitive way).
 *
 * This suceesfully compiles and runs under MANX 3.20a.  Not guaranteed
 * to work with other compilers.  Probably won't work with MANX 3.40,
 * either (without modification).
 *
 * Original version by (and profuse thanks to)
 *	Jim Goodnow II				86??.??
 * Severely enhanced by Leo L. Schwab		8703.12
 */

#include <ctype.h>
#include <fcntl.h>
#include <exec/alerts.h>
#include <exec/memory.h>
#include <libraries/dosextens.h>
#include <workbench/startup.h>

#define	ERRSTR		"Improperly quoted argument.\n"
#define	ERRLEN		28L
#define	FATALSTR	"Fatal error on startup.\n"
#define FATALEN		24L


extern long	_Open(), _Input(), _Output(), _CurrentDir(), CreateProc();
extern void	*_OpenLibrary(), *_GetMsg(), *_AllocMem(), *_FindTask();


struct _dev {
	long	fd;
	short	mode;
} _devtab[20];

struct _preserve {
	struct Message msg;
	char **av, *ab;
	long cd;
	int ac, al;
};

long	_savsp;
int	errno, Enable_Abort;
void	*SysBase, *DOSBase, *MathBase, *MathTransBase;


static struct	WBStartup *WBenchMsg;
static int	argc, arglen;
static char	**argv, *argbuf;

static char	unique[] = "Highly Unlikely Program Invocation Name";


_main(alen, aptr)
long alen;
char *aptr;
{
	register struct Process *pp;
	register struct CommandLineInterface *cli;
	register long l;
	register unsigned short c;
	register char *cp;
	struct FileHandle *fp;
	struct MemList *mm;
	struct _preserve *p;
	static long cdir = 0;
	long *lp;
	void *sav;

	if (!(DOSBase = _OpenLibrary (DOSNAME, 0L))) {
		Alert (AG_OpenLib | AO_DOSLib, 0L);
		_exit (100);
	}

	pp = _FindTask(0L);
	if (pp->pr_CLI) {		/*  CLI invocation (first run)  */
		cdir = _CurrentDir (0L);
		_CurrentDir (cdir);

		/*  Parse out command line arguments  */
		cli = (struct CommandLineInterface *) ((long)pp->pr_CLI << 2);
		cp = (char *) ((long) cli -> cli_CommandName << 2);
		arglen = *cp + alen + 2;	/* *cp is BSTR len */
		argbuf = _AllocMem ((long) arglen, MEMF_PUBLIC | MEMF_CLEAR);

		strncpy (argbuf, cp+1, *cp);	/*  Copy command name  */
		strcpy (argbuf + *cp, " ");	/*  Space separator  */
		strncat (argbuf, aptr, (int) alen+1);	/*  Copy args  */
		argbuf[*cp] = '\0';		/* Terminate full cmd name */

		for (argc=1, cp=argbuf + *cp + 1;; argc++) {
			while (isspace (*cp))
				cp++;
			if (*cp < ' ') {
				*cp = 0;
				break;	/*  Stop on ctl char  */
			}
			if (*cp == '"') {	/*  Handle quoted args  */
				*cp = ' ';	/*  Squash quote mark  */
				while ((c = *cp) && c != '"')
					cp++;
				if (!c) {	/*  No matching quote  */
					_Write (_Output(), ERRSTR, ERRLEN);
					_exit (200);
				}
				*cp++ = 0;
			} else {
				while ((c = *cp) && !isspace (c))
					cp++;
				*cp++ = 0;
				if (!c)
					break;	/*  Stop at end-of-line  */
			}
		}

		/*  Assemble argv[] array  */
		argv = _AllocMem ((long) (argc+1) * sizeof (*argv),
				  MEMF_PUBLIC);
		for (c=0, cp=argbuf; c<argc; c++) {
			while (isspace (*cp))
				cp++;
			argv[c] = cp;
			cp += strlen (cp) + 1;
		}
		argv[c] = 0;

		/*  Preserve argument environment  */
		if (!(p = _AllocMem ((long) sizeof (*p), MEMF_PUBLIC))) {
			Alert (AG_NoMemory, 0L);
			_exit (100);
		}
		p -> ac = argc;
		p -> av = argv;
		p -> ab = argbuf;
		p -> al = arglen;
		p -> cd = cdir;

		/*  Argument list finished.  Detatch program.  */
		l = cli -> cli_Module;
		if (!(sav = _OpenLibrary (DOSNAME, 33L))) {
			/*  Malarkee for 1.1 DOS braindamage  */
			lp = (long *)*((long *)*((long *)*((long *)*((long *)
				_savsp+2)+1)-3)-3)+107;
			if (*lp != cli -> cli_Module) {
				_Write (_Output(), FATALSTR, FATALEN);
				_exit (300);
			}
		} else {
			_CloseLibrary (sav);
			lp = 0;
		}

		/*  Prevent DOS from unloading us  */
		if (lp)
			*lp = 0;
		cli -> cli_Module = 0;
#asm
		move.l	__savsp,-(sp)
#endasm
		_Forbid ();
		PutMsg (CreateProc (unique, 0L, l, 5120L), p);
		_CloseLibrary (DOSBase);
#asm
		move.l	(sp)+,sp
		rts
#endasm
	} else	/*  Check if this is this is the second time through  */
	if (!strcmp (pp->pr_Task.tc_Node.ln_Name, unique)) {
		/*  Recover parent's arguments  */
		_WaitPort (&pp -> pr_MsgPort);
		p = _GetMsg (&pp -> pr_MsgPort);
		argv	= p -> av;
		argc	= p -> ac;
		argbuf	= p -> ab;
		arglen	= p -> al;
		cdir	= p -> cd;
		_FreeMem (p, (long) sizeof (*p));

		/*  Change process name to something reasonable  */
		pp -> pr_Task.tc_Node.ln_Name = argv[0];

		/*  Convert DOS's seglist to MemList  */
		lp = (long *) ((long) pp -> pr_SegList << 2);
		lp = (long *) (lp[3] << 2);
		sav = lp;
		c = 0;
		while (lp) {
			lp = (long *) (*lp << 2);
			c++;
		}
		mm = _AllocMem ((long) sizeof (struct MemList)+
				(c-1)*sizeof (struct MemEntry), MEMF_PUBLIC);
		lp = sav;
		mm -> ml_NumEntries = c;
		c = 0;
		while (lp) {
			mm -> ml_me[c].me_Addr = (APTR) lp - 1;
			mm -> ml_me[c].me_Length = lp[-1];
			lp = (long *) (*lp << 2);
			c++;
		}

		/*  Add MemList to task structure to force auto-unload  */
		AddTail (&((struct Task *)pp)->tc_MemEntry, mm);

		_CurrentDir (cdir);
		main (argc, argv);
		_exit (0);

	} else {	/*  Started from WorkBench  */
		_WaitPort (&pp -> pr_MsgPort);
		WBenchMsg = _GetMsg (&pp -> pr_MsgPort);
		if (WBenchMsg -> sm_ArgList)
			_CurrentDir (WBenchMsg -> sm_ArgList -> wa_Lock);

		if (WBenchMsg -> sm_ToolWindow)
			if (_devtab[0].fd =
			    _Open (WBenchMsg->sm_ToolWindow, MODE_OLDFILE)) {
				_devtab[1].fd = _devtab[2].fd = _devtab[0].fd;
				_devtab[0].mode = 0x8000;
				_devtab[1].mode = _devtab[2].mode = 0x8001;
				fp=(struct FileHandle *) (_devtab[0].fd << 2);
				pp -> pr_ConsoleTask = (APTR) fp -> fh_Type;
			}

		main (0, WBenchMsg);
		_exit (0);
	}
}

void (*_cln)() = 0;

_exit (code)
{
	int fd;

	for (fd = 0; fd < 20; fd++)
		close (fd);
	if (_cln)
		(*_cln)();

	if (MathTransBase)	_CloseLibrary (MathTransBase);
	if (MathBase)		_CloseLibrary (MathBase);
	if (DOSBase)		_CloseLibrary (DOSBase);

	if (!WBenchMsg) {	/*  Free the argument list  */
		_FreeMem (argbuf, (long) arglen);
		if (argv)
			_FreeMem (argv, (long) (argc+1) * sizeof (*argv));
	} else {
		_Forbid ();
		_ReplyMsg (WBenchMsg);
	}
#asm
		move.l	8(a5),d0	; Get return code
		move.l	__savsp,sp	; Restore original stack pointer
		rts			; Bye-bye!
#endasm
}
SHAR_EOF
fi
if test -f 'functions.c'
then
	echo shar: "will not over-write existing file 'functions.c'"
else
cat << \SHAR_EOF > 'functions.c'
/* :ts=8 bk=0
 *
 * functions.c:		Functions to do obscene things to mouse pointer.
 *
 * Leo L. Schwab			8703.18		(415)-456-6565
 */

#include <exec/types.h>
#include <intuition/intuition.h>
#include <graphics/sprite.h>
#include <hardware/custom.h>
#include "robotroff.h"


extern UWORD	hr1[], hr2[], hr3[], hl1[], hl2[], hl3[], hu1[], hu2[], hu3[],
		gr1[], gr2[], gr3[], nforce[],
		hulkcolor[], gruntcolor[], nforcecolor[];

extern struct IOStdReq		*ioreq;
extern struct InputEvent	mouse;
extern struct SimpleSprite	spr;
extern struct Preferences	prefs;
extern struct Window		*win;
extern struct Screen		*wbs;
extern long			sprnum;
extern int			wide, high;
extern void			*vp;

struct Custom *cstm = 0xdff000;

UWORD			*leftseq[] = { hl1, hl2, hl1, hl3 };
UWORD			*rightseq[] = { hr1, hr2, hr1, hr3 };
UWORD			*udseq[] = { hu1, hu2, hu1, hu3 };
UWORD			*gruntseq[] = { gr1, gr2, gr1, gr3 };

static int idxsav, dirchange;


/*
 * This particular piece of code turned out to be gut-wrenchingly messy.
 * But it's the only way I could think of to make it work the way I wanted.
 * If you know of a cleaner algorithm, write it, send it to me, and I'll
 * use it in a revised version of the program.
 */
dohulk ()
{
	register UWORD **sequence;
	register int idx = 0, dx, dy;
	int mx, my, ox, oy, dhx, dhy, x1, y1, x2, y2,  flag = 0;

	setcolors (vp, (int) sprnum, hulkcolor);
	startxy (&spr.x, &spr.y);
	if (spr.x == MINX || spr.x == wide)	/*  Determine starting dir  */
		flag = 1;
	spr.height = HULKHEIGHT;
	ChangeSprite (0L, &spr, leftseq[idx]);
	mx = cstm -> clxdat;	/*  Clear collision status  */

trackmouse:
	while (1) {
		getmousexy (&mx, &my, HULKHEIGHT);
		dx = mx - spr.x;  dy = my - spr.y;
		dhx = 4 * sgn (dx);  dhy = 2 * sgn (dy);
		if (flag) {
			x1 = x2 = rnd (abs (dx)) * sgn (dx) + spr.x;
			y1 = spr.y;
			y2 = my;
		} else {
			y1 = y2 = rnd (abs (dy)) * sgn (dy) + spr.y;
			x1 = spr.x;
			x2 = mx;
		}
		ox = mx;  oy = my;

		if (flag) {
			dx = dhx;  dy = 0;
			if (walkhulkx (spr.x, x1, dhx))
				break;
			getmousexy (&mx, &my, HULKHEIGHT);
			if (mx != ox || my != oy)
				continue;

			dx = 0;  dy = dhy;
			if (walkhulky (y1, y2, dhy))
				break;
			getmousexy (&mx, &my, HULKHEIGHT);
			if (mx != ox || my != oy)
				continue;

			dx = dhx;  dy = 0;
			if (walkhulkx (x2, mx, dhx))
				break;
		} else {
			dx = 0;  dy = dhy;
			if (walkhulky (spr.y, y1, dhy))
				break;
			getmousexy (&mx, &my, HULKHEIGHT);
			if (mx != ox || my != oy)
				continue;

			dx = dhx;  dy = 0;
			if (walkhulkx (x1, x2, dhx))
				break;
			getmousexy (&mx, &my, HULKHEIGHT);
			if (mx != ox || my != oy)
				continue;

			dx = 0;  dy = dhy;
			if (walkhulky (y2, my, dhy))
				break;
		}
		flag = rnd (2);
	}

	if (dx)
		sequence = dx>0 ? rightseq : leftseq;
	else
		sequence = udseq;
	idx = idxsav;
	ox = oy = -9999;
	while (1) {
		spr.x += dx;  spr.y += dy;
		idx = ++idx & 3;
		ChangeSprite (0L, &spr, sequence[idx]);
		movemouse (dx, dy);
		mx = collision();
		Delay (6L);
		if (!collision()) {
			idxsav = idx;
			goto trackmouse;
		}
		getmousexy (&mx, &my, HULKHEIGHT);
		if (mx == ox && my == oy)	/*  Mouse at limit  */
			break;
		ox = mx;  oy = my;
	}

	/*  Finish walking hulk off screen  */
	while ((int) spr.x < wide && (int) spr.x > MINX &&
	       (int) spr.y < high && (int) spr.y > -HULKHEIGHT) {
		spr.x += dx;  spr.y += dy;
		idx = ++idx & 3;
		ChangeSprite (0L, &spr, sequence[idx]);
		Delay (6L);
	}
}

dogrunt ()
{
	register int idx = 0;
	int gx, gy, mx, my;

	setcolors (vp, (int) sprnum, gruntcolor);
	startxy (&gx, &gy);
	spr.x = gx;  spr.y = gy;  spr.height = GRUNTHEIGHT;
	ChangeSprite (0L, &spr, gruntseq[idx]);
	mx = cstm -> clxdat;	/*  Clear collision status  */

	/*  This will keep chasing you around until it gets you :->  */
	while (!collision()) {
		idx = ++idx & 3;
		getmousexy (&mx, &my, GRUNTHEIGHT);
		if (mx != gx)
			gx += mx > gx ? 4 : -4;
		if (my != gy)
			gy += my > gy ? 4 : -4;
		spr.x = gx;  spr.y = gy;
		ChangeSprite (0L, &spr, gruntseq[idx]);
		Delay (rnd (7) + 4L);
	}

	flashpointer ();
	MoveSprite (0L, &spr, (long) MINX, 0L);
}

enforce ()
{
	register int track, lim, inc;
	int mx, my, ox = -999, oy = -999, ex, ey, dx, dy, flag = 0;

	setcolors (vp, (int) sprnum, nforcecolor);
	startxy (&ex, &ey);
	spr.x = ex;  spr.y = ey;  spr.height = NFORCEHEIGHT;
	ChangeSprite (0L, &spr, nforce);
	mx = cstm -> clxdat;	/*  Clear collision bits  */

	/*
	 * This is a DDA algorithm that attempts to track the mouse pointer
	 * in a straight line, no matter where it is.  It keeps tracking
	 * until it gets it.
	 */
	while (!collision()) {
		getmousexy (&mx, &my, NFORCEHEIGHT);
		if (mx != ox || my != oy) {	/*  Mouse moved  */
			dx = mx - ex;  dy = my - ey;
			if (abs (dx) > abs (dy)) {
				flag = 1;
				lim = abs (dx);
				inc = abs (dy);
			} else {
				flag = 0;
				lim = abs (dy);
				inc = abs (dx);
			}
			track = lim/2;
			ox = mx;  oy = my;
		}
		if (flag) {
			ex += sgn (dx);
			if ((track += inc) > lim) {
				track -= lim;
				ey += sgn (dy);
			}
		} else {
			ey += sgn (dy);
			if ((track += inc) > lim) {
				track -= lim;
				ex += sgn (dx);
			}
		}

		MoveSprite (0L, &spr, (long) ex, (long) ey);
		WaitTOF ();
	}

	flashpointer ();
	MoveSprite (0L, &spr, (long) MINX, 0L);
}

walkhulkx (startx, endx, dx)
{
	register UWORD **sequence;
	register int x, idx = 0;

	if (dirchange)
		idx = idxsav;
	dirchange = 1;
	sequence = dx>0 ? rightseq : leftseq;

	for (x=startx; dx>0 ? x<endx : x>endx; x+=dx, idx = ++idx & 3) {
		spr.x = x;
		ChangeSprite (0L, &spr, sequence[idx]);
		Delay (6L);
		if (collision()) {
			idxsav = idx;
			return (1);
		}
	}
	idxsav = idx;
	return (0);
}

walkhulky (starty, endy, dy)
{
	register int y, idx = 0;

	if (!dirchange)
		idx = idxsav;
	dirchange = 0;

	for (y=starty; dy>0 ? y<endy : y>endy; y+=dy, idx = ++idx & 3) {
		spr.y = y;
		ChangeSprite (0L, &spr, udseq[idx]);
		Delay (6L);
		if (collision()) {
			idxsav = idx;
			return (1);
		}
	}
	idxsav = idx;
	return (0);
}

startxy (x, y)
register int *x, *y;
{
	if (rnd (2)) {
		*x = rnd (2) ? MINX : wide;
		*y = rnd (200);
	} else {
		*y = rnd (2) ? MINY : high;
		*x = rnd (320);
	}
}

getmousexy (x, y, sprheight)
register int *x, *y;
{
	register struct Screen *s = wbs;

	*x = (s -> MouseX >> 1) - 8;
	if (s -> ViewPort.Modes & LACE)
		*y = (s -> MouseY >> 1) + s -> TopEdge - sprheight/2;
	else
		*y = s -> MouseY + s -> TopEdge - sprheight/2;
}

setcolors (vp, spritenum, clist)
void *vp;
register UWORD *clist;
{
	long r, g, b, colorbase;
	register int i;

	colorbase = getcbase (spritenum);
	for (i=1; i<4; i++) {
		r = getr (clist[i]);
		g = getg (clist[i]);
		b = getb (clist[i]);
		SetRGB4 (vp, colorbase + i, r, g, b);
	}
}

flashpointer ()
{
	long rs, gs, bs,  dr, dg, db,  rd, gd, bd;
	register int i, n;

	rs = gs = bs = 15;
	rd = getr (prefs.color0);
	gd = getg (prefs.color0);
	bd = getb (prefs.color0);

	/*  Compute increments  */
	dr = rd - rs;
	dg = gd - gs;
	db = bd - bs;

	for (n=15; n>6; n--) {		/*  First flash  */
		for (i=0; i<3; i++)
			SetRGB4 (vp, 17L + i, (long) n, (long) n, (long) n);
		WaitTOF ();
	}

	rs <<= 4;  gs <<= 4;  bs <<= 4;
	for (n=0; n<16; n++) {		/*  Fade to background  */
		rs += dr;
		gs += dg;
		bs += db;
		for (i=0; i<3; i++)
			SetRGB4 (vp, 17L + i, rs>>4, gs>>4, bs>>4);
		WaitTOF ();  WaitTOF ();  WaitTOF ();  WaitTOF ();
	}

	Delay (150L);
	SetRGB4 (vp, 17L, (long) getr (prefs.color17),	/*  Yuck!  */
			  (long) getg (prefs.color17),
			  (long) getb (prefs.color17));
	SetRGB4 (vp, 18L, (long) getr (prefs.color18),
			  (long) getg (prefs.color18),
			  (long) getb (prefs.color18));
	SetRGB4 (vp, 19L, (long) getr (prefs.color19),
			  (long) getg (prefs.color19),
			  (long) getb (prefs.color19));
}

movemouse (dx, dy)
{
	mouse.ie_NextEvent = 0;
	mouse.ie_Class = IECLASS_RAWMOUSE;
	mouse.ie_TimeStamp.tv_secs = mouse.ie_TimeStamp.tv_micro = 0;
	mouse.ie_Code = IECODE_NOBUTTON;
	mouse.ie_Qualifier = IEQUALIFIER_RELATIVEMOUSE;
	mouse.ie_X = prefs.PointerTicks * (dx + dx);
	/*  Is this *really* right?  */
	mouse.ie_Y = prefs.PointerTicks * (dy + dy);
	if (DoIO (ioreq))
		die ("I/O error.");
}
SHAR_EOF
fi
if test -f 'robotroff.c'
then
	echo shar: "will not over-write existing file 'robotroff.c'"
else
cat << \SHAR_EOF > 'robotroff.c'
/*  :ts=8 bk=0
 *
 * robotroff.c:		Insidious display hack.
 *
 * Leo L. Schwab			8703.17		(415)-456-6565
 *
 * Public Domain.  Reproduce at will.  However, the author would greatly
 * appreciate it if:
 *	1) All parts of the program are made available free of charge
 * 	   (or for cost of media),
 *	2) You don't use this in a commercial product unless you at least
 *	   tell me about it,
 *	3) This notice and the author's name are preserved, even across
 * 	   subsequent revisions (I want to be famous, you see).
 *
 * Naturally, you don't *have* to abide by these terms.  Just remember
 * that if you do violate them, it means you are morally bankrupt.  You
 * wouldn't want people to think that about you, would you?
 *
*************************************************************************
			A few notes on the code:

	Bits of it are extremely messy.  Some of it looks unnecessary or
wrong.  As it turns out, this program works as written.  Hope you can
understand the code; some of it is instructive.  Watch out for ternary
operators; I've used quite a few of them.

	Since this is supposed to be a background display hack, it was
important to me to make it as small as I could and still do interesting
things.  This meant leaving out a couple of features I would have liked.
It would have been nice to have an explosion noise when the grunt or
enforcer ran into the mouse.  I was also thinking about arranging things
so that, if you plugged in a joystick at port 2 and tilted it around, you
could shoot the enemy sprites.  These are ideas that the more ambitious
among you might want to add in.  Some of you may have even more insidious
ideas.  Go right ahead.

	I encountered some odd behavior in the system I never noticed
before.  For example, if you AbortIO() an I/O request, the reply gets posted
to your reply port, and the signal you might Wait() on gets set.  WaitIO()
does not clear this signal apparently; I had to do it myself.

	Also, when moving the mouse pointer with false InputEvents, I
noticed that the mouse pointer, even in a 200-line screen, assumes it's in
a 400-line screen, and therefore a "movement" of 2 results in an actual
pointer movement of 1.  Odd.  Does anyone have an explanation for this?

	I tried to write the code in a such a way that it *should* work on
an interlaced screen, as well as one that's been 'morerows'ed.  However,
since 'morerows' causes overscan, which tromps sprite DMA, this may not
work on an overscan screen.  Oddly enough, it also seems to work with any
combination of screens, no matter their arrangement.  

	I had fun writing this program.  I hope you like it at least as
much as you liked Oing.

	"If you've enjoyed this program just half as much as I've enjoyed
writing it, then I'll have enjoyed it twice as much as you."

					Have fun,
					Leo L. Schwab

*************************************************************************/

#include <ctype.h>
#include <exec/types.h>
#include <exec/alerts.h>
#include <intuition/intuition.h>
#include <graphics/sprite.h>
#include <devices/input.h>
#include "robotroff.h"


/*  We're just opening this to get a pointer to the WorkBench ViewPort  */
struct NewWindow windef = {
	0, 0, 3, 10,
	-1, -1,
	NULL,
	NULL,
	NULL, NULL,
	NULL,
	NULL, NULL, 0, 0, 0, 0,
	WBENCHSCREEN
};

struct MsgPort		*ioport, *timeport, *cmdport;
struct IOStdReq		*ioreq;
struct timerequest	*timereq;
struct control		*ctl;
struct InputEvent	mouse;
struct SimpleSprite	spr;
struct Preferences	prefs;
struct Screen		*wbs;
struct Window		*win;
long			sprnum = -1;
int			wide, high;
void			*vp;
void			*IntuitionBase, *GfxBase;
/*  I take so many liberties with void *'s, it's scary.  */


main (ac, av)
char **av;
{
	void *daemon;

	if (daemon = FindPort (PORTNAME))
		reconfigure (ac, av, daemon);
	else {
		dodaemon (ac, av);
		postmessage ("Robotroff daemon terminated.");
	}

	closestuff ();
}

dodaemon (ac, av)
char **av;
{
	/*
	 * cmdport in this context means the port at which commands arrive
	 * for the daemon to act on.
	 */
	register struct control *msg;
	long csig, tsig, signals;
	int hw = DEFAULTHIGHWATER, lw = DEFAULTLOWATER, now = 0;
	void *me, *oldms;

	openstuff ();
	rnd ((short) -VBeamPos());	/*  Seed generator  */
	me = FindTask (NULL);

	/*  Parse command args  */
	if (ac)
		while (++av, --ac) {
			if (!strncmp (*av, "-h", 2)) {
				hw = atoi (&(*av)[2]);
				if (hw <= 0)
					hw = DEFAULTHIGHWATER;
			} else if (!strncmp (*av, "-l", 2)) {
				lw = atoi (&(*av)[2]);
				if (lw <= 0)
					lw = DEFAULTLOWATER;
			} else if (!strncmp (*av, "-p", 2))
				SetTaskPri (me, (long) atoi (&(*av)[2]));
			else if (!strcmp (*av, "-i"))
				now = 1;
		}
	if (hw < lw)	/*  hw time must not be shorter than lw time  */
		lw = hw;

	tsig = TIMESIG;
	csig = CMDSIG;
	while (1) {
		if (now) {
			now = 0;
			goto dosomething;
		}

		timereq -> tr_time.tv_secs = rnd (hw-lw+1) + lw;
		timereq -> tr_time.tv_micro = 0;

		SendIO (timereq);
		signals = Wait (tsig | csig);

		if (signals & csig) {
			/*  Command from another task (user)  */
			msg = GetMsg (cmdport);
			AbortIO (timereq);
			WaitIO (timereq);
			/*
			 * The I/O reply causes a signal to be 
			 * raised, which screws things up.  So
			 * we clear it.
			 */
			Wait (tsig);

			if (msg -> actions & SUICIDE) {
				ReplyMsg (msg);
				break;
			}
			if (msg -> actions & SETHIGH)
				hw = msg -> highwater;
			if (msg -> actions & SETLOW)
				lw = msg -> lowater;
			if (hw < lw)
				lw = hw;
			if (msg -> actions & SETPRI)
				SetTaskPri (me, (long) msg -> newpri);
			if (msg -> actions & DOSOMETHING)
				now = 1;
			ReplyMsg (msg);
		}

		if (signals & tsig) {
			/*  Timer went off, time to do something weird  */
			WaitIO (timereq);
dosomething:
			switch (rnd (5)) {
			case 0:  case 1:
				dohulk ();
				break;
			case 2:  case 3:
				dogrunt ();
				break;
			case 4:
				enforce ();
			}
		}
	}
}

reconfigure (ac, av, daemonport)
char **av;
void *daemonport;
{
	/*
	 * cmdport in this context means the reply port for commands to
	 * the daemon.
	 */

	if (ac < 2)	/*  Nothing to do  */
		return;

	/*  In order to use die(), we have to open Intuition.  */
	if (!(IntuitionBase = OpenLibrary ("intuition.library", 0L))) {
		Alert (AG_OpenLib | AO_Intuition, 0L);
		closestuff ();
		exit (20);
	}
	GetPrefs (&prefs, (long) sizeof (prefs));

	if (!(cmdport = CreatePort (NULL, NULL)))
		die ("Can't create command reply port.");

	if (!(ctl = CreateExtIO (cmdport, (long) sizeof (*ctl))))
		die ("Can't create command block.");

	/*  Parse command line args  */
	while (++av, --ac) {
		if (!strncmp (*av, "-h", 2)) {
			ctl -> highwater = atoi (&(*av)[2]);
			if (ctl -> highwater <= 0)
				ctl -> highwater = DEFAULTHIGHWATER;
			ctl -> actions |= SETHIGH;
		} else if (!strncmp (*av, "-l", 2)) {
			ctl -> lowater = atoi (&(*av)[2]);
			if (ctl -> lowater <= 0)
				ctl -> lowater = DEFAULTLOWATER;
			ctl -> actions |= SETLOW;
		} else if (!strncmp (*av, "-p", 2)) {
			ctl -> newpri = atoi (&(*av)[2]);
			ctl -> actions |= SETPRI;
		} else if (!strcmp (*av, "-i"))
			ctl -> actions |= DOSOMETHING;
		else if (!strcmp (*av, "-kill"))
			ctl -> actions |= SUICIDE;
	}

	/*  Send command to daemon  */
	PutMsg (daemonport, ctl);
	WaitPort (cmdport);
	GetMsg (cmdport);
}

openstuff ()
{
	if (!(IntuitionBase = OpenLibrary ("intuition.library", 0L))) {
		Alert (AG_OpenLib | AO_Intuition, 0L);
		closestuff ();
		exit (20);
	}
	GetPrefs (&prefs, (long) sizeof (prefs));

	if (!(GfxBase = OpenLibrary ("graphics.library", 0L)))
		die ("Art shop closed.");

	/*  Set up a few environment things based on screen info  */
	if (!(win = OpenWindow (&windef)))
		die ("Window painted shut.");
	vp = ViewPortAddress (win);
	wbs = win -> WScreen;
	CloseWindow (win);  win = 0;
	high = wbs -> Height;
	wide = wbs -> Width;
	if (wbs -> ViewPort.Modes & HIRES)
		wide /= 2;
	if (wbs -> ViewPort.Modes & LACE)
		high /= 2;

	if ((sprnum = GetSprite (&spr, 2L)) < 0)
		die ("Couldn't get sprite.");

	if (!(cmdport = CreatePort (PORTNAME)))
		die ("Can't create command port.");

	if (!(timeport = CreatePort (NULL, NULL)))
		die ("Can'r create time port.");

	if (!(timereq = CreateExtIO (timeport, (long) sizeof (*timereq))))
		die ("Can't make timer I/O.");
	if (OpenDevice (TIMERNAME, UNIT_VBLANK, timereq, 0L)) {
		timereq -> tr_node.io_Device = 0;
		die ("Can't open timer device.");
	}
	timereq -> tr_node.io_Command = TR_ADDREQUEST;

	if (!(ioport = CreatePort (NULL, NULL)))
		die ("Can't create I/O port.");

	if (!(ioreq = CreateStdIO (ioport)))
		die ("Can't make I/O request.");
	if (OpenDevice ("input.device", 0L, ioreq, 0L)) {
		ioreq -> io_Device = 0;
		die ("Can't generate input.");
	}
	ioreq -> io_Command = IND_WRITEEVENT;
	ioreq -> io_Flags = 0;
	ioreq -> io_Length = sizeof (struct InputEvent);
	ioreq -> io_Data = (APTR) &mouse;
}

closestuff ()
{
	if (ioreq) {
		if (ioreq -> io_Device)
			CloseDevice (ioreq);
		DeleteStdIO (ioreq);
	}
	if (timereq) {
		if (timereq -> tr_node.io_Device)
			CloseDevice (timereq);
		DeleteExtIO (timereq, (long) sizeof (*timereq));
	}
	if (ctl)		DeleteExtIO (ctl, (long) sizeof (*ctl));
	if (cmdport)		DeletePort (cmdport);
	if (ioport)		DeletePort (ioport);
	if (timeport)		DeletePort (timeport);
	if (sprnum >= 0)	FreeSprite (sprnum);
	if (win)		CloseWindow (win);
	if (GfxBase)		CloseLibrary (GfxBase);
	if (IntuitionBase)	CloseLibrary (IntuitionBase);
}

die (str)
char *str;
{
	postmessage (str);
	closestuff ();
	exit (10);
}

postmessage (str)
char *str;
{
	if (win)	CloseWindow (win);	/*  Should never happen  */

	windef.TopEdge = 0;
	windef.Height = 10;
	windef.Width = 8 + strlen (str) *
			(prefs.FontHeight == TOPAZ_SIXTY ? 10 : 8);
	windef.LeftEdge = (640 - windef.Width) / 2;
	windef.DetailPen = 1;
	windef.BlockPen = 3;
	windef.Flags |= ACTIVATE;
	windef.Title = (UBYTE *) str;

	if (!(win = OpenWindow (&windef)))
		/*  Crash system and let user worry about it  */
		Alert (AN_OpenWindow, 0L);

	Delay (200L);
	CloseWindow (win);  win = 0;
}
SHAR_EOF
fi
if test -f 'sprites.c'
then
	echo shar: "will not over-write existing file 'sprites.c'"
else
cat << \SHAR_EOF > 'sprites.c'
/*  :ts=8 bk=0
 *
 * sprites.c:	Sprite data.  Painted in DPaint and dumped out with 'gi'
 *		('ilbmdump' didn't work for me).
 *
 * Leo L. Schwab			8703.16
 */

#include <exec/types.h>

/*  Hulk sprites (16)  */

UWORD hulkcolor[] = {
	0x0000,
	0x00f0,
	0x0fff,
	0x0f00
};

/*  Left walking hulks  */

UWORD hl1[] = {
	0x0000,0x0000,
	0x0000,0x03e0, 0x0080,0x0080, 0x0080,0x0080, 0x1e3c,0x01c0,
	0x1e3c,0x01c0, 0x1e3c,0x01c0, 0x1e3c,0x01c0, 0x1e3c,0x01c0,
	0x1e3c,0x01c0, 0x1c3c,0x03c0, 0x01c0,0x01c0, 0x01c0,0x01c0,
	0x01c0,0x01c0, 0x07c0,0x07c0, 0x0000,0x0000, 0x0000,0x0000,
	0x0000,0x0000
};

UWORD hl2[] = {
	0x0000,0x0000,
	0x03e0,0x0000, 0x0080,0x0080, 0x0080,0x0080, 0x1e3c,0x01c0,
	0x1c7c,0x0380, 0x18fc,0x0700, 0x11fc,0x0e00, 0x03fc,0x5c02,
	0x07fc,0x3803, 0x0ffc,0x1000, 0x0370,0x0370, 0x661c,0x661c,
	0x3c06,0x3c06, 0x181c,0x181c, 0x0000,0x0000, 0x0000,0x0000,
	0x0000,0x0000
};

UWORD hl3[] = {
	0x0000,0x0000,
	0x03e0,0x03e0, 0x0080,0x0080, 0x0080,0x0080, 0x1f1c,0x00e0,
	0x1f8c,0x0070, 0x1fc4,0x0038, 0x1fe0,0x001c, 0x1ff0,0x200e,
	0x1ff8,0x6006, 0x1ffc,0x0000, 0x0370,0x0370, 0x661c,0x661c,
	0x3c06,0x3c06, 0x181c,0x181c, 0x0000,0x0000, 0x0000,0x0000,
	0x0000,0x0000
};

/*  Right walking hulks  */

UWORD hr1[] = {
	0x0000,0x0000,
	0x0000,0x07c0, 0x0100,0x0100, 0x0100,0x0100, 0x3c78,0x0380,
	0x3c78,0x0380, 0x3c78,0x0380, 0x3c78,0x0380, 0x3c78,0x0380,
	0x3c78,0x0380, 0x3c38,0x03c0, 0x0380,0x0380, 0x0380,0x0380,
	0x0380,0x0380, 0x03e0,0x03e0, 0x0000,0x0000, 0x0000,0x0000,
	0x0000,0x0000
};

UWORD hr2[] = {
	0x0000,0x0000,
	0x07c0,0x0000, 0x0100,0x0100, 0x0100,0x0100, 0x3c78,0x0380,
	0x3e38,0x01c0, 0x3f18,0x00e0, 0x3f88,0x0070, 0x3fc0,0x403a,
	0x3fe0,0xc01c, 0x3ff0,0x0008, 0x0ec0,0x0ec0, 0x3866,0x3866,
	0x603c,0x603c, 0x3818,0x3818, 0x0000,0x0000, 0x0000,0x0000,
	0x0000,0x0000
};

UWORD hr3[] = {
	0x0000,0x0000,
	0x07c0,0x07c0, 0x0100,0x0100, 0x0100,0x0100, 0x38f8,0x0700,
	0x31f8,0x0e00, 0x23f8,0x1c00, 0x07f8,0x3800, 0x0ff8,0x7004,
	0x1ff8,0x6006, 0x3ff8,0x0000, 0x0ec0,0x0ec0, 0x3866,0x3866,
	0x603c,0x603c, 0x3818,0x3818, 0x0000,0x0000, 0x0000,0x0000,
	0x0000,0x0000
};

/*  Up and down walking hulks  */

UWORD hu1[] = {
	0x0000,0x0000,
	0x0000,0x07c0, 0x0100,0x0100, 0x0100,0x0100, 0x1ff0,0xe00e,
	0x1ff0,0xc006, 0x1ff0,0xc006, 0x1ff0,0xc006, 0x1ff0,0xc006,
	0x1ff0,0xe00e, 0x1ff0,0xa00a, 0x06c0,0xa6ca, 0x06c0,0x06c0,
	0x06c0,0x06c0, 0x1ef0,0x1ef0, 0x0000,0x0000, 0x0000,0x0000,
	0x0000,0x0000
};

UWORD hu2[] = {
	0x0000,0x0000,
	0x07c0,0x0000, 0x0100,0x0100, 0x0100,0x0100, 0x1ff0,0xe00e,
	0x1ff0,0xc006, 0x1ff0,0xc006, 0x1ff0,0xc006, 0x1ff0,0xc006,
	0x1ff0,0xe00e, 0x1ff0,0xa00a, 0x06c0,0xa6ca, 0x1ec0,0x1ec0,
	0x00c0,0x00c0, 0x00c0,0x00c0, 0x00c0,0x00c0, 0x00f0,0x00f0,
	0x0000,0x0000
};

UWORD hu3[] = {
	0x0000,0x0000,
	0x07c0,0x07c0, 0x0100,0x0100, 0x0100,0x0100, 0x1ff0,0xe00e,
	0x1ff0,0xc006, 0x1ff0,0xc006, 0x1ff0,0xc006, 0x1ff0,0xc006,
	0x1ff0,0xe00e, 0x1ff0,0xa00a, 0x06c0,0xa6ca, 0x06f0,0x06f0,
	0x0600,0x0600, 0x0600,0x0600, 0x0600,0x0600, 0x1e00,0x1e00,
	0x0000,0x0000
};


/*  Grunt sprites (13)  */

USHORT gruntcolor[] = {
	0x0000,
	0x0fff,
	0x0ff0,
	0x0f00
};

UWORD gr1[] = {
	0x0000,0x0000,
	0x0780,0x0780, 0x0000,0x0fc0, 0x0fc0,0x0000, 0x0780,0x0780,
	0x7ff8,0x6798, 0x3ff0,0x7878, 0x1fe0,0x5ce8, 0x0fc0,0x4fc8,
	0x0fc0,0x0fc0, 0x1ce0,0x1ce0, 0x1ce0,0x1ce0, 0x0000,0x3cf0,
	0x0000,0x0000,
	0x0000,0x0000,
};

UWORD gr2[] = {
	0x0000,0x0000,
	0x0780,0x0780, 0x0000,0x0fc0, 0x0000,0x0fc0, 0x0780,0x0780,
	0x7ff8,0x6798, 0x3ff0,0x7878, 0x1fe0,0x5ce8, 0x0fc0,0x4fc8,
	0x1fc0,0x1fc0, 0x1ce0,0x1ce0, 0x00e0,0x3ce0, 0x00e0,0x00e0,
	0x0000,0x00f0,
	0x0000,0x0000
};

UWORD gr3[] = {
	0x0000,0x0000,
	0x0780,0x0780, 0x0000,0x0fc0, 0x0fc0,0x0fc0, 0x0780,0x0780,
	0x7ff8,0x6798, 0x3ff0,0x7878, 0x1fe0,0x5ce8, 0x0fc0,0x4fc8,
	0x0fe0,0x0fe0, 0x1ce0,0x1ce0, 0x1c00,0x1cf0, 0x1c00,0x1c00,
	0x0000,0x3c00,
	0x0000,0x0000
};


/*  Enforcer sprite (13)  */

USHORT nforcecolor[] = {
	0x0000,
	0x0fff,
	0x0a0f,
	0x033f
};

UWORD nforce[] = {
	0x0000,0x0000,
	0x0000,0x0100, 0x0000,0x0380, 0x0000,0x07c0, 0x0000,0x0fe0,
	0x07c0,0x1ff0, 0x0fe0,0x3018, 0x0000,0x07c0, 0x610c,0x0fe0,
	0x1ff0,0x0380, 0x6fec,0x0fe0, 0x0100,0x0000, 0x1ff0,0x0000,
	0x3ff8,0x0000,
	0x0000,0x0000
};
SHAR_EOF
fi
if test -f 'rnd.asm'
then
	echo shar: "will not over-write existing file 'rnd.asm'"
else
cat << \SHAR_EOF > 'rnd.asm'
*\
*  :ts=8
* Yet Another random number generator.  By Leo Schwab.
* Based on an idea posted on the USENET (Thanks, Sam Dicker!)
* For the Manx assembler.
*
* Calling convention:
*  short rnd (range);
*  short range;
*
* 8606.30
*/

		public    _rnd

_rnd		lea	rndseed,a0	; Get address of seed
		move.w	4(sp),d1	; Get range argument
		tst.w	d1
		ble.s	setseed		; Go reset seed


		move.l	(a0),d0		; Get seed
		ADD.L   D0,D0
		BHI.S   over
		EORI.L  #$1D872B41,D0
over
		move.l	d0,(a0)		; Save new seed
		andi.l	#$ffff,d0	; Coerce into word
		divu	d1,d0		; Divide by range
		swap	d0		;  and get remainder (modulus)
		rts

setseed		neg.w	d1		; Probably don't need this
		move.l	d1,(a0)
		rts

		dseg
rndseed		dc.l	0
		cseg
SHAR_EOF
fi
exit 0
#	End of shell archive