#include <vectrex.h>

static const char const *Version = "UNBLOCKME V0.2A 117 LEVELS";

#ifndef TRUE
#define TRUE (0==0)
#define FALSE (0!=0)
#endif

#define uint8_t unsigned int
#define int8_t  int
#define int16_t long
#define int32_t long long

#define HORIZONTAL 0
#define VERTICAL 1

#define UP 0
#define LEFT 0
#define DOWN 1
#define RIGHT 1

#define SCALETEST 1 /* saves 5152 cycles */

// It's only the surrounding frame which needs vectors to be split in two,
// so we could if we preferred have two scales, one for blocks and one
// for the frame, so that the frame was drawn in single pieces.
// But since we are already down to an average of 30,000 cycles, there's
// no great advantage to be had from optimising more.

// Although, that said, one of the corners I had to cut to get down to 30K cycles
// was removing any permanent on-screen text.  If we ever want that back, then
// more scaling tweaks might be worth implementing.  But I like the clean interface.

#ifdef SCALETEST
#define DRAWING_SCALE 0x60
#define SCALE 40
#define GAP 4
#define FUDGE 2
#define CURSOR_SIZE 6
#else
#define DRAWING_SCALE 0xC0
#define SCALE 20
#define GAP 2
#define FUDGE 0
#define CURSOR_SIZE 3
#endif

#define XOFF (3*SCALE)
#define YOFF (3*SCALE)


static inline void set_scale(unsigned int s) {
  VIA_t1_cnt_lo = s;
}

#ifdef DEBUG
static char debug_buffer[64];
void debug_int32(int y, int x, char *var, int32_t i) {
  char *s = debug_buffer, *startpos, *endpos;
  (void)y; (void)x;
  while (*var != '\0') {
    char c;
    c = *var++;
    *s++ = c;
  }
  *s++ = ':'; *s++ = ' ';
  if (i < 0) *s++ = '-'; else i = -i;
  startpos = s;
  for (;;) {
    int32_t j = i / ((typeof(i))(10));
    char digit = (char)(i - j*((typeof(i))(10)));
    *s++ = '0'-digit;
    i = j;
    if (i == 0) break;
  }
  endpos = s-1;
  //reverse(startpos, s);
  while (startpos < endpos) {
    char t = *startpos;
    *startpos = *endpos;
    *endpos = t;
    startpos += 1; endpos -= 1;
  }
  *s++ = 0x80;
  *s = '\0';
  Reset0Ref(); set_scale(DRAWING_SCALE); Intensity_5F();
  Print_Str_d(y, x, debug_buffer);
}
#endif

#define NUM_GAMES (42U+75U)
const char const *puzzle[NUM_GAMES]  __attribute__ ((section(".text")))  = {
// new 75:

  "..JJFF"
  "AAG.ID"
  "XXG.ID"
  "B.HHHE"
  "B.KKKE"
  "CCC..E",

  "EEC..."
  "IHCAAA"
  "IHXXBK"
  "GG.FBK"
  "...FD."
  ".JJ.D.",

  "JBBKKA"
  "J.HHHA"
  "XXFE.A"
  "..FECC"
  ".GGGI."
  ".DDDI.",

  ".IDDD."
  ".I.AAE"
  "XXJBHE"
  "C.JBH."
  "CF.B.."
  "CFGGKK",

  "JJ..F."
  "IHHHFC"
  "I.EXXC"
  "BBE..C"
  ".ADD.."
  ".AKKGG",

  "G.EEKD"
  "G.I.KD"
  "..IXXJ"
  "BBBF.J"
  "..AFCC"
  "HHAF..",

  "..CCEE"
  "IILBJJ"
  "XXLB.."
  "KHHB.."
  "KF.DD."
  "KFAAGG",

  "J.IIC."
  "JHHHCG"
  "..EXXG"
  "BBEA.G"
  "...AFF"
  "DDDA..",

  "FDDD.J"
  "F.BEEJ"
  "XXBHIL"
  ".CCHIL"
  "...AKK"
  ".GGA..",

  "H....."
  "H.FBJ."
  "XXFBJ."
  ".A.BDD"
  "GA.IIC"
  "GAEE.C",

  "..AFFF"
  "..AHD."
  "XXEHDG"
  ".BEKKG"
  "JBII.G"
  "JLLCC.",

  "..DGGG"
  "CCD..H"
  "LFXXEH"
  "LFIIEH"
  "..ABJJ"
  "KKAB..",

  "H....."
  "H.FBJ."
  "XXFBJ."
  ".A.BDD"
  "GA.IIC"
  "GAEE.C",

  "..AFFF"
  "..AHD."
  "XXEHDG"
  ".BEKKG"
  "JBII.G"
  "JLLCC.",

  "..DGGG"
  "CCD..H"
  "LFXXEH"
  "LFIIEH"
  "..ABJJ"
  "KKAB..",

  "....II"
  "BBAAAK"
  "XXFGEK"
  ".CFGE."
  "JCF..."
  "JDDHHH",

  "..C.GG"
  "..C..E"
  "XXD..E"
  "..DIIF"
  "HHHB.F"
  "AA.B..",

  ".DJJKF"
  ".DCHKF"
  "XXCH.F"
  "..CEBB"
  "IGGE.."
  "I.AAA.",

  ".DGG.."
  ".DHAA."
  "XXHIBC"
  "EEEIBC"
  "K.FFF."
  "K..JJ.",

  "GIKKJF"
  "GI..JF"
  "G.AXXF"
  "DDAB.."
  "H.EB.."
  "H.EBCC",

  "..IIKB"
  "GCCCKB"
  "G.XXLF"
  "JJDALF"
  "..DAHH"
  "EEEA..",

  "..C.EE"
  "G.C.HH"
  "GXXIBJ"
  "FA.IBJ"
  "FA.ID."
  "F...D.",

  "....II"
  "..BAA."
  "XXBF.."
  "J.BFCC"
  "JEEFHD"
  "KKGGHD",

  ".FFFK."
  "JJ.EK."
  "XX.EBH"
  "ALL.BH"
  "A.DCGG"
  "IIDC..",

  "AII..."
  "AGGG.K"
  "XXC..K"
  "HFC.EE"
  "HFJ.DD"
  "H.JBB.",

  "JCCCFF"
  "J..GBB"
  "XXDG.E"
  "KLDAAE"
  "KLHHHE"
  "....II",

  "IFFKKE"
  "I.AAAE"
  "XXHD.E"
  "..HDBB"
  ".GGGC."
  "..JJC.",

  "H.IIB."
  "H.KKB."
  "GGXXDJ"
  "FC..DJ"
  "FCLEDJ"
  ".CLEAA",

  ".HKKBE"
  "GH.FBE"
  "GXXFBJ"
  "DLLF.J"
  "D.ACCC"
  "..A.II",

  "HJJDCC"
  "HGGD.."
  "XXBE.."
  "..BEAA"
  "..BKKF"
  ".II..F",

  "..BGG."
  "..BEE."
  "XXBA.."
  "FDDA.."
  "F..A.."
  "F..CC.",

  "..ACBB"
  "..ACK."
  "XXACKE"
  ".G...E"
  "JGDDFI"
  "J.HHFI",

  "...BKK"
  "..HBEE"
  "XXHI.F"
  "GG.I.F"
  "LDDDJC"
  "L.AAJC",

  "IJJF.."
  "IKKF.."
  "XXCF.."
  ".BCAA."
  "HBEGG."
  "HBEDD.",

  "FFB.J."
  "H.BCJ."
  "HXXC.."
  ".I.C.E"
  ".IDAAE"
  "..DGGE",

  "...FCC"
  "..BFK."
  "XXBHKG"
  "JJ.HEG"
  "IAAAED"
  "ILLL.D",

  "F.J..."
  "F.JIDH"
  "XXBIDH"
  ".CB.AA"
  "ECKK.."
  "ECGG..",

  "AIDDHJ"
  "AIBEHJ"
  "XXBE.."
  "..F..."
  "..FCGG"
  "..FC..",

  "I.KKDD"
  "I.AAAL"
  "XXEB.L"
  "..EBHH"
  ".GGGFC"
  "..JJFC",

  "L.GGGE"
  "L.BBBE"
  "XXFHAI"
  "JCFHAI"
  "JC.D.."
  ".C.DKK",

  "I.GGGC"
  "I....C"
  "XXEFLJ"
  "HHEFLJ"
  "D.EBBB"
  "DKKAAA",

  "DDDBJE"
  "LHHBJE"
  "L.XXJE"
  "CCK..."
  ".GKII."
  ".GAAFF",

  "FDDDKK"
  "F.AAJJ"
  "XXBGEI"
  "CCBGEI"
  ".H.LE."
  ".H.L..",

  "FKKIII"
  "F.JDEE"
  "XXJDBH"
  "..AABH"
  ".CCCBH"
  ".GG...",

  "JFFFBK"
  "J.E.BK"
  "XXE..."
  "..GADD"
  "IIGA.C"
  "HHHA.C",

  ".AA.D."
  "HG..D."
  "HGXXFI"
  "H..CFI"
  "BBBCF."
  "...CEE",

  "CCJJJH"
  "..KK.H"
  "XX..EL"
  "FAAAEL"
  "F.BIGG"
  "DDBI..",

  "..ADDD"
  "..ALLK"
  "XXAFMK"
  "HIIFMG"
  "HECCCG"
  ".EBBJJ",

  "..D.EE"
  "C.DHHH"
  "CXXJ.."
  "CB.J.A"
  "KB.FFA"
  "KBIIGG",

  "A..IKK"
  "AD.I.."
  "ADEXXJ"
  ".DE.CJ"
  ".FFFCG"
  "HHBBCG",

  ".LCCII"
  ".LEEEM"
  "XXFJ.M"
  "..FJDD"
  "G.KKHB"
  "GAAAHB",

  "BCJJJ."
  "BCIII."
  "XXFM.."
  "L.FMEE"
  "LGGDD."
  "KKAAHH",

  "IIHHGD"
  "EEE.GD"
  ".JKXXD"
  "FJKA.."
  "FBBA.."
  "F..ACC",

  "KK.CE."
  "BBBCEJ"
  "XX.CAJ"
  "H.DDA."
  "H.IGGL"
  "FFI..L",

  ".JJHHH"
  "EAAAGG"
  "EXX.FK"
  ".BB.FK"
  ".D.III"
  ".D.CCC",

  ".BFFAD"
  "GB.IAD"
  "GXXI.D"
  "J.CEEE"
  "J.C..."
  "..C.HH",

  "FFH.AA"
  "JJH.ID"
  "CXX.ID"
  "CBBBID"
  "C..LEE"
  "KK.LGG",

  "F.BBB."
  "F.A..."
  "XXA..."
  "..AE.."
  "DDDE.."
  "CCC...",

  "..HIII"
  "DDH..."
  "EXXB.."
  "E..BFF"
  "JJKKKC"
  "AAAGGC",

  ".IJJB."
  ".IEEB."
  "KXXFB."
  "K.HFCC"
  "L.HDDG"
  "L.AAAG",

  "F.IIKK"
  "F.JJGG"
  "AAAXXH"
  "..BBBH"
  ".....E"
  "CC.DDE",

  ".KJJEE"
  ".KDAAA"
  "XXD..G"
  "..DLBG"
  "FCCLB."
  "FIIHH.",

  "IIFB.."
  "..FB.."
  "AXXB.."
  "A.DDD."
  "ACCCEG"
  "JJHHEG",

  "..FBBB"
  "EEF.C."
  ".DXXC."
  ".DAAA."
  ".D...."
  "......",

  "..C.EE"
  "DDC.BK"
  "IXXABK"
  "ILLAHG"
  "IF.AHG"
  ".F.JJJ",

  "..FGGG"
  "DDFA.."
  "KXXAE."
  "KCCAE."
  "..BJJH"
  "IIB..H",

  "JIICCC"
  "JAAB.K"
  "XX.BHK"
  "EEEBHG"
  "...DDG"
  "..FF.G",

  "LG.DCC"
  "LG.D.B"
  "JG.XXB"
  "JKK.IE"
  "AAH.IE"
  "..HFF.",

  "GGG.CI"
  "KK..CI"
  "XX..EH"
  "JDFFEH"
  "JDBAA."
  "LLB...",

  "BEEK.."
  "B.FKHH"
  "XXFG.J"
  "CCCG.J"
  "DIA..."
  "DIA...",

  ".KJJFI"
  ".KB.FI"
  "XXB..I"
  "CCB..."
  "EGGH.."
  "EDDHAA",

  ".IICG."
  "FFFCGE"
  "XX.CHE"
  "B.JJH."
  "B.DAAL"
  "KKD..L",

  "IBBBHH"
  "I.GDDD"
  "XXGE.."
  "..GE.."
  ".AFFF."
  ".ACCJJ",

  "FFAB.."
  "C.AB.."
  "CXXB.."
  "CGIIHH"
  ".GEEJK"
  ".DD.JK",

  "EEEDA."
  "HHHDA."
  ".XXJA."
  "G..JCC"
  "G.BFFI"
  "..B..I",

// original 42:

  "II.B.."
  "JHHB.E"
  "JAAXXE"
  "D.F..E"
  "D.F.CC"
  "D.FGGG",
  
  "EDDD.."
  "E.KICC"
  "XXKIAG"
  ".JFFAG"
  ".J.BB."
  ".J..HH",
  
  ".IIIGG"
  "..B..."
  "XXB..."
  "HEEJJK"
  "HFFADK"
  "CC.ADK",
  
  "..A.JJ"
  "DDABBI"
  "XXAFGI"
  ".CCFGE"
  ".HH.GE"
  ".....E",
  
  "HIIDDD"
  "H.FEJJ"
  "XXFEC."
  "....C."
  ".AAACG"
  "..BBBG",
  
  "...KBB"
  "I.AK.D"
  "I.AXXD"
  "GCCCHL"
  "GFEEHL"
  ".FJJH.",
  
  "..FAAA"
  "..FBEE"
  "XXFB.."
  "CDDD.H"
  "C....H"
  "CGG...",
  
  ".HGGAK"
  ".HF.AK"
  "XXFE.."
  "DDBEJJ"
  "C.BE.."
  "C.II..",
  
  "IIE..."
  "J.EFFL"
  "JXXAGL"
  "...AGD"
  "HHHABD"
  "CCKKB.",
  
  ".AADD."
  "EEEBF."
  "JXXBFI"
  "JHCKKI"
  "GHC..."
  "G.....",
  
  "...BH."
  "I..BH."
  "IXXBJC"
  "G.A.JC"
  "GEADDC"
  "GEFFF.",
  
  ".AABI."
  "HHHBI."
  "XX.BFD"
  "G.KKFD"
  "G.ECCL"
  "JJE..L",
  
  "EFFFJJ"
  "E.HC.D"
  "XXHC.D"
  ".GAA.."
  ".GKK.."
  "IIBBB.",
  
  ".JAAAE"
  ".JC..E"
  "XXC..."
  "H.CGGG"
  "HBBDFI"
  "KK.DFI",
  
  ".KJJAA"
  ".K.BD."
  "XX.BDF"
  "I.CCCF"
  "I.HGGF"
  "EEH...",
  
  ".G...."
  ".G..FF"
  "XXBIAE"
  "C.BIAE"
  "CDB..."
  "CDHH..",
  
  "AIJJJ."
  "AID..."
  "XXDBF."
  "GGDBFL"
  "E..KKL"
  "E.HHCC",
  
  "AAJ..C"
  "I.JBBC"
  "I.XXKC"
  "HH.FK."
  "DDDFE."
  "GGGFE.",
  
  ".DGG.."
  ".D.AFF"
  "XXHA.."
  "..HA.."
  "IEEECC"
  "I.BB..",
  
  "A.EHHH"
  "A.EI.."
  "AXXI.."
  ".JCCFD"
  ".JB.FD"
  ".JBGGD",
  
  "JGGEII"
  "J..E.F"
  "XXA..F"
  "..ACCF"
  "..AHDD"
  "BBBH..",
  
  ".JJ..."
  ".FF.BB"
  "XXA..G"
  "CDAIIG"
  "CDK.HH"
  "CDKEEE",
  
  "GGLL.B"
  "DDDEEB"
  "J.AXXB"
  "J.AHC."
  "IKAHC."
  "IK.HFF",
  
  "C.FFKK"
  "C.BEHH"
  "XXBE.."
  ".LBEAA"
  ".LJJ.G"
  ".IIDDG",
  
  "J..GAA"
  "JFFGIB"
  "XXDHIB"
  "KEDHCC"
  "KEDLL."
  "KEMM..",
  
  "DIIH.."
  "DB.HGG"
  "DBEXXJ"
  "AAE.LJ"
  "CCC.LK"
  "FFF..K",
  
  "..BIIC"
  "..BE.C"
  "XXBEFC"
  "HGGGF."
  "HDDA.."
  "JJ.A..",
  
  ".EJJ.."
  ".E.DBB"
  "XXKD.."
  "GGKD.."
  "HCCCAA"
  "HIIFF.",
  
  "FFEEH."
  "IBBBHC"
  "I.KXXC"
  "JJKD.C"
  "...DAA"
  "GGGD..",
  
  "A..FFF"
  "A.LLCC"
  "XXH..I"
  "JJHG.I"
  "BBBGKE"
  "DDD.KE",
  
  ".G.EED"
  ".GIIID"
  "HG.XXD"
  "HFFFJJ"
  ".AB.CC"
  ".AB.KK",
  
  ".FFJJJ"
  "BB.C.I"
  "KXXCGI"
  "KDAAGL"
  "HD...L"
  "HD.EEE",
  
  ".JHHLL"
  ".JCCCE"
  "XXBG.E"
  "..BGKK"
  "I.AAMD"
  "IFFFMD",
  
  "AAA..G"
  "CC...G"
  "IEEXXF"
  "I....F"
  "HJJBBB"
  "H...DD",
  
  "K.JJLL"
  "K.GGHH"
  "XXFE.."
  "DDFE.C"
  "AAAE.C"
  ".IIBB.",
  
  "EEEHB."
  "FKKHB."
  "FXXD.."
  "F.GDCC"
  "..GAAJ"
  "III..J",
  
  "..EE.."
  "..DFF."
  "XXD..."
  "..DAAA"
  "CCCB.."
  "GGGB..",
  
  "HH..IL"
  "CCC.IL"
  "XXA..K"
  "BFAGGK"
  "BF.EDD"
  "BJJE..",
  
  ".JJFB."
  "DDDFB."
  "XX.FGH"
  "E.KKGH"
  "E.CAAL"
  "IIC..L",
  
  "F.HH.."
  "FDDB.."
  "XXEB.G"
  "..EAAG"
  "..C..."
  "..C...",
  
  "EDD.G."
  "E.A.G."
  "XXAF.."
  "KK.FJJ"
  "CCCHHL"
  ".IIBBL",
  
  ".GCCFF"
  ".G.HD."
  "XX.HD."
  "IBBBDJ"
  "I.AKKJ"
  "I.AEEJ",
};

static unsigned char patList[2];
static void drawline_patterned(int y, int x, unsigned char pat)
{
  patList[0]=(unsigned char)y;
  patList[1]=(unsigned char)x;
  *(volatile unsigned char *)0xC829 = pat;
  *(volatile unsigned char *)0xC823 =0;
  Draw_Pat_VL(patList);
}

// a lot of global state in this program.
int block_x, block_y;
char block_tag;
char dragged_block;
int dragged_block_orientation;
int can_move_forward, can_move_back;

void draw_block(char *p, int ox, int oy, int horizontal, char block) {
  int h,w,blocksize;
  if (horizontal) {
    h = 1; w = 2;
    if ((ox < 4) && (p[oy*6+ox+2] == block)) w = 3;
    blocksize = w;
  } else {
    h = 2; w = 1;
    if ((oy < 4) && (p[(oy+2)*6+ox] == block)) h = 3;
    blocksize = h;
  }

  Reset0Ref(); set_scale(DRAWING_SCALE); Intensity_5F();
  Moveto_d((6-oy)*SCALE-YOFF-GAP, ox*SCALE-XOFF+GAP); // top-left corner of where block will be drawn

  if (block == block_tag) {
    if (horizontal) {
      dragged_block_orientation = HORIZONTAL;
      can_move_forward = (((ox + w) < 6) && (p[oy*6 + ox + w] == '.'));
      can_move_back = (ox > 0) && (p[oy*6 + ox - 1] == '.');
    } else {
      dragged_block_orientation = VERTICAL;
      can_move_forward = (((oy + h) < 6) && (p[(oy+h)*6 + ox] == '.'));
      can_move_back = ((oy > 0) && (p[(oy-1)*6 + ox] == '.'));
    }
    // if there is room for this block to move, draw it dashed - otherwise draw it
    // solid but with Intensity_7F() ... make note of which direction movement is
    // possible. it may be both so need two flags: forward and back maps to right,down and up,left
    // depending on orientation
    Intensity_7F();
    if (can_move_forward || can_move_back) {
      drawline_patterned(0, w*SCALE-GAP*2, 0xC3);
      drawline_patterned(-h*SCALE+GAP*2, 0, 0xC3);
      drawline_patterned(0,-w*SCALE+GAP*2, 0xC3);
      drawline_patterned(h*SCALE-GAP*2, 0, 0xC3);
      if (block == 'X') {
        // The key block
        drawline_patterned(-h*SCALE+GAP*2, w*SCALE-GAP*2, 0xC3);
      }
      return;
    }
  }
  Draw_Line_d(0, w*SCALE-GAP*2);
  Draw_Line_d(-h*SCALE+GAP*2, 0);
  Draw_Line_d(0,-w*SCALE+GAP*2);
  Draw_Line_d(h*SCALE-GAP*2, 0);
  if (block == 'X') {
    Draw_Line_d(-h*SCALE+GAP*2, w*SCALE-GAP*2);
  }
}

void Move(char *p, char c, int dir) {
  int i,j;
  for (j = 0; j < 6; j++) {   // top to bottom
    for (i = 0; i < 6; i++) { // left to right
      if (p[j*6+i] == c) { // first one it hits, topmost or leftmost
        if ((i < 4) && (p[j*6+i+2] == c)) {
          // left of horizontal triple
          //fprintf(stderr, "[3] left/right\n");
          if ((dir==LEFT) && (i > 0) && (p[j*6+i-1] == '.')) {
            // horizontal triple move left
            //fprintf(stderr, "[3] left\n");
            p[j*6+i-1] = c; p[j*6+i+2] = '.';
          } else if ((dir==RIGHT) && (i < 3) && (p[j*6+i+3] == '.')) {
            // horizontal triple move right
            //fprintf(stderr, "[3] right\n");
            p[j*6+i+3] = c; p[j*6+i] = '.';
          }
        } else if ((i < 5) && (p[j*6+i+1] == c)) {
          // left of horizontal pair
          //fprintf(stderr, "[2] left/right\n");
          if ((dir==LEFT) && (i > 0) && (p[j*6+i-1] == '.')) {
            // horizontal pair move left
            //fprintf(stderr, "[2] left\n");
            p[j*6+i-1] = c; p[j*6+i+1] = '.';
          } else if ((dir==RIGHT) && (i < 4) && (p[j*6+i+2] == '.')) {
            // horizontal pair move right
            //fprintf(stderr, "[2] right\n");
            p[j*6+i+2] = c; p[j*6+i] = '.';
          }
        }

        if ((j < 4) && (p[(j+2)*6+i] == c)) {
          // top of vertical triple
          //fprintf(stderr, "[3] up/down\n");
          if ((dir==UP) && (j > 0) && (p[(j-1)*6+i] == '.')) {
            // vertical triple move up
            p[(j-1)*6+i] = c; p[(j+2)*6+i] = '.';
            //fprintf(stderr, "[3] up\n");
          } else if ((dir==DOWN) && (j < 3) && (p[(j+3)*6+i] == '.')) {
            // vertical triple down
            p[(j+3)*6+i] = c; p[j*6+i] = '.'; 
            //fprintf(stderr, "[3] down\n");
         }
        } else if ((j < 5) && (p[(j+1)*6+i] == c)) {
          // top of vertical pair
          //fprintf(stderr, "[2] up/down\n");
          if ((dir==UP) && (j > 0) && (p[(j-1)*6+i] == '.')) {
            // vertical pair move up
            p[(j-1)*6+i] = c; p[(j+1)*6+i] = '.';
            //fprintf(stderr, "[2] up\n");
          } else if ((dir==DOWN) && (j < 4) && (p[(j+2)*6+i] == '.')) {
            // vertical pair down
            p[(j+2)*6+i] = c; p[j*6+i] = '.';
            //fprintf(stderr, "[2] down\n");
          }
        }
        return;
      }
    }
  }
}

static unsigned int next_game = 0;

void draw_screen(char *p, int show_screen_no) {
  int i,j;
  char block;
  char lev[5] = { ' ', ' ', ' ', 0x80, '\0' };
  unsigned int tens, hundreds;
  Reset0Ref();
  if (show_screen_no || (Vec_Btn_State&1)) {
    // 2413 extra cycles for this: :-(
    int first_digit = 2;
    lev[0] = lev[1] = lev[2] = ' ';
    hundreds = 0U;
    tens = next_game/10U;
    if (tens >= 10U) { tens -= 10U; hundreds = 1U; first_digit = 0;}
    if (tens && !hundreds) first_digit = 1;

    if (hundreds) lev[first_digit++] = '1';
    if (tens || hundreds) lev[first_digit++] = (char)tens+'0';
    lev[first_digit] = (char)(next_game - tens*10 - hundreds*100)+'0';

    set_scale(DRAWING_SCALE); Intensity_7F();
    if (hundreds) {
      first_digit = -21;
    } else if (tens && !hundreds) {
      first_digit = -26;
    } else {
      // single_digit
      first_digit = -30;
    }
    Print_Str_d(-120, first_digit, lev);
  }
  Intensity_3F();
  set_scale(DRAWING_SCALE);
  Moveto_d(0*SCALE-YOFF-2-FUDGE, 0*SCALE-XOFF-2-FUDGE); // top-left corner of where block will be drawn
  // might be better to move to center of block at MOVESCALE before drawing at DRAWSCALE,
  // to allow for a small gap around the blocks...

#ifdef SCALETEST
  // on vectrex it is drawing with a small gap in the center of the three
  // long perimeters.  make sure there isn't a bug here in the handling of GAP...
  // (though most likely it is a vectrex issue.  It looks OK in the emulator)
  Draw_Line_d(0, 3*SCALE+GAP);
  Draw_Line_d(0, 3*SCALE+GAP);
#else
  Draw_Line_d(0, 6*SCALE+GAP*2);
#endif

  Draw_Line_d(3*SCALE, 0);
  Moveto_d(SCALE+GAP*2, 0);
  Draw_Line_d(2*SCALE, 0);

#ifdef SCALETEST
  Draw_Line_d(0, -3*SCALE-GAP);
  Draw_Line_d(0, -3*SCALE-GAP);
#else
  Draw_Line_d(0, -6*SCALE-GAP*2);
#endif

#ifdef SCALETEST
  Draw_Line_d(-3*SCALE-GAP, 0);
  Draw_Line_d(-3*SCALE-GAP, 0);
#else
  Draw_Line_d(-6*SCALE-GAP*2, 0);
#endif

  for (j = 0; j < 6; j++) {   // top to bottom
    for (i = 0; i < 6; i++) { // left to right
      block = p[j*6+i];
      if (block == '.') continue;
      if ((i > 0) && (p[j*6+i-1] == block)) {
        // horizontal block handled already
      } else if ((i < 5) && (p[j*6+i+1] == block)) {
        // horizontal block
        draw_block(p, i,j, TRUE, block);
      } else if ((j > 0) && (p[(j-1)*6+i] == block)) {
        // vertical block handled already
      } else if ((j < 5) && (p[(j+1)*6+i] == block)) {
        // vertical block
        draw_block(p, i,j, FALSE, block);
      } else {
        // error
      }
    }
  }
}

void fetch_new_game(char *p) {
  int i,j;

  if (next_game == NUM_GAMES) {
    // Need some bells and whsitles to congratulate the player.
    for (;;) {
      // Should we add a loud musical fanfare?
      Wait_Recal();
      Reset0Ref(); set_scale(DRAWING_SCALE); Intensity_7F();
      Print_Str_d(-120, -48, "CHAMPION!\x80");
      draw_screen(p, FALSE);
    }
  }
  for (j = 0; j < 6; j++) {   // top to bottom
    for (i = 0; i < 6; i++) { // left to right
      p[j*6+i] = puzzle[next_game][j*6+i]; // copy to writable string
    }
  }
  next_game += 1;
}

void Get_Cursor(char *p) {
  int mouse_x, mouse_y;

  // setting this to 0x7f or 0x80 freezes tracki
  //*(volatile int *)0xC81A = (int)0x80; // minimum analog resolution
  Joy_Analog();

  // snap mouse to nearest block
  // avoid divides.
  mouse_x = (((Vec_Joy_1_X>>1)+1) >> 4) + 3;
  mouse_y = (((Vec_Joy_1_Y>>1)+1) >> 4) + 2;

  if (mouse_x < 0) mouse_x = 0;
  if (mouse_y < 0) mouse_y = 0;
  if (mouse_x > 5) mouse_x = 5;
  if (mouse_y > 5) mouse_y = 5;

  block_x = mouse_x; // grid coordinates within p[]
  block_y = 5-mouse_y;
  mouse_y = (mouse_y-3) * SCALE + SCALE/2;
  mouse_x = (mouse_x-3) * SCALE + SCALE/2;

  block_tag = p[block_y*6+block_x]; // used to highlight selected block

  // There is a problem displaying the cursor, where it gets distorted depending
  // on the y coordinate.  Make sure that the cause is a vectrex problem and not
  // an actual bug, perhaps caused by wrong scaling of the positioning of the
  // beam before drawing the two bars of the X. (although it does look OK to me)
  Reset0Ref(); set_scale(DRAWING_SCALE); Intensity_7F();
  // Draw an X at the cursor
  Moveto_d(mouse_y-CURSOR_SIZE, mouse_x-CURSOR_SIZE);
  // lower left to top right
  Draw_Line_d(CURSOR_SIZE*2,CURSOR_SIZE*2); //   /
  Reset0Ref(); set_scale(DRAWING_SCALE); Intensity_7F();
  Moveto_d(mouse_y+CURSOR_SIZE, mouse_x-CURSOR_SIZE);
  // upper left to lower right
  Draw_Line_d(-CURSOR_SIZE*2,CURSOR_SIZE*2); //   \ ...
}


typedef enum {WAITING, MOVING, STOPPING, DONE} STATE;
STATE state = WAITING; // set to DONE to skip intro animation
#define set_scale(s) do { VIA_t1_cnt_lo = s; } while (0)
static long reset_size;
struct cartridge_t // from cartridge.c unfortunately
{
	char copyright[11];			// copyright string, must start with "g GCE" and must end with "\x80"
	const void* music;			// 16 bit memory adress of title music data
	signed int title_height;	// signed 8 bit value, height of game title letters
	unsigned int title_width;	// unsigned 8 bit value, width of game title letters
	int title_y;				// signed 8 bit value, y coordinate of game title
	int title_x;				// signed 8 bit value, x coordinate of game title
	char title[]; 				// game title string, must end with "\x80\x00"
};

extern struct cartridge_t game_header __attribute__((section(".cartridge"))); // dropped const.

void fake_title(int x, int y) {
    Reset0Ref(); set_scale(0x70);
    // default offset is -16 -72
    Moveto_d(15,73);
    Moveto_d(game_header.title_y,game_header.title_x);
    Moveto_d(y,x);
    *(volatile long *)0xC82A = reset_size;
    Print_Str_d(69,-45, game_header.title);
    Reset0Ref(); set_scale(0x70);
    Moveto_d(y,x);
    set_scale(0x38); Moveto_d(0,1);
    *(volatile long *)0xC82A = (long)0xf848; // ht wd
    Print_Str_d(21,-38, game_header.copyright);
}

static int sin, cos;

int main(void) {
  // int steps, direction; char block;
  int prev_x, prev_y;
  unsigned int Prev_Btn_State = 0;
  int mouse_down = 0, mouse_was_down = 0;
  char p[6*6];
  int delay = 127;
  sin = -96; cos = 1;
  reset_size = *(volatile long *)0xC82A;
  for (;;) {

    switch (state) {
      case WAITING:  Wait_Recal();
                     Intensity_7F();
                     fake_title(cos-31, sin);
                     delay -= 1;
                     if (delay == 0) {
                       state = MOVING;
                       delay = 2; // loops/2
                     }
                     break;
      case MOVING:   Wait_Recal();
                     Intensity_7F();
                     sin = sin - (cos >> 4);
                     cos = cos + (sin >> 4);
                     fake_title(cos-31, sin);
                     if (sin == -96 && cos == 1) {
                       delay -= 1;
                       if (delay == 0) {
                         state = STOPPING;
                         delay = 0x7F;
                       }
                     }
                     break;
      case STOPPING: Wait_Recal();
                     Intensity_a((unsigned int)delay);
                     fake_title(cos-31, sin); // fade out
                     delay -= 1;
                     if (delay == 0) state = DONE;
                     break;
      case DONE:     goto PLAY;
                     break;
    }

  }
PLAY:
  dragged_block = 0, dragged_block_orientation = -1;
  can_move_forward = FALSE; can_move_back = FALSE; // short for 'dragged block can move forward'
  fetch_new_game(p);
  Vec_Joy_Mux_1_X = 1; // enable analog joystick mode
  Vec_Joy_Mux_1_Y = 3;
  Get_Cursor(p); // place result in block_x, block_y
  prev_x = block_x; prev_y = block_y; // initialise.

  for (;;) {
    Wait_Recal();
    Reset0Ref(); set_scale(DRAWING_SCALE);

    block_tag = 0;
    Get_Cursor(p); // place result in block_x, block_y

    draw_screen(p, FALSE); // It is IMPORTANT that the screen is drawn before any
                           // drag/drop actions are taken.  If you don't understand why
                           // then DO NOT move this line...

    if (block_tag) { // cursor is over a block, not an empty square
      Read_Btns();
      // if we use buttons to skip forward and back through the levels, add that here...
      if (  ((Vec_Btn_State&3) == 3) && ((Prev_Btn_State&3) != 3)  ) {
        if (next_game > 1) {
          next_game -= 2;
          fetch_new_game(p);
        }
      } else if (  ((Vec_Btn_State&5) == 5) && ((Prev_Btn_State&5) != 5)  ) {
        if (next_game < NUM_GAMES) {
          fetch_new_game(p);
        }
      }
      Prev_Btn_State = Vec_Btn_State;
      mouse_down = ((Vec_Btn_State & 8) != 0);
      if (mouse_down && !mouse_was_down) {
        // this was a click
        dragged_block = block_tag; // all the cool stuff will happen on the next frame
      } else if (mouse_down) {
        // this is a continuing drag
        // constrain movement within allowed directions, move block to nearest square
        // which may be no movement at all
        block_tag = dragged_block;// force it to stay the same even of cursor wandering
        if (dragged_block_orientation == HORIZONTAL) {
          if ((block_x < prev_x) && can_move_back) {
            Move(p, block_tag, LEFT);
          } else if ((block_x > prev_x) && can_move_forward) {
            Move(p, block_tag, RIGHT);
          }
        } else if (dragged_block_orientation == VERTICAL) {
          if ((block_y < prev_y) && can_move_back) {
            Move(p, block_tag, UP);
          } else if ((block_y > prev_y) && can_move_forward) {
            Move(p, block_tag, DOWN);
          }
        }
      } else if (mouse_was_down) { // (but is no longer...)
        // end of drag - drop now.
        // actually if we were moving on the fly, nothing really needs to
        // be done except for unsetting a few variables
        dragged_block = 0; dragged_block_orientation = -1;
        can_move_forward = FALSE; can_move_back = FALSE;
        block_tag = 0; // probably should also do this.
      } else {
        // no change, just redraw
      }
      mouse_was_down = mouse_down;
    }

    if ((p[2*6+4] == 'X') && (p[2*6+5] == 'X')) {
      long t;
      for (t = 0L; t < 128L; t++) {
        // Freeze the display for a short time before starting next level!
        // should we add a small musical feedback?
        // Would also be nice to allow a pause here so that the player can
        // photograph the final position as proof of completion.
        // I considered freezing by default until a button was pressed to
        // move on, but rejected it as bad UI design.
        // But if we invent a pause button, need to show a "PAUSED" message
        // which then fades out to allow a clean photograph. And do we also
        // have to put up a message saying which button to use to unpause?
        // This is all getting rather messy... so in the end, doing nothing.
        Wait_Recal();
        Reset0Ref(); set_scale(DRAWING_SCALE);
        Print_Str_d(-120, -76, "LEVEL    SOLVED\x80");
        draw_screen(p, TRUE); // force screen no to be drawn
      }
      fetch_new_game(p);
    }
    prev_x = block_x;
    prev_y = block_y;
  }
  (void)Version;
  return 0;
}