//#error Not yet finished converting from lua.
#define RIVETJOINT 135

#include <stdio.h>  // for fprintf in lookup_f00
#include <stdlib.h> // for:
// extern long int random(void);
// extern void srandom(unsigned int seed);

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

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

#define sint8_t int8_t

typedef struct pointu8 {
  u_int8_t y;
  u_int8_t x;
} pointu8;

typedef struct points8 {
  sint8_t y;
  sint8_t x;
} points8;

#include "dkong-vectors.h"

u_int32_t randomu32(u_int32_t size) {
  return (u_int32_t)random() % size; // 0 to size-1
}

u_int8_t randomu8(u_int16_t size) {
  return (u_int8_t)((u_int16_t)random() % size); // 0 to size-1
}

u_int32_t randomu32 (u_int32_t size);
u_int8_t randomu8 (u_int16_t size);
void clear_graphic_banks(void);
void draw_girder_stage(void);
void mode_specific_changes(void);
void draw_vector_characters(void);
void draw_kong(u_int8_t y, u_int8_t x, int growl);
void draw_jumpman(void);
void draw_points(void);
// Draw game objects
void draw_ladder (u_int8_t y, u_int8_t x, u_int8_t h);
void draw_girder (u_int8_t y1, u_int8_t x1, u_int8_t y2, u_int8_t x2, char open);
void draw_stacked_barrels (void);
void draw_hammers (u_int8_t hammer1_y, u_int8_t hammer1_x, u_int8_t hammer2_y, u_int8_t hammer2_x);
void draw_oilcan_and_flames (u_int8_t y, u_int8_t x);
// Sprites
void draw_barrels (void);
void draw_fireballs (void);
void draw_pauline (u_int8_t x);
void draw_loveheart (void);
// Vectrex graphics
extern void vectrex_vector(u_int8_t y1, u_int8_t x1, u_int8_t y2, u_int8_t x2);
extern void vectrex_draw_box(u_int8_t y1, u_int8_t x1, u_int8_t y2, u_int8_t x2, u_int32_t color1, u_int32_t color2);
extern void draw_line(u_int8_t y1, u_int8_t x1, u_int8_t y2, u_int8_t x2, u_int32_t color);
void polyline(int items, pointu8 *data,
              u_int8_t offset_y, u_int8_t offset_x,
              int flip_x, int flip_y);

extern u_int32_t frame_number(void);
extern u_int8_t mem[0x10000];

const char *name = "vectorkong";
const char *version = "0.14";
const char *description = "Vector Kong";
const char *license = "GNU GPLv3";
const char *author_name = "Jon Wilson (10yard)";

u_int32_t vector_color;
u_int16_t vector_count;
u_int8_t  game_mode, last_mode, zigzags = FALSE;

//u_int16_t *barrel_state; // NOT SURE HOW TO TRANSLATE THIS ONE.

// Constants
const u_int16_t MODE= 0x600a, STAGE = 0x6227, LEVEL = 0x6229;

// top-right and bottom-left bytes of video ram:
const u_int16_t VRAM_TR = 0x7440, VRAM_BL = 0x77bf;

// colors:
const u_int32_t
  BLK = 0xff000000,
  WHT = 0xffffffff,
  YEL = 0xfff0f050,
  RED = 0xfff00000,
  BLU = 0xff0000f0,
  MBR = 0xe0f27713,
  BRN = 0xffD60609,
  MAG = 0xfff057e8,
  PNK = 0xffffd1dc,
  LBR = 0xfff4b98b,
  CYN = 0xff14f3ff,
  GRY = 0xffb0b0b0;

pointu8 STACKED_BARRELS[] = {
  // coordinate of barrel on screen
  {173,0}, {173,10}, {189,0}, {189,10}
};

u_int16_t BARRELS[] = {
  // address of barrel in rom
  0x6700,0x6720,0x6740,0x6760,0x6780,0x67a0,0x67c0,0x67e0,0x6800,0x6820
};

u_int16_t FIREBALLS[] = {
  // address of fireball in rom
  0x6400, 0x6420, 0x6440, 0x6460, 0x6480
};

// u_int16_t BR = 0xffff;  // instuction to break in a vector chain
                          // - need to handle differently with
                          // my adhoc Vectrex data structures.

u_int32_t mem_read_direct_u32(u_int16_t addr) {
  u_int32_t data = 0;
  u_int8_t *ptr;
  ptr = &mem[addr];
  data = *ptr++;
  data |= (*ptr++) << 8;
  data |= (*ptr++) << 16;
  data |= (*ptr++) << 24;
  return data;
}

void mem_write_direct_u32(u_int16_t addr, u_int32_t data) {
  u_int8_t *ptr;
  ptr = &mem[addr];
  *ptr++ = (data    ) & 255; // not the most efficient coding, but
  *ptr++ = (data>>8 ) & 255; // easier to change if I got the order
  *ptr++ = (data>>16) & 255; // wrong!
  *ptr   = (data>>24) & 255;
}

void mem_write_direct_u16(u_int16_t addr, u_int16_t data) {
  u_int8_t *ptr;
  ptr = &mem[addr];
  *ptr++ = (data    ) & 255; // not the most efficient coding, but
  *ptr++ = (data>>8 ) & 255; // easier to change if I got the order
}

void initialize(void) {
  clear_graphic_banks();
  // force game to start on the title screen:
  mem_write_direct_u32(0x01ea, 0x0000063e);
  // increase timer for title screen:
  mem_write_direct_u16(0x07d3, 0xff3e);
  // load_vector_library(); // statically declared in dkong-vectors.h 
  zigzags = TRUE;
}

void drawframe(void) {
  vector_count = 0;
  vector_color = WHT;
  game_mode = mem[MODE];

  if (mem[VRAM_BL] == 0xf0) draw_girder_stage();
  //if (mem[VRAM_BL] == 0xb0) draw_rivet_stage();
  mode_specific_changes();
  draw_vector_characters();

  //debug_limits(3000);
  //debug_vector_count();
  last_mode = game_mode;
}

////////////////////////////////
//// Draw stage backgrounds ////
////////////////////////////////

void draw_girder_stage(void) {
  int growling = ((game_mode == 0x16) && (mem[0x6388] >= 4));

  // 1st girder
  draw_girder(1, 0, 1, 111, 'R');  // flat section
  draw_girder(1, 111, 8, 223, 'L');  // sloped section
  draw_ladder(8, 80, 8); // broken ladder bottom
  draw_ladder(32, 80, 4); // broken ladder top
  draw_ladder(13, 184, 17); // right ladder
  draw_oilcan_and_flames(8, 16);

  // 2nd Girder
  draw_girder(41, 0, 29, 207, 0);
  draw_ladder(46, 32, 17);  // left ladder
  draw_ladder(42, 96, 25);  // right ladder

  // 3rd Girder
  draw_girder(62, 16, 74, 223, 0);
  draw_ladder(72, 64, 9);  // broken ladder bottom
  draw_ladder(96, 64, 7);  // broken ladder top
  draw_ladder(75, 112, 25);  // middle ladder
  draw_ladder(79, 184, 17);  // right ladder

  // 4th Girder
  draw_girder(107, 0, 95, 207, 0);
  draw_ladder(112, 32, 17);  // left ladder
  draw_ladder(110, 72, 21);  // middle ladder
  draw_ladder(104, 168, 9);  // broken ladder bottom
  draw_ladder(128, 168, 9);  // broken ladder top

  // 5th girder
  draw_girder(128, 16, 140, 223, 0);
  draw_ladder(139, 88, 13);  // broken ladder bottom
  draw_ladder(160, 88, 5);  // broken ladder top
  draw_ladder(145, 184, 17);  // right ladder

  // 6th girder
  draw_girder(165, 0, 165, 143, 'R');  // flat section
  draw_girder(165, 143, 161, 207, 'L');  // sloped section
  draw_ladder(172, 64, 52);  // left ladder
  draw_ladder(172, 80, 52);  // middle ladder
  draw_ladder(172, 128, 21);  // right ladder
  draw_stacked_barrels();

  // Pauline's girder
  draw_girder(193, 88, 193, 136, 'L');
  draw_pauline(90);
  draw_loveheart();

  // Other sprites
  draw_kong(172, 24, growling);
  draw_hammers(56,168,148,17);
  draw_jumpman();
  draw_barrels();
  draw_fireballs();
  draw_points();
}

#ifdef RIVETJOINT
void draw_rivet_stage(void) {
  // Work in progress
  //vector_lib[0xb0] = {BR,GRY,3,2,3,4,4,4,4,2,3,2};  // update basic block
  zigzags = FALSE;

  // 1st floor
  draw_girder(1, 0, 1, 223, 0);
  draw_ladder(8, 8, 33); // left ladder
  draw_ladder(8, 104, 33); // middle ladder
  draw_ladder(8, 208, 33); // right ladder
  // 2nd floor
  draw_girder(41, 8, 41, 56, 0);
  draw_girder(41, 64, 41, 160, 0);
  draw_girder(41, 168, 41, 216, 0);
  draw_ladder(48, 16, 33); // ladder 1
  draw_ladder(48, 72, 33); // ladder 2
  draw_ladder(48, 144, 33); // ladder 3
  draw_ladder(48, 200, 33); // ladder 4
  // 3rd floor
  draw_girder(81, 16, 81, 56, 0);
  draw_girder(81, 64, 81, 160, 0);
  draw_girder(81, 168, 81, 208, 0);
  draw_ladder(88, 24, 33); // left ladder
  draw_ladder(88, 104, 33); // middle ladder
  draw_ladder(88, 192, 33); // right ladder
  // 4th floor
  draw_girder(121, 24, 121, 56, 0);
  draw_girder(121, 64, 121, 160, 0);
  draw_girder(121, 168, 121, 200, 0);
  draw_ladder(128, 32, 33); // ladder 1
  draw_ladder(128, 64, 33); // ladder 2
  draw_ladder(128, 152, 33); // ladder 3
  draw_ladder(128, 184, 33); // ladder 4
  // 5th floor
  draw_girder(161, 32, 161, 56, 0);
  draw_girder(161, 64, 161, 160, 0);
  draw_girder(161, 168, 161, 192, 0);
  // Pauline's floor
  draw_girder(201, 56, 201, 168, 0);
  draw_pauline(104);
  draw_loveheart();
  // Sprites
  draw_hammers(149,105,108,8);
  draw_kong(168, 92, FALSE);
  draw_jumpman();
  draw_fireballs();
  draw_points();
}
#endif

void draw_object(enum sprite name_idx, u_int8_t y, u_int8_t x, u_int32_t color, int flip_x, int flip_y) {
  pointu8 *name = (pointu8 *)lib[name_idx]; // NOT CORRECT! VECTREX VERSION IS DIFFERENT FROM ORIGINAL
  // draw object from the vector library
  if (color) vector_color = color;
  polyline(spritesize[name_idx], name, y, x, flip_x, flip_y);
  vector_color = WHT;
}

void mode_specific_changes(void) {
  u_int16_t y, x;
  if (game_mode == 0x06 && last_mode == 0x06) {

    // display a growling Kong on the title screen
    draw_kong(48, 92, TRUE);

    // show vector logo and "vectorised by 10yard" in the footer
    draw_object(LOGO /*"logo"*/, 96, 16,  0xff444444 + randomu32(0xbbbbbb), 0,0);

    pointu8 objects[] = {
      {0x26,0x15}, {0x13,0x24}, {0x1f,0x22}, {0x19,0x23}, {0x15,0x14},
      {0x10,0x12}, {0x29,0x10}, {0x01,0x00}, {0x29,0x11}, {0x22,0x14}
    };
    pointu8 kv;
    int kvx;
    u_int8_t k = kv.x;
    u_int8_t v = kv.y;
    
    for (kvx = 0; kvx < sizeof(objects)/sizeof(pointu8); kvx++) {
      kv = objects[kvx]; k = kv.x; v = kv.y;
      // TO DO: (BADLY TRANSLATED) draw_object(v, 8, k*8+25, GRY, 0,0);
    }

    // restore the game startup logic
    if (mem_read_direct_u32(0x01ea) != 0xaf622732) {
      // or could just write it unconditionally :-/
      mem_write_direct_u32(0x01ea, 0xaf622732);
    }

  } else if (game_mode == 0x07) {

    // skip the DK climbing/intro scene
    mem[MODE] = 0x08;

  } else if (game_mode == 0x08 && last_mode == 0x16) {

    // stay on the girders stage
    mem[STAGE] = 1;
    mem[LEVEL] = mem[LEVEL] + 1;
    
  } else if (game_mode == 0x10) {
      
    // emphasise the game over message
    vectrex_draw_box(64, 64, 88, 160, BLK, BLK);
    
  } else if (game_mode == 0x15) {
    // highlight selected character during name registration
    // _y = floor(mem[0x6035] / 10) * -16 + 156;
    // _x = mem[0x6035] % 10 * 16 + 36;
    int y, x;
    y = 156 - ((mem[0x6035] / 10)<<4);
    x =  36 + ((mem[0x6035] % 10)<<4);
    
    draw_object(SELECT /*"select"*/, y, x, CYN, 0,0);
    
  } else if (game_mode == 0xa) {
    // display multiple kongs on the how high can you get screen
    int i;
    y = 24; x = 92;
    for (i = 1; i <= mem[0x622e]; i++) {
      draw_kong(y, x, FALSE);
      y = y + 32;
    }
  }
}

//// Basic vector drawing functions
//////////////////////////////////-
void vector(u_int8_t y1, u_int8_t x1, u_int8_t y2, u_int8_t x2) {
  // draw a single vector
  draw_line(y1, x1, y2, x2, vector_color);
  vector_count = vector_count + 1;
}

// Replace this with Vectrex-specific code!

void polyline(int items, pointu8 *data,
              u_int8_t offset_y, u_int8_t offset_x,
              int flip_x, int flip_y) {
  // draw multiple chained lines from a table of y,x points.
  // Optional offset for start y,x.
  u_int8_t _offy = offset_y /* or 0 if not given */,
          _offx = offset_x /* ditto */;
  u_int8_t _savey, _savex, _datay, _datax;
  int i;

  if (data) {
    for (i = 0; i < items; i++) {
      _datay = data[i].y; _datax = data[i].x;
      if (_savey && _savey != BR && _datay != BR) {
	if (flip_y && flip_y > 0) {
          vector(flip_y-_datay+_offy, _datax+_offx, flip_y-_savey+_offy, _savex+_offx);
        } else if (flip_x && flip_x > 0) {
    	  vector(_datay+_offy, flip_x-_datax+_offx, _savey+_offy, flip_x-_savex+_offx);
        } else {
          vector(_datay+_offy, _datax+_offx, _savey+_offy, _savex+_offx);
        }
      } else {
    	// break in the vector chain and maybe change colour
    	if (_datax > 0x00ffffff) vector_color = _datax;
      }
      _savey = _datay; _savex = _datax;
    }
  }
}

void box(int8_t y, int8_t x, int8_t h, int8_t w) {
  // draw a simple box at given position with height and width
  pointu8 name[] = { {y,x}, {y+h,x}, {y+h,x+w}, {y,x+w}, {y,x} };
  polyline(sizeof(name)/sizeof(name[0]), name, 0,0,0,0);
}

void circle(u_int8_t y, u_int8_t x, u_int8_t r, u_int32_t color) {
  // draw a segmented circle at given position with radius
  u_int16_t segment, save_segy, save_segx;
  if (color) vector_color = color;
  for (segment = 0; segment < 360; segment += 24) {
    // THIS NEEDS CONVERTING TO A BETTER VECTREX VERSION,
    // NOT SIMPLY A STRAIGHT CONVERSION TO FIXED POINT FOR EXAMPLE.
    //u_int16_t angle = segment * (math.pi / 180);
    //u_int16_t segy = y + r * sin(angle), segx = x + r * cos(angle);
    //if (save_segy) vector(save_segy, save_segx, segy, segx);
    //save_segy = segy; save_segx = segx;
  }
  vector_color = WHT;
}

void draw_vector_characters(void) {
  // Output vector characters based on contents of video ram ($7400-77ff)
  // (so we do need to update the video ram even if we don't display it :-/ )
  u_int16_t addr = VRAM_TR;
  u_int8_t x, y;
  for ( x = 223; ; x -= 8) {
    for (y = 255; ; y -= 8) {
      // TO DO: (BAD TRANSLATION) draw_object(mem[addr]++, y-6, x-6, (y == 255 ? RED : 0), 0,0);
      if (y == 0) break;
    }
    if (x == 0) break;
  }
}

///////////////////////////
//// Draw game objects ////
///////////////////////////

void draw_ladder(u_int8_t y, u_int8_t x, u_int8_t h) {
  // draw a single ladder at given y, x position of given height in pixels
  // pointu8 arrays are NOT compatible with Vectrex-style polyline calls.
  pointu8 name[] = { {0,0}, {h,0}, {BR,BR}, {0,8}, {h,8} }; /* TO DO! */
  int i;

  polyline(sizeof(name)/sizeof(name[0]), name, y,x,0,0);  // left and right legs

  for (i=0; i < h-2; i++) {
    // draw rung every 4th pixel (skipping 2 pixels at bottom)
    if ((i % 4) == 0) vector(y+i+2,x, y+i+2,x+8);
  }
}

void draw_girder(u_int8_t y1, u_int8_t x1, u_int8_t y2, u_int8_t x2, char open) {
  // draw girder at given y,x position.
  // Girders are based on parallel vectors (offset by 7 pixels).
  pointu8 mid[] = { {y1,x1}, {y2,x2}, {BR,BR}, {y1+7,x1}, {y2+7,x2} };
  int x;

  polyline(sizeof(mid)/sizeof(mid[0]), mid, 0,0,0,0);

  if (!open || open != 'L') {
    pointu8 left[] = {{y1,x1}, {y1+7,x1} };
    // close the girder ends:
    polyline(sizeof(left)/sizeof(left[0]), left, 0,0,0,0);
  }

  if (!open || open != 'R') {
    pointu8 right[] = { {y2,x2}, {y2+7,x2} };
    polyline(sizeof(right)/sizeof(right[0]), right, 0,0,0,0);
  }
  
  if (zigzags) {
    for (x = x1; x < x2 - 1; x += 16) {  // Fill the girders with zig zags
      draw_object(ZIGZAG /*"zigzag"*/,
                  y1 + (((y2 - y1) / (x2 - x1)) * (x - x1)), x,
                  GRY,
                  0,0);
    }
  }
}

void draw_stacked_barrels(void) {
  pointu8 *v;
  int v_idx;
  int barrel_count = sizeof(STACKED_BARRELS)/sizeof(STACKED_BARRELS[0]);
  for (v_idx = 0; v_idx < barrel_count; v_idx++) {
    v = &STACKED_BARRELS[v_idx];
    draw_object(STACKED /*"stacked"*/, v->x, v->y, LBR, 0,0);
  }
}

void draw_hammers(u_int8_t hammer1_y, u_int8_t hammer1_x, u_int8_t hammer2_y, u_int8_t hammer2_x) {
  if (mem[0x6a1c] > 0 && mem[0x6691] == 0)
    draw_object(HAMMER_UP /*"hammer-up"*/, hammer1_y, hammer1_x, MBR, 0,0);
  if (mem[0x6a18] > 0 && mem[0x6681] == 0)
    draw_object(HAMMER_DOWN /*"hammer-up"*/, hammer2_y, hammer2_x, MBR, 0,0);
}

void draw_oilcan_and_flames(u_int8_t y, u_int8_t x) {
  draw_object(OILCAN /*"oilcan"*/,  y, x, 0, 0,0);
  if (0x40 <= mem[0x6a29] && mem[0x6a29] <= 0x43) {  // oilcan is on fire?
    // draw base of flames:
    draw_object(FLAMES /*"flames"*/, y+16, x, YEL, 0,0);
    // draw flames extending upwards:
    draw_object(FLAMES /*"flames"*/, y+16+randomu8(4 /* 0:3 */), x, RED, 0,0);
    // If this random is to make the vectors wobble in the
    // author's raster implementation, then we don't need it
    // here because we have plenty of natural wobble already
    // without simulated wobble to make things worse!
  }
}

/////////////////
//// Sprites ////
/////////////////

void draw_barrels(void) {
  u_int16_t y, x, type, state, addr;
  int i;
  for (i = 0; i < sizeof(BARRELS)/sizeof(BARRELS[0]); i++) {
    addr = BARRELS[i];
    if (mem[addr] == 1 && mem[0x6200] == 1 && mem[addr+3] > 0) {
      // barrel active and Jumpman alive
      y = 251 - mem[addr+5], x = mem[addr+3] - 20;
      type = mem[addr+0x15]; // type of barrel: 0 is normal, 1 is blue/skull

      if ((mem[addr+1] == 1) || ((mem[addr+2]&2) != 0)) {
        // barrel is crazy or going down a ladder
        // Note difference in translation from lua version.
        state = mem[addr+0xf];
        draw_object(DOWN /*"down"*/, y, x-2, type?CYN:LBR, 0,0);
        //draw_object("down-"..tostring(state % 2 + 1), y, x - 2, type?BLU:MBR, 0,0);
        draw_object(DOWN_1+(state % 2), y, x-2, type?BLU:MBR, 0,0);
      } else {  // barrel is rolling
    	//TO DO: state = barrel_state[addr]; // NOT SURE HOW TO TRANSLATE THIS ONE.
        // - DEPENDS ON THE VALUE OF 'addr' AND THE MAX NO OF BARRELS.
        if (frame_number() % 10 == 0) { // This is not going to be efficient on the Vectrex.
          // roll left or right?
          if (mem[addr+2] == 2) state = state - 1; else state = state+1;
          //TO DO: barrel_state[addr] = state; // NOT SURE HOW TO TRANSLATE THIS ONE.
        }
        draw_object(ROLLING /*"rolling"*/, y, x, type?CYN:LBR, 0,0);
	//draw_object(type?"skull-":"roll-"..tostring(state % 4 + 1),
	draw_object((type?SKULL_1:ROLL_1) + (state % 4),
                    y, x,
                    type?BLU:MBR,
                    0,0);
      }
    }
  }
}

void draw_fireballs(void) {
  u_int32_t fireball_color, flame_color;
  u_int16_t addr;
  u_int8_t y, x, flipx;
  int i;
  
  fireball_color = YEL;
  flame_color = RED;
  if (mem[0x6217] == 1) {
    fireball_color = BLU;
    flame_color = CYN;
  }

  for (i = 0; i < sizeof(FIREBALLS)/sizeof(FIREBALLS[0]); i++) {
    addr = FIREBALLS[i];
    if (mem[addr] == 1) {  // fireball is active
      y = 247 - mem[addr+5]; x = mem[addr+3] - 22;
      if (mem[addr+0xd] == 1) {
        // fireball moving right so flip the vectors
        flipx = 13;
      }
      // draw body:
      draw_object(FIREBALL /*"fireball"*/, y, x, fireball_color, flipx,0);
      // draw flames extending upwards:
      draw_object(FB_FLAME /*"fb-flame"*/, y+randomu8(4 /* 0:3 */), x, flame_color, flipx,0);
    }
  }
}

void draw_pauline(u_int8_t x) {
  u_int16_t y = 235 - mem[0x6903];
  // Pauline jumps when heart not showing:
  if (mem[0x6905] != 17 && mem[0x6a20] == 0) y = y + 3;
  draw_object(PAULINE /*"pauline"*/, y, x, MAG, 0,0);
}

enum sprite lookup_f00(u_int8_t i) {
  switch (i) {
  case 0x60: return SPRITE_F60;
  case 0x61: return SPRITE_F61;
  case 0x62: return SPRITE_F62;
  case 0x63: return SPRITE_F63;
  case 0x76: return SPRITE_F76;
  case 0x77: return SPRITE_F77;
  case 0x7d: return SPRITE_F7D;
  case 0x7e: return SPRITE_F7E;
  case 0x7f: return SPRITE_F7F;
  default:
    fprintf(stderr, "Bad F00 sprite lookup (%d)\n", i);
    return FIREBALL; // hopefully this will be noticeable enough. Temp.
  }
}

void draw_loveheart(void) {
  u_int8_t y, x;
  y = 250 - mem[0x6a23]; x = mem[0x6a20] - 23;
  if (x > 0) {
    draw_object(lookup_f00(mem[0x6a21]), y, x, MAG, 0,0);
  }
}

int translate_jm(u_int16_t jm) {
  if (0 <= jm && jm <= 15) return JM_0 + jm;
  if (120 <= jm && jm <= 122) return JM_120 + (jm-120);
  return 0;
}

void draw_jumpman(void) {
  u_int16_t _y = 255 - mem[0x6205], _x = mem[0x6203] - 15;
  u_int16_t _sprite = mem[0x694d];
  u_int16_t _sprite_mod = _sprite % 128;
  u_int32_t _bright_blue = BLU + 0x1f1f0f;
  u_int16_t _smash_offset = 9;
  u_int16_t _grab;
  u_int16_t JM;
  
  if (_y < 255) {
    //if (vector_lib["jm-"..tostring(_sprite_mod)]) {
    JM = translate_jm(_sprite_mod);
    if (JM) {
      // right facing sprites are mirrored.
      // < 128 are left facing sprites

      // I'M NOT SURE THESE NUMBERS ARE STILL VALID AFTER COMPRESSING THE
      // SPRITE RANGES IN dkong-vectors.h - THIS AREA HAS TO BE LOOKED AT CAREFULLY 
      
      // (0,1,2) = walking, (3,4,5,6) = climbing, (8,9,10,11,12,13) = hammer smashing,
      // (14,15) = jumping, (120,121,122) = dead

      _grab = mem[0x6218] == 1;
      if (_grab) { // grabbing hammer
    	if (_sprite < 128) _sprite = 10; else _sprite = 138;  // change default sprite to a hammer grab
      }

      // Display one of Jumpman's various sprites
      if (_sprite < 128) {
        draw_object(JM_0+/*"jm-"..tostring*/(_sprite), _y-7, _x-7, _bright_blue, 0,0);
      } else if (_sprite == 248) {
    	draw_object(JM_0+/*"jm-"..tostring*/(_sprite-128), _y, _x-7, _bright_blue, 0, 8);  // flip y
      } else {
    	draw_object(JM_0+/*"jm-"..tostring*/(_sprite-128), _y-7, _x, _bright_blue, 8, 0);  // flip x
      }

      // Add the hammer?
      if (_grab) { // grabbing hammer
        draw_object(HAMMER_UP /*"hammer-up"*/, _y+9, _x-3, MBR, 0,0);
      } else if (_sprite_mod >= 8 && _sprite_mod <= 13) {  // using hammer
        if (_sprite == 8 || _sprite == 10) {
          draw_object(HAMMER_UP /*"hammer-up"*/, _y+9, _x-3, MBR, 0,0);
        } else if (_sprite == 12 || _sprite == 140) {
          draw_object(HAMMER_UP /*"hammer-up"*/, _y+9, _x-4, MBR, 0,0);
        } else if (_sprite == 136 || _sprite == 138) {
          draw_object(HAMMER_UP /*"hammer-up"*/, _y+9, _x-5, MBR, 0,0);
        } else if( _sprite == 9 || _sprite == 11 || _sprite == 13) {
          draw_object(HAMMER_DOWN /*"hammer-down"*/, _y-4, _x-16, MBR, 0,0);
          _smash_offset = -4;
        } else if (_sprite == 137 || _sprite == 139 || _sprite == 141) {
          draw_object(HAMMER_DOWN /*"hammer-down"*/, _y-4, _x+13, MBR, 4,0);
          _smash_offset = -4;
        }
      }

      // Add smashed item?
      _sprite = mem[0x6a2d];
      if (mem[0x6a2c] > 0 && 0x60 <=_sprite && _sprite <= 0x63) {
    	// v0.14 - prefer growing circle effect to the sprites
    	circle(_y+_smash_offset+4, mem[0x6a2c] - 13, (_sprite - 95) * 5, 0xff444444 + randomu32(0xbbbbbb));
    	//draw_object(BASE_F00/*0xf00*/ + _sprite, _y+_smash_offset, mem[0x6a2c) - 17, BLU, 0,0);
      }
    }
  }
}

typedef struct object {
  u_int16_t f1; // char *f1;
  u_int32_t f2;
  u_int32_t f3;
} object;
 
void draw_kong(u_int8_t y, u_int8_t x, int growl) {
  u_int8_t _state = mem[0x691d]; // state of kong - is he deploying a barrel?
  object data1 = {ROLL_1  /* "roll-1"*/, LBR, MBR}; // default barrel data
  object data2 = {SKULL_1 /*"skull-1"*/, CYN, BLU}; // blue barrel, not the default data
  object *_data = &data1; // default barrel data
  if ((mem[0x6a20] == 0) && (_state == 173 || _state == 45 || _state == 42)) {
    if (0x80 <= mem[0x6382] && mem[0x6382] <= 0x81) {
      _data = &data2;  // blue barrel, not the default data
    }
    // Kong deploying a barrel
    if (_state == 173) {
      // Releasing barrel to right
      draw_object(DK_SIDE /*"dk-side"*/, y, x+1, BRN, 0,0);
      draw_object(ROLLING /*"rolling"*/, y, x+44, _data->f2, 0,0);
      draw_object(_data->f1, y, x+44, _data->f3, 0,0);
    } else if (_state == 45) {
      // Grabbing barrel from left (mirrored)
      draw_object(DK_SIDE /*"dk-side"*/, y, x-3, BRN, 42,0);
      draw_object(ROLLING /*"rolling"*/, y, x-15, _data->f2, 0,0);
      draw_object(_data->f1, y, x-15, _data->f3, 0,0);
    } else if (_state == 42) {
      // Holding barrel in front
      draw_object(DK_HOLD /*"dk-hold"*/, y, x, BRN, 0,0);  // left side
      draw_object(DK_HOLD /*"dk-hold"*/, y, x+20, BRN, 20,0); // mirrored right side
      draw_object(DOWN /*"down"*/, y+2, x+12.4, _data->f2, 0,0);
      draw_object(DOWN_1 /*"down-1"*/, y+2, x+12.4, _data->f3, 0,0);
    }
  } else {
    // Default front facing Kong
    draw_object(DK_FRONT /*"dk-front"*/, y, x, BRN, 0,0);  // left side
    draw_object(DK_FRONT /*"dk-front"*/, y, x+20, BRN, 20,0); // mirrored right side
    if (growl) {
      draw_object(DK_GROWL /*"dk-growl"*/, y, x, MBR, 0,0);  //left side
      draw_object(DK_GROWL /*"dk-growl"*/, y, x+20, MBR, 20,0);  // mirrored right side
    }
  }
}

void draw_points(void) {
  // draw 100, 300, 500 or 800 when points awarded
  int _y, _x;
  if (mem[0x6a30] > 0) {
    _y = 254 - mem[0x6a33]; _x = mem[0x6a30] - 22;
    // move points up a little so they don't overlap as much:
    draw_object(lookup_f00(mem[0x6a31]) /*+0xf00*/, _y+3, _x, YEL, 0,0);
  }
}

///////////////////////////
//// General functions ////
///////////////////////////

// no longer used.
u_int8_t read(u_int16_t address, u_int8_t equal_from, u_int8_t to) {
  // return data from memory address, or boolean, when equal
  // or 'to & from' values are provided.
  u_int8_t _d = mem[address];
  if (to && equal_from)
    return equal_from <=_d && _d <= to;
  else if (equal_from)
    return _d == equal_from;
  else
    return _d;
}

// unused now:

//void write(address, value) {
//  mem[address] = value;
//}

// Not sure what the banking situation is on the DK hardware.
// TO DO: come back to this...
void _bank1_write_u8(u_int16_t address, u_int8_t value) {
  mem[address] = value;
}

void _bank2_write_u8(u_int16_t address, u_int8_t value) {
  mem[address] = value;
}

/////////////////////////////
//// Debugging functions ////
/////////////////////////////

void debug_vector_count(void) {
  if (frame_number() % 60 == 0) {
    fprintf(stderr, "%d vectors\n", vector_count);
  }
}

void debug_limits(u_int8_t limit) {
  int i;
  
  // cycle through the 3 tests, each 3 seconds long
  u_int16_t _cycle = (frame_number() % 540) / 180;

  vector_color = 0x44ffffff;

  if (_cycle == 0) { // first frame
    for (i = 1; i < limit; i++) {
      vectrex_vector(255 /*256*/, 224,  randomu8(248), randomu8(224));  // single vectors
    }
  } else if (_cycle == 1) {
    pointu8 d[limit];
    int i;
    for (i = 0; i < limit; i++) {
      d[i].x = randomu8(256);
      d[i].y = randomu8(224);
    }
    polyline(limit, d, 0,0,0,0);  // polylines
  } else {
    int i;
    for (i = 1; i < limit / 4; i++) {
      box(randomu8(216), randomu8(200), randomu8(32)+8, randomu8(24)+8);  // boxes
    }
  }
  vector_color = WHT;
}

/////////////////////////
//// Graphics memory ////
/////////////////////////

void clear_graphic_banks(void) {
  // clear the contents of the DK graphics banks 1 and 2
  u_int16_t _bank1 = 1, _bank2 = 2, addr;
  if (_bank1 && _bank2) {
    for (addr = 0; addr <=  0xfff; addr++) _bank1_write_u8(addr, 0);
    for (addr = 0; addr <= 0x1fff; addr++) _bank2_write_u8(addr, 0);
  }
}
