#include <vectrex.h>

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

// Handle a 15x15 grid using a bit vector. With fenceposts, that's 16x16 lines.

static unsigned int horiz[32]; // each two bytes are 16 x's
static unsigned int vert[32];  // each two bytes are 16 y's

static const unsigned int bitpos[8] = { 1U, 2U, 4U, 8U, 16U, 32U, 64U, 128U };
static const unsigned int notbitpos[8] = { ~1U, ~2U, ~4U, ~8U, ~16U, ~32U, ~64U, ~128U };

static inline unsigned int vertical(unsigned int x, unsigned int y) {
  // each two bytes are 16 y's
  return (vert[(x<<1U)+(y>>3U)]&bitpos[y&7U]); // 0 or non-zero
}

static inline void setvertical(unsigned int x, unsigned int y) {    
  // each two bytes are 16 y's
  vert[(x<<1U)+(y>>3U)] |= bitpos[y&7U];
}

static inline void clrvertical(unsigned int x, unsigned int y) {
  // each two bytes are 16 y's
  vert[(x<<1U)+(y>>3U)] &= notbitpos[y&7U];
}

static inline void assignvertical(unsigned int x, unsigned int y, unsigned int bitvalue) {
  // each two bytes are 16 y's
  unsigned int *byte = &vert[(x+x)+(y>>3U)];
  *byte &= notbitpos[y&7U];
  if (bitvalue) *byte |= bitpos[y&7U];
}


static inline unsigned int horizontal(unsigned int x, unsigned int y) {
  // each two bytes are 16 x's
  return horiz[(y<<1U)+(x>>3U)]&bitpos[x&7U]; // 0 or non-zero
}

static inline void sethorizontal(unsigned int x, unsigned int y) {    
  // each two bytes are 16 x's
  horiz[(y<<1U)+(x>>3U)] |= bitpos[x&7U];
}
static inline void clrhorizontal(unsigned int x, unsigned int y) {
  // each two bytes are 16 x's
  horiz[(y<<1U)+(x>>3U)] &= notbitpos[x&7U];
}

static inline void assignhorizontal(unsigned int x, unsigned int y, unsigned int bitvalue) {
  // each two bytes are 16 x's
  unsigned int *byte = &horiz[(y+y)+(x>>3U)];
  *byte &= notbitpos[x&7];
  if (bitvalue) *byte |= bitpos[x&7U];
}

static inline void DrawHorizontal(int xl, int xr, int y) { // coordinate range is 0:15,0:15
    Reset0Ref();
    Moveto_d((y-8)*16, (xl-8)*16);
    Draw_Line_d(0, (xr-xl)*16); // draw longest line possible acrld
}

static inline void DrawVertical(int x, int yb, int yt) { // coordinate range is 0:15,0:15
    Reset0Ref();
    Moveto_d((yb-8)*16, (x-8)*16);
    Draw_Line_d((yt-yb)*16, 0);
}

int main(void) {
  unsigned int y = 0, x = 0, i = 0;
  for (i = 0; i < 32; i++) vert[i] = horiz[i] = 0;
/* 
  Y
  5 +--------+
    |        |
  4 |     +--+
    |     |
  3 +-----+
    5  6  7  8 X
*/
      sethorizontal(5,5);sethorizontal(6,5);sethorizontal(7,5);
setvertical(5,4);                                     setvertical(8,4);
                                            sethorizontal(7,4);
setvertical(5,3);                  setvertical(7,3);
      sethorizontal(5,3);sethorizontal(6,3);


  sethorizontal(7,7);sethorizontal(8,7); setvertical(7,7);
  sethorizontal(7,8);sethorizontal(8,8); setvertical(9,7);

  sethorizontal(12,12);sethorizontal(13,12);setvertical(12,11);setvertical(13,10);setvertical(13,9);
  sethorizontal(12,11);setvertical(14,9);setvertical(14,10);setvertical(14,11);
  sethorizontal(13,9);

  for (;;) {
    Wait_Recal();
    set_scale(127);
    Intensity_a(80);
    // Draw horizontal lines first:
    for (y = 0; y < 16; y++ ) { // 15 rows, 16 lines (incl +1 for fenceposting)
      unsigned int *ptr = &horiz[y+y]; // +32 for vertical loop
      if ((ptr[0]|ptr[1]) == 0) continue;
      x = 0;
      if (*ptr==0) x = 8; // fast test for 8 slots at a time
      for (;;) {
        if (horizontal(x,y)) {
          unsigned int x0=x;
          do x++; while (horizontal(x,y));
          // A line starts here at x,y.  It may consist of more than one segment.
          DrawHorizontal((int)x0, (int)x, (int)y);
        } else x++;
        if ((x==8) && (*ptr == 0)) break;
        if (x == 15) break;
      }
    }
    // Then vertical lines
    for (x = 0; x < 16; x++ ) { // 15 columns, 16 lines (incl +1 for fenceposting)
      unsigned int *ptr = &vert[x+x]; // +32 for vertical loop
      if ((ptr[0]|ptr[1]) == 0) continue;
      y = 0;
      if (*ptr==0) y = 8; // fast test for 8 slots at a time
      for (;;) {
        if (vertical(x,y)) {
          unsigned int y0 = y;
          // A line starts here at x,y.  It may consist of more than one segment.
          do y++; while (vertical(x,y));
          DrawVertical((int)x, (int)y0, (int)y); // line from y=0 to y=1 up to y=14 to y=15
        } else y++;
        if ((y==8) && (*ptr == 0)) break;
        if (y == 15) break;
      }
    }
  }
  return 0;
}
