// remove asintab until it is needed?  See how small the code can go...

#define GCC6809 1 // ONLY ON VIDE SYSTEM. Is there a predefined symbol I can test instead?

#ifdef NOTGCC6809
#undef GCC6809 // (sorry, couldn't find an easier way yet.  Makefile fixes the problem...)
#endif

#ifdef VECTREX /* predefined by cmoc - don't define this with GCC for Vectrex */
#undef GCC6809

#include <vectrex/bios.h>

#define debugstr(s) /* ignore */

#define signed16 short
#define unsigned16 unsigned short
#define signed8 char
#define unsigned8 unsigned char
#define const /* const not supported in cmoc - const arrays require a #pragma */
#define inline /* also not supported by cmoc */

#pragma vx_copyright "2017"
#pragma vx_title_pos 0,-80
#pragma vx_title_size -8, 80
#pragma vx_title "CMOC VSN"
#pragma vx_music vx_music_1

/*
F3AD Mov_Draw_VLc_a
F3B1 Mov_Draw_VL_b
F3B5 Mov_Draw_VLcs
F3B7 Mov_Draw_VL_ab
F3B9 Mov_Draw_VL_a
F3BC Mov_Draw_VL
F3BE Mov_Draw_VL_d
F3CE Draw_VLc
F3D2 Draw_VL_b
F3D6 Draw_VLcs
F3D8 Draw_VL_ab
F3DA Draw_VL_a
F3DD Draw_VL
F3DF Draw_Line_d
F404 Draw_VLp_FF
F408 Draw_VLp_7F
F40C Draw_VLp_scale
F40E Draw_VLp_b
F410 Draw_VLp
F434 Draw_Pat_VL_a
F437 Draw_Pat_VL
F439 Draw_Pat_VL_d
F46E Draw_VL_mode


            'PACKET' Style Drawing Routines

RUM         Entry  Label Used By
Subroutine  Point  Mine Storm      Function  
 
DASHY3      $F46E  DASHPK          Draw dashed lines from 'PACKET' list
PAC1X       $F408  PACK1X          Draw from 'PACKET' style list
PAC2X       $F404  PACK2X          Draw from 'PACKET' style list
PACB        $F40E  TPACK           Draw from 'PACKET' list
PACKET      $F410  ---             Draw from 'PACKET' list
PACXX       $F40C  LPACK           Draw from 'PACKET' style list
POTATA      $F61F  PROT            'PACKET' style rotate
POTATE      $F622  APROT           'PACKET' style rotate

Bios source code:

PACXX    LDB       0,X+           ;PICK UP TIMER INDEXED            <---- aka Draw_VLp_scale
PACB     STB       T1LOLC         ;ENTRY IF B HAS TIMER VALUE       <---- aka Draw_VLp_b
PACKET   LDD       1,X            ;ENTRY POINT HERE IF T1LOLC VALID <---- aka Draw_VLp
         STA       DAC            ;FORMAT=STATUS,X,Y
         CLR       PORT
         LDA       0,X            ;PICK UP VIDEO ENABLE
         LEAX      3,X
         INC       PORT           ;Y S/H
         STB       DAC            ;SETUP X
         STA       SR             ;VID CONTROL
         CLR       T1HOC          ;START RAMP- FOR CRT PUT THIS BEFORE 'STB SR'
         LDD       #$40
EXIT     BITB      IFR
         BEQ       EXIT
         NOP                      ;MOUSE DELAY
         STA       SR
         LDA       0,X            ;ANOTHER ONE?
         BLE       PACKET
         JMP       ZEGO

 */
void displaylist(int8_t* list) {
  // scale is already set up.
  asm {
    PSHS    X,Y,U,D,CC  ; err on the side of safety until I get this working...
    JSR     DP_to_D0
    LDX     list
    JSR     Draw_VLp
    PULS    CC,D,U,Y,X
  }
}

#else /* not cmoc */

#ifdef GCC6809

#include <vectrex.h>

#define debugstr(s) /* ignore */

#define signed16 long
#define unsigned16 unsigned long
#define signed8 signed char
#define unsigned8 unsigned char

static inline void intensity(unsigned8 brightness) {Intensity_a(brightness);}
static inline void dot(signed8 y, signed8 x) {Dot_d((int)y, (int)x);}
static inline void move(signed8 y, signed8 x) {Moveto_d((int)y, (int)x);}
static inline void reset_beam(void) {Reset0Ref();}
static inline void set_scale(unsigned8 scale) {/*VIA_t1_cnt_lo*/ *(volatile unsigned char *)0xD004 = scale;}
static inline void line(signed8 y, signed8 x) {*(volatile unsigned char *)0xC823 = 0; Draw_Line_d((int)y, (int)x);}
static inline void lines(unsigned8 size, const signed8 *array) { (void)size; (void)array; }
static inline void displaylist(const signed8 *list) {
  // some assembly required
#define jsrx(v, func)       asm("ldx %0\n\t" \
                                "jsr " #func "\n\t" : : "g" (v) : "d", "x")
#define Draw_VLp(ra) jsrx(ra, 0xF410)   // (pattern y x)* 0x01 
  Draw_VLp(list);
}
static inline void wait_retrace(void) {Wait_Recal();}

#define signed16 long
#define unsigned16 unsigned long
#define signed8 signed char
#define unsigned8 unsigned char

#else /* anything except gcc for the 6809 */

#include <stdio.h>

#ifndef __GNUC__
#define inline /* GCC extension not available elsewhere - https://gcc.gnu.org/onlinedocs/gcc/Inline.html#Inline */
#endif

// Portable version - generate a debug trace only!

#define debugstr(s) printf("// %s\n", s)

inline void wait_retrace(void) {fflush(stdout); printf("  wait_retrace();\n");}
inline void move(char y, char x) {printf("  move(%d, %d);\n", (int)y, (int)x);}
inline void line(char y, char x) {printf("  line(%d, %d);\n", (int)y, (int)x);}
#define lines(size, array) printf("  lines(%d, %s);\n", (int)((unsigned char)(size)), #array)
#define displaylist(array) printf("  displaylist(%s);\n", #array)
inline void dot(char y, char x) {printf("  dot(%d, %d);\n", (int)y, (int)x);}
inline void reset_beam(void) {printf("  reset_beam();\n");}
inline void set_scale(unsigned char scale) {printf("  set_scale(%d);\n", (int)((unsigned char)scale));}
inline void intensity(char brightness) {printf("  intensity(%d);\n", (int)brightness);}

#define signed16 short
#define unsigned16 unsigned short
#define signed8 char
#define unsigned8 unsigned char

#endif /* not GCC6809 */

#endif /* not cmoc */

#define line_rel_XY(x,y) line(y,x)
#define move_rel_XY(x,y) move(y,x)

#define MAX_BRIGHTNESS (0x7f)
#define fp2_14 /* SIGNED */ signed16 /* we use a restricted range (2.14) fixed point for trig support */
#define ONE_POINT_ZERO ((fp2_14)(1<<14))

static signed16 Debug;
static signed16 vectors_dumped;

// All const arrays in cmoc must come in this section.
// Also, static scalars cannot be explicitly initialised
//  - do so with assignments at program start instead.
// (Need to check if they're all set to 0 on startup)

#ifdef VECTREX
#pragma const_data start
#endif

/* Shields get a special proc because they're one continuous line */
static const signed8 shieldvec[] = {
  24,24,  96,-96, 24,24, -72,72, -72,-72, 24,-24, 96,96, 24,-24, -72,-72, -72,72
};

static const signed8 crosshair_points[41] = { 
 0x03, 0x20, 0x3e, 0x20,
 0x34, 0x16, 0x34, 0x2a,
 0x16, 0x34, 0x2a, 0x34,
 0x20, 0x02, 0x20, 0x3e,
 0x16, 0x0c, 0x2a, 0x0c,
 0x0c, 0x16, 0x0c, 0x2a,
 0x1b, 0x2a, 0x25, 0x2a,
 0x2a, 0x1b, 0x2a, 0x25,
 0x1b, 0x16, 0x25, 0x16,
 0x16, 0x1b, 0x16, 0x25,
 0
};

static const signed8 tg_intro_placard[] = {
   163/2 - 64,64/2 - 64,    93/2 - 64,64/2 - 64,    93/2 - 64,64/2 - 64,   93/2 - 64,192/2 - 64,
   93/2 - 64,192/2 - 64,  163/2 - 64,192/2 - 64,  163/2 - 64,192/2 - 64,   163/2 - 64,64/2 - 64,
   155/2 - 64,71/2 - 64,   101/2 - 64,71/2 - 64,   101/2 - 64,71/2 - 64,  101/2 - 64,185/2 - 64,
  101/2 - 64,185/2 - 64,  155/2 - 64,185/2 - 64,  155/2 - 64,185/2 - 64,   155/2 - 64,71/2 - 64,
  149/2 - 64,109/2 - 64,   149/2 - 64,93/2 - 64,  131/2 - 64,101/2 - 64,  149/2 - 64,101/2 - 64,
  149/2 - 64,119/2 - 64,  131/2 - 64,113/2 - 64,  131/2 - 64,125/2 - 64,  149/2 - 64,119/2 - 64,
  140/2 - 64,122/2 - 64,  140/2 - 64,116/2 - 64,  149/2 - 64,137/2 - 64,  131/2 - 64,137/2 - 64,
  131/2 - 64,149/2 - 64,  149/2 - 64,149/2 - 64,  131/2 - 64,161/2 - 64,  131/2 - 64,149/2 - 64,
  107/2 - 64,167/2 - 64,  125/2 - 64,167/2 - 64,  125/2 - 64,179/2 - 64,  125/2 - 64,167/2 - 64,
  116/2 - 64,179/2 - 64,  125/2 - 64,179/2 - 64,  107/2 - 64,179/2 - 64,  116/2 - 64,176/2 - 64,
  116/2 - 64,167/2 - 64,  116/2 - 64,179/2 - 64,  107/2 - 64,149/2 - 64,  107/2 - 64,161/2 - 64,
  125/2 - 64,149/2 - 64,  107/2 - 64,149/2 - 64,  125/2 - 64,161/2 - 64,  125/2 - 64,149/2 - 64,
  116/2 - 64,149/2 - 64,  116/2 - 64,155/2 - 64,  107/2 - 64,143/2 - 64,  125/2 - 64,143/2 - 64,
  125/2 - 64,131/2 - 64,  107/2 - 64,143/2 - 64,  107/2 - 64,131/2 - 64,  125/2 - 64,131/2 - 64,
  107/2 - 64,125/2 - 64,  125/2 - 64,125/2 - 64,  125/2 - 64,113/2 - 64,  107/2 - 64,125/2 - 64,
  107/2 - 64,113/2 - 64,  125/2 - 64,113/2 - 64,  107/2 - 64,107/2 - 64,  125/2 - 64,107/2 - 64,
   107/2 - 64,95/2 - 64,  107/2 - 64,107/2 - 64,   125/2 - 64,95/2 - 64,   107/2 - 64,95/2 - 64,
   114/2 - 64,86/2 - 64,   114/2 - 64,92/2 - 64,   107/2 - 64,89/2 - 64,   114/2 - 64,89/2 - 64,
   107/2 - 64,77/2 - 64,   107/2 - 64,89/2 - 64,   125/2 - 64,77/2 - 64,   107/2 - 64,77/2 - 64,
   125/2 - 64,89/2 - 64,   125/2 - 64,77/2 - 64,
};

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


// I think the first byte of three is really a pattern byte,
// so 0 looks like a move and 255 looks like a solid line,
// but it could equally be 0F for a dashed line etc...

#define DEFINE(name) static const signed8 name[] = {
#define move_rel_xy(x,y) 0 /*MOVE*/, (y), (x)
#define line_rel_xy(x,y) (signed8)-1 /*LINE*/, (y), (x)
#define ENDDEF(name) 1 /*STOP*/ }; static void SHOW_##name(void) { displaylist(name); }



// faster drawing routines
DEFINE(SCORE)
  line_rel_xy(16,0), // to 16,0
  line_rel_xy(0,12), // to 16,12
  line_rel_xy(-16,0), // to 0,12
  line_rel_xy(0,12), // to 0,24
  line_rel_xy(16,0), // to 16,24
  move_rel_xy(8,0), // to 24,24
  line_rel_xy(16,0), // to 40,24
  move_rel_xy(0,-24), // to 40,0
  line_rel_xy(-16,0), // to 24,0
  line_rel_xy(0,24), // to 24,24
  move_rel_xy(24,0), // to 48,24
  line_rel_xy(16,0), // to 64,24
  line_rel_xy(0,-24), // to 64,0
  line_rel_xy(-16,0), // to 48,0
  line_rel_xy(0,24), // to 48,24
  move_rel_xy(40,-24), // to 88,0
  line_rel_xy(-4,12), // to 84,12
  move_rel_xy(-12,0), // to 72,12
  line_rel_xy(16,0), // to 88,12
  line_rel_xy(0,12), // to 88,24
  line_rel_xy(-16,0), // to 72,24
  line_rel_xy(0,-24), // to 72,0
  move_rel_xy(40,0), // to 112,0
  line_rel_xy(-16,0), // to 96,0
  line_rel_xy(0,24), // to 96,24
  move_rel_xy(0,-12), // to 96,12
  line_rel_xy(8,0), // to 104,12
  move_rel_xy(-8,12), // to 96,24
  line_rel_xy(16,0), // to 112,24
ENDDEF(SCORE)

DEFINE(HIGH_SCORE)
  line_rel_xy(0,24), // to 0,24
  move_rel_xy(0,-12), // to 0,12
  line_rel_xy(16,0), // to 16,12
  move_rel_xy(0,-12), // to 16,0
  line_rel_xy(0,24), // to 16,24
  move_rel_xy(16,-24), // to 32,0
  line_rel_xy(0,24), // to 32,24
  move_rel_xy(24,-12), // to 56,12
  line_rel_xy(8,0), // to 64,12
  line_rel_xy(0,-12), // to 64,0
  line_rel_xy(-16,0), // to 48,0
  line_rel_xy(0,24), // to 48,24
  line_rel_xy(16,0), // to 64,24
  move_rel_xy(8,-24), // to 72,0
  line_rel_xy(0,24), // to 72,24
  move_rel_xy(0,-12), // to 72,12
  line_rel_xy(16,0), // to 88,12
  move_rel_xy(0,-12), // to 88,0
  line_rel_xy(0,24), // to 88,24
  move_rel_xy(32,-24), // to 120,0
  line_rel_xy(16,0), // to 136,0
  line_rel_xy(0,12), // to 136,12
  line_rel_xy(-16,0), // to 120,12
  line_rel_xy(0,12), // to 120,24
  line_rel_xy(16,0), // to 136,24
  move_rel_xy(8,0), // to 144,24
  line_rel_xy(16,0), // to 160,24
  move_rel_xy(0,-24), // to 160,0
  line_rel_xy(-16,0), // to 144,0
  line_rel_xy(0,24), // to 144,24
  move_rel_xy(24,0), // to 168,24
  line_rel_xy(16,0), // to 184,24
  line_rel_xy(0,-24), // to 184,0
  line_rel_xy(-16,0), // to 168,0
  line_rel_xy(0,24), // to 168,24
  move_rel_xy(40,-24), // to 208,0
  line_rel_xy(-4,12), // to 204,12
  move_rel_xy(-12,0), // to 192,12
  line_rel_xy(16,0), // to 208,12
  line_rel_xy(0,12), // to 208,24
  line_rel_xy(-16,0), // to 192,24
  line_rel_xy(0,-24), // to 192,0
  move_rel_xy(40,0), // to 232,0
  line_rel_xy(-16,0), // to 216,0
  line_rel_xy(0,24), // to 216,24
  move_rel_xy(0,-12), // to 216,12
  line_rel_xy(8,0), // to 224,12
  move_rel_xy(-8,12), // to 216,24
  line_rel_xy(16,0), // to 232,24
ENDDEF(HIGH_SCORE)

DEFINE(CREDITS)
  move_rel_xy(0,24), // to 0,24
  line_rel_xy(16,0), // to 16,24
  move_rel_xy(0,-24), // to 16,0
  line_rel_xy(-16,0), // to 0,0
  line_rel_xy(0,24), // to 0,24
  move_rel_xy(40,-24), // to 40,0
  line_rel_xy(-4,12), // to 36,12
  move_rel_xy(-12,0), // to 24,12
  line_rel_xy(16,0), // to 40,12
  line_rel_xy(0,12), // to 40,24
  line_rel_xy(-16,0), // to 24,24
  line_rel_xy(0,-24), // to 24,0
  move_rel_xy(40,0), // to 64,0
  line_rel_xy(-16,0), // to 48,0
  line_rel_xy(0,24), // to 48,24
  move_rel_xy(0,-12), // to 48,12
  line_rel_xy(8,0), // to 56,12
  move_rel_xy(-8,12), // to 48,24
  line_rel_xy(16,0), // to 64,24
  move_rel_xy(8,0), // to 72,24
  line_rel_xy(12,0), // to 84,24
  line_rel_xy(4,-12), // to 88,12
  line_rel_xy(-4,-12), // to 84,0
  line_rel_xy(-12,0), // to 72,0
  line_rel_xy(0,24), // to 72,24
  move_rel_xy(32,-24), // to 104,0
  line_rel_xy(0,24), // to 104,24
  move_rel_xy(16,0), // to 120,24
  line_rel_xy(16,0), // to 136,24
  move_rel_xy(-8,0), // to 128,24
  line_rel_xy(0,-24), // to 128,0
  move_rel_xy(16,0), // to 144,0
  line_rel_xy(16,0), // to 160,0
  line_rel_xy(0,12), // to 160,12
  line_rel_xy(-16,0), // to 144,12
  line_rel_xy(0,12), // to 144,24
  line_rel_xy(16,0), // to 160,24
  move_rel_xy(30,-24), // to 190,0
ENDDEF(CREDITS)

DEFINE(INSERT_COIN)
  move_rel_xy(8,0), // to 8,0
  line_rel_xy(0,24), // to 8,24
  move_rel_xy(16,-24), // to 24,0
  line_rel_xy(0,24), // to 24,24
  line_rel_xy(16,-24), // to 40,0
  line_rel_xy(0,24), // to 40,24
  move_rel_xy(8,-24), // to 48,0
  line_rel_xy(16,0), // to 64,0
  line_rel_xy(0,12), // to 64,12
  line_rel_xy(-16,0), // to 48,12
  line_rel_xy(0,12), // to 48,24
  line_rel_xy(16,0), // to 64,24
  move_rel_xy(24,-24), // to 88,0
  line_rel_xy(-16,0), // to 72,0
  line_rel_xy(0,24), // to 72,24
  move_rel_xy(0,-12), // to 72,12
  line_rel_xy(8,0), // to 80,12
  move_rel_xy(-8,12), // to 72,24
  line_rel_xy(16,0), // to 88,24
  move_rel_xy(24,-24), // to 112,0
  line_rel_xy(-4,12), // to 108,12
  move_rel_xy(-12,0), // to 96,12
  line_rel_xy(16,0), // to 112,12
  line_rel_xy(0,12), // to 112,24
  line_rel_xy(-16,0), // to 96,24
  line_rel_xy(0,-24), // to 96,0
  move_rel_xy(24,24), // to 120,24
  line_rel_xy(16,0), // to 136,24
  move_rel_xy(-8,0), // to 128,24
  line_rel_xy(0,-24), // to 128,0
  move_rel_xy(40,24), // to 168,24
  line_rel_xy(16,0), // to 184,24
  move_rel_xy(0,-24), // to 184,0
  line_rel_xy(-16,0), // to 168,0
  line_rel_xy(0,24), // to 168,24
  move_rel_xy(24,0), // to 192,24
  line_rel_xy(16,0), // to 208,24
  line_rel_xy(0,-24), // to 208,0
  line_rel_xy(-16,0), // to 192,0
  line_rel_xy(0,24), // to 192,24
  move_rel_xy(32,-24), // to 224,0
  line_rel_xy(0,24), // to 224,24
  move_rel_xy(16,-24), // to 240,0
  line_rel_xy(0,24), // to 240,24
  line_rel_xy(16,-24), // to 256,0
  line_rel_xy(0,24), // to 256,24
ENDDEF(INSERT_COIN)

DEFINE(PRESS_START)
  line_rel_xy(0,24), // to 0,24
  line_rel_xy(16,0), // to 16,24
  line_rel_xy(0,-12), // to 16,12
  line_rel_xy(-16,0), // to 0,12
  move_rel_xy(40,-12), // to 40,0
  line_rel_xy(-4,12), // to 36,12
  move_rel_xy(-12,0), // to 24,12
  line_rel_xy(16,0), // to 40,12
  line_rel_xy(0,12), // to 40,24
  line_rel_xy(-16,0), // to 24,24
  line_rel_xy(0,-24), // to 24,0
  move_rel_xy(40,0), // to 64,0
  line_rel_xy(-16,0), // to 48,0
  line_rel_xy(0,24), // to 48,24
  move_rel_xy(0,-12), // to 48,12
  line_rel_xy(8,0), // to 56,12
  move_rel_xy(-8,12), // to 48,24
  line_rel_xy(16,0), // to 64,24
  move_rel_xy(8,-24), // to 72,0
  line_rel_xy(16,0), // to 88,0
  line_rel_xy(0,12), // to 88,12
  line_rel_xy(-16,0), // to 72,12
  line_rel_xy(0,12), // to 72,24
  line_rel_xy(16,0), // to 88,24
  move_rel_xy(8,-24), // to 96,0
  line_rel_xy(16,0), // to 112,0
  line_rel_xy(0,12), // to 112,12
  line_rel_xy(-16,0), // to 96,12
  line_rel_xy(0,12), // to 96,24
  line_rel_xy(16,0), // to 112,24
  move_rel_xy(32,-24), // to 144,0
  line_rel_xy(16,0), // to 160,0
  line_rel_xy(0,12), // to 160,12
  line_rel_xy(-16,0), // to 144,12
  line_rel_xy(0,12), // to 144,24
  line_rel_xy(16,0), // to 160,24
  move_rel_xy(8,0), // to 168,24
  line_rel_xy(16,0), // to 184,24
  move_rel_xy(-8,0), // to 176,24
  line_rel_xy(0,-24), // to 176,0
  move_rel_xy(32,0), // to 208,0
  line_rel_xy(0,24), // to 208,24
  move_rel_xy(-16,0), // to 192,24
  line_rel_xy(16,0), // to 208,24
  move_rel_xy(-16,-12), // to 192,12
  line_rel_xy(16,0), // to 208,12
  move_rel_xy(-16,12), // to 192,24
  line_rel_xy(0,-24), // to 192,0
  move_rel_xy(40,0), // to 232,0
  line_rel_xy(-4,12), // to 228,12
  move_rel_xy(-12,0), // to 216,12
  line_rel_xy(16,0), // to 232,12
  line_rel_xy(0,12), // to 232,24
  line_rel_xy(-16,0), // to 216,24
  line_rel_xy(0,-24), // to 216,0
  move_rel_xy(24,24), // to 240,24
  line_rel_xy(16,0), // to 256,24
  move_rel_xy(-8,0), // to 248,24
  line_rel_xy(0,-24), // to 248,0
ENDDEF(PRESS_START)

DEFINE(N0)
  move_rel_xy(0,24), // to 0,24
  line_rel_xy(16,0), // to 16,24
  line_rel_xy(0,-24), // to 16,0
  line_rel_xy(-16,0), // to 0,0
  line_rel_xy(0,24), // to 0,24
  move_rel_xy(24,-24), // to 24,0
ENDDEF(N0)

DEFINE(N1)
  move_rel_xy(8,0), // to 8,0
  line_rel_xy(0,24), // to 8,24
  move_rel_xy(16,-24), // to 24,0
ENDDEF(N1)

DEFINE(N2)
  move_rel_xy(0,24), // to 0,24
  line_rel_xy(16,0), // to 16,24
  line_rel_xy(0,-12), // to 16,12
  line_rel_xy(-16,0), // to 0,12
  line_rel_xy(0,-12), // to 0,0
  line_rel_xy(16,0), // to 16,0
  move_rel_xy(8,0), // to 24,0
ENDDEF(N2)

DEFINE(N3)
  line_rel_xy(16,0), // to 16,0
  move_rel_xy(-8,12), // to 8,12
  line_rel_xy(8,0), // to 16,12
  move_rel_xy(0,-12), // to 16,0
  line_rel_xy(0,24), // to 16,24
  line_rel_xy(-16,0), // to 0,24
  move_rel_xy(24,-24), // to 24,0
ENDDEF(N3)

DEFINE(N4)
  move_rel_xy(0,24), // to 0,24
  line_rel_xy(0,-12), // to 0,12
  line_rel_xy(16,0), // to 16,12
  move_rel_xy(-8,0), // to 8,12
  line_rel_xy(0,-12), // to 8,0
  move_rel_xy(16,0), // to 24,0
ENDDEF(N4)

DEFINE(N5)
  line_rel_xy(16,0), // to 16,0
  line_rel_xy(0,12), // to 16,12
  line_rel_xy(-16,0), // to 0,12
  line_rel_xy(0,12), // to 0,24
  line_rel_xy(16,0), // to 16,24
  move_rel_xy(8,-24), // to 24,0
ENDDEF(N5)

DEFINE(N6)
  move_rel_xy(0,12), // to 0,12
  line_rel_xy(16,0), // to 16,12
  line_rel_xy(0,-12), // to 16,0
  line_rel_xy(-16,0), // to 0,0
  line_rel_xy(0,24), // to 0,24
  move_rel_xy(24,-24), // to 24,0
ENDDEF(N6)

DEFINE(N7)
  move_rel_xy(8,0), // to 8,0
  line_rel_xy(8,24), // to 16,24
  line_rel_xy(-16,0), // to 0,24
  move_rel_xy(24,-24), // to 24,0
ENDDEF(N7)

DEFINE(N8)
  line_rel_xy(16,0), // to 16,0
  move_rel_xy(-16,24), // to 0,24
  line_rel_xy(16,0), // to 16,24
  move_rel_xy(-16,-12), // to 0,12
  line_rel_xy(16,0), // to 16,12
  move_rel_xy(0,12), // to 16,24
  line_rel_xy(0,-24), // to 16,0
  move_rel_xy(-16,24), // to 0,24
  line_rel_xy(0,-24), // to 0,0
  move_rel_xy(24,0), // to 24,0
ENDDEF(N8)

DEFINE(N9)
  move_rel_xy(16,24), // to 16,24
  line_rel_xy(-16,0), // to 0,24
  line_rel_xy(0,-12), // to 0,12
  line_rel_xy(16,0), // to 16,12
  move_rel_xy(0,12), // to 16,24
  line_rel_xy(0,-24), // to 16,0
  move_rel_xy(4,0), // to 24,0
ENDDEF(N9)

#ifdef VECTREX
#pragma const_data end
#endif

// 16 bit maths, no floats.

static signed16 rseed;
static signed16 urandom16(void)
{
  // VECTREX HAS A BUILT-IN RANDOM THAT WE CAN USE!
  return (rseed = (rseed * 2053) + 13849);
}

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

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

#ifdef NOTYET
static unsigned8 numdigits(signed16 num)
{
  if (num < 0) return 1+numdigits(-num);
  if (num < 10) return 1U;
  if (num < 100) return 2U;
  if (num < 1000) return 3U;
  if (num < 10000) return 4U;
  return 5U;
}
#endif

static int /* boolean */ clipoff(signed16 x, signed16 y)
{
  return ( -0x20 <= x && x < 0x20 && -0x12 <= y && y < 0x12);
}

static signed16 pxa[16], pya[16];

static signed16 star_x1(unsigned8 idx) {
  unsigned8 xtype = (idx>>2)&3, ytype = idx&3;

  idx = xtype ^ ytype; // four types of star trail ...
  if (idx == 0) { // left edge
    return -1016; // -127<<3 // COMPILER BUG in folded constant expression
  } else if (idx == 2) { // right edge
    return 1016; // 127<<3
  } else { // top or bottom edges
    return pickrandom(-127, 127)<<3;  // could use Scratch's random byte directly...
  }
}

static signed16 star_y1(unsigned8 idx) {
  unsigned8 xtype = (idx>>2)&3, ytype = idx&3;

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

static void move_star(unsigned8 starno)
{
  signed16 x = pxa[starno], y=pya[starno];

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

static void plot_star(unsigned8 starno)
{
  signed16 brightx, brighty;
  if (pxa[starno] < 0) brightx = -pxa[starno]; else brightx = pxa[starno];
  brightx = (brightx>>3)&127;
  if (pya[starno] < 0) brighty = -pya[starno]; else brighty = pya[starno];
  brighty = (brighty>>3)&127;
  // take this out if we don't want fading over distance...
  // If we don't adjust individual star intensities, we can use
  // the 'dots()' procedure to draw them all at once...
  intensity((unsigned8)((brightx+brighty)>>1)); // good enough approximation for hypotenuse :-)
  dot((signed8)(pxa[starno]>>4),(signed8)(pya[starno]>>4));
  move((signed8)(-(pxa[starno]>>4)),(signed8)(-(pya[starno]>>4))); // correct back to 0,0 for next dot.
}


static void init_stars(void)
{
  unsigned8 i, star;

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

static void stars(unsigned8 speed) // will always be centered on 0,0
{
  unsigned8 i, star;

  debugstr("stars");

  for (star = 0U; star < 16U; star++) {
    for (i = 0U; i < speed; i++) move_star(star);
  }

  for (star = 0U; star < 16U; star++) {
    if ((star & 15) == 0) {
      // control the frequency that the beam is reset!
      // Use a smaller mask if there is jitter if only
      // doing this once every 16 stars
      reset_beam();
      set_scale(0xF0);
      move(0, 0);
    }
    plot_star(star);
  }
  intensity(MAX_BRIGHTNESS); // restore default
}

/* Shields on the vectrex are sideways wrt the 'real' tailgunner */
static void draw_shields(unsigned8 scale)
{
  const signed8 *ptr = shieldvec;
  signed16 i;
  signed8 x1, y1;

  debugstr("draw_shields");

  reset_beam();
  set_scale(0x7f);
  move(0, 0);
  set_scale(scale);

  move(-72, 24); /* Should move the starting point into the array... */

  for (i = 0; i < 10; i++) {
    /* DRAW THE VECTOR */
    y1 = (*ptr++);
    x1 = (*ptr++);
    line(y1, x1); // Swapped x for y because of portrait mode Vectrex display
  }
}

static void Draw_crosshair(signed8 absx, signed8 absy, unsigned8 scale)
{
  signed8  x1, y1, x2, y2, dx, dy;
  const signed8 *ptr = crosshair_points;

  debugstr("Draw_crosshair");

  reset_beam();
  set_scale(0x7f);
  move(absx, absy);
  set_scale(scale);
  move(-0x20, -0x20);

  for (;;) {
    /* DRAW THE VECTOR */
    x1 = *ptr++; if (x1 == 0) break; 
    y1 = *ptr++; 
    x2 = *ptr++; 
    y2 = *ptr++; 

    dx = x2-x1; 
    dy = y2-y1;

    move(y1, x1); 
    line(dy, dx );
    move(-dy-y1,-dx-x1);
    //move(-y1, -x1);    
    /* back at relative 0,0 */
  }
}

/* 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_SHIFT 7 /* 128 */
//#define FOCAL_LENGTH_SHIFT 8 /* 256 */
#define FOCAL_LENGTH_SHIFT 9 /* 512 */
//#define FOCAL_LENGTH_SHIFT 10 /* 1024 */

#define FOCAL_LENGTH ((signed16)1<<(signed16)FOCAL_LENGTH_SHIFT)

static unsigned8 Credits;

static unsigned8 intro_rot;
static signed16 frame_number;
static unsigned8 global_flashing_intensity;

static signed16 HighScore;
static signed16 LastScore;
static signed16 Score;

#ifdef NOTYET
static signed16 UsingShields;
static signed16 Shields;
static signed16 shieldsegment;
static signed16 shieldsubticksleft;
#endif

static fp2_14 sinsymmetry(unsigned8 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(signed16 angle256)
{
  if ((unsigned8)angle256 >= 128U) return -sinsymmetry((unsigned8)(angle256-128)&127); else return sinsymmetry((unsigned8)angle256&127);
}

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

// The revolving intro placard...

static void drawtgintro(unsigned8 scale, unsigned8 bright) {
  signed16 i, from_x, from_y, to_x, to_y, objects, delta_fx, delta_tx;
  const signed8 *ptr;
  fp2_14 sin_rot, cos_rot;

  debugstr("drawtgintro");

  reset_beam();
  set_scale(scale);
  move(0,0); vectors_dumped = 0;
  intensity(bright);

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

  sin_rot = fp2_14_sin (intro_rot); cos_rot = fp2_14_cos (intro_rot);

  ptr = tg_intro_placard;
  for (i = 0; i < objects; i++) {
    from_y = *ptr++; // make coords center on 0,0 - this can be moved into the const array
    from_x = *ptr++;
    to_y = *ptr++;
    to_x = *ptr++;

    if (intro_rot != 64) { // optimise case where the placard is face-on.
      // y coords signed, in range -64:63  =>  0..127

      // These all need some tweaking to reduce inaccuracy due to lack of precision
      // - it is visibly wrong, but for now we will just live with it...
      delta_fx = (from_x * (from_y)) >> 8;
      delta_tx = (to_x * (to_y)) >> 8;
      from_x += (delta_fx * (cos_rot>>4)) >> 10;
      to_x += (delta_tx * (cos_rot>>4)) >> 10;
      from_y = (from_y * (sin_rot>>4)) >> 10;
      to_y = (to_y * (sin_rot>>4)) >> 10;
    }

    // I did implement a layer on top of move/line which optimised consecutive moves
    // and eliminated redundant ones where lines were adjacent, but for reasons that
    // are too long-winded to be worth going into, I've temporarily removed that code;
    // it can be found in the rcs logs if/when I add it back in.
 
    move((signed8)(from_y), (signed8)(from_x));
    line((signed8)(to_y-from_y), (signed8)(to_x-from_x));
    move((signed8)-(to_y), (signed8)-(to_x));

    vectors_dumped += 3;
    if (vectors_dumped > 12) {
      reset_beam(); // we are already at 0,0
      // not sure if these are needed?:
      set_scale(scale);
      move(0,0); vectors_dumped = 0;
      intensity(bright);
    }
  }
}

static void position_and_scale(signed8 absx, signed8 absy, signed16 scale)
{
  reset_beam();
  set_scale(0x7f);
  move_rel_XY(absx, absy);
  set_scale((unsigned8)scale);
}

static void show_digit(signed8 digit)
{
  // a binary chop or an array of procedures might be
  // faster depending on how switch is implemented, however
  // cmoc does not support arrays of procedure pointers.
  switch (digit) {
    case 0: SHOW_N0(); break;
    case 1: SHOW_N1(); break;
    case 2: SHOW_N2(); break;
    case 3: SHOW_N3(); break;
    case 4: SHOW_N4(); break;
    case 5: SHOW_N5(); break;
    case 6: SHOW_N6(); break;
    case 7: SHOW_N7(); break;
    case 8: SHOW_N8(); break;
    case 9: SHOW_N9(); break;
  }
}

static void SHOW_NUM(signed16 num, signed8 absx, signed8 absy, unsigned16 scale) { // left-aligned
  signed8 digit, zeroes;

  if (scale) position_and_scale(absx, absy, 0x20);

  // This replaces code that used divide by 10 and modulo 10.  Much faster.

#ifdef PREV_VERSION
  // doesn't handle -32768
  if (num < 0) num = -num; // TO DO... font character for '-'...
  zeroes = 0;
  // max 33 add/subtracts... could be less if use 8000, 4000, 2000, 1000 etc to chop instead
  digit = 0; while (num >= 10000) { num -= 10000; digit += 1; zeroes = 1; } if (zeroes) show_digit(digit);
  digit = 0; while (num >= 1000) { num -= 1000; digit += 1; zeroes = 1; } if (zeroes) show_digit(digit);
  digit = 0; while (num >= 100) { num -= 100; digit += 1; zeroes = 1; } if (zeroes) show_digit(digit);
  digit = 0; while (num >= 10) { num -= 10; digit += 1; zeroes = 1; } if (zeroes) show_digit(digit);
  show_digit((signed8)num);
#endif

  // handles -32768:32767
  if (num >= 0) num = -num; /* else MINUS(); */  // TO DO: add a font character for '-'.
  digit = 0;
  zeroes = 1; // CLRing is shorter
  // max 11 add/subtracts...
  if (num <= -20000) { num += 20000; digit += 2; zeroes = 0; }
  if (num <= -10000) { num += 10000; digit += 1; zeroes = 0; }
  if (!zeroes) show_digit(digit);
  digit = 0;
  if (num <= -8000) { num += 8000; digit += 8; zeroes = 0; } else if (num <= -4000) { num += 4000; digit += 4; zeroes = 0; }
  if (num <= -2000) { num += 2000; digit += 2; zeroes = 0; }
  if (num <= -1000) { num += 1000; digit += 1; zeroes = 0; }
  if (!zeroes) show_digit(digit);
  digit = 0;
  if (num <= -800) { num += 800; digit += 8; zeroes = 0; } else if (num <= -400) { num += 400; digit += 4; zeroes = 0; }
  if (num <= -200) { num += 200; digit += 2; zeroes = 0; }
  if (num <= -100) { num += 100; digit += 1; zeroes = 0; }
  if (!zeroes) show_digit(digit);
  digit = 0;
  if (num <= -80) { num += 80; digit += 8; zeroes = 0; } else if (num <= -40) { num += 40; digit += 4; zeroes = 0; }
  if (num <= -20) { num += 20; digit += 2; zeroes = 0; }
  if (num <= -10) { num += 10; digit += 1; zeroes = 0; }
  if (!zeroes) show_digit(digit);
  show_digit((signed8)-num);
}

static void Draw_lastscore(void)
{
  debugstr("Draw_lastscore");
  position_and_scale(-100,127,32);
  SHOW_SCORE();
  SHOW_NUM(LastScore, -98,113, 256);
}

static void Draw_highscore(void)
{
  debugstr("Draw_highscore");
  position_and_scale(40,127,32);
  SHOW_HIGH_SCORE();
  SHOW_NUM(HighScore, 52,113, 256);
}

static void Draw_insertcoin(void) // Not yet used
{
  debugstr("Draw_insertcoin");
  intensity(global_flashing_intensity<<1);
  position_and_scale(-40,-80,32);
  SHOW_INSERT_COIN();
}

static void Draw_start(void)
{
  debugstr("Draw_start");
  intensity(global_flashing_intensity<<1);
  position_and_scale(-39,-80,32);
  SHOW_PRESS_START();
}

static void Draw_credits(void)
{
  debugstr("Draw_credits");
  position_and_scale(-46,-30,48);
  SHOW_CREDITS();
  if (Credits > 9U) {
    SHOW_NUM(9, 0,0,0); // display any more than 9 as 9. (This is what many arcade machines did in real life)
  } else {
    SHOW_NUM(Credits, 0,0,0);
  }
}

static void Draw_ships_remaining(void)
{
  SHOW_NUM(10, 90,120, 64);
}

static void Draw_shields_remaining(void)
{
  SHOW_NUM(80, -5,127, 128);
}

static void Draw_score(void)
{
  SHOW_NUM(Score, -100,120, 64);
}

static void next_frame(void)
{
  debugstr("next_frame");
  wait_retrace();
  if ((frame_number & 16) == 0) {
    global_flashing_intensity += 4;
  } else {
    global_flashing_intensity -= 4;
  }
  frame_number++;
  //SHOW_NUM(Debug, -100,-100, 128);
}

int main(void) { // cmoc doesn't allow argc, argv
  signed16 frames;
  unsigned8 placard_scale, placard_brightness;

  vectors_dumped = 0;
  global_flashing_intensity = 0; frame_number = 0; // start flashing from off position
  frame_number = 0;
  init_stars();
  reset_beam();
  set_scale(0x7f);
  move(0,0);
  intensity(MAX_BRIGHTNESS);

  Debug = 0; Score = 72; HighScore = 2047; LastScore = 132; Credits = 0;
  for (;;) {
    debugstr("main loop");
    // approach banner:

    placard_scale = (unsigned8)((signed16)(0xFF-160));
    placard_brightness = (unsigned8)((signed16)(((signed16)MAX_BRIGHTNESS+(signed16)MAX_BRIGHTNESS-160)));
    intro_rot = 128;
    // start at 128 step by -2  end at 64 after one full revolution (128 + 32 steps)
    for (frames = 0; frames < 160; frames++) {
      next_frame();
      drawtgintro(placard_scale, placard_brightness>>1);
      if ((intro_rot&128) != 0) {
        next_frame();
        drawtgintro(placard_scale, placard_brightness>>1);
        next_frame();
        drawtgintro(placard_scale, placard_brightness>>1);
      }
      placard_scale++; placard_brightness++;
      //stars(1);
      intro_rot -= 2;
    }

    {signed16 i;
      for (i = 0; i < 2; i++) {
        placard_brightness = MAX_BRIGHTNESS&0xF8;
        while (placard_brightness != 0) {
          next_frame();
          drawtgintro(placard_scale, placard_brightness);
          //stars(1);
	  placard_brightness -= 8;
        }
        while (placard_brightness != (MAX_BRIGHTNESS&0xF8)) {
	  placard_brightness += 8;
          next_frame();
          drawtgintro(placard_scale, placard_brightness);
          //stars(1);
        }
      }
    }
    placard_brightness = MAX_BRIGHTNESS&0xF8;
    while (placard_brightness != 0) {
      next_frame();
      drawtgintro(placard_scale, placard_brightness);
      // unfortunately this does not affect stars: intensity(MAX_BRIGHTNESS-placard_brightness);
      stars(1);
      placard_brightness -= 8;
    }
    intensity(MAX_BRIGHTNESS);
    //stars:
    debugstr("wait for coins...");
    global_flashing_intensity = 0; frame_number = 0; // start flashing from off position
    for (frames = 0; frames < 255; frames++) {
      next_frame();
      stars(1);
      if (frames < 80) {
        Draw_insertcoin();
        intensity(MAX_BRIGHTNESS);
        Draw_lastscore();
        Draw_highscore();
      } else if (frames < 160) {
        if (frames == 80) Credits += 1;
        if (frames == 120) Credits += 1;
        Draw_start();
        intensity(MAX_BRIGHTNESS);
        Draw_credits();
        Draw_lastscore();
        Draw_highscore();
      } else {
        Draw_ships_remaining();
        Draw_shields_remaining();
        Draw_score();
        Draw_crosshair((signed8)(frames>>1), (signed8)(-(frames>>2)), 0x38);
      }
    }
    Credits = 0;
    // shields:
    debugstr("shields...");
    for (frames = 0; frames < 200; frames++) {
      next_frame();
      if ((frames > 120) && (frames < 200)) {
        intensity((unsigned8)pickrandom(64,127));
        if (pickrandom(0,31)) draw_shields(0xF0);
      } else {
        Draw_crosshair((signed8)(frames>>1), (signed8)(-(frames>>2)), 0x38); // don't allow shooting while shields are up...
      }
      stars(1);
      Draw_ships_remaining();
      Draw_shields_remaining();
      Draw_score();
      //Only in attract mode:
      //Draw_lastscore();
      //Draw_highscore();
    }
    // whoosh:
    debugstr("whoosh...");
    for (frames = 0; frames < 64; frames++) {
      next_frame();
      stars(3);
      intensity((unsigned8)(64-frames)*2);
      Draw_ships_remaining();
      Draw_shields_remaining();
      Draw_score();
      //Only in attract mode:
      //Draw_lastscore();
      //Draw_highscore();
    }
  }
  return 0;
}