#define fp2_14 /* SIGNED */ short /* we use a restricted range (2.14) fixed point for trig support */
#define ONE_POINT_ZERO ((fp2_14)(1<<14))
#define FAKE100 128 /* We use 128 instead of 100 in 'percentage' calculations to avoid dividing by 100 */
                    /* (we also use fake degrees consisting of 256 angles in a circle - keeps variables
                       within a single byte which would otherwise require two bytes.) */
static int TargetFPS, ObservedFPS; // Maybe these should be unsigned chars?
static int millis_time;
static unsigned char vectrex_beam_intensity;
static unsigned char Pressed_Key;
static short mouse_x, mouse_y;
static unsigned char mouse_fire, mouse_shields;
static int ScreenWidth, ScreenHeight;
static short intro_x;
static short intro_y;
static void playTailgunner(void);


// Using the cmoc compiler implies using the Vectrex and the Vectrex's in-built graphics...
//
// Note that the vectrex has a code limitation of about 51K bytes and a data limitation of
// about 800 bytes of RAM. This is for both statics and stack.   Const arrays are held in
// rom (if suitably declared) so only count towards code size.

#define short int
#define inline
#define const

static void graphics_initialisation(int *argc, char **argv)
{
  // Change these to the aspect ratio for the Vectrex screen *only after* I get it working again in OpenGL mode!
  ScreenHeight = 800;
  ScreenWidth = 1024;
}

static void Main_loop(void) {
  for (;;) {
    playTailgunner();
    // wait for vsync-equivalent
    millis_time += 1000/TargetFPS; // quick hack...
  }
}

static void Display_Vectrex_vector(short from_x, short from_y, short to_x, short to_y)
{
  // Use hardware.
}

static void Display_Vectrex_point(short x, short y)
{
}

static void startAsyncSound(unsigned char which)
{
  // Start playing sound 'which' asynchronously
}

static short mouseX(void)
{
  // There's also a mouse_x variable that is looked at directly.  Code could be improved in this area.
  // A call to the procedure can be used to fetch the mouse value and then the cached value
  // can be used until the next frame.  I.e. one call to mouseX(),mouseY() could be made in the main loop
  // Currently, mouseX() and mouseY() are called only once per frame to get the target for the crosshairs.
  return 0; // joystick X
}

static short mouseY(void)
{
  return 0; // joystick X
}

static short millis(void) // 1/1000 of a second units as an integer. Needs work.
{                // Hopefully, never used for any period longer than 32s
                 // which will allow us to use an unsigned short
  // http://www.playvectrex.com/designit/lecture/UP09.HTM

  // in fact we might as well remove the need for real time altogether and
  // just increment a counter on each vsync (sorry, I mean 'beam settling period')

  return millis_time; // Granularity is not critical.
}

static short rseed;
static short urandom16(void)
{
  return (rseed = (rseed * 2053) + 13849);
}


// what follows should be fairly portable I hope...

// All these arrays should be possible to embed in the ROM:

// encoded strings for printing to screen:
static const unsigned char SCORE_STR96[] = {5, 29, 13, 25, 28, 15};
static const unsigned char HIGH_SCORE_STR96[] = {10, 18,19,17,18,0,29,13,25,28,15};
static const unsigned char CREDITS_STR128[] = {8, 13,28,15,14,19,30,29,0};
static const unsigned char INSERT_COIN_STR128[] = {11, 19,24,29,15,28,30,0,13,25,19,24};
static const unsigned char PRESS_START_STR128[] = {11, 26,28,15,29,29,0,29,30,11,28,30};

//
// 18.14 fixed point sine and cosine tables when used with 32 bit ints.  However when used
// with 16 bit ints, these are 2.14 tables :-(  This may not matter as long as all we ever
// do is multiply shorts by fixed point values in the range -1.0 to 1.0 - which is the case
// when multiplying by sin() or cos() of an angle.

//
// These table assume that there are 256 angles in a circle.  We don't use 360 anywhere in this code.
//

static const fp2_14 sine[65] =
  {
    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 // => 1.0
  };


// I've checked the results (see asin_test.c) and they're close enough for game work...

// Can halve the size of this table easily by using rotational symmetry around 0.
// (same as first part of sin table optimisation)

static const fp2_14 asintab[129] = { // multiply by 360 for degrees, by 256 for angles
                                     // (float)asintab[x]*(360.0)/(1<<14) would be directly equivalent to scratch's asin
                                     // but we want asintab[x]>>6 for 256 angles
  -4096,  -3770,  -3634,  -3530,  -3442,  -3365,  -3294,  -3230,
  -3169,  -3112,  -3058,  -3007,  -2958,  -2911,  -2865,  -2821,
  -2778,  -2737,  -2696,  -2657,  -2619,  -2581,  -2544,  -2508,
  -2473,  -2438,  -2404,  -2371,  -2338,  -2306,  -2274,  -2242,
  -2211,  -2181,  -2151,  -2121,  -2091,  -2062,  -2033,  -2005,
  -1977,  -1949,  -1921,  -1894,  -1867,  -1840,  -1813,  -1787,
  -1760,  -1734,  -1709,  -1683,  -1658,  -1632,  -1607,  -1583,
  -1558,  -1533,  -1509,  -1485,  -1460,  -1436,  -1413,  -1389,
  -1365,  -1342,  -1319,  -1295,  -1272,  -1249,  -1226,  -1203,
  -1181,  -1158,  -1136,  -1113,  -1091,  -1069,  -1046,  -1024,
  -1002,   -980,   -959,   -937,   -915,   -893,   -872,   -850,
  -829,   -807,   -786,   -765,   -743,   -722,   -701,   -680,
  -659,   -638,   -617,   -596,   -575,   -554,   -533,   -513,
  -492,   -471,   -450,   -430,   -409,   -389,   -368,   -347,
  -327,   -306,   -286,   -265,   -245,   -224,   -204,   -183,
  -163,   -143,   -122,   -102,    -82,    -61,    -41,    -20,
  0,                                                             // X=-X: antisymmetric
};

static const unsigned char tg_intro_placard[] = {
  163,63,   93,63,    93,63,    93,193,   93,193,   163,193,  163,193,  163,63,
  155,71,   101,71,   101,71,   101,185,  101,185,  155,185,  155,185,  155,71,
  149,109,  149,93,   131,101,  149,101,  149,119,  131,113,  131,125,  149,119,
  140,122,  140,116,  149,137,  131,137,  131,149,  149,149,  131,161,  131,149,
  107,167,  125,167,  125,179,  125,167,  116,179,  125,179,  107,179,  116,176,
  116,167,  116,179,  107,149,  107,161,  125,149,  107,149,  125,161,  125,149,
  116,149,  116,155,  107,143,  125,143,  125,131,  107,143,  107,131,  125,131,
  107,125,  125,125,  125,113,  107,125,  107,113,  125,113,  107,107,  125,107,
  107,95,   107,107,  125,95,   107,95,   114,86,   114,92,   107,89,   114,89, 
  107,77,   107,89,   125,77,   107,77,   125,89,   125,77,
};


// font data:

static const unsigned char font_points[] = {
 /* 0 */ 0xae, 0xe4, 0x40, 0x0a,
 /* 1 */ 0x2c,
 /* 2 */ 0xae, 0xe9, 0x95, 0x50, 0x04,
 /* 3 */ 0x04, 0x79, 0x4e, 0xea,
 /* 4 */ 0xa5, 0x59, 0x72, // originally 0x5a, 0x59, 0x4e,
 /* 5 */ 0x04, 0x49, 0x95, 0x5a, 0xae,
 /* 6 */ 0x59, 0x94, 0x40, 0x0a,
 /* 7 */ 0x2e, 0xea,
 /* 8 */ 0x04, 0xae, 0x59, 0xe4, 0xa0,
 /* 9 */ 0xea, 0xa5, 0x59, 0xe4,
 /* A */ 0x4e, 0xae, 0x59, 0xa0,
 /* B */ 0x69, 0x04, 0x4e, 0xea, 0xb1,
 /* C */ 0xae, 0x40, 0x0a,
 /* D */ 0xad, 0xd9, 0x93, 0x30, 0x0a, // originally 0xae, 0xe4, 0x40, 0x1b, (note fewer segments - changes length and index tables)
 /* E */ 0x40, 0x0a, 0x57, 0xae,
 /* F */ 0x0a, 0x57, 0xae,
 /* G */ 0x79, 0x94, 0x40, 0x0a, 0xae,
 /* H */ 0x0a, 0x59, 0x4e,
 /* I */ 0x2c,
 /* J */ 0x50, 0x03, 0x3d, 0xbe,
 /* K */ 0x0a, 0xe5, 0x54,
 /* L */ 0x0a, 0x04,
 /* M */ 0x0a, 0xa7, 0x7e, 0xe4,
 /* N */ 0x0a, 0xa4, 0x4e,
 /* O */ 0xae, 0xe4, 0x40, 0x0a,
 /* P */ 0x0a, 0xae, 0xe9, 0x95,
 /* Q */ 0x0a, 0xae, 0xe4, 0x40, 0x48,
 /* R */ 0x48, 0x59, 0x9e, 0xea, 0xa0,
 /* S */ 0x04, 0x49, 0x95, 0x5a, 0xae,
 /* T */ 0xae, 0xc2,
 /* U */ 0x0a, 0x04, 0x4e,
 /* V */ 0xa2, 0x2e,
 /* W */ 0xa1, 0x17, 0x73, 0x3e,
 /* X */ 0x0e, 0xa4,
 /* Y */ 0x27, 0x7a, 0x7e,
 /* Z */ 0xae, 0xe0, 0x04,
};

static const unsigned char font_length[] = {
 /* 0 */ 4, /* 1 */ 1, /* 2 */ 5, /* 3 */ 4, /* 4 */ 3, /* 5 */ 5,
 /* 6 */ 4, /* 7 */ 2, /* 8 */ 5, /* 9 */ 4, /* A */ 4, /* B */ 5,
 /* C */ 3, /* D */ 5, /* E */ 4, /* F */ 3, /* G */ 5, /* H */ 3,
 /* I */ 1, /* J */ 4, /* K */ 3, /* L */ 2, /* M */ 4, /* N */ 3,
 /* O */ 4, /* P */ 4, /* Q */ 5, /* R */ 5, /* S */ 5, /* T */ 2,
 /* U */ 3, /* V */ 2, /* W */ 4, /* X */ 2, /* Y */ 3, /* Z */ 3,
};

static const unsigned char font_index[] = {
  /* 0 */ 0,    /* 1 */ 4,    /* 2 */ 5,    /* 3 */ 10,   /* 4 */ 14,
  /* 5 */ 17,   /* 6 */ 22,   /* 7 */ 26,   /* 8 */ 28,   /* 9 */ 33,
  /* A */ 37,   /* B */ 41,   /* C */ 46,   /* D */ 49,   /* E */ 54,
  /* F */ 58,   /* G */ 61,   /* H */ 66,   /* I */ 69,   /* J */ 70,
  /* K */ 74,   /* L */ 77,   /* M */ 79,   /* N */ 83,   /* O */ 86,
  /* P */ 90,   /* Q */ 94,   /* R */ 99,   /* S */ 104,  /* T */ 109,
  /* U */ 111,  /* V */ 114,  /* W */ 116,  /* X */ 120,  /* Y */ 122,
  /* Z */ 125,
};

static const 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 const unsigned char shieldvec[26] = {
  0x6, 0x5,  0x7, 0x4,  0x4, 0x1,  0x1, 0x4,
  0x2, 0x5,  0x4, 0x3,  0x6, 0x1,  0x7, 0x2,
  0x4, 0x5,  0x1, 0x2,  0x2, 0x1,  0x4, 0x3,
  0x6, 0x5,
};

static const unsigned char ShipMeshPointers[] = {
  0,  20,  20+21,  20+21+17,  20+21+17+23
};

// const char ship_mesh_length[] = { 20,  21,  17,  23 };

// Ships hand-coded by Peter Hirschberger.  Great work, Peter!
// Original data was given to me as doubles (eg lines of 1.5 length) but I've scaled everything to ints
static const char ShipMesh[(20+21+17+23)*2*3] = {  // tweaked values -129 => -128 and tweaked 150 => 127
        /* FROM */          /* TO */
       0, 0, -100,       40, 0, -60,
       40, 0, -60,        0, 0, -20,
        0, 0, -20,      -40, 0, -60,
      -40, 0, -60,       0, 0, -100,
       0, 0, -100,       0, 40, -60,
       0, 40, -60,        0, 0, -20,
        0, 0, -20,      0, -40, -60,
      0, -40, -60,       0, 0, -100,
        0, 0, -20,       30, 0, 120,
        0, 0, -20,      20, 20, 120,
        0, 0, -20,     -20, 20, 120,
        0, 0, -20,      -30, 0, 120,
       30, 0, 120,      20, 20, 120,
      20, 20, 120,     -20, 20, 120,
     -20, 20, 120,      -30, 0, 120,
      -30, 0, 120,       30, 0, 120,
       30, 0, 120,     100, -40, 80,
     100, -40, 80,        0, 0, -20,
      -30, 0, 120,    -100, -40, 80,
    -100, -40, 80,        0, 0, -20,
        0, 0, -90,       45, 0, -60,
       45, 0, -60,        45, 0, 60,
        45, 0, 60,         0, 0, 90,
         0, 0, 90,       -45, 0, 60,
       -45, 0, 60,      -45, 0, -60,
      -45, 0, -60,        0, 0, -90,
        45, 0, 60,       60, 24, 60,
       60, 24, 60,      60, 24, 127,
      60, 24, 127,       45, 0, 127,
       45, 0, 127,        45, 0, 60,
       -45, 0, 60,      -60, 24, 60,
      -60, 24, 60,     -60, 24, 127,
     -60, 24, 127,      -45, 0, 127,
      -45, 0, 127,       -45, 0, 60,
       -45, 0, 60,    -60, -27, -90,
    -60, -27, -90,     0, -34, -128,
     0, -34, -128,     60, -27, -90,
     60, -27, -90,        45, 0, 60,
        0, 0, -90,     0, -34, -128,
       45, 0, -60,     60, -27, -90,
      -45, 0, -60,    -60, -27, -90,
       0, 0, -100,       60, 0, -40,
       60, 0, -40,         0, 0, 20,
         0, 0, 20,      -60, 0, -40,
      -60, 0, -40,       0, 0, -100,
       0, 0, -100,       0, 40, -40,
       0, 40, -40,         0, 0, 20,
       60, 0, -40,       0, 40, -40,
       0, 40, -40,      -60, 0, -40,
         0, 0, 20,       0, 80, 100,
       0, 80, 100,       0, 40, 100,
       0, 40, 100,         0, 0, 20,
         0, 0, 20,    100, -40, 100,
    100, -40, 100,     40, -20, 100,
     40, -20, 100,         0, 0, 20,
         0, 0, 20,   -100, -40, 100,
   -100, -40, 100,    -40, -20, 100,
    -40, -20, 100,         0, 0, 20,
        0, 2, -92,       30, 2, -70,
       30, 2, -70,       0, 10, -20,
       0, 10, -20,      -30, 2, -70,
      -30, 2, -70,        0, 2, -92,
        0, 2, -92,      0, -25, -45,
      0, -25, -45,       0, 10, -20,
       30, 2, -70,      22, 15, -60,
      -30, 2, -70,     -22, 15, -60,
       30, 2, -70,      0, -25, -45,
      -30, 2, -70,      0, -25, -45,
      22, 15, -60,     -22, 15, -60,
      22, 15, -60,       0, 10, -20,
     -22, 15, -60,       0, 10, -20,
       30, 2, -70,      -30, 2, -70,
       0, 10, -20,    -20, -10, 110,
       0, 10, -20,     20, -10, 110,
    -20, -10, 110,     20, -10, 110,
      0, -25, -45,    -20, -10, 110,
      0, -25, -45,     20, -10, 110,
       0, 10, -20,      -90, 20, 90,
      -90, 20, 90,    -20, -10, 110,
       0, 10, -20,       90, 20, 90,
       90, 20, 90,     20, -10, 110,
};


//################################################################################################

// This program was originally written in the block programming system "Scratch" for kids.
// It was mostly translated into C by a program I hacked up, then the C was cleaned up by hand
// until it was compilable.  However there were some Scratch language features which didn't
// translate easily, so the section below implements some code to fill the gaps.

// NOTE!!!  Scratch arrays are based at 1 rather than C's arrays at 0, so wherever we have
// code that indexes an array which came from Scratch, it may need to be tweaked to subtract
// one from the index.  This needs to be done, as the alternative is ugly: which is, to add
// an extra element to all arrays at item[0] - and never use it.  Nope, not a good plan.


// There are some aspects of the way the game is coded which I do not like and which I would
// like to revisit once it is ported and running again.  A major one is that the coordinate
// space was originally set up with 0,0 at the center.  That simplified a *lot* of the calculations
// (eg the star field, and any perspective) *but* I changed it to use an all-positive coordinate
// system when I started running into problems such as the signs of angles, which were simplified
// when everything was made positive with 0,0 being one corner of the screen.  At some point I
// think I would like to revert to the original central origin - even if it complicates the
// code in some areas, I think conceptually it is easier to understand, and if I remember
// rightly, it is also how the Vectrex's vector hardware works...



#define TRUE 1
#define FALSE 0

#define UP 0
#define DOWN 1
static unsigned char pen;
static void putPenDown(void)
{
  pen = DOWN;
}

static void putPenUp(void)
{
  pen = UP;
}

static short last_x, last_y;
static void gotoxy(short x, short y)
{
  // since 'down' is 1, we can just do "if (pen) ..." and when we do,
  // can make this a boolean 'pen_down'.  However I'd rather just remove
  // all traces of Scratch's pen up/pen down
  if (pen == DOWN) Display_Vectrex_vector(last_x, last_y, x, y);
  last_x = x; last_y = y;
}

static inline unsigned short irand(short max) {
  // returns int 0..max-1
  return urandom16() % max; // probably not very random, but this is a video game, not crypto
}

static short pickrandom(short low, short high)
{
  // We'll just assume that high > low...
  return (short)irand(high-low+1)+low;  // pick a random integer between low and high inclusive.
}

static void setintensity(unsigned char brightness)
{
  vectrex_beam_intensity = brightness&127; // &127 is redundant if there are no errors in the code :-/
}

//
// Square root is used in two places in this program.  One is in a distance test and can
// be avoided by squaring the other side of the comparison instead; unfortunately the
// other is in the code that computes the attackers' flightpaths and cannot be avoided.
//
// I haven't tried it on the vectrex yet and don't know if this will be fast enough,
// if not there are probably some simpler approximate-square-root algorithms I can
// use instead.  But lets see how well the obvious one works first...
//

static unsigned short isqrt(unsigned short op)
{
  unsigned short res = 0U;
  unsigned short one = 1U<<14U; // The second-to-top bit is set
  unsigned short limit;

  // "one" starts at the highest power of four <= than the argument.
  while (one > op) one >>= 2;

  while (one != 0) {
    if (op >= (limit = res + one)) {
      op -= limit;
      res += one;
      res += one;
    }
    res >>= 1;
    one >>= 2;
  }
  return res;
}

// to whom it may concern ... :-)  entirely irrelevant to this implementation, but nevertheless
// some interesting discussion: https://stackoverflow.com/questions/3380628/fast-arc-cos-algorithm
// (also remember identities to allow the sin code to be used for cos, and asin code to be used for acos;
//  and depending on the speed of your divide, maybe also define tan in terms of sin & cos.)

// Could use asin(x) = atan2 (x, sqrt ((1.0 + x) * (1.0 - x)))
// except our atan2 is pretty slow and isqrt is worse, and then we have muldiv to worry about!

static short asin_of_fp2_14(fp2_14 angle) // parameter is consistent with the result of fp2_14_sin(angle)
{                                       // angle inherited from Scratch implementation is in range -1.0 to 1.0
                                        // so looks pretty standard.  Results appear to be in the -180 to 180
                                        // range (in the Scratch implementation) although since this is
                                        // used on ships facing the player, the actual range is more around
                                        // +/-10 and I don't think ever outside +/-90.  I mention all this
                                        // because, although a simple table-driven solution is obvious,
                                        // it is only an approximation and perhaps a larger table might
                                        // be used within the active range to get better precision?
                                        // Or get an initial value with a table and refine with
                                        // a couple of iterations of code?  We already have a high expense
                                        // for the atan implementation.
                                        //
                                        // Our reimplementation should used 256 angles rather than degrees
                                        // NOTE that I am non-standardly returning -128..128 rather than 0..255 -
                                        // this is to be compatible with the Scratch implementation

  if (angle > 128) return -(asintab[256-angle]>>6);  // or use abs instead of '-'.  first half of table is all negative or 0.
  return asintab[angle]>>6; // returns fp equivalent to -1.0 to 1.0 I think

  // ( Multiply fp result by 360 for degrees, or by 256 for our angles 
  //   - conflate the scaling with the shift to convert down from fp2_14 to int)

  // It would make a lot more sense to move the >>6 to the point of call, where it can cancel out things like the *256 above,
  // and avoid any loss of precision due to the shifting.  Not that accuracy is so important here.

  // This whole area is getting a bit messy, but at least I'm starting to get
  // the last of the missing components in place.  May end up doing a lot of side-by-side comparison with the Scratch code
  // to debug some of this... (after removing all the randoms and making it all repeatable...)
}


// Although lookup tables work pretty well in video games for sin/asin where
// the input domain is very restricted, the technique doesn't work so well
// for atan.  A better solution is to use an approximation, such as this one.

// (or maybe a cruder approximation as in https://dspguru.com/dsp/tricks/fixed-point-atan2-with-self-normalization/ ? )

// This code is taken from https://geekshavefeelings.com/posts/fixed-point-atan2
// I've taken that code and shoe-horned it in to an interface that returns values
// that are very close to the values that Scratch returns for the same parameters
// - at least when used in the context of this program!  I make no claims at all
// for it working in any other context.

// In scratch, the parameters are floats, whereas here they are short ints, so
// there's a small loss of precision there; and the results here are short ints
// too, so there's a second loss of precision.  Despite that, the results are
// pretty close (less than 2 degrees of absolute error) and definitely good enough
// for a video game.

// I leave most of the original comments intact.

/*
 * fxpt_atan2.c
 *
 * Copyright (C) 2012, Xo Wang
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is furnished to do
 * so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

/**
 * Negative absolute value. Used to avoid undefined behavior for most negative
 * integer (see C99 standard 7.20.6.1.2 and footnote 265 for the description of
 * abs/labs/llabs behavior).
 *
 * @param i 16-bit signed integer
 * @return negative absolute value of i; defined for all values of i
 */
static inline short s16_nabs(const short j) {
  return (j < 0 ? j : -j);
}

// I haven't checked this yet for signed parameters, so the places I'm using it
// all assume the parameters are unsigned and handle the negation separately.
// Rather hacky.  I'll revisit this after the code is running well.
unsigned short muldiv(unsigned short a, unsigned short b, unsigned short c)
{
  // replace this with asm and the MUL instruction at some point!

  unsigned short q = 0, r = 0, qn, rn;
  // from https://stackoverflow.com/questions/4144232/how-to-calculate-a-times-b-divided-by-c-only-using-32-bit-integer-types-even-i
  // ... doing it the hard way, without double precision of any kind, either in C or assembler...
  if (a < b) {
    qn = b / c;
    rn = b % c;
    while (a) {
      if (a&1) { q += qn; r += rn; if (r >= c) { q++; r -= c; } }
      a >>= 1; qn <<= 1; rn <<= 1; if (rn >= c) { qn++; rn -= c; }
    }
  } else {
    // the while loop should be over the smaller variable, for speed
    qn = a / c;
    rn = a % c;
    while (b) {
      if (b&1) { q += qn; r += rn; if (r >= c) { q++; r -= c; } }
      b >>= 1; qn <<= 1; rn <<= 1; if (rn >= c) { qn++; rn -= c; }
    }
  }
 return q; // also, r = (a * b) % c   (q = quotient, r = remainder)
}

static inline short q15_mul(const short j, const short k) {
  // I'll fix this later.  Use a flag, and s16_nabs() to unnegate the negative params
  // check it works using the atan_test.c program - trivial mistakes here can really break things
  if (j<0 && k<0) return (short)muldiv(-j,-k,1<<15);
  if (j<0) return -(short)muldiv(-j,k,1<<15);
  if (k<0) return -(short)muldiv(j,-k,1<<15);
  return (short)muldiv(j, k, 1<<15);
}

static inline short q15_div(const short numer, const short denom) {
  // ditto
  if (numer<0 && denom<0) return (short)muldiv(-numer, 1<<15,  -denom);
  if (numer<0) return -(short)muldiv(-numer, 1<<15,  denom);
  if (denom<0) return -(short)muldiv(numer, 1<<15,  -denom);
  return (short)muldiv(numer, 1<<15,  denom);
}

/**
 * 16-bit fixed point four-quadrant arctangent. Given some Cartesian vector
 * (x, y), find the angle subtended by the vector and the positive x-axis.
 *
 * The value returned is in units of 1/65536ths of one turn. This allows the use
 * of the full 16-bit unsigned range to represent a turn. e.g. 0x0000 is 0
 * radians, 0x8000 is pi radians, and 0xFFFF is (65535 / 32768) * pi radians.
 *
 * Because the magnitude of the input vector does not change the angle it
 * represents, the inputs can be in any signed 16-bit fixed-point format.
 *
 * @param y y-coordinate in signed 16-bit
 * @param x x-coordinate in signed 16-bit
 * @return angle in (val / 32768) * pi radian increments from 0x0000 to 0xFFFF
 */
unsigned short fxpt_atan2(const short y, const short x) {
  if (x == y) { // x/y or y/x would return -1 since 1 isn't representable
    if (y > 0) { // 1/8
      return 8192;
    } else if (y < 0) { // 5/8
      return 40960;
    } else { // x = y = 0
      return 0;
    }
  }
  const short nabs_y = s16_nabs(y), nabs_x = s16_nabs(x);
  if (nabs_x < nabs_y) { // octants 1, 4, 5, 8
    const short y_over_x = q15_div(y, x);
    const short correction = q15_mul(/*q15_from_double(0.273 * M_1_PI)*/2847, s16_nabs(y_over_x));
    const short unrotated = q15_mul(/*q15_from_double(0.25 + 0.273 * M_1_PI)*/11039 + correction, y_over_x);
    if (x > 0) { // octants 1, 8
      return unrotated;
    } else { // octants 4, 5
      return 32768 + unrotated;
    }
  } else { // octants 2, 3, 6, 7
    const short x_over_y = q15_div(x, y);
    const short correction = q15_mul(/*q15_from_double(0.273 * M_1_PI)*/2847, s16_nabs(x_over_y));
    const short unrotated = q15_mul(/*q15_from_double(0.25 + 0.273 * M_1_PI)*/11039 + correction, x_over_y);
    if (y > 0) { // octants 2, 3
      return 16384 - unrotated;
    } else { // octants 6, 7
      return 49152 - unrotated;
    }
  }
}


static short Scratch_atan2_fakedegrees(short x, short y)  // substitute for atan2(x/y) but using 256 angles in a circle rather than 360 degrees
{
  short result = ((fxpt_atan2(x, y)>>8)-128);

  if (result <= -64) {
    return result + 128;
  } else if (result >= 64) {
    return result - 128;
  }
  return result;

}


static unsigned char numdigits(short num)
{
  unsigned char result = 0U;
  do {
    num = num / 10;
    result++;
  } while (num > 0);
  return result;
}

//################################################################################################
// This is the machine-translated Scratch code after manual massaging.

// We don't yet have a sound system for the vectrex...
#define SOUND_BOUNCE 1
#define SOUND_EXPLODE 2
#define SOUND_LASER 3
#define SOUND_SHIELD 4
#define SOUND_STAR 5
#define SOUND_COIN 6

// Internal game state
#define PREGAME    0 /* Loops over Tumbling banner, demo wave, then INSERT COIN */
#define TUMBLING   1
#define DEMOWAVE   2
#define INSERTCOIN 3
#define COINED     4 /* loops demo wave while showing PRESS START */
#define PLAYING    5
#define PASSING    6

/* 3d rendering */

// Remember that changing FOCAL_LENGTH changes the scale it is drawn at as well!
// Maybe not the proper name for this parameter?

//#define FOCAL_LENGTH 256
//#define FOCAL_LENGTH 512  /* as long as compilers map /512 to >>9, we're golden... */
#define FOCAL_LENGTH 1024

/* ships */

// Accel is less than 1.0 but velocities are integers.  Currently I believe
// I am adding accel to a velocity but that is not going to work because the
// fixed point delta V will truncate to 0.  Need to look at the acceleration
// code carefully and restructure it in a way that will work, eg perhaps by
// multiplying by 1.1 instead of adding .1 to a value?

// We can't just change velocities to fixed point with the current implementation
// because there are not enough bits in fp2_14!

#define SHIP_ACCEL_FP div_int_by_int_returning_fp2_14(1, 10)
#define SHIP_MAXVEL 20

#define SHIP_SPAWN_Z 2000

#define SHIPSPERWAVE 3U

#define SHIP_TYPE 0U
#define SHIP_X 1U
#define SHIP_Y 2U
#define SHIP_Z 3U
#define SHIP_X_ROTATION 4U
#define SHIP_Y_ROTATION 5U
#define SHIP_Z_ROTATION 6U
#define SHIP_SCALE_FP 7U
#define SHIP_SPEED 8U
#define SHIP_TIME 9U
#define SHIP_TARGET_X 10U
#define SHIP_TARGET_Y 11U
#define SHIP_TARGET_Z 12U
#define SHIP_STATUS 13U  // Adding this so that when we destroy a ship, we can merely flag it as dead,
                         // unlike the current code from Scratch which deleted an entry from the middle
                         // of a list of ships - something that is rather expensive to do on this platform
                         // ... whereas checking a flag in a loop that is only over 3 ships at most is cheap

                         // Statuses may include ATTACKING, EXPLODING, DEAD ? 

#define SHIP_RECORD_LENGTH 14U /* number of fields in the ship struct.  If we made it 16 could use a shift when multiplying is needed */
                           // ship array currently taking up 14*2*3 bytes (84) 
#define SHIP_STATUS_ALIVE 0U     // attacking the player and on-screen
                                 // *OR* bounced off shields and falling into the distance...
#define SHIP_STATUS_EXPLODING 1U // on screen, but rendered with (to be written) explosion code, and no missile hit checks applied
#define SHIP_STATUS_DEAD 2U      // shot, exploded, and off-screen
#define SHIP_STATUS_PASSED 3U    // not on screen, but not dead.  But dead as far as the code is concerned.

static unsigned char Credits; // already hacked to handle > 9 coins.  If anyone inserts 128 virtual coins they get all they deserve.

static short intro_z; // intro_x and intro_y are effectively constant (modulo changes to screen geometry on the fly...)
static unsigned char intro_rot; // 256 'degrees' in a circle...

static short passing_mode_timer; // a timer that counts down from 129 to 0 - a frame counter

static short flashing_intro_timer; // a timer that counts down from 129 to 0 - a frame counter
static unsigned char global_flashing_intensity;
static unsigned char flashingtexttimer;

static short hide_intro_timer; // I think hide was a timer I planned to use in attract mode, to switch from the
                   // tumbling intro placard to a demo attack wave.

static short Mode; // there's a small state machine to denote where in the game we are.
                   // See 'internal game state' above...  Note that Mode is *only* modified
                   // within the playTailgunner() procedure, to keep things easy to find

static short mouseDownPrev;

static unsigned char laserid;
static short lasert;
static short lastfiringtime;

static short HighScore;
static short LastScore;
static short Score;

static short UsingShields;
static short Shields;
static short shieldsegment;
static short shieldsubticksleft;

static short ShipsPassedThisWave;
static short ShipsPassedTotal;

static fp2_14 cosX;  // used in calculating ship rotations
static fp2_14 cosY;
static fp2_14 cosZ;
static fp2_14 sinX;
static fp2_14 sinY;
static fp2_14 sinZ;

static short stopintroflag; // Kill the attract screen when a coin is inserted.  0 allows, 2 stops.  1 - not sure why there are multi values!!???

//static short targettingx; // Used in 'renderlasers' - Scratch version.  May or may not end up in final code.
//static short targettingy;

static short textx;  // coordinates where text is displayed
static short texty;
static short textz;

static short tgi;
static short tgx;
static short tgy;
static short tgz;

static unsigned char nextwaveshiptype;


// 'this_*' variables are valid after the corresponding GET call gets the data from the Ships[] array and places them here
// - they're valid for the current ship, temporarily.  Writing to them changes nothing unless they're written back with a SET call...

static unsigned char this_status; // is ship alive or dead?
static fp2_14 this_scale_fp; // Surprisingly, this does not appear to be used at the moment!  Which is surprising considering how complex it has made the code!
                             // (when I say 'not used', I mean 'never altered' after the original assignment of 2.0 (which I tweaked to 1.999 for safety)
                             // so if I'm not changing it - why is it here????!
static short this_ship_time; // ship time, currently incremented once per frame, so not consistent with real time.  maybe recode later using millis?
static unsigned char this_model; // TO DO: !!! Careful checking needed!  Scratch used 1..4 - now in process of changing to 0..3 - NOT COMPLETED!!!! 
static short this_X;
static short this_Y;
static short this_Z;

static short this_vel;

static short this_X_destination;
static short this_Y_destination;
static short this_Z_destination;

static short this_X_rotation;
static short this_Y_rotation;
static short this_Z_rotation;

// used in steering:

// only used by Transform_point which is called by Draw_ship_at_rotated_scale
static short transformed_X;
static short transformed_Y;
static short transformed_Z;
static short XY;
static short XZ;
static short YX;
static short dirXZ;
static short dirY;

static short ships[SHIPSPERWAVE * SHIP_RECORD_LENGTH]; // max ships * size of ship record.
                                            // remember that hard coded offsets are based at 1
                                            // for Scratch arrays, so need to be replaced with
                                            // suitable-adjusted constants (which I think I've
                                            // done now)

static fp2_14 sinsymmetry(unsigned char angle128)
{
  // '>', not '>=', is a special case for sin(x)=1.0
  if (angle128 > 64U) return sine[128-angle128]; else return sine[angle128];
}

static fp2_14 fp2_14_sin(short angle256)
{
  if ((unsigned char)angle256 >= 128U) return -sinsymmetry((unsigned char)(angle256-128)&127); else return sinsymmetry((unsigned char)angle256&127);
}

static fp2_14 fp2_14_cos(short angle256)
{
  return fp2_14_sin(angle256+64); // implicit &255 in these unsigned char parameters
}

static short mul_short_by_fp2_14_returning_short(short num, fp2_14 fp)
{
  short sign = 1;
  if (num < 0) { num = -num; sign = -sign; }
  if (fp < 0) { fp = -fp; sign = -sign; }
  return sign * muldiv(num, fp, ONE_POINT_ZERO);  // clearly could be a lot more efficient, but this has to do for now while developing
}

static fp2_14 div_int_by_int_returning_fp2_14(short num, short den) // divides 2 ints and returns fp result
{
  // should we range-test the results of these? Even if only on the OpenGL version where I can output some diagnostic to a console?
  short sign = 1;
  if (num < 0) { num = -num; sign = -sign; }
  if (den < 0) { den = -den; sign = -sign; }
  return sign * muldiv(num, ONE_POINT_ZERO, den);
}

#define PROJECTILE_RECORD_LENGTH 3 /* If we added a padding field to make this 4, could use a shift when multiplying is needed */
#define PROJECTILE_FIELD_LASERT 0
#define PROJECTILE_FIELD_X 1
#define PROJECTILE_FIELD_Y 2

#define MAX_LASERS 2 /* haven't looked at this closely yet, needs rewrite anyway... */
//static unsigned char projectile_next;
//static short projectiles[MAX_LASERS * PROJECTILE_RECORD_LENGTH]; // 3 fields in this array of structs.  (.lasert, .x, .y)

static inline void draw_tg_intro_with_rolling_rotation(short from_x, short from_y, short to_x, short to_y, unsigned char rot)
{
  if ((from_x != to_x) || (from_y != to_y)) {
    short PackX, PackY, PackZ;
    short screenx; // 3d coordinates are convered to screen coordinates when displaying vectors
    short screeny;

    tgx = from_x; tgy = from_y; tgz = 0;

    PackX = to_x; PackY = to_y; PackZ = 0;
    PackZ += mul_short_by_fp2_14_returning_short(PackY, fp2_14_sin (rot));
    PackY = mul_short_by_fp2_14_returning_short(PackY, fp2_14_cos (rot));

    PackZ += intro_z;
    // should I change this to use Get_screenpos() ???
    screenx = ((FOCAL_LENGTH * PackX) / (FOCAL_LENGTH + PackZ));
    screeny = ((FOCAL_LENGTH * PackY) / (FOCAL_LENGTH + PackZ));
    gotoxy (intro_x + screenx, intro_y + screeny);

    tgz += mul_short_by_fp2_14_returning_short(tgy, fp2_14_sin (rot));
    tgy = mul_short_by_fp2_14_returning_short(tgy, fp2_14_cos (rot));

    tgz += intro_z;
    // ditto Get_screenpos() ???
    screenx = ((FOCAL_LENGTH * tgx) / (FOCAL_LENGTH + tgz));
    screeny = ((FOCAL_LENGTH * tgy) / (FOCAL_LENGTH + tgz));

    // ultimately would prefer to get rid of pen up/down/gotoxy and call vectrex code directly
    putPenDown (); gotoxy (intro_x + screenx, intro_y + screeny); putPenUp ();
  }
}

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

static void starplot(short x, short y) // At some point add distance fading to the starfield
{
  // x,y based on coords with screen center at 0,0
  // While debugging, draw a cross instead of a star so I can find them! (58yr old eyesight :-( )
  //Display_Vectrex_vector((ScreenWidth>>1)+x-10, (ScreenHeight>>1)+y-10, (ScreenWidth>>1)+x+10, (ScreenHeight>>1)+y+10);
  //Display_Vectrex_vector((ScreenWidth>>1)+x-10, (ScreenHeight>>1)+y+10, (ScreenWidth>>1)+x+10, (ScreenHeight>>1)+y-10);
  Display_Vectrex_point((ScreenWidth>>1)+x, (ScreenHeight>>1)+y);
}


static unsigned char /* boolean */ clipoff(short x, short y)
{
  /* Stars never reach center of screen */
  // size of rectangle still hard-coded
  return ( -0x50 <= x && x < 0x50 && -0x40 <= y && y < 0x40);
}

/* The screen is divided up into 8 sectors:

          \ | /
           \|/
          --+--
           /|\
          / | \

   We want 16 stars on screen at all times.

   At any time, each sector should have 2 stars active in it.

   At least, that was the original algorithm...  now it's less
   strict, and assigns the initial starting position to anywhere
   on one of the four sides with equal probability as to which
   side.  Not guaranteed to be as visually appealing but a little
   cheaper to implement.


 */

static short px[16], py[16];

static unsigned short star_x1(unsigned char idx) {
  unsigned char xtype = (idx>>2)&3, ytype = idx&3;

  idx = xtype ^ ytype; // four types of star trail ...
  if (idx == 0) { // left edge
    return -(ScreenWidth>>1);
  } else if (idx == 2) { // right edge
    return (ScreenWidth>>1)-1;
  } else { // top or bottom edges
    return pickrandom(-(ScreenWidth>>1), (ScreenWidth>>1)-1);
  }
}

static unsigned short star_y1(unsigned char idx) {
  unsigned char xtype = (idx>>2)&3, ytype = idx&3;

  idx = xtype ^ ytype; // four types of star trail ...
  if (idx == 1) { // top edge
    return (ScreenHeight>>1)-1; // right edge
  } else if (idx == 3) { // bottom edge
    return -(ScreenHeight>>1);
  } else {
    return pickrandom(-(ScreenHeight>>1), (ScreenHeight>>1)-1);
  }
}

// The code below replaces an earlier implementation
// which avoided multiplies and divides by using Bresenham's algorithm
// to plot the track of the stars.  Turned out to be complete overkill!
// - this is much simpler and still avoids multiplies and divides!

// The only slight downside of this is that it is (currently) locked to
// moving the stars once every frame, whereas we'd rather move them once
// every fixed clock interval.  However there is no reason that I can't
// modify it to do that, and I probably will once I've finished with
// other areas of the code that are demanding more attention.  In the
// meantime, this has reclaimed *huge* amounts of RAM... :-)

static void move_star(unsigned char starno)
{
  short x = px[starno], y=py[starno];

  // coordinate space is 0,0 center!
  x = ((x<<6)-x)>>6; y = ((y<<6)-y)>>6;  // move stars closer to the center, ie towards 0
  if (clipoff(x,y)) {
    x = star_x1(starno); y = star_y1(starno); // reset when they disappear
  }
  px[starno] = x; py[starno] = y;

}

static inline void plot_star(unsigned int starno)
{
  starplot(px[starno],py[starno]);
}

static inline void init_stars(void)
{
  unsigned char i, star;

  for (star = 0U; star < 16U; star++) {
    px[star] = star_x1(star); py[star] = star_y1(star);
    for (i=0; i < star*13; i++) move_star(star); // skew the starting points (QUICK HACK)
  }
}

static inline void stars(unsigned char speed)
{
  unsigned char i, star;
  for (star = 0U; star < 16U; star++) {
    for (i = 0U; i < speed; i++) move_star(star);
    plot_star(star);
  }
}

// NOTE NOTE NOTE!!!  This code came from a bitmapped system, so the algorithm
// isn't especially well suited to the Vectrex's vector drawing hardware!
// It really needs to be rethought from scratch.
// Talking of Scratch, the other version of the code used sprites so it
// wasn't much better; however the general mechanism may be more appropriate,
// especially if we simulate sprites and scaling with the Vectrex hardware support
//

// Can I get away with something as simple as what I did above for the stars..?

static short firex[2];
static short firey[2];

int abs(int x)
{
  return (((x)>=0)?(x):-(x));
}

int sgn(int x)
{
  return ((x<0)?-1:((x>0)?1:0));
}

static unsigned char /* boolean */ plot_fire(short lr, short x1, short y1, short x2, short y2) // lr = 0,1 for left/right laser
{
/* returns true if drawn to end of vector, false if should call again (co-routine hack) */

// TO DO: when left and right beams meet, this is the point where we check
// to see if we have hit a ship.  So need to store the XY location and light
// up a flag.  The ships themselves can check to see if they've been hit,
// if the point intersects a line *or* is contained within a face.
// (This problem is already handled in the Scratch version of the code...)

  short 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;

  // instead of drawing every individual 'pixel' we could note the
  // start and end points, then draw a single vector between them
  // after the calculations are complete.
  // HOWEVER it is very likely that a simple proportion calculation
  // with the firing x,y and target x,y is likely to be faster than
  // this all-integer bresenham, even with the overhead of multiplying
  // and dividing.  But there's only one way to know for sure and that
  // is to implement both and compare... :-(

  Display_Vectrex_point(px, py);

  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) {
        Display_Vectrex_point(px, py);
        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) {
        Display_Vectrex_point(px, py);
        firex[lr] = px; firey[lr] = py;
      }
      if (i == 50) {firex[lr] = px; firey[lr] = py;}
    }
  }
  return(i <= 30);
}

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

// shields now modified to scale according to screen dimensions.  Previous implementation was fixed size.
static inline void draw_shields(void)
{
  /* add rotation and scaling to simulate approach.  Should recode shields as X/Y/Z for simpler manipulation. */
  unsigned char i;
  if (pickrandom (0, 3) > 0) {
    // deliberate flicker, should go with electrical crackling noise.  Might skip this in the Vectrex version?
    setintensity((unsigned char)pickrandom (80, 127));
    for (i = 0U; i < 12U; i++) {
      // recalculate using shieldvec[] treated as 8th's of the screen width and height
      Display_Vectrex_vector(((shieldvec[i*2]*ScreenWidth)/8), // +0x20 originally had an offset here for some reason
                             (shieldvec[i*2+1]*ScreenHeight)/8,
                             ((shieldvec[i*2+2]*ScreenWidth)/8), // +0x20
                             (shieldvec[i*2+3]*ScreenHeight)/8
                            );
    }
    setintensity(110);
  }
}

static inline void draw_crosshair(short x, short y)
{
  unsigned char i;
  for (i = 0U; i < 10U; i++) {
    // will need to tweak x,y origins to suit Vectrex screen.  Maybe use relative vectors instead...
    Display_Vectrex_vector(x + (crosshair[i*4]) - 0x80,   y + crosshair[i*4+1] - 0x80,
                           x + (crosshair[i*4+2]) - 0x80, y + crosshair[i*4+3] - 0x80);
  }
}

static inline void drawtgintro(void) {
  unsigned char objects;

  if (stopintroflag == 2) return;

  if (fp2_14_cos (intro_rot) > 0) { // are we looking at the front of the tumbling intro placard?
    objects = ((sizeof (tg_intro_placard) / sizeof (tg_intro_placard[0])) / 4); // yes - draw rectangles and text together
  } else {
    objects = 8; // no - just draw the rectangles
  }

  {unsigned char i; short tgfrom_x, tgfrom_y, tgto_x, tgto_y;
    tgi = 0;
    for (i = 0; i < objects; i++) {
      tgfrom_x = tg_intro_placard[tgi++]-128;
      tgfrom_y = tg_intro_placard[tgi++]-128;
      tgto_x = tg_intro_placard[tgi++]-128;
      tgto_y = tg_intro_placard[tgi++]-128;
      // somewhere the x,y,z coordinates got a bit messed up...
      draw_tg_intro_with_rolling_rotation (tgfrom_y, tgfrom_x, tgto_y, tgto_x, intro_rot);
    }
  }
  // restore intensity to 110 after drawing flashing intro placard
  setintensity(110);
}

static inline void Playintro(void)
{
  intro_z = 206; intro_rot = 64; //90; // not sure about Z.  still to test the approach of the placard.
  flashing_intro_timer = 0; hide_intro_timer = 0;
  stopintroflag = 0;
}

static inline void initialise_ship(unsigned char id, unsigned char model_type, short x, short y, short z, short Xrot, short Yrot, short Zrot, short scale_fp)
{
  unsigned char ship_next = id/*0..3*/ * SHIP_RECORD_LENGTH;

  /* Set up all the fields in the ship struct */
  ships[ship_next++] = model_type;  // (was model 1 to 4, now model 0 to 3)    SHIP_TYPE 0U
  ships[ship_next++] = x;     // SHIP_X 1U
  ships[ship_next++] = y;     // SHIP_Y 2U
  ships[ship_next++] = z;     // SHIP_Z 3U
  ships[ship_next++] = Xrot;  // SHIP_X_ROTATION 4U
  ships[ship_next++] = Yrot;  // SHIP_Y_ROTATION 5U
  ships[ship_next++] = Zrot;  // SHIP_Z_ROTATION 6U
  ships[ship_next++] = scale_fp; // (see comment by this_scale_fp declaration)    SHIP_SCALE_FP 7U
  ships[ship_next++] = ((SHIP_MAXVEL * pickrandom (80, 120)) / 100); // integer, range roughly 16:24?    SHIP_SPEED 8U
  ships[ship_next++] = 0;     // SHIP_TIME 9U
  ships[ship_next++] = pickrandom (-400, 400); // SHIP_TARGET_X 10U
  ships[ship_next++] = pickrandom (-300, 300); // SHIP_TARGET_Y 11U
  ships[ship_next++] = z;                      // SHIP_TARGET_Z 12U
    // Initial phase is with ships viewed from above, flying in 2D in the plane Z=max depth,
    // hence why target z is initially same as ship.z
    // Later, they turn and head towards the viewer at z=0.
  ships[ship_next++] = SHIP_STATUS_ALIVE;      // SHIP_STATUS 13U
}

static inline void debug_ship(unsigned char id)
{
  short model_type, x, y, z, Xrot, Yrot, Zrot, scale_fp, speed, shiptime, tgx, tgy, tgz, status;
  short ship_next = id/*0..3*/ * SHIP_RECORD_LENGTH;

  /* Set up all the fields in the ship struct */
  model_type = ships[ship_next++];  // (was model 1 to 4, now model 0 to 3)    SHIP_TYPE 0U
  x = ships[ship_next++];     // SHIP_X 1U
  y = ships[ship_next++];     // SHIP_Y 2U
  z = ships[ship_next++];     // SHIP_Z 3U
  Xrot = ships[ship_next++];  // SHIP_X_ROTATION 4U
  Yrot = ships[ship_next++];  // SHIP_Y_ROTATION 5U
  Zrot = ships[ship_next++];  // SHIP_Z_ROTATION 6U
  scale_fp = ships[ship_next++]; // (see comment by this_scale_fp declaration)    SHIP_SCALE_FP 7U
  speed = ships[ship_next++];    // integer, range roughly 16:24?    SHIP_SPEED 8U
  shiptime = ships[ship_next++]; // SHIP_TIME 9U
  tgx = ships[ship_next++];   // SHIP_TARGET_X 10U
  tgy = ships[ship_next++];   // SHIP_TARGET_Y 11U
  tgz = ships[ship_next++];   // SHIP_TARGET_Z 12U
  status = ships[ship_next++];      // SHIP_STATUS 13U


}

static void Start_attack_wave(void)
{
  short randomedge, X, Y, Z = SHIP_SPAWN_Z, Zrot, scale_fp = div_int_by_int_returning_fp2_14(1999, 5000);// Orig:1000 DEBUG!!!!
                                                             // 2 may not be in range! trying 1.999 instead...
  unsigned char i;

  for (i = 0; i < SHIPSPERWAVE; i++) {
    randomedge = pickrandom (1, 3); // sides and top only.
    if (randomedge == 1) {
      X = -900; Y = pickrandom (-500, 500); Zrot = 64; // 90 degrees
    } else if (randomedge == 2) {
      X = pickrandom (-500, 500); Y = 700; Zrot = 0;
    } else {
      X = 900; Y = pickrandom (-500, 500); Zrot = 128+64; // -90 degrees
    }
    initialise_ship (i, nextwaveshiptype, X, Y, Z, 128+64 /*270 degrees*/, 128 /*180 degrees*/, Zrot, scale_fp);
  }

  // TO DO: !! watch out for a Scratch off-by-1 here.  this was 1..4, change to 0..3? (watch for init to ship #1)
  // nextwaveshiptype = ((nextwaveshiptype % 4) + 1);
  nextwaveshiptype = (nextwaveshiptype + 1)&3;

  ShipsPassedThisWave = 0;
}

static inline void Start_new_game (void) // ie a whole coin's worth, not just 3 ships
{
  stopintroflag = 2; // Kill banner
  Score = 0;

  Shields = 80;
  ShipsPassedTotal = 0;

  nextwaveshiptype = 0;

  //projectile_next = 0;
  Start_attack_wave ();

  // TO DO: This is probably the place to reset millis to 0!  Because on a 16 bit system,
  // it is going to wrap, well within the length of a game session.
  lastfiringtime = millis();
}

static inline void draw_text_line(short fromx, short fromy, short tox, short toy)
{
  Display_Vectrex_vector(fromx + textx + ScreenWidth/2, fromy + texty + ScreenHeight/2, tox + textx + ScreenWidth/2, toy + texty + ScreenHeight/2);
 // coords based on 0,0 screen center?
}

static inline void segment(unsigned char coords, short textscale)
{
  short fromx;
  short fromy;
  short tox;
  short toy;
  short from, to;

  // remember to use shifts instead of divides (unless GCC does it for us?)
  to = coords & 15;
  tox = ((to % 5) *  4 * textscale) / 128;
  toy = ((to / 5) * 12 * textscale) / 128;

  from = coords >> 4;
  fromx = ((from % 5) *  4 * textscale) / 128;
  fromy = ((from / 5) * 12 * textscale) / 128;

  draw_text_line (fromx, fromy, tox, toy);
}

static void ch(unsigned char charno, short textscale)
{
  if (charno /* > 0U */) { // 0 is blank space
    {unsigned char i;
      // If the array indexes, and calculations, in the for loop test comparison are recalculated on every loop, perhaps
      // we should pre-calculate them before entering the loop, and use the cached values?
      for (i = font_index[charno-1]; i < font_index[charno-1]+font_length[charno-1]; i++) {
        segment(font_points[i], textscale);
      }
    }
  }
  textx += (24 * textscale) / 128;
}

// DO NOT INLINE OR MACROFY THESE TWO!
static void ch96(unsigned char charno)
{
  ch(charno, 96);
}

static void ch96str(const unsigned char *text)
{unsigned char i;
  for (i = 1; i <= text[0]; i++) ch96(text[i]);
}

static void ch128(unsigned char charno)
{
  ch(charno, 128);
}

static void ch128str(const unsigned char *text)
{unsigned char i;
  for (i = 1; i <= text[0]; i++) ch128(text[i]);
}

static void Drawnum(short num, unsigned char size) // Up to 32767, no negative numbers.  (Not needed)
{
  unsigned char i;
  short tens = 10000;

  i = 4U;
  for (;;) { // handle leading-zero suppression
    if ((num >= tens) || (i == 0)) {
      // ch(1+...) because Digits are encoded as 1..10, not 0..9
      ch((unsigned char)(1+(num/tens)%10), size); // i==0 for single digit 0 case
    }
    tens /= 10;
    if (i == 0) return;
    i -= 1;
  }
}


// DO NOT inline or turn these into macros.  Keep the code size small.
static void Drawsmallnum(short num)
{
  Drawnum(num, 96);
}

static void Drawbignum(short num)
{
  Drawnum(num, 128);
}

static void Drawbignumat(short num, short x, short y)
{
  textx = x; texty = y;
  Drawbignum(num);
}

static void Drawsmallnumat(short num, short x, short y)
{
  textx = x; texty = y;
  Drawsmallnum(num);
}

static inline void Draw_shields_left(void)
{
  Drawbignumat (Shields, -10, 140);
}

static inline void Draw_score(void)
{
  Drawbignumat (Score, -220, 130);
}

static inline void Draw_highscore(void)
{
  textx = 42;
  texty = 130;
  // HIGH_SCORE
  ch96str(HIGH_SCORE_STR96);
                                                  // remember, char width is (24 * textscale) / 128
  Drawsmallnumat (HighScore, (/* WAS: 208*/204 - ((24 * 96 * (numdigits (HighScore)-1)) / 128)), 105);
}

static inline void InsertCoin(void) // Not yet used
{
  textx = -135;
  texty = -120;
  setintensity(global_flashing_intensity);
  ch128str(INSERT_COIN_STR128);
  setintensity(110);
}

static inline void Draw_ShipsPassedTotal(void) {
  Drawbignumat (ShipsPassedTotal, 200, 130);
}

static inline void Press_start(void)
{
  textx = -120;
  texty = -10;
  setintensity(global_flashing_intensity);
  ch128str(PRESS_START_STR128);
  setintensity(110);
}

static inline void Draw_credits(void)
{
  textx = -100;
  texty = -120;
  ch128str(CREDITS_STR128);
  if (Credits > 9U) {
    ch128 (10);  // display any more than 9 as 9. (This is what many arcade machines did in real life)
  } else {
    ch128 (Credits + 1);
  }
}

static inline void Draw_last_game_score(void)
{
  textx = -220;
  texty = 130;
  ch96str(SCORE_STR96);
  Drawsmallnumat (LastScore, -220, 105);
}

static inline void Shield(void)
{
  if (shieldsubticksleft == 0) {
    startAsyncSound(SOUND_SHIELD);
      // Start sound only when shields start and repeat after appropriate delay
      // rather than multiple overlapping sounds that start on each frame tick!
    shieldsubticksleft = div_int_by_int_returning_fp2_14(10, 3); // was fp2_14_ceiling ((0.1 / 0.03333)); - real numbers not needed here anyway.  recode the timing stuff.
    Shields -= 1;
  } else {
    shieldsubticksleft -= 1;
  }
  draw_shields();
}

static void Get_ship_pos(unsigned char id)
{
  // can optimise by setting int idx = ((id * SHIP_RECORD_LENGTH) + SHIP_X)
  // and using ++idx twice
  // at some point it looks like I was moving from using this_X to this_X_Pos etc
  this_X = ships[((id * SHIP_RECORD_LENGTH) + SHIP_X)];
  this_Y = ships[((id * SHIP_RECORD_LENGTH) + SHIP_Y)];
  this_Z = ships[((id * SHIP_RECORD_LENGTH) + SHIP_Z)];
}

static void Get_ship_scale(unsigned char id)
{
  this_scale_fp = ships[((id * SHIP_RECORD_LENGTH) + SHIP_SCALE_FP)];
}

static void Get_ship_status(unsigned char id)
{
  this_status = (unsigned char)ships[((id * SHIP_RECORD_LENGTH) + SHIP_STATUS)];
}

static short screen_x; // 3d coordinates are convered to screen coordinates when displaying vectors
static short screen_y;

// inline this for speed rather than size?  (Since 6809 parameter passing
// has quite a high overhead...)
static void Get_screenpos(short x, short y, short z)
{
  screen_x = (FOCAL_LENGTH * x) / z; screen_y = (FOCAL_LENGTH * y) / z;

  // Wait - is this a bug?  Shouldn't it be:
  //   screen_x = (FOCAL_LENGTH * x) / (FOCAL_LENGTH + z);
  //   screen_y = (FOCAL_LENGTH * y) / (FOCAL_LENGTH + z);
  // If there is a problem with hit detection when the game is working,
  // this should be the first place to check...
}

static void Set_ship_status(unsigned char id, unsigned char NewStatus)
{
  ships[((id * SHIP_RECORD_LENGTH) + SHIP_STATUS)] = NewStatus;
}

static inline void ship_hit_check(short x, short y)
{
  unsigned char this_ = 0;
  for (this_ = 0U; this_ < SHIPSPERWAVE; this_++) {
    Get_ship_status (this_);
    if (this_status == SHIP_STATUS_ALIVE) {

      Get_ship_pos (this_);
      Get_ship_scale (this_);
      Get_screenpos (this_X, this_Y, this_Z); // -> screen_x,y  (was previously -> this_X,Y)
      if (
           (unsigned short)(isqrt ((((x - screen_x) * (x - screen_x)) + ((y - screen_y) * (y - screen_y))))
          <
           (unsigned short)(mul_short_by_fp2_14_returning_short(15 * (FOCAL_LENGTH / this_Z), this_scale_fp) ))  // TO DO: fix floating point hack
         ) {
        // rewrite above as below?  Also use temporaries to optimise redundancy.
        // if (
        //      (x - this_X) * (x - this_X) + (y - this_Y) * (y - this_Y)
        //     <
        //      ((15 * scale) * (FOCAL_LENGTH / this_Z))) * ((15 * scale) * (FOCAL_LENGTH / this_Z)))
        //     ) {
        Score += 5;
        startAsyncSound(SOUND_EXPLODE);
        Set_ship_status (this_, SHIP_STATUS_EXPLODING);
      }
    }
    this_ += 1;
  }
}

static void Transform_point(short x, short y, short z)
{
  // mul_short_by_fp2_14_returning_short
  XY = (mul_short_by_fp2_14_returning_short(y, cosX) - mul_short_by_fp2_14_returning_short(z, sinX));
  XZ = (mul_short_by_fp2_14_returning_short(y, sinX) + mul_short_by_fp2_14_returning_short(z, cosX));
  transformed_Z = (mul_short_by_fp2_14_returning_short(XZ, cosY) - mul_short_by_fp2_14_returning_short(x, sinY));
  YX = (mul_short_by_fp2_14_returning_short(XZ, sinY) + mul_short_by_fp2_14_returning_short(x, cosY));
  transformed_X = (mul_short_by_fp2_14_returning_short(YX, cosZ) - mul_short_by_fp2_14_returning_short(XY, sinZ));
  transformed_Y = (mul_short_by_fp2_14_returning_short(YX, sinZ) + mul_short_by_fp2_14_returning_short(XY, cosZ));
}

static inline void Change_ship_pos_by(unsigned char id, short dx, short dy, short dz)
{
  // can optimise with an index and increments
  ships[((id * SHIP_RECORD_LENGTH) + SHIP_X)] += dx;
  ships[((id * SHIP_RECORD_LENGTH) + SHIP_Y)] += dy;
  ships[((id * SHIP_RECORD_LENGTH) + SHIP_Z)] += dz;
}

static inline void Change_ship_rotation_by(unsigned char id, short this_X_rotation, short this_Y_rotation, short this_Z_rotation)
{
  // can optimise with an index and increments
  ships[((id * SHIP_RECORD_LENGTH) + SHIP_X_ROTATION)] += this_X_rotation;
  ships[((id * SHIP_RECORD_LENGTH) + SHIP_Y_ROTATION)] += this_Y_rotation;
  ships[((id * SHIP_RECORD_LENGTH) + SHIP_Z_ROTATION)] += this_Z_rotation;
}

static inline void Get_ship_rotation(unsigned char id)
{
  // can optimise with an index and increments
  this_X_rotation = ships[((id * SHIP_RECORD_LENGTH) + SHIP_X_ROTATION)];
  this_Y_rotation = ships[((id * SHIP_RECORD_LENGTH) + SHIP_Y_ROTATION)];
  this_Z_rotation = ships[((id * SHIP_RECORD_LENGTH) + SHIP_Z_ROTATION)];
}

static void Get_ship_time(unsigned char id) // called twice so don't inline currently
{
  this_ship_time = ships[((id * SHIP_RECORD_LENGTH) + SHIP_TIME)];
}

static void Get_ship_destination(unsigned char id)
{
  // can optimise with an index and increments
  // I DO NOT KNOW WHY this_X ETC IS ASSIGNED HERE!
  /*this_X =*/ this_X_destination = ships[((id * SHIP_RECORD_LENGTH) + SHIP_TARGET_X)];
  /*this_Y =*/ this_Y_destination = ships[((id * SHIP_RECORD_LENGTH) + SHIP_TARGET_Y)];
  /*this_Z =*/ this_Z_destination = ships[((id * SHIP_RECORD_LENGTH) + SHIP_TARGET_Z)];
}

static inline void Change_ship_velocity_by(unsigned char id, fp2_14 accel) // accel is broken.  It might work better as a scaling factor rather than a delta-V
{
  // save index and reuse
  short this_speed = (id * SHIP_RECORD_LENGTH) + SHIP_SPEED;
  // TO DO: NO ACCELERATION FOR NOW!!!   ships[this_speed] += accel;
  // fp is in a restricted range 2.0 <= fp <= -2.0 at best.  So cannot add to an int - even if
  // implemented correctly, the fp value would truncate down to 0.  A possible solution
  // would be to multiply by something like 1.05 or 0.95 rather than adding a fixed value
  if (ships[this_speed] > SHIP_MAXVEL) ships[this_speed] = SHIP_MAXVEL;
}

static inline void Set_rotation_vars(short Xrot, short Yrot, short Zrot)
{
  sinX = fp2_14_sin (Xrot);
  cosX = fp2_14_cos (Xrot);
  sinY = fp2_14_sin (Yrot);
  cosY = fp2_14_cos (Yrot);
  sinZ = fp2_14_sin (Zrot);
  cosZ = fp2_14_cos (Zrot);
}


// This is never called with a ship id.  The id has been determined by the caller and all the 'this_*' variables
// set up in place already.  I.e. this code does not need to check the ship status to see if it is dead or alive
// - we know it is alive.

static inline void Draw_ship_at_rotated_scale(unsigned char model, short x, short y, short z, short Xrot, short Yrot, short Zrot, short scale_fp)
{
  unsigned char meshIndex; // shipmeshpointers are in range 0..82 so using a byte for the index is OK

  setintensity(85);

  // ship mesh array (of shorts) contains from_x,y,z and to_x,y,z

  Set_rotation_vars (Xrot, Yrot, Zrot);
  for (meshIndex = ShipMeshPointers[model]; meshIndex < ShipMeshPointers[model+1]; meshIndex++) {
                                                                           // +1 is the start of the *next* set of data (or past the array end)
                                                                           // We could have used ship_mesh_length instead
    // Transform_point updates XY, XZ, YX and this_X,Y,Z
                                                                                     // (already tweaked offsets from +1:+6 to +0:+5
                                                                                     //  to sort Scratch array base issue)
    Transform_point (mul_short_by_fp2_14_returning_short(ShipMesh[((meshIndex * 6) + 0)], scale_fp),
                     mul_short_by_fp2_14_returning_short(ShipMesh[((meshIndex * 6) + 1)], scale_fp),
                     mul_short_by_fp2_14_returning_short(ShipMesh[((meshIndex * 6) + 2)], scale_fp)); // -> transformed_X,Y,Z
    Get_screenpos (x + transformed_X, y + transformed_Y, z + transformed_Z); // -> screen_x,y  (was previously -> this_X,Y)
    // original: gotoxy (screen_x, screen_y);
    // currently being hacked:
    gotoxy (screen_x+ScreenWidth/2, screen_y+ScreenHeight/2);
    putPenDown ();

    Transform_point (mul_short_by_fp2_14_returning_short(ShipMesh[((meshIndex * 6) + 3)], scale_fp),
                     mul_short_by_fp2_14_returning_short(ShipMesh[((meshIndex * 6) + 4)], scale_fp),
                     mul_short_by_fp2_14_returning_short(ShipMesh[((meshIndex * 6) + 5)], scale_fp)); // -> transformed_X,Y,Z
    Get_screenpos (x + transformed_X, y + transformed_Y, z + transformed_Z); // -> screen_x,y  (was previously -> this_X,Y)
    // original: gotoxy (screen_x, screen_y); // TO DO: clip the ends of these vectors!
    // currently being hacked:
    gotoxy (screen_x+ScreenWidth/2, screen_y+ScreenHeight/2);
     // ultimately get rid of pen up/down/gotoxy and call vectrex code directly with from/to vectors?
    putPenUp ();
  }

  setintensity(110);
}

static void Change_ship_time_by(unsigned char id, short dt) /* maybe dt should be 8 bits */
{
  ships[((id * SHIP_RECORD_LENGTH) + SHIP_TIME)] += dt;
}

static void Get_ship_vel(unsigned char id)
{
  this_vel = ships[((id * SHIP_RECORD_LENGTH) + SHIP_SPEED)];
}

static void Apply_vel_to_ship_pos(unsigned char id)
{
  Get_ship_vel (id);
  Get_ship_rotation (id);
  Change_ship_pos_by (id,
		      mul_short_by_fp2_14_returning_short(mul_short_by_fp2_14_returning_short(this_vel, fp2_14_sin (this_Y_rotation)), fp2_14_cos (this_X_rotation)),
		      mul_short_by_fp2_14_returning_short(this_vel, fp2_14_sin (this_X_rotation)),
		      mul_short_by_fp2_14_returning_short(mul_short_by_fp2_14_returning_short(this_vel, fp2_14_cos (this_Y_rotation)), fp2_14_cos (this_X_rotation)));
}

static inline void Set_ship_posto(unsigned char id, short x, short y, short z) // Not used yet???
{
  // can optimise with an index and increments
  ships[((id * SHIP_RECORD_LENGTH) + SHIP_X)] = x;
  ships[((id * SHIP_RECORD_LENGTH) + SHIP_Y)] = y;
  ships[((id * SHIP_RECORD_LENGTH) + SHIP_Z)] = z;
}

static inline void Get_eigenvector_to(short dx, short dy, short dz)
{
  // this is the only place where we really need atan and asin :-(

  // WAS: dirXZ = (fp2_14_atan ((dx / dz)) + (128 * (dz < 0)));

  // TO DO (Well, new code is in place for the atan.  UNTESTED in context)
  //dirXZ = atan_of_fp2_14(dx / dz);  // not entirely sure what values to expect for the parameter

  dirXZ = Scratch_atan2_fakedegrees(dx, dz);  // not entirely sure what values to expect for the parameter

  if (dz < 0) dirXZ += 128;

  // TO DO: still to write asin
  dirY = asin_of_fp2_14( dy / isqrt(dx*dx + dy*dy + dz*dz) );  // The parameter should be an fp2_14 fixed point?
}

static inline void Set_ship_rotationto(unsigned char id, short this_X_rotation, short this_Y_rotation, short this_Z_rotation) // Not used yet?
{
  // can optimise with an index and increments
  ships[((id * SHIP_RECORD_LENGTH) + SHIP_X_ROTATION)] = this_X_rotation;
  ships[((id * SHIP_RECORD_LENGTH) + SHIP_Y_ROTATION)] = this_Y_rotation;
  ships[((id * SHIP_RECORD_LENGTH) + SHIP_Z_ROTATION)] = this_Z_rotation;
}

static inline void Rotate_ship_toward_by_fakepercent(unsigned char id, short x, short y, short z, short fakepercent /* or unsigned char? */)
{
  // If we use 0..127 (128) instead of 0..99 (100) then divides can be implemented
  // with a shift rather than an actual divide which doesn't exist on the 6809.
  Get_ship_pos (id);
  Get_eigenvector_to ((x - this_X), (y - this_Y), (z - this_Z));
  Get_ship_rotation (id);

  // &255 is really %256 which is the number of angles in a circle
  Change_ship_rotation_by (id,
                        (fakepercent * ((((dirY  - this_X_rotation) + 128) & 255) - 128)) / FAKE100,
                        (fakepercent * ((((dirXZ - this_Y_rotation) + 128) & 255) - 128)) / FAKE100,
                        (fakepercent * -this_Z_rotation) / FAKE100);
                        // since these parameters are shorts, not bytes, there is
                        // no need for MULDIV here.  16 bit arithmetic should be OK.
}

//static inline void Getlengthof(short x, short y, short z)
//{
//  distance = isqrt (((x * x) + ((y * y) + (z * z))));
//}

static void Head_ship_towards_destination(unsigned char id)
{
  Get_ship_pos (id);
  Get_ship_destination (id);
  //Getlengthof ((this_X_destination - this_X), (this_Y_destination - this_Y), (this_Z_destination - this_Z)); // result is placed in 'distance' and does not appear to be used???
  Rotate_ship_toward_by_fakepercent(id, this_X_destination, this_Y_destination, this_Z_destination, 16); // was 10%  Now 16/128 (1/8, or about 12%)

  // WHERE IS THE SHIP ACTUALLY MOVED?  IT IS ROTATED, BUT IS IT EVER MOVED FORWARD IN ITS DIRECTION OF FLIGHT???
}

static void Set_ship_velocityto(unsigned char id, short vel)
{
  ships[((id * SHIP_RECORD_LENGTH) + SHIP_SPEED)] = vel;
}

static void Set_ship_destinationto(unsigned char id, short x, short y, short z)
{
  // can optimise with an index and increments
  ships[((id * SHIP_RECORD_LENGTH) + SHIP_TARGET_X)] = x;
  ships[((id * SHIP_RECORD_LENGTH) + SHIP_TARGET_Y)] = y;
  ships[((id * SHIP_RECORD_LENGTH) + SHIP_TARGET_Z)] = z;
}

static inline void Update_ship(unsigned char id)
{
  Get_ship_pos (id);
  Apply_vel_to_ship_pos (id);   // MOVE THE SHIP

  Change_ship_velocity_by (id, SHIP_ACCEL_FP); // THIS IS BROKEN.  velocity is an int, accel is FP.  CANNOT ADD THEM, can only multiply.
  if (((this_Z < 100) && (UsingShields == 1))) { // 100 is near the screen where the notional shields are.  (lower Z is close, high Z is distant)
    startAsyncSound(SOUND_BOUNCE);
    Set_ship_destinationto (id,
                            pickrandom (-100, 100),
                            pickrandom (-100, 100),
                            (SHIP_SPAWN_Z + 1000));  // This is a point in the far far distance - the ship has bounced off
                                                     // our shields and is receding backwards into the distance
    Get_ship_vel (id);
    Set_ship_velocityto (id, -this_vel); // BoUnCe!
    Apply_vel_to_ship_pos (id);
    Head_ship_towards_destination (id);
    Change_ship_time_by (id, 1);  // so ship's time is effectively a frame count - not real time - and will mess up with different FPS's on other platforms
    Get_ship_pos (id);
  }
  if ((this_Z > (SHIP_SPAWN_Z + 500))) { // The only way Z can be greater than the spawn point is if the ship is falling backwards (ie bouncing off shields)
    Set_ship_status (id, SHIP_STATUS_DEAD); // so remove it from the field of play, same as if it had been shot
  } else {
    if ((this_Z < 30)) {
      Set_ship_status (id, SHIP_STATUS_PASSED); // effectively the same as DEAD
      ShipsPassedThisWave += 1; // TO DO: "whoosh" sound, scroll stars past fast for a short time.  Not entirely handled yet.
      ShipsPassedTotal += 1; // TO DO: "whoosh" sound, scroll stars past fast for a short time.  Not entirely handled yet.
    } else { // active ship chasing us.  default action here.
      Get_ship_destination (id);
      if (! ((((this_X == 0) && (this_Y == 0)) && (this_Z > SHIP_SPAWN_Z)))) {
      // can I rewrite the above as: if (this_X || this_Y || (this_Z <= SHIP_SPAWN_Z)) {
        Get_ship_time (id);
        if (((this_ship_time % 40) == 39)) {  // this is where this_ship_time is used - to change flightpath - should probably use millis so that it remains independent of FPS
          Get_ship_pos (id);
          if (this_Z < 1000) {
            Set_ship_destinationto (id, 0, 0, 0);
          } else {
            // Can I make this 2048 and use a shift?
            Set_ship_destinationto (id,
                                  pickrandom (((-300 * this_Z) / 2000), ((300 * this_Z) / 2000)),
                                  pickrandom (((-200 * this_Z) / 2000), ((200 * this_Z) / 2000)),
                                  -200);
          }
        }
      }
      Head_ship_towards_destination (id);
      Change_ship_time_by (id, 1);
    }
  }
}

static unsigned char /* boolean */ firing, firedone1, firedone2, reloaded; // to be worked on...
static inline void Render_lasers(void)
{
  // sort of works on OpenGL but really needs a major rewrite before using on the Vectrex...
  if (Pressed_Key == '\n' || Pressed_Key == '\r' || mouse_fire) {
    Pressed_Key = 0;
    if (!firing && reloaded) {
      /* initiate fire */;
      firing = TRUE; reloaded = FALSE;
      firedone1 = plot_fire(0, 40,40, mouse_x, mouse_y);
      firedone2 = plot_fire(1, 1000,40, mouse_x, mouse_y);
    } else {
      if (!firedone1) {
        firedone1 = plot_fire(0, firex[0], firey[0], mouse_x, mouse_y);
      }
      if (!firedone2) {
        firedone2 = plot_fire(1, firex[1], firey[1], mouse_x, mouse_y);
      }
      firing = (!firedone1 || !firedone2);
    }
  } else {
    if (firing) { /* Finish off the last shot */
      if (!firedone1) {
        firedone1 = plot_fire(0, firex[0], firey[0], mouse_x, mouse_y);
      }
      if (!firedone2) {
        firedone2 = plot_fire(1, firex[1], firey[1], mouse_x, mouse_y);
       }
      firing = !(firedone1 && firedone2);
    } else {reloaded = TRUE;}
  }
}

static inline void Get_ship_type(unsigned char id)
{
  this_model = (unsigned char)ships[((id * SHIP_RECORD_LENGTH) + SHIP_TYPE)]; // WHERE IS THIS SET????
}

static void Render_ships(void)
{
  unsigned char id = 0;
  for (id = 0; id < SHIPSPERWAVE; id++) {
    Get_ship_status (id);
    if (this_status == SHIP_STATUS_ALIVE) { // an exploding ship needs special handling that has not been written yet
      Get_ship_type (id);
      Get_ship_pos (id);
      Get_ship_rotation (id);
      Get_ship_scale (id);
      Draw_ship_at_rotated_scale (this_model, this_X, this_Y, this_Z, -this_X_rotation, this_Y_rotation, this_Z_rotation, this_scale_fp);
    }
  }
}

static void Update_ships(void)
{
  unsigned char shipid = 0;
  for (shipid = 0; shipid < SHIPSPERWAVE; shipid++) {
    Get_ship_status(shipid);
    if (this_status < SHIP_STATUS_DEAD) {
      Update_ship (shipid); // Is that the right test?  There are other options...
    }
  }
}

//static unsigned char DEBUGrot;
static void playTailgunner(void) { // This is the main workhorse that is executed to draw every frame

  // Emulate a hardware intensity pulsing by calculating a pulsed intensity value once
  // here per frame, then any text which pulses uses that value, so that everything on screen
  // pulses at the same rate and the same time.

  // works, but doesn't pulse the way I would like.  The other versions I've written looked nicer.
  global_flashing_intensity = (unsigned char)(95+(fp2_14_sin(flashingtexttimer)>>9)); /* added part is -32..32 */
  flashingtexttimer = flashingtexttimer + 8; // faster flashing rate

  // Can accept more coins at any time.
  if (Pressed_Key == '3') {
    Pressed_Key = 0;
    Credits += 1;
    startAsyncSound(SOUND_COIN);
    if (Mode < COINED) {
      Mode = COINED;  // However adding a coin during play etc does nothing *visible*
    }
  }

  if (Mode == PASSING) {
    stars(2); // stars pass more quickly in this case
  } else {
    stars(1); // always draw stars regardless of what else is happening
  }



  // Testing the components one by one...

  // WORKS: alphabet_test();
  // WORKS: Draw_shields_left();
  // WORKS: Draw_highscore();
  // WORKS: Draw_last_game_score();
  // WORKS: Draw_score();
  // WORKS: InsertCoin();
  // WORKS: Draw_ShipsPassedTotal();
  // WORKS: Press_start();
  // WORKS: Draw_credits();
  // WORKS: draw_shields();


  // The game state logic is now cleaned up a little bit, but this is still pretty verbose
  // and could be much improved.  However it is time to move on to handling the ships, so
  // improvements here are on hold until they become critical to the gameplay.

  if (Mode == PREGAME) {

    // Begin attract mode.

    // I'm not sure this is even needed now.  It might be useful as a place in the game loop
    // where we could do power-up initialisation, but currently that's done elsewhere and I
    // think I even switch the game state *back* into PREGAME at some point (I've forgotten why -
    // it happens at the end of a game when there are no coins left)
    // so doing one-time init stuff here would be a bad idea!

    // Also it's not really like power-up status because the high score needs to be retained

    Playintro();

    //Draw_ship_at_rotated_scale (0, 0, 0, 1000, DEBUGrot, DEBUGrot, DEBUGrot, ONE_POINT_ZERO);
    //DEBUGrot++;


    Mode = TUMBLING;

  } else if (Mode == TUMBLING) {

    // "Tailgunner" intro placard is tumbling through space, towards player.
    // Until it stops, the only thing that can happen is a coin being inserted
    Draw_highscore ();
    Draw_last_game_score (); // or not?

    if (flashing_intro_timer > 0) {
      flashing_intro_timer -= 1;
      setintensity(global_flashing_intensity);
      if (flashing_intro_timer == 0) {
        intro_rot = 64; // 90 degrees
        intro_z = 206;
        hide_intro_timer = 60; // Frames
      }
    }

    if (hide_intro_timer == 0) { // better use of the state machine could do away with this timer altogether

      drawtgintro (); // side-effect of resetting intensity to 110

      // It should tumble towards us, ie getting larger
      // then stop rotating when it hits the closes point/largest size,
      // at which point it pulses with a sine-wave intensity,
      // and then disappears to allow a demo attack wave to pass.
      // It does not yet do these things.
      // Once it does, improve it slightly over the original TG by having it
      // fade in from black in the distance to normal intensity once up close.

      if (flashing_intro_timer == 0) {
        intro_rot -= 2; // wraps around in 8-bit unsigned.  Can adjust rotation speed here.
        intro_z -= 1;
        if (intro_z < -199) { // this constant may be wrong after the switch from 360 to 256 angles per circle,
                              // however it seems to be working so I'll leave it for now...
          if (stopintroflag == 1) { // NEVER SET TO 1 ANYWHERE SO THIS WILL NEVER HAPPEN!!!
            return;
          }
          flashing_intro_timer = 129;// was 181 - must have been half a circle plus 1.
          intro_rot = 0;
        }
      }
    } else {
      setintensity(110); // undo change of intensity for flashing intro placard which was never drawn...
      hide_intro_timer -= 1; // this is executed on each frame so the timer is variable with the FPS :-(  Needs work
      if (hide_intro_timer == 0) {
	Start_new_game (); // this is a fake game.  It's like starting a game BUT the user has no inputs.
        Mode = DEMOWAVE;
      }
    }

  } else if (Mode == DEMOWAVE) {

    // part of Attract mode.  Ships fly towards you, but no cursor showing,
    // no response to firing or shields buttons

    // This mode lasts as long as a wave - once the ships pass you, we switch to INSERTCOIN

    // Mind you, until I get the ship stuff working again, there is no demo wave...

    Update_ships ();
    Render_ships ();
    Draw_highscore ();
    Draw_last_game_score ();

  } else if (Mode == INSERTCOIN) {

    // "Insert Coin" prompt on screen.
    // After a few flashes of INSERTCOIN, we switch back to TUMBLING...

    Draw_highscore ();
    Draw_last_game_score ();
    InsertCoin();

  } else if (Mode == COINED) {

    // Shows "Press START".  Can accept more coins.  Does not show placard or demo wave.
    // Encourage the player to stop wasting time now they have paid...
    Draw_highscore ();
    Draw_last_game_score ();

    if (Credits /* > 0U */) {
      stopintroflag = 2; // Kill banner
      Draw_credits ();
      Press_start ();

      if (Pressed_Key == '1') {
        Pressed_Key = 0;
        Credits -= 1;
	Start_new_game();
        Mode = PLAYING;
          // This is the only place a new game can start.  We do not automatically start a new game
          // after a game finishes, even if there is credit available, i.e a player cannot advance
          // to a higher level by continuing to play solely because they keep feeding more coins
          // in - the high score is the high score on one coin!
      }

    } else {
      // Shouldn't happen.  If we're coined, we have credits.
    }

  } else if (Mode == PLAYING) {
    // Playing.  Shows crosshair.  Responds to firing, shields.  Can accept more coins (silently)
    draw_crosshair(mouseX(), mouseY());

    if (((Pressed_Key == ' ') || (Pressed_Key == '\t') || mouse_shields) && (Shields > 0)) {
      Pressed_Key = 0;
      UsingShields = 1;
    } else {
      UsingShields = 0;
    }

    /* from the 'shields' sprite... */
    if (UsingShields == 1) { // slowly removing redundancies from merging the two programs
      Shield();
    } else {
      shieldsubticksleft = 0;
    }

    Update_ships ();

    if (Score > HighScore) HighScore = Score;
    Render_ships ();
    Render_lasers ();

    Draw_score ();
    Draw_ShipsPassedTotal ();
    Draw_shields_left ();

    // Is this wave over?
    if (   (unsigned char)ships[((0 * SHIP_RECORD_LENGTH) + SHIP_STATUS)] >= SHIP_STATUS_DEAD
	&& (unsigned char)ships[((1 * SHIP_RECORD_LENGTH) + SHIP_STATUS)] >= SHIP_STATUS_DEAD
	&& (unsigned char)ships[((2 * SHIP_RECORD_LENGTH) + SHIP_STATUS)] >= SHIP_STATUS_DEAD ) {
      // Question: do we do the 'passing' thing if we shot all 3 ships or only when one got by us?
      // In other words, do I need some more code to handle the end of a wave in other circumstances
      if (ShipsPassedThisWave > 0) {
        passing_mode_timer = 200;
        Mode = PASSING; // and set a timer ...
      } else {
        if (ShipsPassedTotal < 10) { // You can keep playing on this coin until 10 ships get past you!
          Start_attack_wave ();
          Mode = PLAYING;
        } else {
          if (Credits /* > 0U */) { // No!  Don't start automatically - let the user click 'start'...
            Mode = COINED;
          } else {
            Mode = PREGAME;  // Or TUMBLING????
            LastScore = Score;
          }
        }
      }
    }

  } else if (Mode == PASSING) {
    // A wave is complete.  Stars whoosh by at double speed *IF* any attackers got past you.
    // Can't do anything until a couple of seconds later, when next wave starts or game is over. (no coins)

    if (passing_mode_timer > 0) {
      passing_mode_timer -= 1;
      if (passing_mode_timer == 0) { // passing-mode is over
        if (ShipsPassedTotal < 10) { // You can keep playing on this coin until 10 ships get past you!
          Start_attack_wave ();
          Mode = PLAYING;
        } else {
          if (Credits /* > 0U */) { // No!  Don't start automatically - let the user click 'start'...
            Mode = COINED;
          } else {
            Mode = PREGAME;  // Or TUMBLING????
            LastScore = Score;
          }
        }
      }
    }
  } else {
    // should not happen.  If it does, take steps during development to find out why and either fix
    // it properly or bodge it here by hackily resetting global variables ;-)  In production games
    // this MUST NOT break the game or lose coins.  (Defensive programming!)
  }
  debug_ship(0);debug_ship(1);debug_ship(2);
}

static void pre_graphics_initialisation(void)
{

  // NOTE!!!  These initialisations are all done one-time-only when the program is started.
  // Since the statics in a C program are all initialised to 0 in a single operation, there
  // is no need to explicitly initialise anything that would be set to 0 here.  I have
  // removed all those and marked them with "//0:"

  //0: Pressed_Key = 0; // no key pressed (non-Vectrex implementations)
  //0: millis_time = 0;
  //0: started = 0;

  //0: firing = 0; firedone1 = 0; firedone2 = 0;
  reloaded = 1; // to be worked on...

  //0: last_x = 0; last_y = 0;

  //0: pen = UP;
  vectrex_beam_intensity = 110U;
  //0: intro_rot = 0U; // 256 'degrees' in a circle...

  //0: flashingtexttimer = 0;

  //0: mouseDownPrev = 0;
  //0: lastfiringtime = millis();

  /* shields */
  //0: shieldsegment = 0;
  //0: shieldsubticksleft = 0;

  //0: nextwaveshiptype = 0;

  //0: Credits = 0;
  //0: Mode = PREGAME;
  ObservedFPS = TargetFPS = 30; // Target for this platform

  //0: Score = HighScore = LastScore = 0;
}

static void post_graphics_initialisation(void)
{
  intro_x = ScreenWidth/2; intro_y = ScreenHeight/2; 
  putPenUp ();
  init_stars();
}

int main (int argc, char **argv)
{

  pre_graphics_initialisation();

  graphics_initialisation(&argc, argv);

  post_graphics_initialisation();

  Main_loop();

  return(0);
}
