#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <graphics/gfxbase.h>
#include <graphics/gfxmacros.h>
#include <graphics/sprite.h>
#include <hardware/custom.h>
#include <hardware/dmabits.h>
#include <hardware/cia.h>
#include <hardware/intbits.h>
#include <devices/audio.h>
#include <functions.h>
#include "stdio.h"
struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct DiskfontBase *DiskfontBase ;
extern struct DosLibrary *DOSBase;
struct Screen *myscreen ;
struct Window *mywindow ;
struct ViewPort *vp;
struct UCopList *ucop;
struct Task *task, *rtask ;
int origpri ;
static struct TextFont *font ;
static struct TextAttr myfont = { (STRPTR) "topaz.font", 11, 0, 0 };
char tempbuf[100] ;
struct IntuiText itext = { 1, 0, JAM2, 0, 0, &myfont, (UBYTE *)&tempbuf } ;
#define MAXLINES (50)
#define MAXPOINTS (10)
int maxpoints ;
/*
 *   The external variables we access.
 */
struct RastPort *rastport ;
short screenheight, screenwidth, bytewidth ;
short bytewidthm[700] ;
short color1[700], color2[700] ;
/*
 *   Some locals to this file.
 */
static struct BitMap mybitmap ;
static struct NewScreen newscreen = {
   0, 0, 640, 400, 2, 0, 1, HIRES | LACE | SCREENQUIET | SPRITES,
   CUSTOMSCREEN | CUSTOMBITMAP, 0, 0, 0, &mybitmap } ;
static struct NewWindow newwindow = {
   0, 0, 32, 10, 0, 1, CLOSEWINDOW | VANILLAKEY,
   WINDOWCLOSE | SIMPLE_REFRESH | NOCAREREFRESH | ACTIVATE, 0, 0, 0, 0, 0, 0,
   0, 0, 0, CUSTOMSCREEN } ;
#define BSTRtoS(a) ((char *)(((long)(a))<<2))
long globalreplysignum ;
long rassize ;
long nilh ;
cleanup() {
   struct Process *p ;

   downsprites() ;
   freechannels() ;
   if (font != NULL)
      CloseFont(font) ;
   if (rtask) {
      Signal(rtask, 1L << SIGBREAKB_CTRL_C) ;
      SetTaskPri(rtask, 11L) ;
      Wait(1L << globalreplysignum) ;
      RemTask(rtask);
      FreeMem(rtask, (long)sizeof(struct Task)) ;
      rtask = NULL ;
   }
   if (globalreplysignum != -1)
      FreeSignal(globalreplysignum) ;
   if (vp) {
      FreeVPortCopLists(vp) ;
      RemakeDisplay() ;
   }
   if (mywindow)
      CloseWindow(mywindow) ;
   if (myscreen)
      CloseScreen(myscreen) ;
   if (mybitmap.Planes[0])
      FreeMem(mybitmap.Planes[0], rassize) ;
   if (mybitmap.Planes[1])
      FreeMem(mybitmap.Planes[1], rassize + 2 * bytewidth) ;
   if (DiskfontBase)
      CloseLibrary(DiskfontBase) ;
   if (IntuitionBase)
      CloseLibrary(IntuitionBase) ;
   if (GfxBase)
      CloseLibrary(GfxBase) ;
   RestoreFilter() ;
   if (task)
      SetTaskPri(task, (long)origpri) ;
/*
 *   We also kill any CLI processes that are running a program that
 *   starts with our play command.
 */
   p = (struct Process *)FindTask("player.task") ;
   if (p)
      Signal(p, 1L << SIGBREAKB_CTRL_C) ;
   if (nilh)
      Close(nilh) ;
   exit(0) ;
}
error(s)
register char *s ;
{
   Write(Output(), s, (long)strlen(s)) ;
   Write(Output(), "\n", 1L) ;
   Delay(10L) ;
   if (*s == '!')
      cleanup() ;
}
/*
 *   Whenever we need a guaranteed clear row, we use this.
 */
short *emptyrow ;
/*
 *   This routine opens a screen and fires off the task if apropriate.
 */
blankscreen() {
   register short *p, *q ;
   register long t, tt ;

   IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 0L) ;
   GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 0L) ;
   if (IntuitionBase == NULL || GfxBase == NULL)
      error("! no library") ;
   if ((t = (long)OpenLibrary("mathtrans.library", 0L))==0)
      error("! no mathtrans.library") ;
   CloseLibrary(t) ;
   screenheight = 2 * GfxBase->NormalDisplayRows ;
   screenwidth = GfxBase->NormalDisplayColumns ;
   newscreen.Height = screenheight ;
   newscreen.Width = screenwidth ;
   bytewidth = ((screenwidth + 15) >> 3) & ~1 ;
   rassize = screenheight * (long)bytewidth ;
   for (t=0; t<screenheight; t++)
      bytewidthm[t] = t * bytewidth ;
/*
 *   We want to make sure we can manipulate the bitmap pointer for the second
 *   bitplane simply by changing the lower 16-bits.  So we allocate a ton of
 *   memory, grab the right part, and then free the stuff above and below.
 */
   tt = (rassize + 2 * bytewidth + 7) & ~7 ;
   p = AllocMem(65536L + tt, MEMF_CHIP | MEMF_CLEAR) ;
   if (p==NULL)
      error("! no memory") ;
   q = (short *)((((long)p) + 65535L) & ~65535L) ;
   t = ((long)q) - ((long)p) ;
   if (t)
      FreeMem(p, t) ;
   t = 65536L - t ;
   if (t)
      FreeMem(((long)p) + tt + 65536L - t, t) ;
   p = AllocMem(rassize, MEMF_CHIP | MEMF_CLEAR) ;
   if (p==NULL) {
      FreeMem(q, rassize + 2 * bytewidth) ;
      error("! out of memory") ;
   }
   emptyrow = (short *)(((long)q) + rassize) ;
   mybitmap.BytesPerRow = bytewidth ;
   mybitmap.Rows = screenheight ;
   mybitmap.Depth = 2 ;
   mybitmap.Planes[0] = (PLANEPTR)p ;
   mybitmap.Planes[1] = (PLANEPTR)q ;
   if ((myscreen = OpenScreen(&newscreen)) == NULL)
      error("! no screen\n") ;
   if (screenheight > 700)
      error("! we don't work on screens that large") ;
   vp = &(myscreen->ViewPort) ;
   SetRGB4(vp, 0L, 0L, 0L, 0L) ;
   SetRGB4(vp, 1L, 0L, 0L, 0L) ;
   SetRGB4(vp, 2L, 0L, 0L, 0L) ;
   SetRGB4(vp, 3L, 0L, 0L, 0L) ;
   newwindow.Height = screenheight ; /* to disable dragging */
   newwindow.Width = screenwidth ;
   newwindow.Screen = myscreen ;
   if ((mywindow = OpenWindow(&newwindow)) == NULL)
      error("! no window\n") ;
   if ((globalreplysignum = AllocSignal(-1L)) == -1)
      error("! no signal\n") ;
   rastport = &(myscreen->RastPort) ;
   SetRast(rastport, 0L) ;
   InstallList() ;
   font = (struct TextFont *)OpenFont(&myfont) ;
   if (font == NULL || font->tf_YSize != 11) {
      if (font) {
         CloseFont(font) ;
         font = NULL ;
      }
      DiskfontBase = (struct DiskfontBase *)OpenLibrary("diskfont.library", 0L) ;
      if (DiskfontBase != NULL)
         font = (struct TextFont *)OpenDiskFont(&myfont) ;
   }
   if (font != NULL)
      SetFont(rastport, font) ;
   else
      error("! I need topaz 11 to run") ;
   SetPointer(mywindow, emptyrow+bytewidth/2, 0L, 0L, 0L, 0L) ;
}
/*
 *   This routine returns a random value from 0 to n-1.
 */
static long seed = 304019 ;
int randm(i)
int i ;
{
   register long rseed ;
   register unsigned int rval ;

   rseed = seed ;
   rseed = rseed * 23213 + 121 ;
   rval = (rseed >> 5) & 65535 ;
   seed = rseed ;
   return ((i * (long)rval) >> 16) ;
}
/*
 *   This routine sets x and y values to a random number.
 */
static long x, y ;
randomxy() {
   x = randm(screenwidth) ;
   y = randm(screenheight) ;
}
/*
 *   Main routines are always fun.
 */
struct box {
   short x[MAXPOINTS], y[MAXPOINTS] ;
} store[MAXLINES] ;
#define FIX(x) (((long)(x)) << 7)
#define FIXH(x) (((long)((*(x)+(x)[1])>>1)) << 7)
#define UNFIX(x) ((x)>>7)
struct box *ptr ;
struct box *eptr ;
int numlines ;
int mdelta = -1 ;
int maxlines = MAXLINES/2 ;
short dx[MAXPOINTS], dy[MAXPOINTS] ;
short ox[MAXPOINTS], oy[MAXPOINTS] ;
short nx[MAXPOINTS], ny[MAXPOINTS] ;
short dr, dg, db ;
short or, og, ob ;
short nr, ng, nb ;
long oldx, oldy, oldwidth, oldptr ;
int oldcol ;
/*
 *   Draws a spline!  Expects all arguments in registers.
 */
#asm
	public	_Draw
	cseg
rspline
	move.l	a0,d0
	sub.l	d6,d0
	move.l	d0,d3
	bpl	save1
	neg.l	d0
save1
	move.l	a1,d1
	sub.l	d7,d1
	move.l	d1,d4
	bpl	save2
	neg.l	d1
save2
	move.l	d0,d2
	cmp.l	d0,d1
	bmi	save3
	lsr.l	#3,d2
	bra	save9
save3
	lsr.l	#3,d1
save9
	add.l	d1,d2
	asr.l	#3,d2
	beq	check2
	asr.l	#5,d3
	asr.l	#5,d4
	move.l	a2,d0
	sub.l	a0,d0
	move.l	a3,d1
	sub.l	a1,d1
	asr.l	#5,d0
	asr.l	#5,d1
	muls.w	d4,d0
	muls.w	d3,d1
	sub.l	d1,d0
	bpl	save4
	neg.l	d0
save4
	cmp.l	d0,d2
	ble	pushem
	move.l	a5,d0
	sub.l	a0,d0
	move.l	a6,d1
	sub.l	a1,d1
	asr.l	#5,d0
	asr.l	#5,d1
	muls.w	d4,d0
	muls.w	d3,d1
	sub.l	d1,d0
	bpl	save5
	neg.l	d0
save5
	cmp.l	d0,d2
	ble	pushem
makeline
	lsr.l	#7,d7
	move.l	d7,d1
	lsr.l	#7,d6
	move.l	d6,d0
	movem.l	d2-d5/a0-a1,-(sp)
	move.l	_oldx,d2
	move.l	_oldy,d3
	move.l	d0,_oldx
	move.l	d1,_oldy
	move.l	_oldwidth,d4
	move.l	_oldptr,a0
	jsr	mdraw
	movem.l	(sp)+,d2-d5/a0-a1
	rts
check2
	move.l	a0,d0
	sub.l	a2,d0
	bpl	ch1
	neg.l	d0
ch1
	move.l	a1,d1
	sub.l	a3,d1
	bpl	ch2
	neg.l	d1
ch2
	add.l	d0,d1
	asr.l	#3,d1
	bne	pushem
	move.l	a0,d0
	sub.l	a5,d0
	bpl	ch3
	neg.l	d0
ch3
	move.l	a1,d1
	sub.l	a6,d1
	bpl	ch4
	neg.l	d1
ch4
	add.l	d0,d1
	asr.l	#3,d1
	beq	makeline
pushem
	movem.l	d6/d7,-(sp)
	move.l	a5,d0
	add.l	d6,d0
	asr.l	#1,d0
	move.l	a6,d1
	add.l	d7,d1
	asr.l	#1,d1
	movem.l	d0/d1,-(sp)
	move.l	a2,d2
	add.l	a5,d2
	asr.l	#1,d2
	move.l	a3,d3
	add.l	a6,d3
	asr.l	#1,d3
	move.l	d0,d4
	add.l	d2,d4
	asr.l	#1,d4
	move.l	d1,d5
	add.l	d3,d5
	asr.l	#1,d5
	movem.l	d4/d5,-(sp)
	move.l	a0,d6
	add.l	a2,d6
	asr.l	#1,d6
	move.l	a1,d7
	add.l	a3,d7
	asr.l	#1,d7
	move.l	d2,d0
	add.l	d6,d0
	asr.l	#1,d0
	move.l	d3,d1
	add.l	d7,d1
	asr.l	#1,d1
	move.l	d6,a2
	move.l	d7,a3
	move.l	d0,d6
	add.l	d4,d6
	asr.l	#1,d6
	move.l	d1,d7
	add.l	d5,d7
	asr.l	#1,d7
	movem.l	d6/d7,-(sp)
	move.l	d0,a5
	move.l	d1,a6
	jsr	rspline
	movem.l	(sp)+,a0/a1
	movem.l	(sp)+,a2/a3/a5/a6
	movem.l	(sp)+,d6/d7
	bra	rspline
;
	include 'exec/types.i'
	include 'hardware/custom.i'
	include 'hardware/blit.i'
	include 'hardware/dmabits.i'
;
	xref	_custom
;
;
;   Our entry point.
;
mdraw:
	move.l	#$dff000,a1	; Manx requires this
	sub.w	d0,d2		; calculate dx
	bmi	xneg		; if negative, octant is one of [3,4,5,6]
	sub.w	d1,d3		; calculate dy   ''   is one of [1,2,7,8]
	bmi	yneg		; if negative, octant is one of [7,8]
	cmp.w	d3,d2		; cmp |dx|,|dy|  ''   is one of [1,2]
	bmi	ygtx		; if y>x, octant is 2
	moveq.l	#OCTANT1+LINEMODE,d5	; otherwise octant is 1
	bra	lineagain	; go to the common section
ygtx:
	exg	d2,d3		; X must be greater than Y
	moveq.l	#OCTANT2+LINEMODE,d5	; we are in octant 2
	bra	lineagain	; and common again.
yneg:
	neg.w	d3		; calculate abs(dy)
	cmp.w	d3,d2		; cmp |dx|,|dy|, octant is [7,8]
	bmi	ynygtx		; if y>x, octant is 7
	moveq.l	#OCTANT8+LINEMODE,d5	; otherwise octant is 8
	bra	lineagain
ynygtx:
	exg	d2,d3		; X must be greater than Y
	moveq.l	#OCTANT7+LINEMODE,d5	; we are in octant 7
	bra	lineagain
xneg:
	neg.w	d2		; dx was negative! octant is [3,4,5,6]
	sub.w	d1,d3		; we calculate dy
	bmi	xyneg		; if negative, octant is one of [5,6]
	cmp.w	d3,d2		; otherwise it's one of [3,4]
	bmi	xnygtx		; if y>x, octant is 3
	moveq.l	#OCTANT4+LINEMODE,d5	; otherwise it's 4
	bra	lineagain
xnygtx:
	exg	d2,d3		; X must be greater than Y
	moveq.l	#OCTANT3+LINEMODE,d5	; we are in octant 3
	bra	lineagain
waitmore:
	nop
	nop
	btst	#DMAB_BLTDONE-8,dmaconr(a1)
	beq	donewait
	bra	waitmore
xyneg:
	neg.w	d3		; y was negative, in one of [5,6]
	cmp.w	d3,d2		; is y>x?
	bmi	xynygtx		; if so, octant is 6
	moveq.l	#OCTANT5+LINEMODE,d5	; otherwise, octant is 5
	bra	lineagain
xynygtx:
	exg	d2,d3		; X must be greater than Y
	moveq.l	#OCTANT6+LINEMODE,d5	; we are in octant 6
lineagain:
	mulu.w	d4,d1		; Calculate y1 * width
	ror.l	#4,d0		; move upper four bits into hi word
	add.w	d0,d0		; multiply by 2
	add.l	d1,a0		; ptr += (x1 >> 3)
	add.w	d0,a0		; ptr += y1 * width
	swap	d0		; get the four bits of x1
	or.w	_oldcol,d0	; or with USEA, USEC, USED, F=A~C+~AC
	lsl.w	#2,d3		; Y = 4 * Y
	add.w	d2,d2		; X = 2 * X
	move.w	d2,d1		; set up size word
	lsl.w	#5,d1		; shift five left
	add.w	#$42,d1		; and add 1 to height, 2 to width
	btst	#DMAB_BLTDONE-8,dmaconr(a1)	; safety check
waitblit:
	btst	#DMAB_BLTDONE-8,dmaconr(a1)	; wait for blitter
	bne	waitmore
donewait:
	move.w	d3,bltbmod(a1)	; B mod = 4 * Y
	sub.w	d2,d3
	ext.l	d3
	move.l	d3,bltapt(a1)	; A ptr = 4 * Y - 2 * X
	bpl	lineover	; if negative,
	or.w	#SIGNFLAG,d5	; set sign bit in con1
lineover:
	move.w	d0,bltcon0(a1)	; write control registers
	move.w	d5,bltcon1(a1)
	move.w	d4,bltcmod(a1)	; C mod = bitplane width
	move.w	d4,bltdmod(a1)	; D mod = bitplane width
	sub.w	d2,d3
	move.w	d3,bltamod(a1)	; A mod = 4 * Y - 4 * X
	move.w	#$8000,bltadat(a1)	; A data = 0x8000
	moveq.l	#-1,d5		; Set masks to all ones
	move.l	d5,bltafwm(a1)	; we can hit both masks at once
	move.l	a0,bltcpt(a1)	; Pointer to first pixel to set
	move.l	a0,bltdpt(a1)
	move.w	d1,bltsize(a1)	; Start blit
	rts			; and return, blit still in progress.
#endasm
/*
 *   Now our linkage to the spline routine.  Parameters are in 8(a5)...
 */
int drawspline(x1, y1, x2, y2, x3, y3, x4, y4)
long x1, y1, x2, y2, x3, y3, x4, y4 ;
{
#asm
	movem.l	saver,-(sp)
	move.l	8(a5),a0
	move.l	12(a5),a1
	move.l	16(a5),a2
	move.l	20(a5),a3
	move.l	28(a5),a6
	move.l	32(a5),d6
	move.l	36(a5),d7
	move.l	24(a5),a5
	jsr	rspline
	movem.l	(sp)+,saver
saver	reg	d0-d7/a0-a6
#endasm
}
int closed ;
char *nextlegal[] = { "01458", "236", "01458", "236", "01458", "23", "01458",
                      "", "0145" } ;
int advval[] = { 3, 2, 3, 2, 1, 0, 1, 0, 1 } ;
unsigned char realfunc[14] ;
char namefunc[20] ;
makefunc() {
   register int i ;
   register int goallen ;
   register int sofar = 0 ;
   register unsigned char *p ;
   register char *nextpossib ;

   closed = randm(3) ;
   switch(closed) {
case 2:
      goallen = 2 + randm(3) ;
      break ;
case 1:
      goallen = 3 + randm(6) ;
      break ;
case 0:
      goallen = 1 + randm(7) ;
      break ;
   }
   while (1) {
      if (closed == 0)
         nextpossib = "0145" ;
      else
         nextpossib = "0123456" ;
      sofar = 0 ;
      p = realfunc ;
      while (sofar < goallen) {
         i = nextpossib[randm(strlen(nextpossib))] - '0' ;
         *p++ = i ;
         nextpossib = nextlegal[i] ;
         sofar += advval[i] ;
      }
      if (sofar == goallen) {
         if (closed == 0) {
            if (nextpossib[0] == '0')
               break ;
         } else {
            if (*nextpossib == '0' || realfunc[0] < 4 || *(p-1) < 4) {
               if ((*nextpossib == '0') ?
                      ((realfunc[0] & 2) != 0) : ((realfunc[0] & 2) == 0)) {
                  if (realfunc[0] != 5) {
                     realfunc[0] ^= 2 ;
                     break ;
                  }
               } else {
                  break ;
               }
            }
         }
      }
   }
   *p = 100 ;
   maxpoints = goallen ;
   switch (closed) {
case 2:
      for (i=0; i<p-realfunc; i++)
         p[i] = realfunc[i] ;
      p[p-realfunc] = 100 ;
      break ;
case 1:
      break ;
case 0:
      maxpoints++ ;
      break ;
   }
   for (i=0, p=realfunc; *p < 100; p++, i++)
      namefunc[i] = *p + '0' ;
   namefunc[i++] = ' ' ;
   namefunc[i++] = '0' + closed ;
   namefunc[i++] = ' ' ;
   namefunc[i++] = '0' + maxpoints ;
   namefunc[i] = 0 ;
}
#define HALF(a) ((*(a)+(a)[1])>>1)
draw_s_f(xptr, yptr)
register short *xptr, *yptr ;
{
   oldx = HALF(xptr) ;
   oldy = HALF(yptr) ;
   OwnBlitter() ;
   drawspline(FIX(oldx), FIX(oldy), FIX(xptr[1]), FIX(yptr[1]),
              FIX(xptr[2]), FIX(yptr[2]), FIXH(xptr+2), FIXH(yptr+2)) ;
   DisownBlitter() ;
}
draw_sf(xptr, yptr)
register short *xptr, *yptr ;
{
   oldx = HALF(xptr) ;
   oldy = HALF(yptr) ;
   OwnBlitter() ;
   drawspline(FIX(oldx), FIX(oldy), FIX(xptr[1]), FIX(yptr[1]),
              FIX(xptr[2]), FIX(yptr[2]), FIX(xptr[3]), FIX(yptr[3])) ;
   DisownBlitter() ;
}
draws_f(xptr, yptr)
register short *xptr, *yptr ;
{
   oldx = *xptr ;
   oldy = *yptr ;
   OwnBlitter() ;
   drawspline(FIX(*xptr), FIX(*yptr), FIX(xptr[1]), FIX(yptr[1]),
              FIX(xptr[2]), FIX(yptr[2]), FIXH(xptr+2), FIXH(yptr+2)) ;
   DisownBlitter() ;
}
drawsf(xptr, yptr)
register short *xptr, *yptr ;
{
   oldx = *xptr ;
   oldy = *yptr ;
   OwnBlitter() ;
   drawspline(FIX(*xptr), FIX(*yptr), FIX(xptr[1]), FIX(yptr[1]),
              FIX(xptr[2]), FIX(yptr[2]), FIX(xptr[3]), FIX(yptr[3])) ;
   DisownBlitter() ;
}
draw_lf(xptr, yptr)
register short *xptr, *yptr ;
{
   Move(rastport, (long)HALF(xptr), (long)HALF(yptr)) ;
   xptr++ ;
   yptr++ ;
   Draw(rastport, (long)*xptr, (long)*yptr) ;
}
drawl_f(xptr, yptr)
register short *xptr, *yptr ;
{
   Move(rastport, (long)*xptr, (long)*yptr) ;
   Draw(rastport, (long)HALF(xptr), (long)HALF(yptr)) ;
}
drawlf(xptr, yptr)
register short *xptr, *yptr ;
{
   Move(rastport, (long)*xptr, (long)*yptr) ;
   xptr++ ;
   yptr++ ;
   Draw(rastport, (long)*xptr, (long)*yptr) ;
}
drawnlf() {}
int (*funcs[])() = { &drawsf, &draws_f, &draw_sf, &draw_s_f,
                     &drawlf, &drawl_f, &draw_lf, NULL, &drawnlf } ;
drawfunc(bptr)
register struct box *bptr ;
{
   register long i ;
   register short *x, *y ;
   register unsigned char *p ;

   oldwidth = rastport->BitMap->BytesPerRow ;
   oldptr = (long)rastport->BitMap->Planes[0] ;
   switch(closed) {
case 2:
      for (i=0, x=&(bptr->x[0]), y=&(bptr->y[0]); i<maxpoints; i++, x++, y++) {
         x[maxpoints] = screenwidth - 1 - *x ;
         y[maxpoints] = screenheight - 1 - *y ;
      }
setup:
      x[maxpoints] = bptr->x[0] ;
      y[maxpoints] = bptr->y[0] ;
      x++, y++ ;
      x[maxpoints] = bptr->x[1] ;
      y[maxpoints] = bptr->y[1] ;
      break ;
case 1:
      x = &(bptr->x[0]) ;
      y = &(bptr->y[0]) ;
      goto setup ;
   }
   p = realfunc ;
   x = &(bptr->x[0]) ;
   y = &(bptr->y[0]) ;
   while (*p < 20) {
      (funcs[*p])(x, y) ;
      i = advval[*p] ;
      x += i ;
      y += i ;
      p++ ;
   }
}
/*
 *   Initialize things for the first lines.
 */
startlines() {
   register int i ;

   ptr = store ;
   eptr = store ;
   numlines = 0 ;
   if (dx[0] == 0) {
      for (i=0; i<MAXPOINTS; i++) {
         ox[i] = randm(screenwidth) ;
	 oy[i] = randm(screenheight) ;
	 dx[i] = 2 + randm(3) ;
	 dy[i] = 2 + randm(3) ;
      }
   }
   nr = 53 ;
   ng = 33 ;
   nb = 35 ;
   dr = -3 ;
   dg = 5 ;
   db = 7 ;
   SetRGB4(vp, 0L, 0L, 0L, 0L) ;
   SetRGB4(vp, 1L, (long)(nr >> 3), (long)(ng >> 3), (long)(nb >> 3)) ;
   myscreen->RastPort.Mask = 1 ;
   for (i=0; i<maxlines; i++) {
      advancelines() ;
      drawnew() ;
   }
}
/*
 *   Advance the number by the delta, and check the boundaries.
 */
adv(o, d, n, w)
register short *o, *d, *n ;
short w ;
{
   *n = *o + *d ;
   if (*n < 0) {
      *n = 0 ;
      *d = randm(6) + 1 ;
   } else if (*n >= w) {
      *n = w - 1 ;
      *d = - randm(6) - 1 ;
   }
}
/*
 *   Advance the two points which make up the lines.
 */
advancelines() {
   register int i ;

   for (i=0; i<maxpoints; i++) {
      adv(ox+i, dx+i, nx+i, screenwidth) ;
      adv(oy+i, dy+i, ny+i, screenheight) ;
   }
}
/*
 *   Draw a new set of lines.
 */
drawnew() {
   register int i ;
   register short oldpen ;
   register struct box *bptr ;

   while (numlines >= maxlines) {
      oldpen = rastport->FgPen ;
      oldcol = 0xb0a ;
      SetAPen(rastport, 0L) ;
      bptr = eptr ;
      drawfunc(bptr) ;
      oldcol = 0xbfa ;
      SetAPen(rastport, (long)oldpen) ;
      numlines-- ;
      bptr++ ;
      if (bptr == store + MAXLINES)
         bptr = store ;
      eptr = bptr ;
   }
   bptr = ptr ;
   for (i=0; i<maxpoints; i++) {
      bptr->x[i] = ox[i] = nx[i] ;
      bptr->y[i] = oy[i] = ny[i] ;
   }
   drawfunc(bptr) ;
   numlines++ ;
   bptr++ ;
   if (bptr == store + MAXLINES) {
      bptr = store ;
      if (mdelta == 1) {
         maxlines++ ;
         if (maxlines >= MAXLINES - 1)
            mdelta = -1 ;
      } else {
         maxlines-- ;
         if (maxlines <= 2)
            mdelta = 1 ;
      }
   }
   ptr = bptr ;
}
/*
 *   This routine mucks with the colors.
 */
colors() {
   or = nr ;
   og = ng ;
   ob = nb ;
   adv(&or, &dr, &nr, 128) ;
   adv(&og, &dg, &ng, 128) ;
   adv(&ob, &db, &nb, 128) ;
   SetRGB4(vp, 1L, (long)(nr >> 3), (long)(ng >> 3), (long)(nb >> 3)) ;
}
long time = 0 ;
settime() {
   time = 0 ;
}
long gettime() {
   return time ;
}
/*
 *   Our actual task, in an infinite loop.
 */
void taskrout() {
   register struct Task *mtask ;

   geta4() ;
   settime() ;
   makefunc() ;
   mtask = FindTask(0L) ;
   startlines() ;
   colors() ;
   while (SetSignal(0L, 0L)==0) {
      advancelines() ;
      drawnew() ;
      advancelines() ;
      drawnew() ;
      advancelines() ;
      drawnew() ;
      advancelines() ;
      drawnew() ;
      advancelines() ;
      drawnew() ;
      advancelines() ;
      drawnew() ;
      colors() ;
      if (gettime() >= 1000) {
         settime() ;
         makefunc() ;
         SetRast(rastport, 0L) ;
         startlines() ;
      }
   }
done:
   Signal(task, 1L << globalreplysignum) ;
   Wait(0L) ;
}
/*
 *   Our `delay' call that checks for an abort.
 */
MDelay(n)
register long n ;
{
   while (n > 0) {
      checktof() ;
      n-- ;
   }
}
extern int Enable_Abort ;
#define STACKSIZE 1000
long stackmem[STACKSIZE] ;
int (*nextf)(), nextcolor() ;
long flags ;
#define checkflag(c) (flags&(1L<<((c)-'a')))
int onsprites = 32767 ;
main(argc, argv)
int argc ;
char *argv[] ;
{
   register char *p ;

   Enable_Abort = 0 ;
   while (argc > 1) {
      argc-- ;
      argv++ ;
      for (p=argv[0]; *p; p++)
         if ('a' <= *p && *p <= 'z')
            flags |= (1L << (*p - 'a')) ;
         else if ('A' <= *p && *p <= 'Z')
            flags |= (1L << (*p - 'A')) ;
   }
   if (checkflag('s'))
      flags |= (1L << ('g'-'a')) | (1L << ('m'-'a')) ;
   blankscreen() ;
   nilh = (long)Open("nil:", MODE_NEWFILE) ;
   loadfont() ;
   readiff("cracked.iff", 0) ;
   task = FindTask(0L) ;
   origpri = task->tc_Node.ln_Pri ;
   if (!checkflag('g'))
      loadsound() ;
   else
      flashscreen() ;
   if (!checkflag('m'))
      Execute("Play * P=Script <nil: >nil:", nilh, nilh) ;
   SetTaskPri(task, (long)(task->tc_Node.ln_Pri+6)) ;
   if (!checkflag('c'))
      rollcredits() ;
   SetTaskPri(task, (long)(task->tc_Node.ln_Pri-5)) ;
   if (!checkflag('b'))
      upsprites() ;
   SetRast(rastport, 0L) ;
   readiff("radboogie.iff", 1) ; /* this needs to go faster */
   onsprites = 420 ;
   rtask = (struct Task *)AllocMem((long)sizeof(struct Task),
             MEMF_CLEAR | MEMF_PUBLIC) ;
   if (rtask != NULL) {
      rtask->tc_Node.ln_Pri = task->tc_Node.ln_Pri - 2 ;
      rtask->tc_Node.ln_Type = NT_TASK ;
      rtask->tc_Node.ln_Name = "ri.Lines" ;
      rtask->tc_SPLower = (APTR)stackmem ;
      rtask->tc_SPUpper = rtask->tc_SPReg =
                   (APTR)(stackmem + STACKSIZE/4 - 8) ;
      AddTask(rtask, taskrout, 0L) ;
   }
   nextf = &nextcolor ;
   MDelay(250L) ;
   while (1) {
      checktof() ;
      nextf() ;
      time++ ;
   }
done: ;
   cleanup() ;
}

short *copinst[700] ;
/*
 *   Copper manipulations.
 */
grabcopper(cpr, ptr)
register struct cprlist *cpr ;
int ptr ;
{
   register short *p ;
   register int i ;

   for (i=cpr->MaxCount, p=(short *)cpr->start; i; i--, p += 2)
      if (*p == 0xe6)
         break ;
   if (i <= 0)
      error("! couldn't get copper") ;
   i-- ;
   p += 2 ;
   while (ptr < screenheight) {
      for (; i > 0; i--, p += 2)
         if (*p == 0x182)
            break ;
      if (i <= 0) {
         printf("Failed for pointer %d\n", ptr) ;
         error("! couldn't get copper 2") ;
      }
      *p = 0xe6 ;
      copinst[ptr] = p + 1 ;
      i -= 2 ;
      p += 4 ;
      ptr += 2 ;
   }
}
struct View *safeview ;
InstallList() {
   int i ;

   ucop=AllocMem((long)sizeof(struct UCopList), MEMF_CHIP|MEMF_CLEAR);
   if (ucop == NULL)
      error("! no copper") ;
   for (i=0; i<screenheight; i += 2) {
      CWAIT(ucop, (long)i, 0L) ;
      CMOVE(ucop, custom.color[1], 0L) ;
      CMOVE(ucop, custom.color[2], 0L) ;
   }
   CEND(ucop);
   Forbid();
   vp->UCopIns=ucop;
   Permit();
   RethinkDisplay();
/*
 *   Now we grab pointers to the instructions for each row.
 *   Note that this is guaranteed to break.  Don't fuck with the
 *   screens while we are grabbing all of these pointers.
 */
   safeview = GfxBase->ActiView ;
   grabcopper(GfxBase->ActiView->LOFCprList, 0) ;
   grabcopper(GfxBase->ActiView->SHFCprList, 1) ;
}

/*
 *   This routine checks to make sure we are still the displayed view.  If
 *   we aren't, we bail out before munging a copper list we may not be
 *   allowed to touch.  Note that this is *not* how to exit this demo.
 *   Note also that we should Forbid()/Permit() around our copper
 *   munging code, but we take our chances instead.
 */

checktof() {
   struct IntuiMessage *msg ;
   static int msgctr = 4 ;

   msgctr-- ;
   if (msgctr <= 0) {
      if (msg=(struct IntuiMessage *)GetMsg(mywindow->UserPort)) {
         if (msg->Class == CLOSEWINDOW ||
             (msg->Class == VANILLAKEY && (msg->Code == 3 ||
                                           msg->Code == 27)))
            cleanup() ;
         ReplyMsg(msg) ;
      }
      msgctr = 4 ;
   }
   nextsprite() ;
   WaitTOF() ;
   if (GfxBase->ActiView != safeview ||
       IntuitionBase->FirstScreen != myscreen ||
       IntuitionBase->ActiveWindow != mywindow)
      error("!*hey*!  Don't do that!  (I'm no longer in front.)") ;
}

long sine = 65536 ;
long cosine = 0 ;
long ocos = -10 ;
static long curcstart = 0 ;
static int ncrev = 5 ;

onextcolor()
{
   register long acc, delta ;
   register short **p ;
   register short *q ;
   register long t ;
   register long i ;

   t = sine + (cosine >> 5) ;
   ocos = cosine ;
   cosine -= (sine >> 5)  ;
   if ((t <= 0 && sine >= 0) ||
       (t >= 0 && sine <= 0))
      newcolor(&color1) ;
   if (cosine <= 0 && ocos >= 0) {
      ncrev-- ;
      if (ncrev == 0) {
         nextf = &nextcolor ;
         ncrev = 7 ;
         nextcolor() ;
      }
   }
   sine = t ;
/*
 *   Every time around, correct it back into a circle.
 */
   if (-500 < sine && sine < 500) {
      if (cosine < 0)
          cosine = -65536 ;
      else
          cosine = 65536 ;
      sine = 0 ;
      delta = 0 ;
      acc = -203034 ;
   } else {
      acc = ((((-(long)screenheight)<<21) + (sine << 4)) / sine
                                 + (screenheight << 5)) << 5 ;
      delta = (268435456L + sine) / (sine << 1) ;
   }
   pokefast(copinst, screenheight, acc, delta, bytewidthm, color1) ;
/* for (i=screenheight, p=copinst; i; i--, p++) {
      q = *p ;
      acc += delta ;
      t = (acc >> 11) ;
      if (((unsigned long)t) >= screenheight) {
         *q = ((long)emptyrow) ;
         q[2] = 0 ;
      } else {
         *q = bytewidthm[t] ;
         q[2] = color1[t] ;
      }
   } */
}
pokefast(cp, sh, acc, del, bw, c1)
short **cp ;      /* 8 */ /* a3 */
short sh ;        /* 12 */ /* d0 */ /* d4 */
long acc, del ;   /* 14, 18 */ /* d1, d2 */
short *bw, *c1 ;  /* 22, 26 */ /* a0, a5 */
{
#asm
	movem.l	sav37,-(sp)
	move.l	8(a5),a3
	move.w	12(a5),d0
	move.w	d0,d4
	ext.l	d4
	lsr.w	#1,d0
	move.l	14(a5),d1
	move.l	18(a5),d2
	move.l	22(a5),a0
	move.l	26(a5),a5
	move.l	#11,d5
	move.w	_emptyrow+2,d6
	bra	bot37
top37:
	move.l	(a3)+,a2
	add.l	d2,d1
	move.l	d1,d3
	lsr.l	d5,d3
	cmp.l	d3,d4
	bcc.s	ovr37
	move.w	d6,(a2)
;	clr.w	4(a2)
	move.l	(a3)+,a2
	add.l	d2,d1
	move.l	d1,d3
	lsr.l	d5,d3
	cmp.l	d3,d4
	bcs.s	ovr37c
	add.w	d3,d3
	move.w	(a0,d3.w),(a2)
	move.w	(a5,d3.w),4(a2)
	dbra	d0,top37
	bra	don37
ovr37:
	add.w	d3,d3
	move.w	(a0,d3.w),(a2)
	move.w	(a5,d3.w),4(a2)
ovr37b:
	move.l	(a3)+,a2
	add.l	d2,d1
	move.l	d1,d3
	lsr.l	d5,d3
	cmp.l	d3,d4
	bcs.s	ovr37c
	add.w	d3,d3
	move.w	(a0,d3.w),(a2)
	move.w	(a5,d3.w),4(a2)
	dbra	d0,top37
	bra	don37
ovr37c:
	move.w	d6,(a2)
;	clr.w	4(a2)
bot37:
	dbra	d0,top37
don37:
	movem.l	(sp)+,sav37
sav37	reg	d0-d6/a0-a3/a5
#endasm
}

static int curhpos = -30 ;

nextcolor()
{
   register short **p ;
   register short *q ;
   register long acc, div ;
   register long i, reali ;

   curhpos -= 4 ;
   if (curhpos <= 0) {
      if (curhpos < -10)
          newcolor(&color2) ;
      for (i=0; i<screenheight; i++)
         color1[i] = color2[i] ;
      newcolor(&color2) ;
      curhpos = screenheight - 2 ;
      ncrev-- ;
      if (ncrev == 0) {
         nextf = &onextcolor ;
         ncrev = 5 ;
         onextcolor() ;
      }
   }
   acc = 0 ;
   div = screenheight * 65536L / curhpos ;
   poke2fast(copinst, curhpos, acc, div, bytewidthm, color1) ;
/* for (i=curhpos, p=copinst; i; i--, p++) {
      q = *p ;
      reali = acc >> 16 ;
      *q = bytewidthm[reali] ;
      q[2] = color1[reali] ;
      acc += div ;
   } */
   div = screenheight * 65536L / (screenheight - curhpos) ;
   acc = 0 ;
   poke2fast(copinst + curhpos, screenheight - curhpos,
                                         acc, div, bytewidthm, color2) ;
/* for (i=screenheight-curhpos; i; i--, p++) {
      q = *p ;
      reali = acc >> 16 ;
      *q = bytewidthm[reali] ;
      q[2] = color2[reali] ;
      acc += div ;
   } */
}
poke2fast(cp, sh, acc, del, bw, c1)
short **cp ;      /* 8 */ /* a3 */
short sh ;        /* 12 */ /* d0 */ /* d4 */
long acc, del ;   /* 14, 18 */ /* d1, d2 */
short *bw, *c1 ;  /* 22, 26 */ /* a0, a5 */
{
#asm
	movem.l	sav38,-(sp)
	move.l	8(a5),a3
	move.w	12(a5),d0
	move.w	d0,d4
	lsr.w	#1,d0
	ext.l	d4
	move.l	14(a5),d1
	move.l	18(a5),d2
	move.l	22(a5),a0
	move.l	26(a5),a5
	bra	bot38
top38:
	move.l	(a3)+,a2
	add.l	d2,d1
	move.l	d1,d3
	swap	d3
	add.w	d3,d3
	move.w	(a0,d3.w),(a2)
	move.w	(a5,d3.w),4(a2)
	move.l	(a3)+,a2
	add.l	d2,d1
	move.l	d1,d3
	swap	d3
	add.w	d3,d3
	move.w	(a0,d3.w),(a2)
	move.w	(a5,d3.w),4(a2)
bot38:
	dbra	d0,top38
	movem.l	(sp)+,sav38
sav38	reg	d0-d4/a0-a3/a5
#endasm
}

/*
 *   This routine has to generate a new color.  We grab two random vectors
 *   in color space and concatenate them and distribute them over the array.
 *
 *   This code should be moderately fast; we assume that the random number
 *   generator generates fairly good numbers.
 */
int raininc[6] = { 1, -16, 256, -1, 16, -256 } ;
newcolor(p)
short *p ;
{
   register int a ;
   register int i ;
   int inc ;
   int s1, s2, s3 ;

   switch(randm(8)) {
case 0: /* shaded stripes */
case 5:
case 6:
      inc = randm(2) * 2 - 1 ;
      a = randm(8192) ;
      s1 = randm(3) * 4 + 1 ;
      s2 = randm(2) * 4 + 1 ;
      if (s1 == s2)
         s2 = 9 ;
      s3 = 15 - s1 - s2 ;
      for (i=screenheight; i; i--, a += inc) {
         *p++ = ((a >> s1) & 15) +
               (((a >> s2) & 15) << 4) +
               (((a >> s3) & 15) << 8) ;
      }
      break ;
case 1: /* solid color */
case 4:
      while (1) {
         a = randm(4096) ;
         s1 = (a & 15) + ((a >> 4) & 15) + ((a >> 8) & 15) ;
         if (a > 12)
            break ;
      }
      for (i=screenheight; i; i--)
         *p++ = a ;
      break ;
case 2: /* banded with two colors */
case 3:
      while (1) {
         a = randm(4096) ;
         s1 = (a & 15) + ((a >> 4) & 15) + ((a >> 8) & 15) ;
         if (a > 12)
            break ;
      }
      s2 = a ;
      while (1) {
         a = randm(4096) ;
         s1 = (a & 15) + ((a >> 4) & 15) + ((a >> 8) & 15) ;
         if (a > 12)
            break ;
      }
      s3 = a ;
      s1 = randm(40) + 10 ;
      a = s1 ;
      for (i=screenheight; i; i--) {
         *p++ = s2 ;
         a-- ;
         if (a==0) {
            a = s2 ;
            s2 = s3 ;
            s3 = a ;
            a = s1 ;
         }
      }
      break ;
case 7: /* rainbow */
      a = 240 ;
      s1 = 0 ;
      s3 = 1 ;
      s2 = 15 ;
      for (i=screenheight; i; i--) {
         *p++ = a ;
         a += s3 ;
         s2-- ;
         if (s2 == 0) {
            s2 = 15 ;
            s1++ ;
            if (s1 == 6)
               s1 = 0 ;
            s3 = raininc[s1] ;
         }
      }
      break ;
   }
}
/*
 *   Now the code that reads in an IFF file.  We center the file on the
 *   screen.
 */

int compression ;
long amigaflags ;
char colormap[2048] ;
int numcolors, numplanes ;
int width, height, rowwidth ;
int masking ;
FILE *ifffile ;
struct bmhddata {
   short w, h, x, y ;
   unsigned char numplanes, masking, compression, dmy1 ;
   short transcolor ;
   unsigned char xas, yas ;
   short ow, oh ;
} bmhd ;

static long getfour()
{
   long t ;

   *((char *)(&t)) = getc(ifffile) ;
   *((char *)(&t)+1) = getc(ifffile) ;
   *((char *)(&t)+2) = getc(ifffile) ;
   *((char *)(&t)+3) = getc(ifffile) ;
   return(t) ;
}

static long getfourup()
{
   return((getfour() + 1) & ~1L) ;
}
/*
 *   Starts up an iff file.  Returns `1' on success; 0 on error.
 */
static short startiff()
{
   register unsigned char *d ;
   register short i ;
   register long thislen, ifflen, curform ;
   short min, max ;

   if (getfour() != 'FORM')
      return(-1) ;
   ifflen = getfour() ;
   if (getfour() != 'ILBM')
      return(-1) ;
   while (1) {
      curform = getfour() ;
      switch(curform) {
case 'CMAP' :
         thislen = getfourup() ;
         if (thislen > 2048)
            return(-1) ;
         numcolors = thislen / 3 ;
         d = (unsigned char *)colormap ;
         while (thislen > 0) {
            *d++ = getc(ifffile) ;
            thislen-- ;
         }
         break ;
case 'CAMG' :
         if (getfour() != 4)
            return(-1) ;
         amigaflags = getfour() & (HAM | EXTRA_HALFBRITE) ;
         break ;
case 'BMHD' :
         thislen = getfourup() ;
         d = (unsigned char *)&bmhd ;
         i = sizeof(struct bmhddata) ;
         thislen -= i ;
         while (i > 0) {
            *d++ = getc(ifffile) ;
            i-- ;
         }
         while (thislen > 0) {
            getc(ifffile) ;
            thislen-- ;
         }
         width = bmhd.w ;
         rowwidth = (bmhd.w + 15) / 16 ;
         height = bmhd.h ;
         compression = bmhd.compression ;
         numplanes = bmhd.numplanes ;
         if (numplanes < 1 || numplanes > 1)
            error("! can only handle one-bitplane images") ;
         masking = bmhd.masking ;
         break ;
case 'BODY' :
         getfour() ;
         return(0) ;
case -1:
         return(-1) ;
default:
         thislen = getfourup() ;
         thislen = (thislen + 1) & ~1 ;
         while (thislen--)
            getc(ifffile) ;
         break ;
      }
   }
   return(-1) ;
}
/*
 *   Here we get one row from the IFF file.
 */
static getrow(put)
register short *put ;
{
   register char *rr ;
   register short lackbits ;
   register short i ;
   register short data ;

   if (compression)
      lackbits = 0 ;
   else
      lackbits = 32767 ;
   for (i = 2*rowwidth, rr = (char *)put; i; i--) {
      while (lackbits == 0) {
         lackbits = (getc(ifffile) & 255) ;
         if (lackbits > 128) {
            lackbits = lackbits - 257 ;
            data = getc(ifffile) ;
         } else if (lackbits == 128) {
            lackbits = 0 ;
         } else
            lackbits++ ;
      }
      if (lackbits > 0) {
         data = getc(ifffile) ;
         lackbits-- ;
      } else {
         lackbits++ ;
      }
      *rr++ = data ;
   }
}
short *bodys[6] ;

readbody(p)
register short *p ;
{
   register int i, j ;
   int delta = bytewidth >> 1 ;

   if (width > screenwidth || height > screenheight) {
      printf("%d %d %d %d\n", width, screenwidth, height, screenheight) ;
      error("! IFF file too large for screensize") ;
   }
   p += ((screenheight - height) >> 1) * delta + 
                                ((screenwidth - width + 16) >> 5) ;
   for (j=0; j<height; j++) {
      getrow(p) ;
      p += delta ;
   }
}

readiff(s, n)
char *s ;
int n ;
{
   int i ;

   ifffile = fopen(s, "r") ;
   if (ifffile==NULL)
      error("! couldn't open IFF file\n") ;
   if (startiff())
      error("! Couldn't start up IFF file\n") ;
   i = rastport->Mask ;
   rastport->Mask = 1 << n ;
   SetRast(rastport, 0L) ;
   rastport->Mask = i ;
   readbody(rastport->BitMap->Planes[n]) ;
   fclose(ifffile) ;
   ifffile = NULL ;
}

/*
 *   This code loads our font.
 */
#define HEIGHT (11)
long expand[256] ;
long *final ;
long fontdat[96*HEIGHT*3] ;
saytext() {
   PrintIText(&(myscreen->RastPort), &itext, 0L, 0L) ;
   WaitBlit() ;
}
loadfont() {
   register int i, b ;
   register long e ;
   register unsigned char *p ;
   int base ;
   int c ;

   for (i=0; i<256; i++)
      for (b=1, e=7; b<256; b <<= 1, e <<= 3)
         if (i & b)
            expand[i] |= e ;
   final = fontdat ;
   for (base=32; base < 128; base += 16) {
      p = (unsigned char *)tempbuf ;
      for (c=base; c < base+16; c++, p++)
         *p = c ;
      *p = 0 ;
      saytext() ;
      p = (unsigned char *)(rastport->BitMap->Planes[0]) ;
      for (c=base; c < base+16; c++, p++) {
         for (i=0; i<HEIGHT; i++) {
            e = expand[p[i*bytewidth]] ;
            final[3*i+1] = e ;
            final[3*i+2] = ~((e ^ (e >> 1)) | (e ^ (e << 1))) ;
         }
         final[0] = final[1] ^ (final[1] & ~final[2]) ;
         for (i=1; i<(HEIGHT-1)*3; i += 3) {
            e = final[i] ^ final[i+3] ;
            final[i+2] = final[i+3] ^ (e & final[i+1] & ~final[i+4]) ;
            final[i+1] = final[i] ^ (e & final[i+4] & ~final[i+1]) ;
         }
         final[HEIGHT*3-1] = final[HEIGHT*3-2] ^
                                  (final[HEIGHT*3-2] & ~final[HEIGHT*3-1]) ;
         for (i=0; i<HEIGHT*3; i++)
            final[i] <<= 4 ;
         final += HEIGHT * 3 ;
      }
   }
   SetAPen(rastport, 0L) ;
   RectFill(rastport, 0L, 0L, (long)screenwidth-1, 30L) ;
   SetAPen(rastport, 1L) ;
}

/*
 *   Kludged code to load a sound.
 */

short *sounddata ;
long samrate, len ;

loadsound() {
   register long h, *p ;

   allocchannels() ;
   ifffile = fopen("crash.snd", "r") ;
   if (ifffile==0)
      error("! don't separate me from my sounds (crash.snd).") ;
   while (1) {
      h = getfour() ;
      switch (h) {
case 'BODY' :
         getfour() ;
         break ;
case 'VHDR' :
         getfour() ;
         len = getfour() ;
         getfour() ;
         getfour() ;
         samrate = getfour() >> 16 ;
         break ;
case -1:
         error("! ran off end of sound file before body") ;
default:
         break ;
      }
      if (h=='BODY')
         break ;
   }
   if (len == 0)
      error("! no length in sound file") ;
   sounddata = AllocMem(len, MEMF_CHIP | MEMF_CLEAR) ;
   if (sounddata == NULL)
      error("! no memory in sound") ;
   h = len ;
   p = (long *)sounddata ;
   while (h >= 0) {
      *p++ = getfour() ;
      h -= 4 ;
   }
   fclose(ifffile) ;
   ifffile = NULL ;
   playsound() ;
   freechannels() ;
}

UBYTE whichannel[] = { 3, 5, 10, 12 } ;
struct IOAudio *AudioIOAptr, *AudioIOBptr ;
struct MsgPort *port ;
ULONG device = -1 ;
long clock ;

allocchannels() {
   SetFilter() ;
   clock = ((GfxBase->DisplayFlags & PAL) ? 3546895 : 3579545) ;
   AudioIOBptr = (struct IOAudio *)AllocMem((long)sizeof(struct IOAudio),
             MEMF_PUBLIC | MEMF_CLEAR) ;
   AudioIOAptr = (struct IOAudio *)AllocMem((long)sizeof(struct IOAudio),
             MEMF_PUBLIC | MEMF_CLEAR) ;
   port = CreatePort(0L, 0L) ;
   if (AudioIOBptr == NULL || AudioIOBptr == NULL || port == NULL)
      error("! no memory") ;
   AudioIOBptr->ioa_Request.io_Message.mn_ReplyPort = port ;
   AudioIOBptr->ioa_Request.io_Message.mn_Node.ln_Pri = 0 ;
   AudioIOBptr->ioa_Request.io_Command = ADCMD_ALLOCATE ;
   AudioIOBptr->ioa_Request.io_Flags = ADIOF_NOWAIT ;
   AudioIOBptr->ioa_AllocKey = 0 ;
   AudioIOBptr->ioa_Data = whichannel ;
   AudioIOBptr->ioa_Length = 4 ;
   device = (ULONG)OpenDevice("audio.device", 0L, AudioIOBptr, 0L) ;
   if (device != 0) {
      printf("Error returned is %ld\n", device) ;
      error("! no audio device") ;
   }
}

/*
 *   We kill the filter; we *need* the high frequencies.
 */
freechannels() {
   if (port) {
      DeletePort(port) ;
      port = 0 ;
   }
   if (device == 0) {
      CloseDevice(AudioIOBptr) ;
      device = -1 ;
   }
   if (AudioIOBptr) {
      FreeMem(AudioIOBptr, (long)sizeof(struct IOAudio)) ;
      AudioIOBptr = 0 ;
   }
   if (AudioIOAptr) {
      FreeMem(AudioIOAptr, (long)sizeof(struct IOAudio)) ;
      AudioIOAptr = 0 ;
   }
   if (sounddata) {
      FreeMem(sounddata, len) ;
      sounddata = 0 ;
   }
   RestoreFilter() ;
}

int oldbits = -1 ;

SetFilter() {
   Disable() ;
   oldbits = ciaa.ciapra ;
   ciaa.ciapra |= CIAF_LED ;
   Enable() ;
}

RestoreFilter() {
   Disable() ;
   if (!(oldbits & CIAF_LED)) {
      ciaa.ciapra &= ~CIAF_LED ;
      oldbits = -1 ;
   }
   Enable() ;
}

playsound() {
   AudioIOBptr->ioa_Request.io_Message.mn_ReplyPort = port ;
   AudioIOBptr->ioa_Request.io_Command = CMD_WRITE ;
   AudioIOBptr->ioa_Request.io_Flags = ADIOF_PERVOL ;
   AudioIOBptr->ioa_Data = (UBYTE *)sounddata ;
   AudioIOBptr->ioa_Length = len ;
   AudioIOBptr->ioa_Period = clock / samrate ;
   AudioIOBptr->ioa_Volume = 64 ;
   AudioIOBptr->ioa_Cycles = 1 ;
   *AudioIOAptr = *AudioIOBptr ;
   AudioIOBptr->ioa_Period += 5 ; /* make a slight modification for kicks */
   AudioIOAptr->ioa_Request.io_Unit = (struct Unit *)
                         (((long)AudioIOBptr->ioa_Request.io_Unit) & 0x9) ;
   AudioIOBptr->ioa_Request.io_Unit = (struct Unit *)
                         (((long)AudioIOBptr->ioa_Request.io_Unit) & 0x6) ;
   BeginIO(AudioIOBptr) ;
   BeginIO(AudioIOAptr) ;
   flashscreen() ;
   WaitPort(port) ;
   GetMsg(port) ;
   WaitPort(port) ;
   GetMsg(port) ;
   AudioIOBptr->ioa_Request.io_Unit = (struct Unit *)
                         (((long)AudioIOBptr->ioa_Request.io_Unit) |
                          ((long)AudioIOBptr->ioa_Request.io_Unit)) ;
}

/*
 *   This routine flashes our screen bright white for a short time (two
 *   frames) and then turns the colors on right for normal stuff.
 */
flashscreen() {
   long i ;

   WaitTOF() ;
   for (i=0; i<4; i++)
      SetRGB4(vp, i, 15L, 15L, 15L) ;
   WaitTOF() ;
   WaitTOF() ;
   for (i=0; i<4; i++)
      SetRGB4(vp, i, 15L, 15L, 0L) ;
   WaitTOF() ;
   WaitTOF() ;
   for (i=0; i<4; i++)
      SetRGB4(vp, i, 15L, 0L, 0L) ;
   WaitTOF() ;
   WaitTOF() ;
   SetRGB4(vp, 0L, 0L, 0L, 0L) ;
   SetRGB4(vp, 1L, 4L, 4L, 4L) ;
   SetRGB4(vp, 2L, 0L, 0L, 0L) ;
   SetRGB4(vp, 3L, 0L, 0L, 0L) ;
}

/*
 *   Finally we get to use that text.  We roll the credits here, at
 *   the beginning, while everything is loading.  Too bad we can't make
 *   the music stop and wait for us!  We try and make things take long
 *   enough to load all of the music off floppy disks.
 *
 *   Of course, we do all of this with copper tricks.
 *
 *   We do a lot of `bouncing'---assume everything is perfectly elastic
 *   and just let them bounce onto the screen.
 */
struct billiard {
   int pos ;
   int vel ;
   int ptr ;
   int color ;
} balls[12] ;
#define space 36
#define realh 33
#define velocity (randm(3)+2)
int scrolloff = 0 ;
rollcredits() {
   register struct billiard *bbp ;
   register struct billiard *bp = balls ;
   register int i, j ;
   long *p, *q ;
   short **rp, *rq ;
   int curiptr = 0 ;
   FILE *f ;
   char *pp ;
   int alive = 0 ;
   char *done ;
   extern char *fgets() ;

   for (i=0, rp=copinst; i<screenheight; i++, rp++)
     *(*rp) = (long)emptyrow ;
   f = fopen("TextScript", "r") ;
   if (f==NULL)
      error("! don't leave me without a text script (TextScript)") ;
   while (1) {
      done = fgets(tempbuf, 100, f) ;
      if (strlen(tempbuf) > 0)
         tempbuf[strlen(tempbuf)-1] = 0 ;
      if (tempbuf[0]) {
         bp->pos = screenheight ;
         bp->vel = -velocity ;
         bp->ptr = curiptr ;
         bp->color = randm(4096) | 2184 ;
         bp++ ;
         alive = 1 ;
         p = (long *)(((char *)(rastport->BitMap->Planes[1])) +
            bytewidth * curiptr + 
            (((screenwidth - 32 * strlen(tempbuf) + 16) >> 4) & ~1)) ;
         for (pp = tempbuf; *pp; pp++) {
            q = fontdat + (*pp-32)*HEIGHT*3 ;
            for (j=0; j<realh; j++)
               *(long *)(((char *)p) + j * bytewidth) = q[j] ;
            p++ ;
         }
         curiptr += realh ;
         if (curiptr > screenheight)
            error("! too many lines") ;
      } else
         scrolloff = 1 ;
      while (1) {
         for (i=25+randm(25); i; i--) {
            for (bbp=balls; bbp < bp; bbp++)
               bbp->pos += bbp->vel ;
            if (scrolloff < 5 && balls[0].pos < 0) {
               balls[0].pos = 0 ;
               balls[0].vel = - balls[0].vel ;
            }
            for (bbp=balls; bbp+1 < bp; bbp++) {
               if (bbp->pos + space > bbp[1].pos) {
                  j = bbp->vel ;
                  bbp->vel = bbp[1].vel ;
                  bbp[1].vel = j ;
                  bbp[1].pos = bbp->pos + space ;
               }
            }
            if ((bp-1)->pos + space > screenheight && (bp-1)->vel > 0)
               (bp-1)->vel = -(bp-1)->vel ;
            checktof() ;
            alive = fastcop(bp) ;
         }
         if (alive == 0)
            break ;
         if (scrolloff == 0)
            break ;
         scrolloff++ ;
      }
      if (!done)
         break ;
      if (alive == 0) {
         bp = balls ;
         curiptr = 0 ;
         i = rastport->Mask ;
         rastport->Mask = 2 ;
         SetRast(rastport, 0L) ;
         rastport->Mask = i ;
         WaitBlit() ;
      }
      scrolloff = 0 ;
   }
   for (i=0, rp=copinst; i<screenheight; i++, rp++)
     *(*rp) = (long)emptyrow ;
   fclose(f) ;
}

int fastcop(max)
struct billiard *max ;
{
   register struct billiard *bbp ;
   register short *rq ;
   register int j ;
   register short **rp ;
   int ret ;

   bbp = balls ;
   while (1 - bbp->pos >= realh) {
      bbp++ ;
      if (bbp == max)
         bbp = NULL ;
   }
   ret = (bbp != NULL) ;
   for (j=0, rp=copinst; j<screenheight; j++, rp++) {
      rq = *rp ;
      if (bbp == NULL || j < bbp->pos) {
         *rq = (long)emptyrow ;
/*       rq[2] = 0L ; */
      } else {
         if (bbp->vel < 0)
            *rq = bytewidthm[bbp->ptr + (j - bbp->pos)] ;
         else
            *rq = bytewidthm[bbp->ptr + realh - 1 - (j - bbp->pos)] ;
         rq[2] = bbp->color ;
         if (j - bbp->pos >= realh - 1) {
            bbp++ ;
            if (bbp == max)
               bbp = NULL ;
         }
      }
   }
   return(ret) ;
}
/*
 *   Now we can deal with sprite issues.  (This program just keeps getting
 *   longer.)  We grab sprites 1 and 2; no real need to grab a matched pair,
 *   and this (slightly) increases the chances that things will work when
 *   the pointer is moved all the way to the left.
 */
#define NUMPHASES (8)
#define PI (3.14159265358)
short *sprts[NUMPHASES][2] ;
struct SimpleSprite hsprites[2] ;
int havesprite[2] ;
double asin(), sqrt() ;
unsigned char ftab[32][32] ;
unsigned char fitab[32][32] ;
initcolor() {
   int x, y ;
   float fx, fy ;
   float rx, ry ;
   float lat ;

   for (x=0; x<32; x++) {
      for (y=0; y<32; y++) {
         fx = (x - 15.5) / 16.0 ;
         fy = (y - 15.5) / 16.0 ;
         rx = 0.9798 * fx + 0.2 * fy ;
         ry = 0.9798 * fy - 0.2 * fx ;
         if (rx * rx + ry * ry >= 0.999)
            ftab[x][y] = 255 ;
         else {
            lat = asin(ry) * 4.0 / PI + 8.0 ;
            fitab[x][y] = ((int)lat) & 1 ;
            lat = asin(rx / sqrt(1.0 - ry * ry)) * 64.0 / PI + 32.5 ;
            ftab[x][y] = ((int)lat) ;
         }
      }
   }
}
int color(x, y, n)
int x, y ;
int n ;
{
   int f ;
   int t ;

   f = ftab[x][y] ;
   if (f == 255)
      return (0) ;
   else
      return ((((fitab[x][y]) ^ ((f + n) >> 4)) & 1) ? 1 : 2) ;
}

upsprites() {
   register int i, j, k, m ;
   register short *p ;
   register int t ;
   int n ;

   initcolor() ;
   for (i=0; i<2; i++) {
      if (GetSprite(&(hsprites[i]), i + 1L) == -1)
         error("! no sprites") ;
      havesprite[i] = 1 ;
      hsprites[i].height = 32 ;
   }
   for (i=0; i<NUMPHASES; i++) {
      n = (i << 4) / NUMPHASES ;
      for (j=0; j<2; j++) {
         p = (short *)AllocMem(136L, MEMF_CHIP | MEMF_CLEAR) ;
         if (p)
            sprts[i][j] = p ;
         else
            error("! no sprite memory") ;
         for (m=0; m<32; m++) {
            for (k=0; k<16; k++) {
               t = color(k + (j << 4), m, n) ;
               if (t)
                  p[1+t+(m << 1)] |= 1 << (15-k) ;
            }
         }
      }
   }
}

downsprites() {
   register int i, j ;
   register short *p ;

   for (i=0; i<2; i++)
      if (havesprite[i]) {
         FreeSprite(i + 1L) ;
         havesprite[i] = 0 ;
      }
   for (i=0; i<NUMPHASES; i++)
      for (j=0; j<2; j++) {
         p = sprts[i][j] ;
         if (p)
            FreeMem(p, 136L) ;
      }
   SetRGB4(vp, 17L, 15L, 0L, 0L) ;
   SetRGB4(vp, 21L, 15L, 0L, 0L) ;
   SetRGB4(vp, 18L, 15L, 15L, 15L) ;
   SetRGB4(vp, 22L, 15L, 15L, 15L) ;
}

int hvel = 4 ;
int xpos = -64 ;
int ypos = 0 ;
int vvel = 0 ;
int vacc = 1 ;
int phase = 1000 ;
long parity = 15 ;
int cc ;
#define TURNCC (3)
nextsprite() {
   register short **ss ;

   if (onsprites > 0) {
      onsprites-- ;
      return ;
   }
   if ((xpos += hvel) > screenwidth - 64) {
      if (onsprites < -200) {
         if (xpos > screenwidth + 6) {
            onsprites = 400 ;
            xpos = -68 ;
         }
      } else {
         xpos = screenwidth - 64 ;
         hvel = - hvel ;
         onsprites -= 700 ;
      }
   } else if (xpos < 0) {
      xpos = 0 ;
      hvel = - hvel ;
   }

   if ((ypos += ((vvel += vacc) >> 3)) > screenheight - 64) {
      ypos = screenheight - 64 ;
      vvel = - vvel ;
   } else if (ypos < 0) {
      ypos = 0 ;
      vvel = 0 ;
   }
   if ((cc--) <= 0) {
      cc = TURNCC ;
      phase++ ;
      if (phase >= NUMPHASES)
         phase = 0 ;
      ss = sprts[phase] ;
      ChangeSprite(vp, &hsprites, ss[0]) ;
      ChangeSprite(vp, &(hsprites[1]), ss[1]) ;
      if (phase == 0) {
         SetRGB4(vp, 17L, 15L, parity, parity) ;
         SetRGB4(vp, 21L, 15L, parity, parity) ;
         parity = 15 - parity ;
         SetRGB4(vp, 18L, 15L, parity, parity) ;
         SetRGB4(vp, 22L, 15L, parity, parity) ;
      }
   }
   if (hvel > 0) {
      MoveSprite(vp, &hsprites, (long)xpos, (long)ypos) ;
      MoveSprite(vp, &(hsprites[1]), 32L + xpos, (long)ypos) ;
   } else {
      MoveSprite(vp, &(hsprites[1]), 32L + xpos, (long)ypos) ;
      MoveSprite(vp, &hsprites, (long)xpos, (long)ypos) ;
   }
}

_wb_parse() {}
