// ***************************************************************************
// main
// ***************************************************************************
// This file was developed by Prof. Dr. Peer Johannsen as part of the 
// "Retro-Programming" and "Advanced C Programming" class at
// Pforzheim University, Germany.
// 
// It can freely be used, but at one's own risk and for non-commercial
// purposes only. Please respect the copyright and credit the origin of
// this file.
//
// Feedback, suggestions and bug-reports are welcome and can be sent to:
// peer.johannsen@pforzheim-university.de
// ---------------------------------------------------------------------------

#include <vectrex.h>

#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
static inline void displaylist (const int *list)
{
   Draw_VLp (list);
}
static inline void wait_retrace (void)
{
   Wait_Recal ();
}
static inline void set_scale (int scale)
{                    /* VIA_t1_cnt_lo */
   *(volatile int *) 0xD004 = scale;
}

// ---------------------------------------------------------------------------
// cold reset: the vectrex logo is shown, all ram data is cleared
// warm reset: skip vectrex logo and keep ram data
// ---------------------------------------------------------------------------
// at system startup, when powering up the vectrex, a cold reset is performed
// if the reset button is pressed, then a warm reset is performed
// ---------------------------------------------------------------------------
// after each reset, the cartridge title is shown and then main() is called
// ---------------------------------------------------------------------------

// We will be updating the x and y positions in situ, and extending the array dynamically by
// poking a '1' into the first element of the entry after the last active object.  All
// asteroids must be the same number of vectors or use moves to pad. (the Procustean option)

// make the move/remove into a single separate ram array
// and just have one copy of each asteroid. use pointers to it...
// (although that stops working when we use rotation)

#define ASTEROIDS 19

#define BIG    -1,120,120, -1,120,0,  -1,120,-120, -1,0,-120,  -1,-120,-120, -1,-120,0,  -1,-120,120,  -1,0,120,  1
#define MEDIUM -1,80,80,   -1,80,0,   -1,80,-80,   -1,0,-80,   -1,-80,-80,   -1,-80,0,   -1,-80,80,  -1,0,80,     1
#define SMALL  -1,60,60,   -1,60,0,   -1,60,-60,   -1,0,-60,   -1,-60,-60,   -1,-60,0,   -1,-60,60,  -1,0,60,     1

const int vector[((unsigned long) ASTEROIDS) * 10UL * 3UL + 1UL] = {	/* octagons for now, later asteroids */
/*  0 */ BIG,
/*  1 */ BIG,
/*  2 */ MEDIUM,
/*  3 */ MEDIUM,
/*  4 */ MEDIUM,
/*  5 */ MEDIUM,
/*  6 */ SMALL,
/*  7 */ SMALL,
/*  8 */ SMALL,
/*  9 */ SMALL,
/* 10 */ SMALL,
/* 11 */ SMALL,
/* 12 */ SMALL,
/* 13 */ SMALL,
/* 14 */ SMALL,
/* 15 */ SMALL,
/* 16 */ BIG,
/* 17 */ BIG,
/* 18 */ MEDIUM,
};

int x[ASTEROIDS], y[ASTEROIDS], size2[ASTEROIDS];

int dx[ASTEROIDS * 2] = {
   1, 1, 2, 1, 1, 2, 1, 1, 1, 2, -1, -1, -2, -1, -1, -2, -1, -2, -1,
   2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
};

int dy[ASTEROIDS * 2] = {
   -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
   -1, 2, -2, 1, -2, 2, -2, 1, -2, 2, -2, 1, -2, 2, -1, 2, -2, 2, -2,
};

static unsigned long rseed;
static int random (void)
{
   // VECTREX HAS A BUILT-IN RANDOM THAT WE CAN USE!
   // (though it doesn't seem very good - this may be better and faster)
   return (int) (rseed = (rseed * 2053UL) + 13849UL);
}

int main (void) {
   int base, row, tmp1;		// , tmp2, other, diffx, diffy;
   int *vec;

   vec = (int *) (vector + 3);
   for (row = 0; row < ASTEROIDS; row++) {
      x[row] = random ();
      y[row] = random ();
      size2[row] = *vec * *vec;
      vec += 3 * 8 + 1;
   }
   base = 0;
   for (;;) {
      Wait_Recal ();
      vec = (int *) vector;
      for (row = 0; row < ASTEROIDS; row++) {
	 Reset0Ref ();
	 Intensity_a (0x7f);
	 set_scale (0x58);
	 Moveto_d (y[row], x[row]);
	 set_scale (0x01);
	 displaylist (vec);
	 vec += 3 * 8 + 1;
      }

      // now move asteroids around
      for (row = 0; row < ASTEROIDS; row++) {
	 // Bounce off walls? Marginally more expensive than wrapping.
	 tmp1 = x[row] + dx[base + row];
	 if ((dx[base + row] > 0 && x[row] > 0 && tmp1 < 0)
	     || (dx[base + row] < 0 && x[row] < 0 && tmp1 > 0)) {
	    dx[base + row] = -dx[base + row];
	 } else {
	    x[row] = tmp1;
	 }
	 tmp1 = y[row] + dy[base + row];
	 if ((dy[base + row] > 0 && y[row] > 0 && tmp1 < 0)
	     || (dy[base + row] < 0 && y[row] < 0 && tmp1 > 0)) {
	    dy[base + row] = -dy[base + row];
	 } else {
	    y[row] = tmp1;
	 }

	 // Bump asteroid?
#ifdef NEVER
	 for (other = row + 1; other < ASTEROIDS; other++) {
	    tmp1 = x[row] ^ x[other];
	    tmp2 = y[row] ^ y[other];
	    if ((tmp1 < 0) || (tmp2 < 0)) {
	       diffx = x[row] - x[other];
	       diffy = y[row] - y[other];
	       diffx *= diffx;
	       diffy *= diffy;
	       if (diffx + diffy < size2[row] + size2[other]) {
		  if (tmp1 < 0)
		     dx[row] = -dx[row];
		  if (tmp2 < 0)
		     dy[row] = -dy[row];
	       }
	    }
	 }
#endif
      }
      base ^= ASTEROIDS;
   }

   // if return value is <= 0, then a warm reset will be performed,
   // otherwise a cold reset will be performed
   return 0;
}

// ***************************************************************************
// end of file
// ***************************************************************************