//
// OK, I've done as much as I can do on this code for the moment and plan to take
// a few days off from it to clear my head.  This is based on earlier code that has been
// through at least two translations into other programming languages, with the
// result that it's a bit piecemeal in places and not as well structured as perhaps
// it ought to be.

// The most recent version of this code before this conversion back into C was
// in a language called Scratch (from scratch.mit.edu) and that code worked reasonably
// well - certainly well enough to be a really good starting point for a more
// vector-perfect clone of the game.  HOWEVER Scratch was an interpreted system
// using 96 bit floats for everything, and to run on the Vectrex I had to recode
// a *lot* of maths to be 16 bit - as well as moving from using geometry based
// on 360 angles in a circle to a scheme where we use 256 angles in a circle in
// order to use 8-bit data and a processor lacking a divide instruction!

// So somewhere in that conversion, the code has broken.  I'll be quite frank
// and admit that my math skills are nowhere near as good as my programming
// skills, and that I'm finding it quite hard to get my head around the remaining
// problems which I strongly suspect are in the matrix manipulation area.  It's probably
// quite a simple issue that is keeping the code from running properly, but I can't
// see it, and at this point I would really appreciate a fresh set of eyes on the problem.

// Something to watch out for is my conversions of sin, cos, asin and atan from
// Scratch into C.  I added some diagnostics to the Scratch version of the game
// and made sure that things like the signs of these functions were correct for
// the quadrants where angles can exist, but there's a possibility that this is
// only true for the quadrants that were ever exercised by the running code - for
// example spaceships never flew backwards or upsides down so it is not impossible
// that if they did, the results of those functions *might* have the wrong sign.
// I don't know.  Could be the problem is elsewhere entirely, I'm just pointing
// out issues I know about that could be relevant.

// If anyone would like to help out with this game, the help will be gratefully
// received and of course fully credited.

// This source file compiles under linux using gcc and requires Open GL to run.
// I strongly suspect it will also run easily under Windows, and all the machine-
// dependent parts are in one place so converting to X, SDL, or Allegro for
// example ought to be trivial.  The use of a Vectrex and the 6809 processor is
// not required at this point in order to debug the game play, and indeed I have
// no idea whether this code will run on the 6809 at this stage of development.

// The Scratch code, for comparison, can be viewed at
//   https://scratch.mit.edu/projects/33089150/#editor
// and the game played either there or at this alternative system:
//   https://sulfurous.aau.at/versions/v0.86/html/app.html?id=33089150&turbo=false&full-screen=true&aspect-x=4&aspect-y=3&resolution-x=&resolution-y=

// Please post any progress in the thread at:
//   http://vectorgaming.proboards.com/thread/2003/working-on-tailgunner-again
// (or mail gtoal@gtoal.com)

// Thanks!!!

// Graham

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


// Thanks to Dan Sunday who wrote the original - this uses some of his techniques and
// a few of his vector arrays. (Specifically the font, crosshairs and shields)
// Thanks to peter@peterhirschberg.com who recreated the ship vectors by hand/eye
// Thanks to ProdigyZeta7 on Scratch.mit.edu who wrote the eigenvector code.


// Currently, no library dependencies when not generating graphics. Not even stdio.h :-)
// The portable part of this code does not use any long, float, or double variables -
// it assumes a 16/8 bit platform and is pure 100% stand-alone C.  Even things that
// would be significantly faster in assembler or by using extensions on other platforms
// such as 'long long' to implement a fused multiply+divide with extended intermediate
// results, are implemented using a portable C solution.


// I am in the process of porting this software, and not all of it is expected to work
// right now.  Specifically the ship display and associated ship movement is incomplete -
// the final missing code to be added was fixed point replacements for Scratch's asin and
// atan.  They are now written, but the code that calls them is untested.  Unfortunately
// this program has so many moving parts that I had to get a *lot* of new code in place
// before I could even start testing some areas, which is not good engineering design.
// It is now going to take me some time to break things down into components and get the
// ship display and then ship movement working again.


// Most of the other components have been individually tested and work to some extent.
// Some items (specifically missile firing and the starfield implementation) are based
// on raster code and could probably be simplified *a lot* - which I intend to do, but
// not until after everything else about the game works correctly.

// The plan for development is:
//
//  1. Get the portable version working under OpenGL
//  2. Try running on Vectrex emulator
//  3. Replace inefficient modules with better portable C versions
//  4. If code doesn't fit or doesn't run fast enough,
//        replace modules with Vectrex-specific versions that
//           take advantage of the vector hardware
//           or are written in 6809 assembler
//  5. Run on the Vectrex!
//
// I will undoubtedly be looking for help from the Vectrex homebrew community once
// we get to stage 2.  Until then, it's mostly on me (unless ProdigyZeta7 is available
// to help out - he's the only other person at the moment who already understands the code)

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

// The first section of this code is the machine-dependent graphics.  The implementation
// is targeted at the Vectrex graphics console which is a 8/16 bit system running on a 6809,
// however it is being compiled with support for OpenGL on 32 bit systems for quicker development.

// I am hoping to get some help from Vectrex enthusiasts, and if you are a volunteer looking
// at this code, the relevant section follows "#ifdef VECTREX_CMOC" below.  I assume we'll be
// using the cmoc compiler, but if not feel free to use the ifdef mechanism to add support
// for any other 6809 C compiler.

// Compile with: cmoc --vectrex -Wsign-compare -DVECTREX_CMOC tgvectrex.c


// At this point I don't care if the Vectrex port plays slowly or flickers badly, I just want
// to get the basic functionality in place.  There are lots of places in the code where major
// speed improvements will be possible, but I haven't prematurely optimised any of them
// just in case this is already fast enough (also I don't want to break anything while still
// trying to get the basic functionality working).  Once we get vectors on screen and see where
// the bottlenecks are, then we can decide where to put the effort.



// (this first little group of declarations are only here at the start because
// they interact with both the graphics code and the game itself.  Otherwise
// they would be with the other declarations in the machine-independent section
// following the graphics implementations.)

#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);




#ifdef VECTREX_CMOC

// 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);
}

#else // NOT VECTREX_CMOC

#define RAND_MAX_32 ((1UL << 31UL) - 1UL)
//#define RAND_MAX ((1U << 15) - 1)
static unsigned long rseed = 0UL;
static inline unsigned short urandom16(void)
{
  return (rseed = (rseed * 214013L + 2531011L) & RAND_MAX_32) >> 16;
}

#ifdef OPENGL
#define OPENGL_DEBUGGING 1

// This is an alternative version of the graphics code that can be used on Linux and probably Windows,
// and assumes a regular 32 bit C compiler.  I intend for this section to be portable and to allow
// versions of this game to be played on other platforms, but for now primarily this is here only
// to speed up the development cycle, and for debugging.

#ifdef OPENGL_DEBUGGING
#include <stdio.h> // stdio used for debugging *only*
#endif

#include <GL/gl.h>
#include <GL/glut.h>
#include <time.h> // for clock() to supply millis  *HOWEVER* OpenGL has its own timer system and perhaps I should be using it instead??!

// The opengl framework used here was originally 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.
 */

// initial requested size of screen in OpenGL implementation
#define SCREEN_W 1024
#define SCREEN_H 800


// This table is only ever used one line at a time (ie as an array of 3 items) when
// drawing lines or points in the Display_Vectrex_* procedures.  Since the values are
// a trivial function of the first index, there's no real need for this table and it
// can be easily replaced.  Whether it is worth the effort is a question for later.
#define COLORS 16
GLubyte colors[COLORS][3] = { // grey scale - only used for fading stars into the distance
   { 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 },
};

static 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_MINUS_SRC_ALPHA*/GL_ONE);
}

static void display(void)
{
    int i;
    glClear(GL_COLOR_BUFFER_BIT);    
    glBegin(GL_LINES);
      playTailgunner ();
    glEnd();
    glutSwapBuffers();
}

/* This handles the unix/cygwin-style OpenGL main loop */
/* doesn't appear to be used anywhere at the moment? */
/* however glutPostRedisplay() *is* called directly once, in 'timer()' */
#ifdef NEVER
static void idle(void)
{
    glutPostRedisplay();
}
#endif

static 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) || (key == 'q') || (key == 'Q')) {

        // Should I be doing some clean shutdown stuff here?  Seems to work OK for now...
        exit(0);

    } else if ((key == 'w') || (key == 'W')) {

        glutPositionWindow(old_x, old_y);
        glutReshapeWindow(old_width, old_height);

        ScreenWidth = old_width;
        ScreenHeight = old_height;
        intro_x = ScreenWidth/2; intro_y = ScreenHeight/2; 
        // TO DO: reinit the star field!!!!

    } else if ((key == 'f') || (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();

            // TO DO: when we update ScreenWidth, ScreenHeight - make sure all the components
	    // adjust automatically - then use this to test the conversion to
	    // the Vectrex aspect ratio...  Don't forget the scale of the
	    // components matters as well as their positioning (which currently
	    // has no support in the code at all... everything is absolute :-( )

            ScreenWidth = glutGet(GLUT_SCREEN_WIDTH);
            ScreenHeight = glutGet(GLUT_SCREEN_HEIGHT);
            intro_x = ScreenWidth/2; intro_y = ScreenHeight/2; 
            // TO DO: reinit the star field!!!!

	}

    } else {
      Pressed_Key = key; // communicate with game in a semi-portable way...
    }
}
 
static void timer(int value)
{
  // timer code from http://www.opengl.org/discussion_boards/showthread.php/168204-OpenGL-Setting-FPS/page2

  static GLint Frames = 0;         // frames averaged over 1000mS
  static GLuint Clock;             // [milliSeconds]
  //static GLuint PreviousClock = 0; // [milliSeconds]
  static GLuint NextClock = 0;     // [milliSeconds]
 
  glutTimerFunc(1000/TargetFPS, timer, ++value);

  Frames++;
  Clock = glutGet(GLUT_ELAPSED_TIME); //has limited resolution, so average over 1000mS
  if ( Clock >= NextClock ) {
    //PreviousClock = Clock;
    NextClock = Clock+1000; // 1000mS=1S in the future
    ObservedFPS = Frames;
    Frames=0; 
  }
  glutPostRedisplay();
} 

static void graphics_initialisation(int *argc, char **argv)
{
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
  glutInitWindowPosition(50, 50);
  glutInitWindowSize(ScreenWidth = SCREEN_W, ScreenHeight = SCREEN_H); // should really read it back to be sure...
  glutInit(argc, argv);

  glutCreateWindow("Tailgunner");
  glutTimerFunc(0,timer,0);
  glutDisplayFunc(display);
  glutReshapeFunc(reshape);
  glutKeyboardFunc(keyboard);
#ifdef LINEWIDTH
  glLineWidth(LINEWIDTH); // Width of drawn lines.
#endif
  glutWarpPointer(ScreenWidth/2, ScreenHeight/2); // start with cursor crosshairs in center of screen
}

static void My_mouse_routine(int x, int y) {
  mouse_x = x; // place current mouse pos in mouse_x
  mouse_y = ScreenHeight-y;
}

static void Mouse_click(int button, int state, int x, int y) {
  // button: GLUT_LEFT_BUTTON, GLUT_MIDDLE_BUTTON, GLUT_RIGHT_BUTTON
  // state:  GLUT_DOWN, GLUT_UP
  // May need to negotiate between here and the game logic as to where to
  // handle key debouncing, and whether to pass on a single pulse of the
  // button going down versus reporting whether the button is being held down

  mouse_fire = (button == GLUT_LEFT_BUTTON) && (state == GLUT_DOWN);
  if (button == GLUT_RIGHT_BUTTON) {
    if (state == GLUT_DOWN) {
      mouse_shields = 1;
    } else if (state == GLUT_UP) {
      mouse_shields = 0;
    }
  }
  mouse_x = x;
  mouse_y = ScreenHeight-y;
}

static void Main_loop(void) {
  glutMouseFunc( Mouse_click );
  // I've noticed that in OpenGL, the cursor does not move while the mouse button is down.  Why?
  glutPassiveMotionFunc( My_mouse_routine );
  glutMainLoop();
}

static void Display_Vectrex_vector(short from_x, short from_y, short to_x, short to_y)
{
  glColor3ubv(colors[(vectrex_beam_intensity>>3)&15]);
  glVertex2i(from_x, from_y);
  glVertex2i(to_x, to_y);
}

static void Display_Vectrex_point(short x, short y)
{
  glColor3ubv(colors[(vectrex_beam_intensity>>3)&15]); // see comment near declaration of 'colors'
  glVertex2i(x, y);
  glVertex2i(x+1, y+1); // look up the docs and find if there is a better way to do this in OpenGL (if no +1's, no dots are drawn)
}

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

static short mouseX(void)
{
  return mouse_x; // joystick X
}

static short mouseY(void)
{
  return mouse_y; // joystick Y
}

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

  // the code isn't used yet, but the intention of adding it was for things
  // that used real-time in the game (eg the length of time between blowing
  // up an enemy ship, and having it fade away into the distance) in a way
  // that was independent of frame rate - i.e. if we play on hardware with
  // 15FPS rather than 50FPS, we don't want the game to slow down by a factor
  // of 3...
  // To be clear this is *not* the timer used to force the FPS in the game
  // loop.  Also, to keep it within 16 bits, it may be necessary to reset
  // the millis value to 0 at the start of a wave or whatever period it is
  // used in.  This is probably OK as a wave ought to be complete within 32s.

  // This can be implemented either with a real time clock, or by jumping
  // an appropriate number of ticks once every frame, depending on the known FPS.

  // ( http://www.playvectrex.com/designit/lecture/UP09.HTM shows how this info
  // ought to be possible on the Vectrex hardware if required...)


  // BUT SEE ALSO glutTimerFunc 'timer()' - PERHAPS WE CAN USE OpenGL TIMERS INSTEAD???
  return clock()/(CLOCKS_PER_SEC / 1000);
}
#endif /* OPENGL */
#endif // NOT VECTREX_CMOC

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

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

#ifdef VECTREX_CMOC
#pragma const_data start
#endif

// 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}; // Hmmm... I think it actually said PUSH start...

//
// 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
#ifdef NEVER
           20,     41,     61,     82,    102,    122,    143,
  163,    183,    204,    224,    245,    265,    286,    306,
  327,    347,    368,    389,    409,    430,    450,    471,
  492,    513,    533,    554,    575,    596,    617,    638,
  659,    680,    701,    722,    743,    765,    786,    807,
  829,    850,    872,    893,    915,    937,    959,    980,
  1002,   1024,   1046,   1069,   1091,   1113,   1136,   1158,
  1181,   1203,   1226,   1249,   1272,   1295,   1319,   1342,
  1365,   1389,   1413,   1436,   1460,   1485,   1509,   1533,
  1558,   1583,   1607,   1632,   1658,   1683,   1709,   1734,
  1760,   1787,   1813,   1840,   1867,   1894,   1921,   1949,
  1977,   2005,   2033,   2062,   2091,   2121,   2151,   2181,
  2211,   2242,   2274,   2306,   2338,   2371,   2404,   2438,
  2473,   2508,   2544,   2581,   2619,   2657,   2696,   2737,
  2778,   2821,   2865,   2911,   2958,   3007,   3058,   3112,
  3169,   3230,   3294,   3365,   3442,   3530,   3634,   3770,
#endif
};

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,
};


#ifdef VECTREX_CMOC
#pragma const_data end
#endif

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

// 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;
  }
#ifdef SQRT_ROUNDING
  /* Do arithmetic rounding to nearest integer */
  if (op > res) res++;
#endif
  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) {
#if (((short)-1) >> 1) == ((short)-1)
  // signed right shift sign-extends (arithmetic)
  const short negSign = ~(j >> 15); // splat sign bit into all 16 and complement
  // if j is positive (negSign is -1), xor will invert j and sub will add 1
  // otherwise j is unchanged
  return (j ^ negSign) - negSign;
#else
  return (j < 0 ? j : -j);
#endif
}

// 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)
{
#ifdef VECTREX_CMOC

  // 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)
#else
 return (unsigned short)(((unsigned long)a * (unsigned long)b) / (unsigned long)c);
#endif
}

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 8192U;
    } else if (y < 0) { // 5/8
      return 40960U;
    } else { // x = y = 0
      return 0U;
    }
  }
  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 32768U + 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 16384U - unrotated;
    } else { // octants 6, 7
      return 49152U - unrotated;
    }
  }
}

#ifdef NEVER
static short Scratch_atan2(short x, short y)  // substitute for atan2(x/y)
{
  // sign shenannigans needed to match Scratch's behaviour...
  short result = ((fxpt_atan2(x, y)>>8)-128)*360/256;

  if (result <= -90) {
    return result + 180;
  } else if (result >= 90) {
    return result - 180;
  }
  return result;

}
#endif

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;

}


#ifdef NEVER // replaced by atan2 - we don't have Scratch's safety net of trapping /0 errors...
static short atan_of_fp2_14(fp2_14 angle) // angle in is arbitrary value
{
  // TO DO FP:
  return 0; // returns atan of angle - watch for range issues!
}
#endif

static unsigned char numdigits(short num)
{
  // a chain of if/then/elses may be more efficient than a software divide by 10 ?
  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.
#define SHIP_STATUS_LOST 4U      // FOR DEBUGGING ONLY.  ship has wandered outside the cube!

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 distance;
//static short edge;

// since these times are used in mutually exclusive sections, I could conflate them
// down to a single timer variable... (no need so far, ram usage is low...)
static short insertcoin_mode_timer; // a timer that counts down from 129 to 0 - a frame counter

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

#define sgn(x) ((x<0)?-1:((x>0)?1:0)) /* macro to return the sign of a number */
// might switch over to using s16_nabs() ..?
#define abs(x) (((x)>=0)?(x):-(x))

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?
               /* (this should be a compile-time constant)                 */
    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 = /*0..3*/id * 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
                                                             // change to a range that encourages /128 instead 
  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 = /*0..3*/id * 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

#ifdef OPENGL_DEBUGGING
  fprintf(stdout, "ship[%d] = {", id);
  fprintf(stdout, "  STATUS %d\n", status);
  fprintf(stdout, "  type %d\n", model_type);
  fprintf(stdout, "  location %d %d %d\n",x,y,z);
  fprintf(stdout, "  rotation %d %d %d\n",Xrot,Yrot,Zrot);
  fprintf(stdout, "  scale %f\n", scale_fp/(float)(1<<14));
  fprintf(stdout, "  speed %d\n", speed);
  fprintf(stdout, "  target location %d %d %d\n", tgx,tgy,tgz);
  fprintf(stdout, "  ships time %d\n", shiptime);
  fprintf(stdout, "}\n");
#endif
}

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?)

  // clever packing may be undone by overhead of div&modulo 5 ... reconsider using less dense tables?
  to = coords & 15;
  tox = ((to % 5) *  4 * textscale) >> 7; // / 128;
  toy = ((to / 5) * 12 * textscale) >> 7; // / 128;

  from = coords >> 4;
  fromx = ((from % 5) *  4 * textscale) >> 7; // / 128;
  fromy = ((from / 5) * 12 * textscale) >> 7; // / 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)) >> 7 /* / 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);
}

#ifdef NEVER
  InsertCoin ();
#endif

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.
                         // substitute the actual constant?
    Shields -= 1;
  } else {
    shieldsubticksleft -= 1;
  }
  draw_shields();
}

#ifdef NEVER
static inline void alphabet_test(void)
{
  unsigned char ptr = 1;
  textx = -400;
  texty = 300;
  {unsigned char i;
    for (i = 0U; i < 10U; i++) ch (ptr++, 256);
  }
  {unsigned char i;
    for (i = 0U; i < 8U; i++) ch (ptr++, 128);
  }
  textx = 100;
  texty -= 64;
  {unsigned char i;
    for (i = 0U; i < 18U; i++) ch (ptr++, 96);
  }
}
#endif

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)
{
#ifdef OPENGL_DEBUGGING
  fprintf(stderr, "ship[%d] += (%d,%d,%d)\n", id, dx,dy,dz);
#endif
  // 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)) >> 7 /* / FAKE100 */,
			   (fakepercent * ((((dirXZ - this_Y_rotation) + 128) & 255) - 128)) >> 7 /* / FAKE100 */,
                           (fakepercent * -this_Z_rotation) >> 7 /* / 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???

#ifdef NEVER // did this get here by accident in translation from Scratch?
  this_Z = (SHIP_SPAWN_Z - 1); // (I have forgotten why I did this. Z is set to a smidgeon closer to us than the spawn point)
  // I think it may be that when the ship's Z is exactly equal to SHIP_SPAWN_Z, the ship is still in a sort of 2D mode
  // as viewed from above, and hasn't yet turned to face us on the level.
#endif
}

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 < 50 /* DEBIGGING: Was 30*/) || (this_X < -1900 || this_X > 1900 || this_Y < -1900 || this_Y > 1900)) {
      if (this_Z < 50 /* DEBIGGING: Was 30*/) {
        Set_ship_status (id, SHIP_STATUS_PASSED); // effectively the same as DEAD
      } else {
        Set_ship_status (id, SHIP_STATUS_LOST); // debugging movement problems
      }
      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?  Yes, almost certainly!
            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;}
  }

  // This is the old sprite-based code.  Currently being replaced
#ifdef NEVER
  laserid = 0;
  {unsigned char i;
    for (i = 0; i < (projectile_next / PROJECTILE_RECORD_LENGTH); i++) {
      lasert = projectiles[((PROJECTILE_RECORD_LENGTH * laserid) + PROJECTILE_FIELD_LASERT)];
      targettingx = mouseX ();
      targettingy = mouseY ();
      if ((lasert < 0.98)) {
        setSizeTo((100 * (1 - lasert)));
        gotoxy (-240, -180);
        Point_to (targettingx, targettingy);
        forward((lasert *
          isqrt ((((targettingx - xpos ()) * (targettingx - xpos ())) +
                  ((targettingy - ypos ()) * (targettingy - ypos ()))))));
        stampCostume ();
        gotoxy (240, -180);
        Point_to (targettingx, targettingy);
        forward((lasert *
          isqrt ((((targettingx - xpos ()) * (targettingx - xpos ())) +
                  ((targettingy - ypos ()) * (targettingy - ypos ()))))));
        stampCostume ();
        projectiles[((PROJECTILE_RECORD_LENGTH * laserid) + PROJECTILE_FIELD_LASERT)] = lasert + (0.25 * (1 - lasert));
        laserid += 1;
      } else {
        ship_hit_check (targettingx, targettingy);
        {unsigned char i;
          for (i = 0; i < PROJECTILE_RECORD_LENGTH; i++) {
            // TO DO: deleteLineOfList(((PROJECTILE_RECORD_LENGTH * laserid) + 1), projectiles); // to stop wave disappearing early...
          }
        }
      }
    }
  }
#endif
}

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);
      // THERE MAY BE A MAJOR BUG HERE (subsequent to the re-implementation, that was not in the original)
      // (and I don't just mean the sign issue that I've temporarily tweaked below)
      //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);
      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) && (ShipsPassedThisWave > 0)) {
    stars(4); // 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 ();

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

    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
      insertcoin_mode_timer = 200;
      Mode = INSERTCOIN;
    }

    // TO DO: need to do the 'whoosh' with the stars here too.
    //  At the moment, the state machine code is very verbose and not true to the game -
    //  my expectation is that as I follow the original logic more closely, all the
    //  special cases will disappear and this game loop will shrink considerably.  However
    //  for now I can live with it as I have bigger fish to fry...

  } 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();
    insertcoin_mode_timer -= 1;
    if (insertcoin_mode_timer == 0) {
      Mode = PREGAME;
    }

  } 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.
#ifdef OPENGL_DEBUGGING
      fprintf(stderr, "Coined but no credits\?  Is this a bug\?\n");
#endif
    }

  } 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 = 50;
        Mode = PASSING; // and set a timer ...
      } else {
        // Wrong game logic - game is over as soon as 10 ships get past you - *not* when the wave
	// ends and 10 or more ships have got past you.  Fix this...
        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)

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

    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();
}

#ifdef VECTREX_CMOC
int main (void)
{
  int argc;
  char **argv;// unused
#else
int main (int argc, char **argv)
{
#endif

  pre_graphics_initialisation();

  graphics_initialisation(&argc, argv);

  post_graphics_initialisation();

  Main_loop();

  return(0);
}