/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* |_o_o|\\ Copyright (c) 1986 The Software Distillery.  All Rights Reserved */
/* |. o.| || This program may not be distributed without the permission of   */
/* | .  | || the authors.                                                    */
/* | o  | ||    Dave Baker     Ed Burnette  Stan Chow    Jay Denebeim        */
/* |  . |//     Gordon Keener  Jack Rouse   John Toebes  Doug Walker         */
/* ======          BBS:(919)-471-6436      VOICE:(919)-469-4210              */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/*
 * VERY loosely based on the input.device example by Rob Peck, 12/1/85
 *
 *
 * Additions and modifications Copyright (c) 1989, 1990, 1991 Loren J. Rittle
 * Modifications Copyright (c) 1990 Ed A. Hutchins and Loren J. Rittle
 *
 * As stated in the original POPCLI documentation file:
 *
 * Permission is hereby granted to distribute this program provided
 * both the documentation accompanies the executable and that no
 * charge is made for its distribution.
 * I assume that this statement includes the right to modify the source
 * and futher distribute the modified work under this same agreement. - LJR
 * (Actually, at this point, 'diff newpop.c popcli3.c | wc'
 * > 'wc -c newpop.c' + 'wc -c popcli3.c'. :-)
 *
 *
 *	Note:	Some code to do the Q*X pattern lifted (and modified) from:
 * 		Line drawing demo for the Commodore/Amiga
 * 		Written by John Riley, Lattice, Inc.
 *
 *	Note:   The spawning of a lower priority task to do graphics
 *		was inspired by Mackie to some degree (and by
 *		one person who claimed that NewPop slowed down
 *		HST modem transfers! I think that they were
 *		on a non-A3000 class machine :-)  Later, I got
 *		a report that it also happened on an A3000! :-(
 *		See NICENEWPOP in `[s:].newpoprc'.
 *
 *	UpfrontLayer is used instead of WindowToFront() because of
 *	a WorkBench/INTUITION bug. From dmouse.doc:
 *
 *	WORKBENCH USERS!!!!!!!!!! There appears to be a bug in
 *	intuition's WindowToFront() call, which can lock up
 * 	intuition when workbench icons are active.  Under the
 *	defaults, this will occur whenever you depress the left
 * 	mouse button over an icon.
 *
 * 	Thanks to Matt Dillon for showing the work around
 * 	in DMouse's source code.
 *
 *	See ChangeLog for the record of changes.
 *
 *	Loren J. Rittle
 *	l-rittle@uiuc.edu
 */


/* * * * * * * * * * * INCLUDES * * * * * * * * * * * * */
#pragma msg 148 ignore push
#pragma msg 149 ignore push
#pragma msg 61 ignore push
#include <exec/exec.h>
#include <dos/dos.h>
#include <dos/dostags.h>
#include <devices/timer.h>
#include <devices/input.h>
#include <devices/inputevent.h>
#include <intuition/intuitionbase.h>
#include <graphics/gfxmacros.h>
#include <graphics/layers.h>
#include <graphics/gfxbase.h>
#include <graphics/text.h>
/*int GetString (char *, char *, struct Window *, int, int);
#include <proto/req.h>*/
#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/layers.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <proto/diskfont.h>
#pragma msg 149 pop
#pragma msg 61 pop

#include <string.h>
#include <time.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>

LoadRGB32 (void *, unsigned long *table);
#pragma libcall GfxBase LoadRGB32 372 9802

/* * * * * * * * * * * CONSTANTS * * * * * * * * * * * * */
#define PORTNAME 	"NewPop.port"
 /* The name of our well known *PRIVATE* message port */

#define MAXCMD		200
#define MAXTASKNAME	32
#define MAXFONTNAME	32
#define MAXPASSWORD	32
 /* Maximum string size of various things */

#define BANNER \
"\x9B" "0;33mNewPop\x9B" \
"0m v4.0 by Loren J. Rittle - Copyright \xA9 1989 - 1991 Based upon:\n" \
"\x9B" "0;33mPOPCLI III\x9B" \
"0m by John Toebes - Copyright \xA9 1987 The Software Distillery\n"
 /* The hello banner */

#define ALREADYHEREMESSAGE \
"NewPop is already running, you should use `break [cli number of NewPop]'\n" \
"to kill it before starting another.  Or, if started via the WorkBench,\n" \
"another double click on the NewPop icon will stop it.\n"
 /* The already here message :-) */


/* * * * * * * * * * * * * * * * TYPES * * * * * * * * * * * * * */
struct color
{
  int red, green, blue;
};

enum OptionTypes
{
  boolean, integer, string
};

struct NewPopOption
{
  char *name;
  enum OptionTypes type;
  union
  {
    int ivalue;
    char *svalue;
  } value;
  int min, max;
};


/* * * * * * * * * * * EXTERNALS AND GLOBAL DATA * * * * * * * * */
char FontName[MAXFONTNAME] = "Helvetica.font";
char TaskName[MAXTASKNAME] = "gvpscsi.device";
#ifndef LJR_FAVORITE
char Cmd[MAXCMD] = "NEWCLI >NIL:";
#else
char Cmd[MAXCMD] = "NewWSH <nil: >nil: cnn:0/11/640/189/WShell/c";
#endif
char PassWord[MAXPASSWORD] = "Amiga";

/*
 *  See .newpoprc for the meanings of these options!
 */
struct NewPopOption NewPopOptions[] =
{
#define NICENEWPOP (NewPopOptions[0].value.ivalue)
  {"NICENEWPOP", boolean, 1, 0, 1},
#define NEWPOPLITE (NewPopOptions[1].value.ivalue)
  {"NEWPOPLITE", boolean, 0, 0, 1},
#define WEWANTWINDOWDRAG (NewPopOptions[2].value.ivalue)
  {"WEWANTWINDOWDRAG", boolean, 0, 0, 1},
#define SCSITASKNAME (NewPopOptions[3].value.svalue)
  {"SCSITASKNAME", string, (int) TaskName, 0, MAXTASKNAME},
#define FONTNAME (NewPopOptions[4].value.svalue)
  {"FONTNAME", string, (int) FontName, 0, MAXFONTNAME},
#define COMMAND (NewPopOptions[5].value.svalue)
  {"COMMAND", string, (int) Cmd, 0, MAXCMD},
#define MAXSCSIUSAGE (NewPopOptions[6].value.ivalue)
  {"MAXSCSIUSAGE", integer, 55, 20, 255},
#define SCSIIOMONWIDTH (NewPopOptions[7].value.ivalue)
  {"SCSIIOMONWIDTH", integer, 100, 0, 1024},
#define CPULOADMONWIDTH (NewPopOptions[8].value.ivalue)
  {"CPULOADMONWIDTH", integer, 100, 0, 1024},
#define GAPBETWEEN2 (NewPopOptions[9].value.ivalue)
  {"GAPBETWEEN2", integer, 16, 0, 256},
#define GAPBETWEEN (NewPopOptions[10].value.ivalue)
  {"GAPBETWEEN", integer, 16, 0, 256},
#define FONTSIZE (NewPopOptions[11].value.ivalue)
  {"FONTSIZE", integer, 9, 4, 56},
#define STARTYOFFSET (NewPopOptions[12].value.ivalue)
  {"STARTYOFFSET", integer, 7, 2, 60},
#define MAXTEXTWIDTH (NewPopOptions[13].value.ivalue)
  {"MAXTEXTWIDTH", integer, 107, 10, 1024},
#define STARTXOFFSET (NewPopOptions[14].value.ivalue)
  {"STARTXOFFSET", integer, 56, 10, 200},
#define WINDOWHEIGHT (NewPopOptions[15].value.ivalue)
  {"WINDOWHEIGHT", integer, 8, 4, 56},
#define TIMEOUT (NewPopOptions[16].value.ivalue)
  {"TIMEOUT", integer, 180, 0, 0x00ffffff},
#define POPKEY (NewPopOptions[17].value.ivalue)
  {"POPKEY", integer, 69, 0, 255},
#define BLANKERPRIORITY (NewPopOptions[18].value.ivalue)
  {"BLANKERPRIORITY", integer, -20, -20, 10},
#define ULC (NewPopOptions[19].value.ivalue)
  {"ULC", integer, 0, 0, 2},
#define URC (NewPopOptions[20].value.ivalue)
  {"URC", integer, 1, 0, 2},
#define LLC (NewPopOptions[21].value.ivalue)
  {"LLC", integer, 0, 0, 2},
#define LRC (NewPopOptions[22].value.ivalue)
  {"LRC", integer, 2, 0, 2},
#define INSTANTKEY (NewPopOptions[23].value.ivalue)
  {"INSTANTKEY", integer, 0, 0, 255},
#define DEFEATKEY (NewPopOptions[24].value.ivalue)
  {"DEFEATKEY", integer, 0, 0, 255},
#define LOCK (NewPopOptions[25].value.ivalue)
  {"LOCK", boolean, 0, 0, 1},
#define PASSWORD (NewPopOptions[26].value.svalue)
  {"PASSWORD", string, (int) PassWord, 0, MAXPASSWORD},
#define MEMORYMONWIDTH (NewPopOptions[27].value.ivalue)
  {"MEMORYMONWIDTH", integer, 0, 0, 1024},
#define GAPBETWEEN3 (NewPopOptions[28].value.ivalue)
  {"GAPBETWEEN3", integer, 0, 0, 256},
#define BACKDROPWINDOW (NewPopOptions[29].value.ivalue)
  {"BACKDROPWINDOW", boolean, 1, 0, 1},
#define WIMPYCLOCK (NewPopOptions[30].value.ivalue)
  {"WIMPYCLOCK", boolean, 0, 0, 1},
/* Add new enties before this line! */
  {NULL, boolean, 0, 0, 0}
};

struct IntuitionBase *IntuitionBase;
struct LayersBase *LayersBase;
struct GfxBase *GfxBase;
struct Library *DiskfontBase;
struct DosLibrary *DosBase;
struct ExecBase *ExecBase;
struct Library *ReqBase;

struct NewScreen NewScreen =
{0, 0, 640, 400, 1, 0, 1, HIRES | LACE, SCREENBEHIND | SCREENQUIET | CUSTOMSCREEN, NULL, NULL, NULL, NULL};

struct NewWindow NewBlankerWindow =
{0, 0, 640, 400, 0xff, 0xff, 0, RMBTRAP | BORDERLESS | BACKDROP | SIMPLE_REFRESH | ACTIVATE, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, CUSTOMSCREEN};

int WindowWidth, WindowHeight;

struct Gadget WDragGadget =
{NULL, 0, 0, 0, 0, GADGHNONE, NULL, WDRAGGING, NULL, NULL, NULL, NULL, NULL, 0, 0};

struct NewWindow NewWindow =
{0, 0, 0, 0, 2, 1, 0, BORDERLESS | NOCAREREFRESH, &WDragGadget, NULL, NULL, NULL, NULL, 0, 0, 0, 0, WBENCHSCREEN};

struct Task *buddy;
ULONG creatclisig;
ULONG unblanksig;
ULONG mousemovesig;
ULONG noevents;
short creatsignum = -1;
short blanksignum = -1;
short mousemovenum = -1;
struct Screen *blankscreen;
struct Window *blankwindow;
USHORT instantblank, defeatblank;
ULONG scsiDisp;
struct Task *child;
struct Task *securer;
struct ViewPort *blankVP;
struct RastPort *blankRP;
USHORT xlim, ylim;
int maxchip, maxfast;
USHORT StartedFromWB;

/* * * * * * * * * * * PROTOTYPES * * * * * * * * * * * * */
struct InputEvent *__saveds __asm myhandler (register __a0 struct InputEvent *);
void createchild (void);
void __saveds blankertask (void);
void killchild (void);
void createsecurer (void);
void __saveds securertask (void);
void killsecurer (void);
void __saveds scsifriendlosing (void);
int ParseConfigFile (void);
int __stdargs main (int argc, char *argv[]);
void changecolor (void);
void updatedisplay (void);
void QueueTimer (struct timerequest *, ULONG);


/* * * * * * * * * * * OUR ROUTINES * * * * * * * * * * * */
struct InputEvent *__saveds __asm 
myhandler (register __a0 struct InputEvent * ev)
{
  struct InputEvent *ep, *laste;

  for (ep = ev, laste = NULL; ep != NULL; ep = ep->ie_NextEvent)
    {
      if ((ep->ie_Class == IECLASS_RAWKEY) &&
	  (ep->ie_Qualifier & IEQUALIFIER_LCOMMAND))
	{
	  if ((ep->ie_Code == POPKEY) && (POPKEY))
	    Signal (buddy, creatclisig);
	  else if ((ep->ie_Code == DEFEATKEY) && (DEFEATKEY))
	    {
	      defeatblank = !defeatblank;
	      instantblank = 0;
	    }
	  else if ((ep->ie_Code == INSTANTKEY) && (INSTANTKEY))
	    {
	      defeatblank = 0;
	      instantblank = !instantblank;
	    }
	  else
	    goto nomatch;
	  if (laste == NULL)
	    ev = ep->ie_NextEvent;
	  else
	    laste->ie_NextEvent = ep->ie_NextEvent;
	}
      else
nomatch:
	laste = ep;

      if (ep->ie_Class == IECLASS_RAWMOUSE)
	Signal (buddy, mousemovesig);

      if (ep->ie_Class != IECLASS_TIMER)
	{
	  noevents = 0;
	  if (blankscreen)
	    Signal (buddy, unblanksig);
	}
    }
  return (ev);
}

void 
createchild (void)
{
  child = CreateTask ("NewPopBlanker", BLANKERPRIORITY, (APTR) blankertask, 4000L);
}

void __saveds 
blankertask (void)
{
  struct MsgPort *timerport2;
  struct timerequest *timerreq2 = NULL;

  if (timerport2 = CreatePort ("NewPopTimerPort2", 0))
    if (timerreq2 = (struct timerequest *) CreateExtIO (timerport2, sizeof (struct timerequest)))
      if (!(OpenDevice (TIMERNAME, UNIT_VBLANK, (struct IORequest *) timerreq2, 0)))
	{
	  while (!(SetSignal (0L, 0L) & SIGBREAKF_CTRL_C))
	    {
	      changecolor ();
	      updatedisplay ();
	      if (child->tc_Node.ln_Pri == 10)
		SetTaskPri (child, BLANKERPRIORITY);
	      timerreq2->tr_node.io_Command = TR_ADDREQUEST;
	      timerreq2->tr_time.tv_secs = 0;
	      timerreq2->tr_time.tv_micro = 1000000 / 60;
	      DoIO ((struct IORequest *) timerreq2);
	    }
	}
      else
	Wait (SIGBREAKF_CTRL_C);
  if (timerreq2)
    {
      if (timerreq2->tr_node.io_Device)
	CloseDevice ((struct IORequest *) timerreq2);
      DeleteExtIO ((struct IOStdReq *) timerreq2);
    }
  if (timerport2)
    DeletePort (timerport2);
  Signal (buddy, SIGBREAKF_CTRL_D);
  Wait (0L);
}

void 
killchild (void)
{
  Signal (child, SIGBREAKF_CTRL_C);
  SetTaskPri (child, 20L);
  Wait (SIGBREAKF_CTRL_D);
  DeleteTask (child);
  child = NULL;
}

void 
createsecurer (void)
{
  securer = CreateTask ("NewPopSecurer", 19, (APTR) securertask, 4000L);
}

void __saveds 
securertask (void)
{
  while (!(SetSignal (0L, 0L) & SIGBREAKF_CTRL_C))
    {
      ScreenToFront (blankscreen);
      changecolor ();
    }
  Signal (buddy, SIGBREAKF_CTRL_D);
  Wait (0L);
}

void 
killsecurer (void)
{
  Signal (securer, SIGBREAKF_CTRL_C);
  Wait (SIGBREAKF_CTRL_D);
  DeleteTask (securer);
  securer = NULL;
}

void __saveds 
scsifriendlosing (void)
{
  scsiDisp++;
}

int 
ParseConfigFile (void)
{
  BPTR configFile;
#define INPUTBUFFERSIZE 512
  char buffer[INPUTBUFFERSIZE];
  int bp;
  int keyword;
#define KEYWORDBUFFERSIZE 31
  char keywordbuffer[KEYWORDBUFFERSIZE + 1];
  int keywordbp;
  int charinbuffer;
  int state = 0;
  int line = 1;
  int i;
  int neg;

  configFile = Open (".newpoprc", MODE_OLDFILE);
  if (!configFile)
    configFile = Open ("s:.newpoprc", MODE_OLDFILE);
  if (!configFile)
    return 0;

  while (charinbuffer = Read (configFile, buffer, INPUTBUFFERSIZE))
    {
      for (bp = 0; bp != charinbuffer; bp++)
	{
	  switch (state)
	    {
	    case 0:
	      if (buffer[bp] == '#')
		state = 1;
	      else if (buffer[bp] == '\n')
		line++;
	      else if ((buffer[bp] <= 'z') && (buffer[bp] >= 'a'))
		{
		  keywordbuffer[0] = buffer[bp] - 32;
		  keywordbp = 1;
		  state = 2;
		}
	      else if ((buffer[bp] <= 'Z') && (buffer[bp] >= 'A'))
		{
		  keywordbuffer[0] = buffer[bp];
		  keywordbp = 1;
		  state = 2;
		}
	      else if ((buffer[bp] != ' ') && (buffer[bp] != '\t'))
		{
		  state = 1;
		  goto out;
		}
	      break;
	    case 1:
	      if (buffer[bp] == '\n')
		{
		  line++;
		  state = 0;
		}
	      break;
	    case 2:
	      if ((buffer[bp] <= 'z') && (buffer[bp] >= 'a'))
		{
		  if (keywordbp >= KEYWORDBUFFERSIZE)
		    {
		      state = 1;
		      goto out;
		    }
		  keywordbuffer[keywordbp++] = buffer[bp] - 32;
		}
	      else if ((buffer[bp] <= 'Z') && (buffer[bp] >= 'A'))
		{
		  if (keywordbp >= KEYWORDBUFFERSIZE)
		    {
		      state = 1;
		      goto out;
		    }
		  keywordbuffer[keywordbp++] = buffer[bp];
		}
	      else if ((buffer[bp] <= '9') && (buffer[bp] >= '0'))
		{
		  if (keywordbp >= KEYWORDBUFFERSIZE)
		    {
		      state = 1;
		      goto out;
		    }
		  keywordbuffer[keywordbp++] = buffer[bp];
		}
	      else if ((buffer[bp] == ' ') || (buffer[bp] == '\t'))
		{
		  keywordbuffer[keywordbp] = '\0';
		  for (i = 0; NewPopOptions[i].name; i++)
		    if (!strcmp (NewPopOptions[i].name, keywordbuffer))
		      break;
		  if (!NewPopOptions[i].name)
		    {
		      state = 1;
		      goto out;
		    }
		  keyword = i;
		  state = 3;
		}
	      else
		{
		  state = 1;
		  goto out;
		}
	      break;
	    case 3:
	      if ((buffer[bp] != ' ') && (buffer[bp] != '\t'))
		switch (NewPopOptions[keyword].type)
		  {
		  case boolean:
		    state = 4;
		    if ((buffer[bp] <= 'z') && (buffer[bp] >= 'a'))
		      {
			keywordbuffer[0] = buffer[bp] - 32;
			keywordbp = 1;
		      }
		    else if ((buffer[bp] <= 'Z') && (buffer[bp] >= 'A'))
		      {
			keywordbuffer[0] = buffer[bp];
			keywordbp = 1;
		      }
		    else
		      {
			state = 1;
			goto out;
		      }
		    break;
		  case integer:
		    state = 5;
		    neg = 0;
		    if (buffer[bp] == '-')
		      {
			neg = 1;
			NewPopOptions[keyword].value.ivalue = 0;
		      }
		    else if ((buffer[bp] <= '9') && (buffer[bp] >= '0'))
		      NewPopOptions[keyword].value.ivalue = buffer[bp] - '0';
		    else
		      {
			state = 1;
			goto out;
		      }
		    break;
		  case string:
		    state = 6;
		    if (buffer[bp] != '"')
		      {
			state = 1;
			goto out;
		      }
		    i = 0;
		    break;
		  }
	      break;
	    case 4:
	      if ((buffer[bp] <= 'z') && (buffer[bp] >= 'a'))
		{
		  if (keywordbp >= KEYWORDBUFFERSIZE)
		    {
		      state = 1;
		      goto out;
		    }
		  keywordbuffer[keywordbp++] = buffer[bp] - 32;
		}
	      else if ((buffer[bp] <= 'Z') && (buffer[bp] >= 'A'))
		{
		  if (keywordbp >= KEYWORDBUFFERSIZE)
		    {
		      state = 1;
		      goto out;
		    }
		  keywordbuffer[keywordbp++] = buffer[bp];
		}
	      else if ((buffer[bp] == ' ') || (buffer[bp] == '\t') || (buffer[bp] == '\n'))
		{
		  keywordbuffer[keywordbp] = '\0';
		  NewPopOptions[i].value.ivalue = 2;
		  if (!strcmp ("TRUE", keywordbuffer))
		    NewPopOptions[i].value.ivalue = 1;
		  if (!strcmp ("FALSE", keywordbuffer))
		    NewPopOptions[i].value.ivalue = 0;
		  if (!strcmp ("ON", keywordbuffer))
		    NewPopOptions[i].value.ivalue = 1;
		  if (!strcmp ("OFF", keywordbuffer))
		    NewPopOptions[i].value.ivalue = 0;
		  if (NewPopOptions[i].value.ivalue == 2)
		    {
		      state = 1;
		      goto out;
		    }
		  if (buffer[bp] == '\n')
		    {
		      line++;
		      state = 0;
		    }
		  else
		    state = 7;
		}
	      else
		{
		  state = 1;
		  goto out;
		}
	      break;
	    case 5:
	      if (buffer[bp] == '\n')
		{
		  if (neg)
		    NewPopOptions[keyword].value.ivalue *= -1;
		  if (NewPopOptions[keyword].value.ivalue > NewPopOptions[keyword].max)
		    {
		      state = 1;
		      goto out;
		    }
		  if (NewPopOptions[keyword].value.ivalue < NewPopOptions[keyword].min)
		    {
		      state = 1;
		      goto out;
		    }
		  line++;
		  state = 0;
		}
	      else if ((buffer[bp] == ' ') || (buffer[bp] == '\t'))
		{
		  if (neg)
		    NewPopOptions[keyword].value.ivalue *= -1;
		  if (NewPopOptions[keyword].value.ivalue > NewPopOptions[keyword].max)
		    {
		      state = 1;
		      goto out;
		    }
		  if (NewPopOptions[keyword].value.ivalue < NewPopOptions[keyword].min)
		    {
		      state = 1;
		      goto out;
		    }
		  state = 7;
		}
	      else if ((buffer[bp] <= '9') && (buffer[bp] >= '0'))
		{
		  NewPopOptions[keyword].value.ivalue *= 10;
		  NewPopOptions[keyword].value.ivalue += buffer[bp] - '0';
		}
	      else
		{
		  state = 1;
		  goto out;
		}
	      break;
	    case 6:
	      if ((buffer[bp] == '\n') || (i >= NewPopOptions[keyword].max))
		{
		  state = 1;
		  goto out;
		}
	      NewPopOptions[keyword].value.svalue[i++] = buffer[bp];
	      if (buffer[bp] == '"')
		{
		  NewPopOptions[keyword].value.svalue[--i] = '\0';
		  state = 8;
		}
	      break;
	    case 7:
	      if (buffer[bp] == '#')
		state = 1;
	      else if (buffer[bp] == '\n')
		{
		  line++;
		  state = 0;
		}
	      else if ((buffer[bp] != ' ') && (buffer[bp] != '\t'))
		{
		  state = 1;
		  goto out;
		}
	      break;
	    case 8:
	      if (buffer[bp] == '\n')
		{
		  line++;
		  state = 0;
		}
	      else if ((buffer[bp] != ' ') && (buffer[bp] != '\t'))
		{
		  state = 1;
		  goto out;
		}
	      else
		state = 7;
	      break;
	    default:
	      state = 1;
	      goto out;
	    }
	}
    }
out:

  Close (configFile);
  if (state)
    {
      if (!StartedFromWB)
	{
          sprintf (buffer, "NewPop: parse error on line %d of .newpoprc\n", line);
	  Write (Output (), buffer, strlen (buffer));
	}
      return 1;
    }
  return 0;
}

int __stdargs 
main (int argc, char *argv[])
{
  USHORT scsifriendinstalled = 0;
  ULONG timersig, LIdle, LDisp;
  BPTR nullfh = NULL;
  struct MsgPort *port;
  struct MsgPort *timerport = NULL;
  struct MsgPort *inputDevPort = NULL;
  struct timerequest *timerreq = NULL;
  struct IOStdReq *inputRequestBlock = NULL;
  struct TextFont *textFont = NULL;
  struct Task *scsifriend;
  struct Interrupt handlerStuff;
  struct Window *titlewindow = NULL;
  struct RastPort *titleRP;
  UBYTE oldPriority;

  {
    buddy = FindTask (NULL);
    oldPriority = SetTaskPri (buddy, 20);
    if (!argc)
      StartedFromWB = 1;
    Forbid ();
    if (!(port = FindPort (PORTNAME)))
      {
	if (!(port = CreatePort (PORTNAME, 0)))
	  {
	    Permit ();
	    goto grandexit;
	  }
	Permit ();
	if (!StartedFromWB)
	  Write (Output (), BANNER, sizeof (BANNER));
	if (ParseConfigFile ())
	  goto abort;
      }
    else
      {
	Permit ();
	if (StartedFromWB)
	  Signal (port->mp_SigTask, SIGBREAKF_CTRL_C);
	else
	  Write (Output (), ALREADYHEREMESSAGE, sizeof (ALREADYHEREMESSAGE));
	goto grandexit;
      }
    if (!(nullfh = Open ("NIL:", MODE_NEWFILE)))
      goto abort;
    if (!(inputDevPort = CreatePort ("NewPopInputPort", 0)))
      goto abort;
    if (!(inputRequestBlock = (void *) CreateExtIO (inputDevPort, sizeof (struct IOStdReq))))
      goto abort;
    if (!(timerport = CreatePort ("NewPopTimerPort", 0)))
      goto abort;
    if (!(timerreq = (struct timerequest *) CreateExtIO (timerport, sizeof (struct timerequest))))
      goto abort;
    if ((creatsignum = AllocSignal (-1)) == -1)
      goto abort;
    if ((blanksignum = AllocSignal (-1)) == -1)
      goto abort;
    if ((mousemovenum = AllocSignal (-1)) == -1)
      goto abort;
    if (!(GfxBase = (struct GfxBase *) OpenLibrary ("graphics.library", 0)))
      goto abort;
    if (!(LayersBase = (struct LayersBase *) OpenLibrary ("layers.library", 0)))
      goto abort;
    if (!(IntuitionBase = (struct IntuitionBase *) OpenLibrary ("intuition.library", 0)))
      goto abort;
    if (!(DiskfontBase = OpenLibrary ("diskfont.library", 0)))
      goto abort;
    if (!(ExecBase = (struct ExecBase *) OpenLibrary ("exec.library", 0)))
      goto abort;
    if (LOCK)
      ReqBase = OpenLibrary ("req.library", 0);
    if (!NEWPOPLITE)
      {
	if (MEMORYMONWIDTH)
	  {
	    struct MemHeader *mem;

	    Forbid ();
	    for (mem = (struct MemHeader *)ExecBase->MemList.lh_Head;
		 mem->mh_Node.ln_Succ;
		 mem = (struct MemHeader *)mem->mh_Node.ln_Succ)
	      {
		if (mem -> mh_Attributes & MEMF_CHIP)
		  maxchip += ((ULONG) mem -> mh_Upper - (ULONG) mem -> mh_Lower);
		/* Just in case we have something that is both chip and fast... */
		if (mem -> mh_Attributes & MEMF_FAST)
		  maxfast += ((ULONG) mem -> mh_Upper - (ULONG) mem -> mh_Lower);
	      }
	    Permit ();
	  }
	else
	  GAPBETWEEN3 = 0;
	if (SCSIIOMONWIDTH)
	  {
	    Forbid ();
	    if (scsifriend = FindTask (SCSITASKNAME))
	      if (!(scsifriend->tc_Flags & TF_SWITCH))
	        {
	          scsifriend->tc_Switch = scsifriendlosing;
	          scsifriend->tc_Flags |= TF_SWITCH;
	          scsifriendinstalled = 1;
	        }
	    Permit ();
	  }
	else
	  GAPBETWEEN2 = 0;
	if (!CPULOADMONWIDTH)
	  GAPBETWEEN = 0;
	WindowWidth = MEMORYMONWIDTH + GAPBETWEEN3 + SCSIIOMONWIDTH +
	  GAPBETWEEN2 + CPULOADMONWIDTH + GAPBETWEEN + MAXTEXTWIDTH;
	WindowHeight = WINDOWHEIGHT;
	NewWindow.LeftEdge = GfxBase->NormalDisplayColumns - WindowWidth - STARTXOFFSET;
	NewWindow.TopEdge = 1;
	NewWindow.Width = WindowWidth;
	NewWindow.Height = WindowHeight;
	if (WEWANTWINDOWDRAG)
	  {
	    WDragGadget.Height = WindowHeight;
	    WDragGadget.Width = WindowWidth;
	  }
	else
	  NewWindow.FirstGadget = NULL;
	if (BACKDROPWINDOW)
	  NewWindow.Flags |= BACKDROP;
	if ((NewWindow.LeftEdge < 0) || !(titlewindow = OpenWindow (&NewWindow)))
	  goto abort;
	titleRP = titlewindow->RPort;
	SetAPen (titleRP, 1);
	SetBPen (titleRP, 1);
	SetDrMd (titleRP, JAM2);
	RectFill (titleRP, 0, 0, (WindowWidth - 1), (WindowHeight - 1));
	if (MAXTEXTWIDTH)
	  {
	    struct TextAttr textAttr = {NULL, 0, 0, 0};

	    textAttr.ta_Name = FONTNAME;
	    textAttr.ta_YSize = FONTSIZE;
	    if (textFont = OpenFont (&textAttr))
	      {
	        if ((textFont->tf_YSize != FONTSIZE) || (textFont->tf_Style != 0))
	          {
		    CloseFont (textFont);
		    if (!(textFont = OpenDiskFont (&textAttr)))
		      goto abort;
	          }
	      }
	    else
	      {
	        if (!(textFont = OpenDiskFont (&textAttr)))
	          goto abort;
	      }
	    SetFont (titleRP, textFont);
	  }
	LIdle = ExecBase->IdleCount;
	LDisp = ExecBase->DispCount;
      }
    if (OpenDevice ("input.device", 0, (struct IORequest *) inputRequestBlock, 0))
      goto abort;
    handlerStuff.is_Data = (APTR) NULL;
    handlerStuff.is_Code = (VOID (*) ()) myhandler;
    handlerStuff.is_Node.ln_Pri = 51;
    timersig = (1 << timerport->mp_SigBit);
    creatclisig = 1 << creatsignum;
    unblanksig = 1 << blanksignum;
    mousemovesig = 1 << mousemovenum;
    inputRequestBlock->io_Command = IND_ADDHANDLER;
    inputRequestBlock->io_Data = (APTR) & handlerStuff;
    DoIO ((struct IORequest *) inputRequestBlock);
    if (OpenDevice (TIMERNAME, UNIT_VBLANK, (struct IORequest *) timerreq, 0))
      goto abort;
    QueueTimer (timerreq, 1 /* 1/60th of a second */ );
  }

  {
    UWORD newsecond;
    ULONG sig, Seconds, Micros, LastSeconds = 0;
    int topscreenwidth = GfxBase->NormalDisplayColumns;
    int topscreenheight = GfxBase->NormalDisplayRows;

    for (;;)
      {
	sig = Wait (creatclisig | unblanksig | timersig |
		    mousemovesig | SIGBREAKF_CTRL_C);

	if (sig & mousemovesig)
	  {
	    int left, right, upper, lower;

	    if (!blankscreen)
	      {
		int lock = LockIBase (0);
		struct Screen *screen = IntuitionBase->FirstScreen;

		while (screen && IntuitionBase->MouseY <
		       (screen->TopEdge << ((screen->ViewPort.Modes & LACE) ? 0 : 1)))
		  screen = screen->NextScreen;
		if (screen)
		  {
		    topscreenwidth = screen->Width << ((screen->ViewPort.Modes & HIRES) ? 0 : 1);
		    topscreenheight = screen->Height >> ((screen->ViewPort.Modes & LACE) ? 1 : 0);
		  }
		else
		  {
		    topscreenwidth = GfxBase->NormalDisplayColumns;
		    topscreenheight = GfxBase->NormalDisplayRows;
		  }
		UnlockIBase (lock);
	      }

	    left = IntuitionBase->MouseX < 10;
	    right = (topscreenwidth - IntuitionBase->MouseX) < 10;
	    upper = IntuitionBase->MouseY < 10;
	    lower = (topscreenheight - IntuitionBase->MouseY / 2) < 10;

	    instantblank = ((((ULC == 1) && upper) || ((LLC == 1) && lower)) && left) ||
	      ((((URC == 1) && upper) || ((LRC == 1) && lower)) && right);
	    defeatblank = ((((ULC == 2) && upper) || ((LLC == 2) && lower)) && left) ||
	      ((((URC == 2) && upper) || ((LRC == 2) && lower)) && right);
	  }

	CurrentTime (&Seconds, &Micros);
	newsecond = (Seconds != LastSeconds);
	LastSeconds = Seconds;

	if (newsecond && !NEWPOPLITE && !blankscreen)
	  {
	    SHORT x = 0;
	    ULONG DIdle, DDisp, Usage;
	    long t;
	    struct tm *p;
	    char *timestr;

	    UpfrontLayer (0L, titlewindow->WLayer);

	    DIdle = ExecBase->IdleCount - LIdle;
	    DDisp = ExecBase->DispCount - LDisp;
	    LIdle = ExecBase->IdleCount;
	    LDisp = ExecBase->DispCount;
	    if (!DDisp)
	      DDisp++;

	    if (MEMORYMONWIDTH)
	      {
		int pixels;

		SetAPen (titleRP, 3);
		pixels = AvailMem (MEMF_CHIP) * MEMORYMONWIDTH / maxchip;
		RectFill (titleRP, x, 0, x+pixels, ((WindowHeight/2) - 1));
	        SetAPen (titleRP, 1);
		RectFill (titleRP, x+pixels+1, 0, MEMORYMONWIDTH, ((WindowHeight/2) - 1));

		SetAPen (titleRP, 2);
		pixels = AvailMem (MEMF_FAST) * MEMORYMONWIDTH / maxfast;
		RectFill (titleRP, x, (WindowHeight/2), x+pixels, (WindowHeight - 1));
	        SetAPen (titleRP, 1);
		RectFill (titleRP, x+pixels+1, (WindowHeight/2), MEMORYMONWIDTH, (WindowHeight - 1));

	        x = MEMORYMONWIDTH;
	      }

	    x += GAPBETWEEN3;

	    if (scsifriendinstalled)
	      {
		Usage = (scsiDisp * 256) / (DDisp + DIdle);
		if (Usage > MAXSCSIUSAGE)
		  Usage = MAXSCSIUSAGE;
		SetAPen (titleRP, 3);
		ScrollRaster (titleRP, 1, 0, x, 0, x + SCSIIOMONWIDTH, (WindowHeight - 1));
		x += SCSIIOMONWIDTH;
		Move (titleRP, x, (WindowHeight - 1));
		Draw (titleRP, x, WindowHeight - ((WindowHeight * Usage) / MAXSCSIUSAGE));
		scsiDisp = 0;
	      }
	    else
	      x += SCSIIOMONWIDTH;

	    x += GAPBETWEEN2;

	    if (CPULOADMONWIDTH)
	      {
	        /* There be magic below!  Where does 456 come from? :-) */
	        if (scsiDisp < DDisp)
	          Usage = ((DDisp - scsiDisp) * 456) / (DDisp + DIdle);
	        else
	          Usage = 0;
	        if (Usage < 200)
	          Usage = 0;
	        else
	          Usage = Usage - 200;
	        ScrollRaster (titleRP, 1, 0, x, 0, x + CPULOADMONWIDTH, (WindowHeight - 1));
	        x += CPULOADMONWIDTH;
	        SetAPen (titleRP, 2);
	        Move (titleRP, x, (WindowHeight - 1));
	        Draw (titleRP, x, WindowHeight - ((WindowHeight * Usage) / 256));
	      }

	    x += GAPBETWEEN;

	    if (MAXTEXTWIDTH)
	      {
	        time (&t);
	        p = localtime (&t);
	        timestr = asctime (p);
		if (WIMPYCLOCK)
		  switch ((timestr[11] - '0') * 10 + (timestr[12] - '0'))
		    {
		    case 0: timestr[11] = '1'; timestr[12] = '2'; goto am;
		    case 12: goto pm;
		    case 13: timestr[11] = '0'; timestr[12] = '1'; goto pm;
		    case 14: timestr[11] = '0'; timestr[12] = '2'; goto pm;
		    case 15: timestr[11] = '0'; timestr[12] = '3'; goto pm;
		    case 16: timestr[11] = '0'; timestr[12] = '4'; goto pm;
		    case 17: timestr[11] = '0'; timestr[12] = '5'; goto pm;
		    case 18: timestr[11] = '0'; timestr[12] = '6'; goto pm;
		    case 19: timestr[11] = '0'; timestr[12] = '7'; goto pm;
		    case 20: timestr[11] = '0'; timestr[12] = '8'; goto pm;
		    case 21: timestr[11] = '0'; timestr[12] = '9'; goto pm;
		    case 22: timestr[11] = '1'; timestr[12] = '0'; goto pm;
		    case 23: timestr[11] = '1'; timestr[12] = '1'; goto pm;
		    default: am: timestr[19] = 'a'; break;
		    pm: timestr[19] = 'p'; break;
		    }
	        Move (titleRP, x, STARTYOFFSET);
	        Text (titleRP, timestr, 24);

	        x += TextLength (titleRP, timestr, 24);
	        SetAPen (titleRP, 1);
	        if (x < (WindowWidth - 1))
	          RectFill (titleRP, x, 0, (WindowWidth - 1), (WindowHeight - 1));
	      }
	  }

	if (blankscreen)
	  {
	    if (NICENEWPOP)
	      {
		if (newsecond)
		  {
		    /*changecolor ();*/
		    SetTaskPri (child, 10);
		  }
	      }
	    else
	      {
		/*if (newsecond)*/
		  changecolor ();
		updatedisplay ();
	      }
	  }

	if (sig & SIGBREAKF_CTRL_C)
	  goto abort;

	if (sig & creatclisig)
	  {
	    if (!(blankscreen && LOCK))
	      {
		WBenchToFront ();
		if (DOSBase->dl_lib.lib_Version < 36)
		  Execute(COMMAND, nullfh, nullfh);
		else
	  	  {
		    static struct TagItem stags[4] =
		    {
		      {SYS_Input, NULL},
		      {SYS_Output, NULL},
		      {SYS_UserShell, TRUE},
		      {TAG_DONE, 0}
		    };

		    stags[0].ti_Data = nullfh;
		    System(COMMAND, stags);
	          }
		if (blankscreen)
		  goto unblank;
	      }
	  }

	{
	  static WORD joy0, oldjoy0, joy1, oldjoy1;

	  joy0 = *((WORD *)0x00dff00a) & 0x0303;
	  joy1 = *((WORD *)0x00dff00c) & 0x0303;
	  if ((joy0 != oldjoy0) || (joy1 != oldjoy1))
	    {
	      oldjoy0 = joy0;
	      oldjoy1 = joy1;
	      if (blankscreen && !instantblank)
		goto unblank;
	    }
	}

	if ((sig & unblanksig) && blankscreen && !instantblank)
	  {
	  unblank:
	    if (LOCK && ReqBase)
	      {
		char InputPassWord[MAXPASSWORD];
	        static int trywait;

		SetRGB4 (blankVP, 17, 4, 4, 4);
		SetRGB4 (blankVP, 18, 8, 8, 8);
		SetRGB4 (blankVP, 19, 12, 12, 12);
		createsecurer ();
		InputPassWord[0] = 0;
		if ((trywait++ % 2) /*&&
		    GetString (InputPassWord, "Type password, then hit return.",
		    blankwindow, MAXPASSWORD + 10, MAXPASSWORD)*/)
		  {
		    killsecurer ();
		    UpfrontLayer (0L, blankwindow->WLayer);
		    if (strcmp (InputPassWord, PASSWORD))
		      goto noway;
		  }
		else
		  {
		    killsecurer ();
		    UpfrontLayer (0L, blankwindow->WLayer);
		    goto noway;
		  }
	      }
	    if (NICENEWPOP)
	      killchild ();
	    CloseWindow (blankwindow);
	    CloseScreen (blankscreen);
	    blankscreen = NULL;
	    noevents = 0;
	  noway: ;
	  }

	if (sig & timersig)
	  {
	    GetMsg (timerport);
	    if (blankscreen && NICENEWPOP)
	      QueueTimer (timerreq, 60 /* One second */ );
	    else
	      QueueTimer (timerreq, 1 /* 1/60th of a second*/ );

	    if ((instantblank || (noevents++ >= (TIMEOUT * 60))) && !blankscreen && !defeatblank && TIMEOUT)
	      {
		NewScreen.Height = GfxBase->NormalDisplayRows * 2;
		NewScreen.Width = GfxBase->NormalDisplayColumns;

		if (blankscreen = OpenScreen (&NewScreen))
		  {
		    ylim = blankscreen->Height - 1;
		    xlim = blankscreen->Width - 1;
		    NewBlankerWindow.Screen = blankscreen;
		    NewBlankerWindow.Height = blankscreen->Height;
		    NewBlankerWindow.Width = blankscreen->Width;
		    if (blankwindow = OpenWindow (&NewBlankerWindow))
		      {
			static UWORD __chip NULLpointer[1] = {0x0000};

			blankRP = blankwindow->RPort;
			blankVP = &(blankscreen->ViewPort);
			SetPointer (blankwindow, NULLpointer, 0, 0, 0, 0);
			changecolor ();
			SetRGB4 (blankVP, 0, 0, 0, 0);
			SetRGB4 (blankVP, 17, 0, 0, 0);
			SetRGB4 (blankVP, 18, 0, 0, 0);
			SetRGB4 (blankVP, 19, 0, 0, 0);
			SetDrMd (blankRP, JAM1);
			UpfrontLayer (0L, blankwindow->WLayer);
			if (NICENEWPOP)
			  createchild ();
		      }
		    else
		      {
		        CloseScreen (blankscreen);
		        blankscreen = NULL;
		      }
		  }
	      }
	  }

	if (blankscreen)
	  ScreenToFront (blankscreen);
      }
  }

abort:
#define SafeClose(fh) if (fh) Close (fh)
#define SafeDeletePort(port) if (port) DeletePort (port)
#define SafeCloseLibrary(lib) if (lib) CloseLibrary ((struct Library *) lib)
#define SafeFreeSignal(signum) if (signum != -1) FreeSignal (signum)
#define SafeCloseFont(font) if (font) CloseFont (font)
#define SafeCloseWindow(window) if (window) CloseWindow (window)
  SetTaskPri (buddy, oldPriority);
  SafeDeletePort (port);
  if (scsifriendinstalled)
    {
      Forbid ();
      if (scsifriend = FindTask (SCSITASKNAME))
	if (scsifriend->tc_Switch == scsifriendlosing)
	  {
	    UBYTE oldpri = SetTaskPri (scsifriend, 21);
	    Permit ();
	    /* Make SURE scsifriend has lost the CPU!
	    // I don't know why this fixed a problem
	    // people were having with A3000's/2.0/C=
	    // controllers, but it seems to work! */
	    Forbid ();
	    SetTaskPri (scsifriend, oldpri);
	    scsifriend->tc_Flags &= ~TF_SWITCH;
	    scsifriend->tc_Switch = NULL;
	  }
      Permit ();
    }
  SafeCloseWindow (titlewindow);
  if (timerreq)
    {
      if (timerreq->tr_node.io_Device)
        {
          if (!CheckIO ((struct IOStdReq *) timerreq));
	    {
	      AbortIO ((struct IOStdReq *) timerreq);
	      WaitIO ((struct IOStdReq *) timerreq);
	    }
	  CloseDevice ((struct IORequest *) timerreq);
	}
      DeleteExtIO ((struct IOStdReq *) timerreq);
    }
  if (inputRequestBlock)
    {
      if (inputRequestBlock->io_Device)
	{
	  inputRequestBlock->io_Command = IND_REMHANDLER;
	  inputRequestBlock->io_Data = (APTR) & handlerStuff;
	  DoIO ((struct IORequest *) inputRequestBlock);
	  CloseDevice ((struct IORequest *) inputRequestBlock);
	}
      DeleteExtIO (inputRequestBlock);
    }
  SafeCloseFont (textFont);
  SafeDeletePort (timerport);
  SafeFreeSignal (creatsignum);
  SafeFreeSignal (blanksignum);
  SafeFreeSignal (mousemovenum);
  if (blankscreen)
    {
      if (NICENEWPOP)
	killchild ();
      CloseWindow (blankwindow);
      CloseScreen (blankscreen);
    }
  SafeCloseLibrary (IntuitionBase);
  SafeCloseLibrary (GfxBase);
  SafeCloseLibrary (LayersBase);
  SafeCloseLibrary (DiskfontBase);
  SafeCloseLibrary (ExecBase);
  SafeCloseLibrary (ReqBase);
  SafeDeletePort (inputDevPort);
  SafeClose (nullfh);

  grandexit: ;
  return (0);
}

void 
changecolor (void)
{
  static struct color currentcolor = {127, 0, 254};
  static struct color colordelta = {-1, 1, 1};
  static unsigned long table[] = { (1<<16) + 1, 0, 0, 0, 0 };

  switch (rand () % 3)
    {
    case 0:
      currentcolor.red += colordelta.red;
      if (currentcolor.red == 0 ||
	  currentcolor.red == 255)
	colordelta.red = -colordelta.red;
      break;
    case 1:
      currentcolor.green += colordelta.green;
      if (currentcolor.green == 0 ||
	  currentcolor.green == 255)
	colordelta.green = -colordelta.green;
      break;
    case 2:
      currentcolor.blue += colordelta.blue;
      if (currentcolor.blue == 0 ||
	  currentcolor.blue == 255)
	colordelta.blue = -colordelta.blue;
      break;
    }

  table[1] = ((unsigned long) currentcolor.red) << 24;
  table[2] = ((unsigned long) currentcolor.green) << 24;
  table[3] = ((unsigned long) currentcolor.blue) << 24;
  LoadRGB32 (blankVP, table);
}

void 
updatedisplay (void)
{
  static SHORT x[2], y[2], xd[2], yd[2], ox[2][16], oy[2][16];
  static j;
  SHORT i, k;

  if (j == 0)
    {
      x[0] = rand () % xlim + 1;
      if ((x[1] = x[0] + rand () % (xlim / 6) - (xlim / 12)) > xlim)
	x[1] = xlim;
      if (x[1] < 0)
	x[1] = 0;
      y[0] = rand () % ylim + 1;
      if ((y[1] = y[0] + rand () % (ylim / 4) - (ylim / 8)) > ylim)
	y[1] = ylim;
      if (y[1] < 0)
	y[1] = 0;
      xd[0] = 11;
      yd[0] = 7;
      xd[1] = 3;
      yd[1] = 4;
    }
  SetAPen (blankRP, 0);
  Move (blankRP, ox[0][j & 15], oy[0][j & 15]);
  Draw (blankRP, ox[1][j & 15], oy[1][j & 15]);
  SetAPen (blankRP, 1);
  Move (blankRP, x[0], y[0]);
  Draw (blankRP, x[1], y[1]);
  for (i = 0; i < 2; i++)
    {
      ox[i][j & 15] = x[i];
      oy[i][j & 15] = y[i];
      x[i] += xd[i];
      y[i] += yd[i];
      if (abs (x[1] - x[0]) > xlim / 4)
	{
	  x[i] -= xd[i] * 4 / 3;
	  xd[i] = -xd[i] / 2;
	}
      if (abs (y[1] - y[0]) > ylim / 3)
	{
	  y[i] -= yd[i] * 4 / 3;
	  yd[i] = -yd[i] / 2;
	}
      if (x[i] < 0)
	{
	  x[i] = 0;
	  xd[i] = -xd[i];
	}
      else if (x[i] > xlim)
	{
	  x[i] = xlim;
	  xd[i] = -xd[i];
	}
      if (y[i] < 0)
	{
	  y[i] = 0;
	  yd[i] = -yd[i];
	}
      else if (y[i] > ylim)
	{
	  y[i] = ylim;
	  yd[i] = -yd[i];
	}
      if (((rand () >> 5) & 127) < 2)
	{
	  if (xd[i] < 1)
	    k = 1;
	  xd[i] = (rand () >> 5) & 7;
	  if (k == (1 - i))
	    xd[i] = -xd[i];
	  k = 0;
	}
      if (((rand () >> 5) & 255) < 50)
	{
	  if (yd[i] < 1)
	    k = 1;
	  yd[i] = (rand () >> 5) & 7;
	  if (k == (1 - i))
	    yd[i] = -yd[i];
	  k = 0;
	}
    }
  ++j;
}

void 
QueueTimer (struct timerequest * tr, ULONG ticks)
{
  tr->tr_node.io_Command = TR_ADDREQUEST;
  tr->tr_time.tv_secs = ticks / 60;
  tr->tr_time.tv_micro = (ticks % 60) * 1000000 / 60;
  SendIO ((struct IORequest *) tr);
}
