#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; }