#include <vectrex.h>
// compile with -O and display is consistently closer to 30,000 cycles :-)
// (however -O3 crashes on startup)
// WARNING! This is some pretty horrendous code, and too close examination of
// it may make you go blind. I've basically enumerated the perspective view from
// each of four quadrants manually. I have no doubt that this code could be
// greatly simplified, but that is for another day and probably another programmer.
// One in a series of quick & dirty hacks.
// unfortunately I made the size of the display area even, i.e. with block boundaries
// running down x==0 and y==0 which require special cases for various vectors (not
// completely implemented).
//
// If I had chosen an odd-sized playfield there would be blocks running down the
// centers and no ambiguity on viewing the vectors on either side...
// I might handle that in a later revision. Also need to consider scrolling
// by smaller units than a complete block... could be implemented using 2x2 blocks
// and only stopping on odd boundaries
// would be nice to coalesce multiple vectors into a single line...
// The only mild tweak needed is when a pillar is aligned with a horizontal/vertical in x/y
// - should suppress the shorter vector. This only happens on the x==0 or y==0 boundary
#define set_scale(s) do { VIA_t1_cnt_lo = (unsigned int)s; } while (0)
// not in a position to change SIZE at the moment!
#define SIZE 2
#define ROOF_SCALE ((int)(0xFFU/SIZE))
static int sq[SIZE*2][SIZE*2];
static int ix,x;
static int iy,y;
#define MAX_VECS 780L
static int displayfile[MAX_VECS];
static long nextp = 0L;
#define RESET 0
#define RESETANDMOVE 1
#define MOVETOABS 2
#define DRAWTOREL 3
#define INTENSITY 4
#define END 5
static void Display(void) {
static void *Case[] = { &&ResetL, &&ResetSkipL, &&MovetoAbsL, &&DrawtoRelL, &&IntensityL, &&EndL };
int dx, dy;
int *p = displayfile;
for (;;) {
// originally was switch but this is much faster
goto *Case[*p++];
ResetL:
Reset0Ref();set_scale(ROOF_SCALE);Moveto_d(0,0);
// reset0ref has to have a moveto after it when drawing from 0,0
// most resets are followed by a move anyway...
// so have replaced reset by reset and move so we only insert once per frame
// old implementation was too expensive
continue;
ResetSkipL:
MovetoAbsL:
Reset0Ref();set_scale(ROOF_SCALE);
dy = *p++; dx = *p++;
Moveto_d(dy, dx);
continue;
DrawtoRelL:
dy = *p++; dx = *p++;
Draw_Line_d(dy,dx);
continue;
IntensityL:
Intensity_a((unsigned int)*p++);
continue;
EndL:
return;
}
}
static int last = 0;
static long pendingx = 0L, pendingy = 0L;
static long beam_x = 0L, beam_y = 0L;
static inline void Moveto(int dy, int dx) {
pendingx += (long)dx; pendingy += (long)dy;
}
static void Draw_Line(int dy, int dx) {
if (nextp>=MAX_VECS-6L) return;
if ((beam_x != pendingx) || (beam_y != pendingy)) {
displayfile[nextp++] = last = MOVETOABS;
displayfile[nextp++] = (int)(beam_y = pendingy);
displayfile[nextp++] = (int)(beam_x = pendingx);
}
#ifdef NEVER // not making a cycle of difference now
if (last == DRAWTOREL) {
long ody = (long)displayfile[nextp-2];
long odx = (long)displayfile[nextp-1];
long ndy = ody+(long)dy;
long ndx = odx+(long)dx;
if (-128L <= ndx && ndx <= 127L && -128L <= ndy && ndy <= 127L &&
((odx==0L && dx==0) || (ody==0L && dy==0))) {
displayfile[nextp-2] = (int)ndy;
displayfile[nextp-1] = (int)ndx;
pendingx += (long)dx; pendingy += (long)dy;
beam_x = pendingx; beam_y = pendingy;
return;
}
}
#endif
displayfile[nextp++] = last = DRAWTOREL;
displayfile[nextp++] = dy;
displayfile[nextp++] = dx;
displayfile[nextp] = END;
pendingx += (long)dx; pendingy += (long)dy;
beam_x = pendingx; beam_y = pendingy;
}
static void Intensity(int i) {
if (nextp>=MAX_VECS-2L) return;
displayfile[nextp++] = last = INTENSITY;
displayfile[nextp++] = i;
displayfile[nextp] = END;
}
#ifdef NEVER
static void Reset0(void) {
if (nextp>=MAX_VECS-1L) return;
displayfile[nextp++] = last = RESET;
displayfile[nextp] = END;
beam_x = pendingx = 0L; beam_y = pendingy = 0L;
}
#endif
static void ResetSkip(int x, int y) {
if (nextp>=MAX_VECS-1L) return;
displayfile[nextp++] = last = RESETANDMOVE;
displayfile[nextp++] = y;
displayfile[nextp++] = x;
displayfile[nextp] = END;
beam_x = pendingx = x; beam_y = pendingy = y;
}
#define RSkip(dx, dy) Moveto((int)(dy), (int)(dx))
#define RLine(dx, dy) Draw_Line((int)(dy), (int)(dx))
#define GSkip(dx, dy) Moveto((int)(dy), (int)(dx))
#define GLine(dx, dy) Draw_Line((int)(dy), (int)(dx))
#define YFAC 5
#define XFAC 6
static int dx(int x) {
return (int)((long)x*(long)XFAC*(long)8/(long)SIZE);
}
static int dy(int y) {
return (int)((long)y*(long)YFAC*(long)8/(long)SIZE);
}
// crashes if this is inline or macro!
// starts at roof pos, ends at ground pos
static void Pillar(int x, int y) {
RLine(-dx(x),-dy(y));
}
static void roof(void) { // floor is half the size of roof
#define YH ((YFAC*20)/SIZE)
#define XW ((XFAC*20)/SIZE)
Intensity(0x6F);
for (iy = -SIZE, y = -SIZE*YH; iy < SIZE; y+=YH, iy+=1) {
for (ix = -SIZE, x = -SIZE*XW; ix < SIZE; x+=XW, ix+=1) {
ResetSkip(ix*XW, iy*YH);
if ((sq[ix+SIZE][iy+SIZE]) || ((iy > -SIZE) && sq[ix+SIZE][iy+SIZE-1])) {
if (iy >= 0) {
if (sq[ix+SIZE][iy+SIZE] != sq[ix+SIZE][iy+SIZE-1]) {
RLine(XW, 0);
} else {
RSkip(XW, 0);
}
} else if (iy < 0) {
if ( ((iy == -SIZE) && sq[ix+SIZE][iy+SIZE])
|| (iy > -SIZE && !sq[ix+SIZE][iy+SIZE])
|| ((iy > -SIZE) && sq[ix+SIZE][iy+SIZE] && !sq[ix+SIZE][iy+SIZE-1])) {
RLine(XW, 0); // horizontal line below block
} else {
RSkip(XW, 0);
}
} else {
RSkip(XW, 0);
}
} else {
RSkip(XW, 0); // horizontal below block
}
if ( (!sq[ix+SIZE][iy+SIZE] && (sq[ix+SIZE+1][iy+SIZE]) && (ix < SIZE-1)) // lhs of next
|| (sq[ix+SIZE][iy+SIZE] && !sq[ix+SIZE+1][iy+SIZE] && (ix < SIZE-1))
|| (ix == SIZE-1 && sq[ix+SIZE][iy+SIZE])
) { // rhs
RLine(0, YH);
} else {
RSkip(0, YH); // rhs verticals
}
if (iy == SIZE-1) {
if (sq[ix+SIZE][iy+SIZE]) {
RLine(-XW, 0);
} else {
RSkip(-XW, 0); // top row horizontals
}
RSkip(XW, 0);
}
RSkip(0, -YH);
}
// ix would be -SIZE if it were set here.
ResetSkip(-SIZE*XW, y);
ix = -SIZE;
if (sq[ix+SIZE][iy+SIZE]) {
RLine(0, YH); // leftmost vertical
}
}
}
static void support_nearest_center(void) {
Intensity(0x40);
for (iy = -SIZE, y = -SIZE*YH; iy <= SIZE; y+=YH, iy+=1) {
for (ix = -SIZE, x = -SIZE*XW; ix <= SIZE; x+=XW, ix+=1) {
if (ix||iy) {
ResetSkip(x, y);
if (ix <= 0) {
if (iy <= 0) {
// diagonals at bottom left of sq[ix+SIZE][iy+SIZE]
if (!sq[ix+SIZE][iy+SIZE]) { // solid squares occlude themselves
if (ix == -SIZE) {
if (iy != -SIZE) {
if (sq[ix+SIZE][iy+SIZE-1]) {
Pillar(ix, iy);
}
}
} else {
if (iy == -SIZE) {
if (sq[ix+SIZE-1][iy+SIZE]) Pillar(ix, iy);
} else {
if (!sq[ix+SIZE-1][iy+SIZE] && !sq[ix+SIZE-1][iy+SIZE-1] && sq[ix+SIZE][iy+SIZE-1]) {
Pillar(ix, iy);
} if (sq[ix+SIZE-1][iy+SIZE] && !sq[ix+SIZE-1][iy+SIZE-1] && !sq[ix+SIZE][iy+SIZE-1]) {
Pillar(ix, iy);
} else if (sq[ix+SIZE-1][iy+SIZE] && sq[ix+SIZE][iy+SIZE-1]) {
Pillar(ix, iy);
} else if (sq[ix+SIZE-1][iy+SIZE-1] && !sq[ix+SIZE-1][iy+SIZE] && !sq[ix+SIZE][iy+SIZE-1]) {
Pillar(ix, iy);
}
}
}
}
} else if (iy > 0) {
// diagonal is at lower left corner of sq[ix][iy]
if (ix == -SIZE) {
if (iy < SIZE) {
if (sq[ix+SIZE][iy+SIZE] && !sq[ix+SIZE][iy+SIZE-1]) {
Pillar(ix, iy);
}
}
} else {
if (iy == SIZE) {
if (sq[ix+SIZE-1][iy+SIZE-1] && !sq[ix+SIZE][iy+SIZE-1]) {
Pillar(ix, iy);
}
} else {
// diagonal is at lower left corner of sq[ix][iy]
if (!sq[ix+SIZE][iy+SIZE-1]) {
if (sq[ix+SIZE-1][iy+SIZE-1] && !sq[ix+SIZE-1][iy+SIZE] && !sq[ix+SIZE][iy+SIZE-1]) {
Pillar(ix, iy);
} else if (sq[ix+SIZE-1][iy+SIZE] && !sq[ix+SIZE][iy+SIZE] && !sq[ix+SIZE-1][iy+SIZE-1]) {
Pillar(ix, iy);
} else if (sq[ix+SIZE-1][iy+SIZE-1] && sq[ix+SIZE][iy+SIZE]) {
Pillar(ix, iy);
} else if (!sq[ix+SIZE-1][iy+SIZE] && sq[ix+SIZE][iy+SIZE]) {
Pillar(ix, iy);
} else if (sq[ix+SIZE-1][iy+SIZE-1] && !sq[ix+SIZE][iy+SIZE] && !sq[ix+SIZE-1][iy+SIZE-1]) {
Pillar(ix, iy);
}
}
}
}
}
} else if (ix > 0) { // ALL DONE EXCEPT THE ZERO SPECIAL CASES
if (iy > 0) { // DONE
// iy == SIZE is the invisible row above the area
// diagonals are drawn at bottom left corner of sq[ix+SIZE][iy+SIZE]
if (ix == SIZE) {
if ((iy < SIZE) && sq[ix+SIZE-1][iy+SIZE] && !sq[ix+SIZE-1][iy+SIZE-1]) {
Pillar(ix, iy);
}
} else {
if (iy == SIZE) {
if (sq[ix+SIZE][iy+SIZE-1] && !sq[ix+SIZE-1][iy+SIZE-1]) {
Pillar(ix, iy);
}
} else {
if (sq[ix+SIZE][iy+SIZE] && !sq[ix+SIZE-1][iy+SIZE-1] && !sq[ix+SIZE][iy+SIZE-1]) {
if (!sq[ix+SIZE-1][iy+SIZE]) {
Pillar(ix, iy);
}
} else if (!sq[ix+SIZE][iy+SIZE] && !sq[ix+SIZE-1][iy+SIZE-1] &&
(sq[ix+SIZE-1][iy+SIZE] || sq[ix+SIZE][iy+SIZE-1])) {
Pillar(ix, iy);
} else if (!sq[ix+SIZE-1][iy+SIZE-1]) {
// diagonals are drawn at bottom left corner of sq[ix+SIZE][iy+SIZE]
// space left and below is free to draw into
if ( ((sq[ix+SIZE-1][iy+SIZE] && sq[ix+SIZE][iy+SIZE-1]))
|| (sq[ix+SIZE][iy+SIZE] && !sq[ix+SIZE-1][iy+SIZE] && !sq[ix+SIZE][iy+SIZE-1])) {
Pillar(ix, iy);
}
}
}
}
} else if (iy <= 0) { // DONE!
// diagonal at top-left corner of sq[ix+SIZE][iy+SIZE]
if (ix == SIZE) { // absent row to right of area
if (iy == -SIZE) { // absent row below area
} else {
if (sq[ix+SIZE-1][iy+SIZE-1] && !sq[ix+SIZE-1][iy+SIZE]) {
Pillar(ix, iy);
}
}
} else {
// ALWAYS draw diagonal at bottom-left corner of sq[ix+SIZE][iy+SIZE]
if (iy == -SIZE) {
if (!sq[ix+SIZE-1][iy+SIZE] && sq[ix+SIZE][iy+SIZE]) {
Pillar(ix, iy);
}
} else if (!sq[ix+SIZE-1][iy+SIZE]) {
// room to draw into
if (sq[ix+SIZE][iy+SIZE]) {
if (sq[ix+SIZE-1][iy+SIZE-1]) {
Pillar(ix, iy);
} else {
if (!sq[ix+SIZE][iy+SIZE-1]) {
Pillar(ix, iy);
}
}
} else if (sq[ix+SIZE-1][iy+SIZE-1]) {
if (!sq[ix+SIZE][iy+SIZE-1]) {
Pillar(ix, iy);
}
} else if (sq[ix+SIZE][iy+SIZE-1]) {
Pillar(ix, iy);
}
}
}
} else {
// iy == 0 - special case?
}
} else {
// center - ignore
}
}
}
}
}
static void ground(void) {
#undef YH
#undef XW
// 2/3rds of roof scale
#define YH ((YFAC*12)/SIZE)
#define XW ((XFAC*12)/SIZE)
Intensity(0x5F);
for (iy = -SIZE, y = -SIZE*YH; iy < SIZE; y+=YH, iy+=1) {
for (ix = -SIZE, x = -SIZE*XW; ix < SIZE; x+=XW, ix+=1) {
ResetSkip(ix*XW, iy*YH); // small fudge factor for positioning
if (iy > 0) {
if (ix >= 0) {
// truncate on left
if (sq[ix+SIZE][iy+SIZE] && !sq[ix+SIZE][iy+SIZE-1] ) {
if (sq[ix+SIZE-1][iy+SIZE-1]) {
GSkip(dx(ix), 0); GLine(XW - dx(ix), 0);
} else {
GLine(XW, 0);
}
} else {
GSkip(XW, 0);
}
} else {
// truncate on right
if (sq[ix+SIZE][iy+SIZE] && !sq[ix+SIZE][iy+SIZE-1] ) {
if (sq[ix+SIZE+1][iy+SIZE-1]) { // do we extend under a cube?
GLine(XW + dx(ix+1), 0); GSkip(-dx(ix+1), 0);
} else {
GLine(XW, 0);
}
} else {
GSkip(XW, 0);
}
}
} else if (iy < 0) {
if ((iy > -SIZE) && sq[ix+SIZE][iy+SIZE-1] && !sq[ix+SIZE][iy+SIZE] ) {
if (ix >= 0) { // >
// truncate on left
if (sq[ix+SIZE-1][iy+SIZE]) {
GSkip(dx(ix), 0); GLine(XW - dx(ix), 0);
} else {
GLine(XW, 0);
}
} else {
if (sq[ix+SIZE+1][iy+SIZE]) { // do we extend under a cube?
GLine(XW + dx(ix+1), 0); GSkip(-dx(ix+1), 0);
} else {
GLine(XW, 0);
}
}
} else {
GSkip(XW, 0);
}
} else {
GSkip(XW, 0);
}
// now do vertical overshoots...
if ((ix >= 0) && (ix < SIZE-1)) {
if (!sq[ix+SIZE][iy+SIZE] && sq[ix+SIZE+1][iy+SIZE]) {
if (iy >= 0) {
// extends down
if (sq[ix+SIZE][iy+SIZE-1]) {
GSkip(0, dx(iy)); GLine(0, YH-dy(iy));
} else {
GLine(0, YH);
}
} else {
// extends up
// for an unknown reason one in a small number of the vectors below
// is mis-positioned. This bodges it back. COYOTE UGLY
ResetSkip(x+XW, y);
if (sq[ix+SIZE][iy+SIZE+1]) {
GLine(0, YH+dy(iy+1)); GSkip(0, -dy(iy+1));
} else {
GLine(0, YH);
}
}
} else {
GSkip(0, YH);
}
} else if (ix < 0) {
if (sq[ix+SIZE][iy+SIZE] && !sq[ix+1+SIZE][iy+SIZE]) {
if (iy >= 0) {
if (sq[ix+SIZE+1][iy+SIZE-1]) {
GSkip(0, dy(iy)); GLine(0, YH-dy(iy));
} else {
GLine(0, YH);
}
} else {
// lower left quadrant incl center line
if (sq[ix+SIZE+1][iy+SIZE+1]) {
GLine(0, YH+dy(iy+1)); GSkip(0, dy(iy+1));
} else {
GLine(0, YH);
}
}
} else {
GSkip(0, YH);
}
} else {
GSkip(0, YH); // vertical at rhs of block
}
GSkip(0, -YH);
}
ResetSkip(-SIZE*XW, y);
}
}
static unsigned int _x, _a, _b, _c;
static void initRandom(unsigned int s1,unsigned int s2,unsigned int s3, unsigned int x0) {
_x = x0; _a = s1; _b = s2; _c = s3; _x++; _a = (_a^_c^_x); _b = (_b+_a); _c = ((_c+(_b>>1))^_a);
}
static int random(void) { // assert returns unsigned value that fits in an int.
_x++; _a = (_a^_c^_x); _b = (_b+_a); _c = ((_c+(_b>>1))^_a);
return (int)_c;
}
static unsigned int Prev_Btn_State = 0;
int main(void)
{
int x, y;
initRandom(12,34,56,78); (void)random();
for (y = -SIZE; y < SIZE; y++) {
for (x = -SIZE; x < SIZE; x++) {
sq[x+SIZE][y+SIZE] = random()&1;
}
}
Read_Btns();
nextp = 0;
roof();
support_nearest_center();
ground();
for (;;) {
Prev_Btn_State = Vec_Btn_State;
Read_Btns();
Wait_Recal();
Display();
if (((Vec_Btn_State&12) == 12) && (Vec_Btn_State != Prev_Btn_State)) {
for (y = -SIZE; y < SIZE; y++) {
for (x = -SIZE; x < SIZE; x++) {
sq[x+SIZE][y+SIZE] = random()&1;
}
}
nextp = 0;
roof();
support_nearest_center();
ground();
}
}
return 0;
}