//#define GTCHARS

/*
      We use OpenGL here but only to draw low-level vectors.  All other
    graphics is done by this code.  It should be easily portable to any
    other system, including, hopefully, the Vectrex, and Zonn Moore's Zektor.

    The original version of this code used the internal coord system of
    the TG rom.  This code is now being modified so that it uses a coord
    system with 0,0 at the center of the screen, so that translating
    to device coords is a simple scaling operation plus a translation.
    The only drawback of this is that we need to be careful with signs,
    especially for our fixed point arithetic.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


//----------------------------------------------------------- OPENGL
#ifdef OPENGL

#include <GL/glut.h>

#define COLORS 16
GLubyte colors[COLORS][3] = {
    0,0,0,
    1,1,1,
    3,3,3,
    7,7,7,
    15,15,15,
    31,31,31,
    63,63,63,
    127,127,127,
    0+128,0+128,0+128,
    1+128,1+128,1+128,
    3+128,3+128,3+128,
    7+128,7+128,7+128,
    15+128,15+128,15+128,
    31+128,31+128,31+128,
    63+128,63+128,63+128,
    127+128,127+128,127+128,
};
#endif
//------------------------------------------------------- END OPENGL

#ifndef FALSE


#define FALSE (0!=0)
#define TRUE (0==0)
#endif

#define SCREEN_H 800
#define SCREEN_W 1023

#include "externs.h"
#include "mdep.h"
#include "mdep.c"


static int mouse_x = 0, mouse_y = 0, mouse_b = 0; // have not yet added mouse support!


//-------------------------------------------- FIXED POINT GRAPHICS PACKAGE
// Included verbatim.  Needs to be trimmed down to what is used.  Probably
// not much more than the sin/cos tables.
// (talking of which can't we use the same table just with an offset and
//  mask into the index?)


//
// Written by: Robert C. Pendleton
// 
// Copyright 1993 by Robert C. Pendleton, all right reserved
//
// Non-commercial use by individuals is permitted.
//
//


//#include "fixed.h"
//
// Written by: Robert C. Pendleton
// 
// Copyright 1993 by Robert C. Pendleton, all right reserved
//
// Non-commercial use by individuals is permitted.
//
//

#ifndef _FIXED_H_
#define _FIXED_H_

//----------------------------------------------
//
// handy macros
//

#define max(a,b) (((a) > (b)) ? (a) : (b))
#define min(a,b) (((a) < (b)) ? (a) : (b))
#define abs(a) (((a)<0) ? -(a) : (a))
#define sign(a) (((a)<0) ? -1 : (a)>0 ? 1 : 0)

//----------------------------------------------
//
// Integer math
//

//----------------------------------------------
//
// Multiply to 32 bit numbers giving a 64 bit
// result then divide the 64 bit number by a 32 
// bit value and return the result.
//

unsigned long
mulDiv(unsigned long value, unsigned long mulBy, unsigned long divBy);
//#pragma aux mulDiv = \
//    "imul   ecx" \
//    "idiv   ebx" \
//    parm [eax] [ecx] [ebx]\
//    modify [edx] \
//    value [eax];

extern unsigned long iSqrt(unsigned long value);

//----------------------------------------------
//
// fixed point math
//

typedef unsigned long long fp14;

//----------------------------------------------
//
// Multiply two 18.14 fixed point numbers giving
// a 18.14 fixed point result.
//

fp14
fpDiv(fp14 d1, fp14 d2);
//#pragma aux fpDiv = \
//    "xor    edx, edx" \
//    "shld   edx, eax, 14" \
//    "sal    eax,14" \
//    "idiv   ebx" \
//    parm [eax] [ebx]\
//    modify [edx] \
//    value [eax];

//----------------------------------------------
//
// Divide two 18.14 fixed point numbers giving
// a 18.14 fixed point result.
//

fp14
fpMul(fp14 m1, fp14 m2);
//#pragma aux fpMul = \
//    "imul   ebx" \
//    "shrd   eax, edx, 14" \
//    parm [eax] [ebx]\
//    modify [edx]\
//    value [eax];

//----------------------------------------------
//
// Add and subtract fixed point numbers. :-)
//

#define fpAdd(a1, a2) ((a1) + (a2))
#define fpSub(s1, s2) ((s1) - (s2))

//----------------------------------------------
//
// Convert an integer to n N.14 fixed point 
// number and back.
//

#define int2fp(x) ((x) << 14)
#define fp2int(x) ((x) >> 14)

//----------------------------------------------
//
// Get the fractional part of an N.14 fixed point
// number.

#define fpFract(x) ((x) & 0x3fff)

//----------------------------------------------
//
// Convert a N.14 fixed point number to a
// double and back. Handy for printing and for 
// those times when you really do want to work
// with floating point values.
//

#define fp2float(x) (((double)(x)) / (1 << 14))
#define float2fp(x) ((fp14) ((x) * (1 << 14)))

//----------------------------------------------
//
// Table based fixed point sine and cosine 
// functions.
//

#define fpSin(x) (sine[(x) & 0xff])
#define fpCos(x) (cosine[(x) & 0xff])
#define fpSqrt(x) fpSqrt2(x)

#endif

//----------------------------------------------
//
// 18.14 fixed point sine and cosine tables.
//
// These table assume that there are 256 degrees
// in a circle.
//

fp14 sine[] =
{
     0,    402,    803,   1205,   1605,   2005,   2404,   2801, 
  3196,   3589,   3980,   4369,   4756,   5139,   5519,   5896, 
  6269,   6639,   7005,   7366,   7723,   8075,   8423,   8765, 
  9102,   9434,   9759,  10079,  10393,  10701,  11002,  11297, 
 11585,  11866,  12139,  12406,  12665,  12916,  13159,  13395, 
 13622,  13842,  14053,  14255,  14449,  14634,  14810,  14978, 
 15136,  15286,  15426,  15557,  15678,  15790,  15892,  15985, 
 16069,  16142,  16206,  16260,  16305,  16339,  16364,  16379, 
 16384,  16379,  16364,  16339,  16305,  16260,  16206,  16142, 
 16069,  15985,  15892,  15790,  15678,  15557,  15426,  15286, 
 15136,  14978,  14810,  14634,  14449,  14255,  14053,  13842, 
 13622,  13395,  13159,  12916,  12665,  12406,  12139,  11866, 
 11585,  11297,  11002,  10701,  10393,  10079,   9759,   9434, 
  9102,   8765,   8423,   8075,   7723,   7366,   7005,   6639, 
  6269,   5896,   5519,   5139,   4756,   4369,   3980,   3589, 
  3196,   2801,   2404,   2005,   1605,   1205,    803,    402, 
     0,   -402,   -803,  -1205,  -1605,  -2005,  -2404,  -2801, 
 -3196,  -3589,  -3980,  -4369,  -4756,  -5139,  -5519,  -5896, 
 -6269,  -6639,  -7005,  -7366,  -7723,  -8075,  -8423,  -8765, 
 -9102,  -9434,  -9759, -10079, -10393, -10701, -11002, -11297, 
-11585, -11866, -12139, -12406, -12665, -12916, -13159, -13395, 
-13622, -13842, -14053, -14255, -14449, -14634, -14810, -14978, 
-15136, -15286, -15426, -15557, -15678, -15790, -15892, -15985, 
-16069, -16142, -16206, -16260, -16305, -16339, -16364, -16379, 
-16384, -16379, -16364, -16339, -16305, -16260, -16206, -16142, 
-16069, -15985, -15892, -15790, -15678, -15557, -15426, -15286, 
-15136, -14978, -14810, -14634, -14449, -14255, -14053, -13842, 
-13622, -13395, -13159, -12916, -12665, -12406, -12139, -11866, 
-11585, -11297, -11002, -10701, -10393, -10079,  -9759,  -9434, 
 -9102,  -8765,  -8423,  -8075,  -7723,  -7366,  -7005,  -6639, 
 -6269,  -5896,  -5519,  -5139,  -4756,  -4369,  -3980,  -3589, 
 -3196,  -2801,  -2404,  -2005,  -1605,  -1205,   -803,   -402, 
};

fp14 cosine[] =
{
 16384,  16379,  16364,  16339,  16305,  16260,  16206,  16142, 
 16069,  15985,  15892,  15790,  15678,  15557,  15426,  15286, 
 15136,  14978,  14810,  14634,  14449,  14255,  14053,  13842, 
 13622,  13395,  13159,  12916,  12665,  12406,  12139,  11866, 
 11585,  11297,  11002,  10701,  10393,  10079,   9759,   9434, 
  9102,   8765,   8423,   8075,   7723,   7366,   7005,   6639, 
  6269,   5896,   5519,   5139,   4756,   4369,   3980,   3589, 
  3196,   2801,   2404,   2005,   1605,   1205,    803,    402, 
     0,   -402,   -803,  -1205,  -1605,  -2005,  -2404,  -2801, 
 -3196,  -3589,  -3980,  -4369,  -4756,  -5139,  -5519,  -5896, 
 -6269,  -6639,  -7005,  -7366,  -7723,  -8075,  -8423,  -8765, 
 -9102,  -9434,  -9759, -10079, -10393, -10701, -11002, -11297, 
-11585, -11866, -12139, -12406, -12665, -12916, -13159, -13395, 
-13622, -13842, -14053, -14255, -14449, -14634, -14810, -14978, 
-15136, -15286, -15426, -15557, -15678, -15790, -15892, -15985, 
-16069, -16142, -16206, -16260, -16305, -16339, -16364, -16379, 
-16384, -16379, -16364, -16339, -16305, -16260, -16206, -16142, 
-16069, -15985, -15892, -15790, -15678, -15557, -15426, -15286, 
-15136, -14978, -14810, -14634, -14449, -14255, -14053, -13842, 
-13622, -13395, -13159, -12916, -12665, -12406, -12139, -11866, 
-11585, -11297, -11002, -10701, -10393, -10079,  -9759,  -9434, 
 -9102,  -8765,  -8423,  -8075,  -7723,  -7366,  -7005,  -6639, 
 -6269,  -5896,  -5519,  -5139,  -4756,  -4369,  -3980,  -3589, 
 -3196,  -2801,  -2404,  -2005,  -1605,  -1205,   -803,   -402, 
     0,    402,    803,   1205,   1605,   2005,   2404,   2801, 
  3196,   3589,   3980,   4369,   4756,   5139,   5519,   5896, 
  6269,   6639,   7005,   7366,   7723,   8075,   8423,   8765, 
  9102,   9434,   9759,  10079,  10393,  10701,  11002,  11297, 
 11585,  11866,  12139,  12406,  12665,  12916,  13159,  13395, 
 13622,  13842,  14053,  14255,  14449,  14634,  14810,  14978, 
 15136,  15286,  15426,  15557,  15678,  15790,  15892,  15985, 
 16069,  16142,  16206,  16260,  16305,  16339,  16364,  16379, 
};

//----------------------------------------------
//
// Integer square root. Take the square root of an integer.
//

#define step(shift) \
    if((0x40000000l >> shift) + root <= value)          \
    {                                                   \
        value -= (0x40000000l >> shift) + root;         \
        root = (root >> 1) | (0x40000000l >> shift);    \
    }                                                   \
    else                                                \
    {                                                   \
        root = root >> 1;                               \
    }

fp14
fpDiv(fp14 d1, fp14 d2)
{  // just guessing here!!!
  unsigned long long x = ((unsigned long long)d1<<14) / (unsigned long long)d2;
//  x = x << 14;
  return ((fp14)x);
}

fp14
fpMul(fp14 m1, fp14 m2)
{
  unsigned long long x = (unsigned long long)m1 * (unsigned long long)m2;
  x = x >> 14;
  return ((fp14)x);
}

unsigned long 
iSqrt(unsigned long value)
{
    unsigned long root = 0;

    step( 0);
    step( 2);
    step( 4);
    step( 6);
    step( 8);
    step(10);
    step(12);
    step(14);
    step(16);
    step(18);
    step(20);
    step(22);
    step(24);
    step(26);
    step(28);
    step(30);

    // round to the nearest integer, cuts max error in half

    if(root < value)
    {
        ++root;
    }

    return root;
}

//----------------------------------------------
//
// fixed point square root routines.
//

//
// slow but very accurate
//
fp14
fpSqrt1(fp14 value)
{
    fp14 root;
    fp14 next;

    if (value < int2fp(1))
    {
        return 0;
    }

    next = value >> 2;

    do
    {
        root = next;
        next = (next + fpDiv(value, next)) >> 1;
    } while (root != next);

    return root;
}

//----------------------------------------------
//
// The sqrt(a * b) = sqrt(a) * sqrt(b) and
// fixed point numbers are scaled integers
// you can take the square root of a fixed 
// point number by taking the square root of
// the integer representation of the number
// and then dividing by the square root of the
// scale factor.
//
// For fixed point formats with an even number
// of fractional bits the division can be done
// with a simple shift operation.
//

//
// fast but not as accurate as Newton iteration
//
fp14
fpSqrt2(fp14 value)
{
    unsigned long root = 0;

    step( 0);
    step( 2);
    step( 4);
    step( 6);
    step( 8);
    step(10);
    step(12);
    step(14);
    step(16);
    step(18);
    step(20);
    step(22);
    step(24);
    step(26);
    step(28);
    step(30);

    // round to the nearest integer, cuts max error in half

    if(root < value)
    {
        ++root;
    }

    root <<= 7;

    return root;
}
//---------------------------------------- END FIXED POINT GRAPHICS PACKAGE



//---------------------------------------------------- FLIGHTPATH GENERATOR
#include <stdio.h>
// once code is performing properly, replace this with a version using fixed-
// point arithmetic (which is currently broken :-( )

typedef struct XYZ {
  double x,y,z;
} XYZ;

double SplineBlend(int k,int t,int *u,double v);
/*
   This returns the point "output" on the spline curve.
   The parameter "v" indicates the position, it ranges from 0 to n-t+2
   
*/
void SplinePoint(int *u,int n,int t,double v,XYZ *control,XYZ *output)
{
   int k;
   double b;

   output->x = 0;
   output->y = 0;
   output->z = 0;

   for (k=0;k<=n;k++) {
      b = SplineBlend(k,t,u,v);
      output->x += control[k].x * b;
      output->y += control[k].y * b;
      output->z += control[k].z * b;
   }
}

/*
   Calculate the blending value, this is done recursively.
   
   If the numerator and denominator are 0 the expression is 0.
   If the deonimator is 0 the expression is 0
*/
double SplineBlend(int k,int t,int *u,double v)
{
   double value;

   if (t == 1) {
      if ((u[k] <= v) && (v < u[k+1]))
         value = 1;
      else
         value = 0;
   } else {
      if ((u[k+t-1] == u[k]) && (u[k+t] == u[k+1]))
         value = 0;
      else if (u[k+t-1] == u[k]) 
         value = (u[k+t] - v) / (u[k+t] - u[k+1]) * SplineBlend(k+1,t-1,u,v);
      else if (u[k+t] == u[k+1])
         value = (v - u[k]) / (u[k+t-1] - u[k]) * SplineBlend(k,t-1,u,v);
     else
         value = (v - u[k]) / (u[k+t-1] - u[k]) * SplineBlend(k,t-1,u,v) + 
                 (u[k+t] - v) / (u[k+t] - u[k+1]) * SplineBlend(k+1,t-1,u,v);
   }
   return(value);
}

/*
   The positions of the subintervals of v and breakpoints, the position
   on the curve are called knots. Breakpoints can be uniformly defined
   by setting u[j] = j, a more useful series of breakpoints are defined
   by the function below. This set of breakpoints localises changes to
   the vicinity of the control point being modified.
*/
void SplineKnots(int *u,int n,int t)
{
   int j;

   for (j=0;j<=n+t;j++) {
      if (j < t)
         u[j] = 0;
      else if (j <= n)
         u[j] = j - t + 1;
      else if (j > n)
         u[j] = n - t + 2;	
   }
}

/*-------------------------------------------------------------------------
   Create all the points along a spline curve
   Control points "inp", "n" of them.
   Knots "knots", degree "t".
   Output curve "outp", "res" of them.
*/
void SplineCurve(XYZ *inp,int n,int *knots,int t,XYZ *outp,int res)
{
   int i;
   double interval,increment;

   interval = 0;
   increment = (n - t + 2) / (double)(res - 1);
   for (i=0;i<res-1;i++) {
      SplinePoint(knots,n,t,interval,inp,&(outp[i]));
      interval += increment;
   }
   outp[res-1] = inp[n];
}

/*
   Example of how to call the spline functions
	Basically one needs to create the control points, then compute
   the knot positions, then calculate points along the curve.
*/

#define SCL 2048   // x/y fudge factor
#define ZSCL 512   // distance away
#define PSCL 1024  // perspective scale

#define MAXN 10
XYZ inp[] = {
//X        Y        Z
  450.0, 1000.0, 10.0,        // final coord is center of 1280x1024 screen
  450.0, 1000.0, 6.66,        // final coord is center of 1280x1024 screen
  450.0, 1000.0, 3.33,        // final coord is center of 1280x1024 screen
  450.0, 1000.0, 0.0,        // final coord is center of 1280x1024 screen
};

#define T 3
int knots[MAXN+T+1];
#define RESOLUTION 512
XYZ outp[RESOLUTION];

void newpaths(void) // yet another quick hack...
{
   int i, N;
   double x,y,z,x2,y2;

   for (i = 0; i < MAXN; i++) {
     if (inp[i].z < 0.0001) break;
   }
   N = i-1;

   SplineKnots(knots,N,T);
   SplineCurve(inp,N,knots,T,outp,RESOLUTION);
return;
   printf("static int xyflightpath[RES*2] = {\n");
   for (i=0;i<RESOLUTION;i++) {

      x=outp[i].x; y=outp[i].y; z=outp[i].z;

      x2 = ((PSCL*x)/(PSCL+x)); y2 = ((PSCL*y)/(PSCL+y));
      printf("  %d, %d,\n",(int)x2, (int)y2);
   }
   printf("};\n");

return;

   /* Display the curve */
   printf("#define RES %d\n",RESOLUTION);
#ifdef VERBOSE
   /* Control point polygon */
   printf("#define POINTS %d\nstatic double controls[POINTS*3] = {\n", N+1);
   for (i=0;i<=N;i++)
      printf("  %g, %g, %g,\n",inp[i].x,inp[i].y,inp[i].z);
   printf("}\n");



   /* Display the curve */
   printf("static double curve[RES*3] = {\n",RESOLUTION);
   for (i=0;i<RESOLUTION;i++)
      printf("  %g, %g, %g,\n",outp[i].x,outp[i].y,outp[i].z);
   printf("}\n");
#endif
   printf("static int xyflightpath[RES*2] = {\n");
   for (i=0;i<RESOLUTION;i++) {

      x=outp[i].x; y=outp[i].y; z=outp[i].z;

      x2 = ((PSCL*x)/(PSCL+x)); y2 = ((PSCL*y)/(PSCL+y));
      printf("  %d, %d,\n",(int)x2, (int)y2);
   }
   printf("};\n");
}
//------------------------------------------------ END FLIGHTPATH GENERATOR


static int MinX = 1000, MinY = 1000, MaxX = 0, MaxY = 0;

static int debounce_oneshots = 0, debounce_shields = 0;


// The Cinematronics code here mirrors the original emulated version including
// its coordinate system and structure.  "CinemaClearScreen" should probably
// be reworked completely in this new implementation

void
CinemaClearScreen (void)
{
  static unsigned long Frames = 0L;
  int mx = 0, my = 0;

  mousecode = 0;

  mousecode |= IO_LEFT;
  mousecode |= IO_RIGHT;

  if (mx > xmousethresh)
    mousecode &= ~IO_RIGHT;
  if ((-mx) > xmousethresh)
    mousecode &= ~IO_LEFT;

  if (my > ymousethresh)
    mousecode |= IO_UP;
  else
    mousecode &= ~IO_UP;
  if (-my > ymousethresh)
    mousecode |= IO_DOWN;
  else
    mousecode &= ~IO_DOWN;

  /*
     if (... fire button is pressed ...) fireflag &= ~IO_FIRE; else fireflag |= IO_FIRE;
     if (... shields button is pressed ...) shieldsflag &= ~IO_SHIELDS; else shieldsflag |= IO_SHIELDS;

     If the start button is pressed, set "startflag = 0;" here
     If the coin button is pressed, set 
     ioSwitches &= ~IO_COIN; / * Clear coin counter the bodgy way * /
     coinflag = 0;
     if the shields button is pressed, set "shieldsflag = IO_SHIELDS;"

     HOWEVER if *nothing* is pressed,       

     startflag = IO_START; / * Bodge.  Clear start button. Allows it to be down for 1/50sec * /

   */

  if (debounce_oneshots > 0) {
    debounce_oneshots -= 1;
    if (debounce_oneshots == 0) {
      startflag = IO_START; fireflag |= IO_FIRE;
    }
  }

  if (debounce_shields > 0) {
    debounce_shields -= 1;
    if (debounce_shields == 0) {
      shieldsflag |= IO_SHIELDS;
    }
  }

  ioInputs =
    (unsigned short) (mousecode | fireflag | shieldsflag | startflag);

  /* CAN WE DO SOMETHING LIKE: vsync(); */

#ifdef DOUBLE_BUFFERING
  blit (buffer, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
  clear (buffer);
#endif

#ifdef OPENGL
//  fprintf (stdout, "Clear: %ld [bb: (%ld,%ld) (%ld,%ld)]\n", Frames++,
//    MinX, MinY, MaxX, MaxY);
#endif
}


// This is the low-level vector (line) drawing routine.  It uses
// the original ROM version coordinate space and maps to the local
// hardware.  Clipping is crude but doesn't appear to hurt in
// this application.  Ideally this code should be trivially short
// and fast.
void
CinemaVectorData (int FromX, int FromY, int ToX, int ToY, int vgColour)
{

  /* TWEAK THESE TO MAKE THEM FIT... */

  ToX = ToX << 16;  // sign extend short to unsigned long
  ToX = ToX >> 16;
  FromX = FromX << 16;
  FromX = FromX >> 16;

  if (FromX < MinX)
    MinX = FromX;
  if (ToX < MinX)
    MinX = ToX;
  if (FromY < MinY)
    MinY = FromY;
  if (ToY < MinY)
    MinY = ToY;
  if (FromX > MaxX)
    MaxX = FromX;
  if (ToX > MaxX)
    MaxX = ToX;
  if (FromY > MaxY)
    MaxY = FromY;
  if (ToY > MaxY)
    MaxY = ToY;

#ifdef OPENGL
//fprintf (stdout, "Line: %d %d -> %d %d (%d)\n", FromX, FromY, ToX, ToY, vgColour);
  glColor3ubv(colors[vgColour % COLORS]);
  glVertex2i(FromX, FromY);
  glVertex2i(ToX, ToY);
#endif

}


/* main program loop body */


#ifdef OPENGL
// This code based on:
/* 
    qix.c
    Nate Robins, 1997

    An example of a 'qix'-like line demo, but without the traditional
    erase lines, and with anti-aliased lines.

 */


void
reshape(int width, int height)
{
    glViewport(0, 0, width, height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0, width, 0, height, -1, 1);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glEnable(GL_BLEND);
    glEnable(GL_LINE_SMOOTH);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE/*GL_ONE_MINUS_SRC_ALPHA*/);
}

void
display(void)
{
    int i;
    static int color = 0;
    static int odd = 0;

    odd = (odd+1)&15;
    if (odd == 0) {
#ifdef OPENGL
      glClear(GL_COLOR_BUFFER_BIT);    
      glBegin(GL_LINES);
#endif
      cineExecuteFrame ();
#ifdef OPENGL
      glEnd();
      glutSwapBuffers();
#endif
    }
}

void
idle(void)
{
    glutPostRedisplay();
}

static int PENDING_KEY = -1;
void
keyboard(unsigned char key, int x, int y)
{
    static int old_x = 50;
    static int old_y = 50;
    static int old_width = SCREEN_W;
    static int old_height = SCREEN_H;

    if (key == 27) {
        exit(0);
    } else if (key == 'w') {
        glutPositionWindow(old_x, old_y);
        glutReshapeWindow(old_width, old_height);
    } else if (key == 'f') {
	if (glutGet(GLUT_WINDOW_WIDTH) != glutGet(GLUT_SCREEN_WIDTH)) {
	    old_x = glutGet(GLUT_WINDOW_X);
	    old_y = glutGet(GLUT_WINDOW_Y);
	    old_width = glutGet(GLUT_WINDOW_WIDTH);
	    old_height = glutGet(GLUT_WINDOW_HEIGHT);
	    glutFullScreen();
	}
    } else {
      PENDING_KEY = key;
    }
}
#endif /* OPENGL */

char charvec[] = {
 '0', 4, 0xae, 0xe4, 0x40, 0x0a,
 '1', 1, 0x2c,
 '2', 5, 0xae, 0xe9, 0x95, 0x50, 0x04,
 '3', 4, 0x04, 0x79, 0x4e, 0xea,
#ifdef GTCHARS
 '4', 3, 0xa5, 0x59, 0x72, /* mine */
#else
 '4', 3, 0x5a, 0x59, 0x4e, /* orig */
#endif
 '5', 5, 0x04, 0x49, 0x95, 0x5a, 0xae,
 '6', 4, 0x59, 0x94, 0x40, 0x0a,
 '7', 2, 0x2e, 0xea,
 '8', 5, 0x04, 0xae, 0x59, 0xe4, 0xa0,
 '9', 4, 0xea, 0xa5, 0x59, 0xe4,
 'A', 4, 0x4e, 0xae, 0x59, 0xa0,
 'B', 5, 0x69, 0x04, 0x4e, 0xea, 0xb1,
 'C', 3, 0xae, 0x40, 0x0a,
#ifdef GTCHARS
 'D', 5, 0xad, 0xd9, 0x93, 0x30, 0x0a,
#else
 'D', 4, 0xae, 0xe4, 0x40, 0x1b,
#endif
 'E', 4, 0x40, 0x0a, 0x57, 0xae,
 'F', 3, 0x0a, 0x57, 0xae,
 'G', 5, 0x79, 0x94, 0x40, 0x0a, 0xae,
 'H', 3, 0x0a, 0x59, 0x4e,
 'I', 1, 0x2c,
 'J', 4, 0x50, 0x03, 0x3d, 0xbe,
 'K', 3, 0x0a, 0xe5, 0x54,
 'L', 2, 0x0a, 0x04,
 'M', 4, 0x0a, 0xa7, 0x7e, 0xe4,
 'N', 3, 0x0a, 0xa4, 0x4e,
 'O', 4, 0xae, 0xe4, 0x40, 0x0a,
 'P', 4, 0x0a, 0xae, 0xe9, 0x95,
 'Q', 5, 0x0a, 0xae, 0xe4, 0x40, 0x48,
 'R', 5, 0x48, 0x59, 0x9e, 0xea, 0xa0,
 'S', 5, 0x04, 0x49, 0x95, 0x5a, 0xae,
 'T', 2, 0xae, 0xc2,
 'U', 3, 0x0a, 0x04, 0x4e,
 'V', 2, 0xa2, 0x2e,
 'W', 4, 0xa1, 0x17, 0x73, 0x3e,
 'X', 2, 0x0e, 0xa4,
 'Y', 3, 0x27, 0x7a, 0x7e,
 'Z', 3, 0xae, 0xe0, 0x04,
 '\0'
};

char *nibbles[256];

void initchars()
{
  int i;
  char *s = charvec;
  for (i = 0; i < 256; i++) nibbles[i] = NULL;
  for (;;) {
    if (*s == '\0') break;
    nibbles[*s] = s+1;
#ifdef DEBUG
    fprintf(stdout,"nibbles[%c] = %d char string\n", *s, *(s+1));
#endif 
    s = s+1;
    s = s + (*s) + 1;
  }
};

int lookupx[16] = {
  0x00, 0x04, 0x08, 0x0c, 0x10,
  0x00, 0x04, 0x08, 0x0c, 0x10,
  0x00, 0x04, 0x08, 0x0c, 0x10,
  0
};

int lookupy[16] = {
  0x00, 0x00, 0x00, 0x00, 0x00,
  0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
  0x18, 0x18, 0x18, 0x18, 0x18,
  0
};

int curx = 0, cury = 0, curcol = 15;;

void vecmoveto(int x, int y)
{
  curx = x; cury = y;
}

void veccol(int col)
{
  curcol = col;
}

void printchar(int c)
{
  char *nibblep = nibbles[c&255];
  int i, nibble, from, to, len;

  if (nibblep == NULL) {
    curx += 0x18; /* space */
    return;
  }

  len = *nibblep++;
  for (i = 0; i < len; i++) {
    nibble = *nibblep++;
    to = nibble&15; from = (nibble>>4)&15;

    CinemaVectorData(curx+lookupx[from], cury+lookupy[from],
                     curx+lookupx[to],   cury+lookupy[to],
                     curcol);
#ifdef DEBUG
    fprintf(stdout, "%01x to %01x\n", from, to);
    fprintf(stdout, "(%d,%d) to (%d,%d) at %d\n", curx+lookupx[from], cury+lookupy[from],
                     curx+lookupx[to],   cury+lookupy[to],
                     7);
#endif
  }
  curx += 0x18;
  /* at current x y and do implicit move */
}

void printstring(char *s)
{
  while (*s != '\0') printchar(*s++);
}

unsigned char rectangle_lookup_x[8] = {
 0x47, 0xb9, 0xb9, 0x47, 0x3f, 0xc1, 0xc1, 0x3f
};

unsigned char rectangle_lookup_y[8] = {
 0x9b, 0x9b, 0x65, 0x65, 0xa3, 0xa3, 0x5d, 0x5d
};

unsigned char rectangle_lookup_z[8] = {
 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80
};

unsigned char rectangles[] = {
  0x01, 0x12, 0x23, 0x30, 0x45, 0x56, 0x67, 0x74, 0xff
};

void print_rectangles_seg(int c)
{
  unsigned char *coords = rectangles;
  int fromx, fromy, fromz, tox, toy, toz;
  int nib1 = c >> 4, nib2 = c&7;
  fromx = rectangle_lookup_x[nib1];
  fromy = rectangle_lookup_y[nib1];
  fromz = rectangle_lookup_z[nib1];
  tox = rectangle_lookup_x[nib2];
  toy = rectangle_lookup_y[nib2];
  toz = rectangle_lookup_z[nib2];

  CinemaVectorData(curx+fromx, cury+fromy,
                   curx+tox,   cury+toy,
                   15);
}

void print_rectangles(void)
{
  char *s = rectangles;
  unsigned char ch = *s;
  while (ch != 0xff) {
    print_rectangles_seg(ch);
    s += 1;
    ch = *s;
  }
}

unsigned char tg1_lookup_x[16] = {
 0x5d, 0x6d, 0x65, 0x65, 0x71, 0x77, 0x7d, 0x74, 0x7a, 0x89, 0x89, 0x95, 0x95, 0xa1, 0xa7, 0xa7
};

unsigned char tg1_lookup_y[16] = {
 0x95, 0x95, 0x95, 0x83, 0x83, 0x95, 0x83, 0x8c, 0x8c, 0x83, 0x95, 0x95, 0x83, 0x83, 0x7d, 0x6b
};

unsigned char tg1_lookup_z[16] = {
 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80
};

unsigned char tg1[] = {
  0x01, 0x23, 0x45, 0x56, 0x78, 0x9a, 0xbc, 0xcd, 0xef, 0xff
};

unsigned char tg2_lookup_x[16] = {
 0xa7, 0xb3, 0xb3, 0xb0, 0xb3, 0xa7, 0xa1, 0x95, 0x95, 0xa1, 0x9b, 0x95, 0x8f, 0x8f, 0x83, 0x83
};

unsigned char tg2_lookup_y[16] = {
 0x7d, 0x7d, 0x74, 0x74, 0x6b, 0x74, 0x6b, 0x6b, 0x7d, 0x7d, 0x74, 0x74, 0x7d, 0x6b, 0x7d, 0x6b
};

unsigned char tg2_lookup_z[16] = {
 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80
};

unsigned char tg2[] = {
  0x01, 0x12, 0x34, 0x25, 0x67, 0x78, 0x89, 0xab, 0xcd, 0xde, 0xef, 0xff
};

unsigned char tg3_lookup_x[16] = {
 0x7d, 0x7d, 0x71, 0x71, 0x6b, 0x6b, 0x5f, 0x5f, 0x5c, 0x56, 0x59, 0x59, 0x4d, 0x4d, 0x59
};

unsigned char tg3_lookup_y[16] = {
 0x7d, 0x6b, 0x7d, 0x6b, 0x7d, 0x6b, 0x6b, 0x7d, 0x72, 0x72, 0x72, 0x6b, 0x6b, 0x7d, 0x7d
};

unsigned char tg3_lookup_z[16] = {
 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80
};

unsigned char tg3[] = {
  0x01, 0x12, 0x23, 0x45, 0x56, 0x67, 0x89, 0xab, 0xbc, 0xcd, 0xde, 0xff
};

void print_tg_seg(char c, unsigned char *tg_lookup_x, unsigned char *tg_lookup_y, unsigned char *tg_lookup_z) {
  char *coords = rectangles;
  int fromx, fromy, fromz, tox, toy, toz;
  int nib1 = (c >> 4)&15, nib2 = c&15;
  fromx = tg_lookup_x[nib1];
  fromy = tg_lookup_y[nib1];
  fromz = tg_lookup_z[nib1];
  tox = tg_lookup_x[nib2];
  toy = tg_lookup_y[nib2];
  toz = tg_lookup_z[nib2];

// quick hack while testing - change distance but don't roll

  CinemaVectorData(curx+fromx, cury+fromy,
                   curx+tox,   cury+toy,
                   15);
#ifdef NEVER
  {
#define VSCL 1024
     int x1, y1, x2, y2;
     static int fakez = -1;
     static int delay = 0;
     if (fakez == -1) {
       fakez = fromz;
     } else if (delay == 2) {
       fakez -= 1;
       if (fakez == (fromz/8)) fakez = fromz*8;
       delay = 0;
     }
     delay += 1;
     x1 = (fromx * VSCL)/(fakez + VSCL); y1 = (fromy * VSCL)/(fakez + VSCL);
     x2 = (tox * VSCL)/(fakez + VSCL); y2 = (toy * VSCL)/(fakez + VSCL);
     CinemaVectorData(curx+x1, cury+y1,
                      curx+x2, cury+y2,
                      15);
  }
#endif
}

void print_tg_word(unsigned char *tg, unsigned char *tg_lookup_x, unsigned char *tg_lookup_y, unsigned char *tg_lookup_z)
{
  char *s = tg;
  unsigned char ch = *s;
  while (ch != 0xff) {
    print_tg_seg(ch, tg_lookup_x, tg_lookup_y, tg_lookup_z);
    s += 1;
    ch = *s;
  }
}

void print_tg(void)
{
   print_tg_word(tg1, tg1_lookup_x, tg1_lookup_y, tg1_lookup_z);
   print_tg_word(tg2, tg2_lookup_x, tg2_lookup_y, tg2_lookup_z);
   print_tg_word(tg3, tg3_lookup_x, tg3_lookup_y, tg3_lookup_z);
}

void starplot(int x, int y)
{
  CinemaVectorData(x, y,
                   x+1, y+1,
                   /* 7 is too faint */ 15);
}


/* The screen is divided up into 8 sectors:

          \ | /
           \|/
          --+--
           /|\
          / | \

   We want 16 stars on screen at all times.

   At any time, each sector must have 2 stars active in it.

   To provide variety, we will have 4 tracks through each sector, and
   cycle them as each star finishes its journey.

 */

int star_x1[32] = {
  0, 0, 0, 0,
  25, 125, 225, 325,
  425, 525, 625, 725,
  1000, 1000, 1000, 1000,
  0, 0, 0, 0,
  25, 125, 225, 325,
  425, 525, 625, 725,
  1000, 1000, 1000, 1000,
};

int star_y1[32] = {
  25, 125, 225, 325,
  0,0,0,0,
  0,0,0,0,
  35, 135, 235, 335,
  425,525,62,725,
  800,800,800,800,
  800,800,800,800,
  425,525,62,725,
};

int clipoff(int x, int y)
{
  return ((0x1b0 < x && x <= 0x250) && (0x140 <= y && y <= 0x1c0));
}

/**************************************************************************
 *  line_fast                                                             *
 *    draws a line using Bresenham's line-drawing algorithm, which uses   *
 *    no multiplication or division.                                      *
 **************************************************************************/

#define sgn(x) ((x<0)?-1:((x>0)?1:0)) /* macro to return the sign of a number */

void plot_star(int starno)
{
  static int i[16],dx[16],dy[16],x[16],y[16],px[16],py[16],dxabs[16],dyabs[16],sdx[16],sdy[16];
  static int state[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
  static int flip[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
  static int maxspeed[16], ticks[16];
  int speed, done=(0!=0);
  int x1 = star_x1[(starno<<1)+flip[starno]], y1 = star_y1[(starno<<1)+flip[starno]],
      x2 = 0x200, y2 = 0x180;

  if (state[starno] == 0) { /* initialise */
    dx[starno]=x2-x1;      /* the horizontal distance of the line */
    dy[starno]=y2-y1;      /* the vertical distance of the line */
    dxabs[starno]=abs(dx[starno]);
    dyabs[starno]=abs(dy[starno]);
    sdx[starno]=sgn(dx[starno]);
    sdy[starno]=sgn(dy[starno]);
    x[starno]=dyabs[starno]>>1;
    y[starno]=dxabs[starno]>>1;
    px[starno]=x1;
    py[starno]=y1;
    state[starno] = 1; maxspeed[starno] = 9 /* critical, depends on vector length */;
    ticks[starno] = 0;
    /* and draw first pixel */
    starplot(px[starno],py[starno]);
    i[starno] = 0; /* initialise 'loop' counter */
    return;
  }

  if (dxabs[starno]>=dyabs[starno]) { /* the line is more horizontal than vertical */
    for (speed = 0; speed < maxspeed[starno]; speed++) {
      y[starno]+=dyabs[starno];
      if (y[starno]>=dxabs[starno])
      {
        y[starno]-=dxabs[starno];
        py[starno]+=sdy[starno];
      }
      px[starno]+=sdx[starno];
      i[starno] += 1;
      if (done = (i[starno] >= dxabs[starno])) {
        break;
      }
    }
  } else {  /* the line is more vertical than horizontal */
    for (speed = 0; speed < maxspeed[starno]; speed++) {
      x[starno]+=dxabs[starno];
      if (x[starno]>=dyabs[starno])
      {
        x[starno]-=dyabs[starno];
        px[starno]+=sdx[starno];
      }
      py[starno]+=sdy[starno];
      i[starno] += 1;
      if (done = (i[starno] >= dyabs[starno])) {
        break;
      }
    }
  }
  if (done) {
    state[starno] = 0; /* Done! */
    flip[starno] = 1-flip[starno];
  }
  ticks[starno] += 1;
  if (ticks[starno] == 12) {
    ticks[starno] = 0;
    maxspeed[starno] -= 1;
  }
  if (maxspeed[starno] <= 0) maxspeed[starno] = 1;
  if (clipoff(px[starno], py[starno])) {
    state[starno] = 0;
    flip[starno] = 1-flip[starno];
  } else {
    starplot(px[starno],py[starno]);
    /* for moving stars we just do one of these each time increment */
    /* this is in effect a very slow line draw */
  }
}

void stars(int speed)
{
  static int started = 0;
  int star;
  if (started == 0) {
    int laststar;
    for (laststar = 1; laststar <= 16; laststar++) { /* skew */
      int i;
      for (i = 0; i < 20; i++) { /* gap between skew */
        for (star = 0; star < laststar; star++) plot_star(star);
      }
    }
    started = 1;
  } else {
    int i;
    for (i = 0; i < speed; i++) {
      for (star = 0; star < 16; star++) plot_star(star);
    }
  }
}

static int firex[2];
static int firey[2];

/* MISTAKE HERE - we need a line drawing algorithm which increments in one
   Y unit each call, *NOT* one X unit. */

/* returns true if drawn to end of vector, false if should call again */
int plot_fire(int lr, int x1, int y1, int x2, int y2)
{
  int i,dx,dy,sdx,sdy,dxabs,dyabs,x,y,px,py;
  dx=x2-x1;      /* the horizontal distance of the line */
  dy=y2-y1;      /* the vertical distance of the line */
  dxabs=abs(dx);
  dyabs=abs(dy);
  sdx=sgn(dx);
  sdy=sgn(dy);
  x=dyabs>>1;
  y=dxabs>>1;
  px=x1;
  py=y1;

  CinemaVectorData(px, py, px+1, py+1, 15);

  if (dxabs>=dyabs) /* the line is more horizontal than vertical */
  {
    for(i=0;i<dxabs;i++)
    {
      y+=dyabs;
      if (y>=dxabs)
      {
        y-=dxabs;
        py+=sdy;
      }
      px+=sdx;
      if (i <= 25) {
        CinemaVectorData(px, py, px+1, py+1, 15);
        firex[lr] = px; firey[lr] = py;
      }
      if (i == 50) {firex[lr] = px; firey[lr] = py;}  /* draw next line 100 pixels in from this one */
    }
  }
  else /* the line is more vertical than horizontal */
  {
    for(i=0;i<dyabs;i++)
    {
      x+=dxabs;
      if (x>=dyabs)
      {
        x-=dyabs;
        px+=sdx;
      }
      py+=sdy;
      if (i <= 25) {
        CinemaVectorData(px, py, px+1, py+1, 15);
        firex[lr] = px; firey[lr] = py;
      }
      if (i == 50) {firex[lr] = px; firey[lr] = py;}
    }
  }
  return(i <= 30);
}

static unsigned char crosshair[40] = {
 0x63, 0x80, 0x9e, 0x80,
 0x94, 0x76, 0x94, 0x8a,
 0x8a, 0x94, 0x76, 0x94,
 0x80, 0x9e, 0x80, 0x62,
 0x8a, 0x6c, 0x76, 0x6c,
 0x6c, 0x76, 0x6c, 0x8a,
 0x7b, 0x8a, 0x85, 0x8a,
 0x8a, 0x85, 0x8a, 0x7b,
 0x85, 0x76, 0x7b, 0x76,
 0x76, 0x7b, 0x76, 0x85,
};

static unsigned char shieldvec[26] = {
 0xc0, 0xa0,
 0xe0, 0x80,
 0x80, 0x20,
 0x20, 0x80,
 0x40, 0xa0,
 0x80, 0x60,
 0xc0, 0x20,
 0xe0, 0x40,
 0x80, 0xa0,
 0x20, 0x40,
 0x40, 0x20,
 0x80, 0x60,
 0xc0, 0xa0,
};

void draw_shields(void)
{
  int i;
  for (i = 0; i < 12; i++) {
    CinemaVectorData((shieldvec[i*2]<<2) - 0x30, shieldvec[i*2+1]<<2,
                     (shieldvec[i*2+2]<<2) - 0x30, shieldvec[i*2+3]<<2,
                     curcol);
  }
}

void draw_crosshair(int x, int y)
{
  int i;
  for (i = 0; i < 10; i++) {
    CinemaVectorData(x + (crosshair[i*4]) - 0x40,   y + crosshair[i*4+1] - 0x80,
                     x + (crosshair[i*4+2]) - 0x40, y + crosshair[i*4+3] - 0x80,
                     curcol);
  }
}

#define PREGAME 1
#define COINED 2
#define PLAYING 3


void Fixed_Point_Test(void)
{
  fp14 x, y;
  static fp14 r;
  static int init = -1;
  static int theta;
  if (init == RESOLUTION) {
    init += 1;
    // radius = 200
    // centre = 500 500
    // loop round 256 'degrees'
    theta = -1;
    r = int2fp(300);
  } else if (init > RESOLUTION) {
    theta += 1;
    if (theta == 256) theta = 0;
    // x = r sin theta  y = r cos theta
    x = fpMul(sine[theta], r);
    y = fpMul(cosine[theta], r);
    mouse_x = fp2int(x)+310;
    mouse_y = fp2int(y)+205;
  } else {
    init += 1; // 0..RES-1

    mouse_x = ((PSCL*outp[init].x)/(PSCL+outp[init].y));
    mouse_y = ((PSCL*outp[init].z)/(PSCL+outp[init].y));

    if (init+1 == RESOLUTION) init = -1;
  }
}


static int tgstate = PREGAME;

static int credits = '0';
static int passed = '0';
static char shields[3] = { '8', '0', '\0'};

int
main(int argc, char** argv)
{
#ifdef OPENGL
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
    glutInitWindowPosition(50, 50);
    glutInitWindowSize(SCREEN_W, SCREEN_H);
    glutInit(&argc, argv);

    glutCreateWindow("Tailgunner");
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutKeyboardFunc(keyboard);
#endif

    initchars();
    newpaths();
    CinemaClearScreen ();		/* Initialise ioInputs etc before game starts (optional) */

#ifdef OPENGL
    glutIdleFunc(idle);
    glutMainLoop();
#endif
    return 0;
}
/* #endif */

void cineExecuteFrame(void) {
//int main(int argc, char **argv) {
static int i;
int k = 0;
static int start = 0;
static int shieldtick = 0;
static int firing = (0!=0);
static int reloaded = (0==0);
static int firedone1, firedone2;

  /* CinemaVectorData(50,50, 300,300, 7); -- debug tool... */

//  for (;;) {
    switch (tgstate) {

    case PREGAME:

      vecmoveto(0x190, 210); for (i = '0'; i <= '9'; i++) printchar(i);
      vecmoveto(200, 270); for (i = 'A'; i <= 'Z'; i++) printchar(i);

      vecmoveto(0x96, 0x28a); printstring("SCORE");
      vecmoveto(0xb8, 0x258); printstring(" 00");

      vecmoveto(0x2bc, 0x28a); printstring("HIGH[SCORE");
      vecmoveto(0x2f8, 0x258); printstring(" 00");

      /* Display 'tailgunner' rectangle.
         Rotate it one increment.
         If it is facing away, discard the text, just keep the rectangles
       */

      /* do a cycle of : */

      vecmoveto(0x180, 0x100);  /* This will also need to be changed when applying scaling */
      print_rectangles();       /* needs rotation applied */
      print_tg();               /* identical matrix needed */

      /* when rectangle at maximum size, stop it and flash intensity */

      /* followed by a cycle of: move and show ships (quit when all 3 pass the screen) */

      /* move existing stars in by a time unit, remove old stars that
         hit internal rectangle, replace with new stars on outer border.
         This is independent of game state */

      stars(1);

      break;

    case COINED:

      /*vecmoveto(0x190, 210); for (i = '0'; i <= '9'; i++) printchar(i);
      vecmoveto(200, 270); for (i = 'A'; i <= 'Z'; i++) printchar(i);*/

      vecmoveto(0x96, 0x28a); printstring("SCORE");
      vecmoveto(0xb8, 0x258); printstring(" 00");

      vecmoveto(0x2bc, 0x28a); printstring("HIGH SCORE");
      vecmoveto(0x2f8, 0x258); printstring(" 00");

      start = (start + 1) & 31;
      veccol(start >= 16 ? 15 : 7);
      vecmoveto(0x190, 0x190);
      printstring("PUSH START");
      veccol(15);

      vecmoveto(0x190, 0x32); printstring("CREDITS");
      vecmoveto(0x258, 0x32); printstring("  "); printchar(credits > '9' ? '9' : credits);

      /* move existing stars in by a time unit, remove old stars that
         hit internal rectangle, replace with new stars on outer border.
         This is independent of game state */

      stars(1);


      break;

    case PLAYING:
      vecmoveto(0xb8, 0x258); printstring(" 00"); /* score */
      vecmoveto(0x2f8+0x18, 0x258); printchar(passed); /* ships passed */
      vecmoveto(0x1cc, 0x28a); printstring(shields);  /* shields value */

      /* PLAY ONE FRAME OF GAME HERE */

      /* move existing stars in by a time unit, remove old stars that
         hit internal rectangle, replace with new stars on outer border.
         This is independent of game state */

      {
        static int passing = 0;
        passing = passing + 1;
        if (passing >= 500) {
          if (passing == 500) {
            passed += 3;
            if (passed > '9') {
              passed = '0'; credits -= 1;
              if (credits == '0') tgstate = PREGAME; else tgstate = COINED;
              firing = (0!=0);
            }
          }
          stars(2);
        } else stars(1);
        if (passing == 540) passing = 0;
      }
      /* move and show ships */

      Fixed_Point_Test();

//      vecmoveto((mouse_x*5)/4+0x30, 640-((mouse_y*10)/8)); draw_crosshair(curx, cury);
      vecmoveto(mouse_x, mouse_y); draw_crosshair(curx, cury);

      if (mouse_b & 1) {
        if (!firing && reloaded) {
          /* initiate fire */;
          firing = (0==0); reloaded = (0!=0);
          firedone1 = plot_fire(0, 40,40, (mouse_x*5)/4+0x70, 640-((mouse_y*10)/8));
          firedone2 = plot_fire(1, 1000,40, (mouse_x*5)/4+0x70, 640-((mouse_y*10)/8));
        } else {
          if (!firedone1) {
            firedone1 = plot_fire(0, firex[0], firey[0],
                                  (mouse_x*5)/4+0x70, 640-((mouse_y*10)/8));
          }
          if (!firedone2) {
            firedone2 = plot_fire(1, firex[1], firey[1],
                                  (mouse_x*5)/4+0x70, 640-((mouse_y*10)/8));
          }
          firing = (!firedone1 || !firedone2);
        }
      } else {
        if (firing) { /* Finish off the last shot */
          if (!firedone1) {
            firedone1 = plot_fire(0, firex[0], firey[0],
                                  (mouse_x*5)/4+0x70, 640-((mouse_y*10)/8));
          }
          if (!firedone2) {
            firedone2 = plot_fire(1, firex[1], firey[1],
                                  (mouse_x*5)/4+0x70, 640-((mouse_y*10)/8));
          }
          firing = !(firedone1 && firedone2);
        } else reloaded = (0==0);
      }

      /* we might need a debouncing algorithm here to match the bahaviour of the real game */
      if ((mouse_b & 2) && (shields[0] != '0' || shields[1] != '0')) {
        draw_shields();
        if (shieldtick == 0) {
          shields[1] -= 1;
          if (shields[1] < '0') {
            shields[1] = '9'; shields[0] -= 1;
            if (shields[0] < '0') {
              shields[0] = shields[1] = '0';
            }
          }
          shieldtick = 8;
        } else shieldtick -= 1;
      } else shieldtick = 0;

mouse_b = 0; // while testing keyboard

      break;
    }

    CinemaClearScreen();

//    if (/*keypressed()*/PENDING_KEY != -1) {
//      k = /*readkey()*/ PENDING_KEY & 0x7f; PENDING_KEY = -1;
//      fprintf(stdout, "K: %d\n", k);
//    }
    if (k == 27) {
        exit(0);
    } else if (k == '3') {
        tgstate = COINED; credits += 1;
    } else if (k == '1') {
        if (tgstate == COINED) tgstate = PLAYING;
    } else if (k == 13) {
        mouse_b |= 1; // fire
    } else if (k == 'z') {
        mouse_x -= 16; // zx/'
    } else if (k == 'x') {
        mouse_x += 16; // zx/'
    } else if (k == '/') {
        mouse_y += 16; // zx/'
    } else if (k == '\'') {
        mouse_y -= 16; // zx/'
    } else if (k == 9) {
        mouse_b |= 2; // shields (needs special handling if done by keyboard)
    } else if (k == ' ') {
        if (tgstate == PLAYING) {
          credits -= 1;
          if (credits == '0') tgstate = PREGAME; else tgstate = COINED;
        }
    }
}

