#include <vectrex.h> #include "controller.h" // set -O3 in the gcc options. // This creates a 6x6 unit play area similar to Spike Gets Squished except // that the procedurally-generated world is static, meaning that you can // return to any part of the map that you moved away from. // It's quite complex code to get right so I'm sharing this with Brett just // as an example of the technique, not to put in his game which is fine the // way it is. It might come in useful for some new game some day in the future. // Performance is good, staying close to 30K cycles throughout. static inline void displaylist(const int *list) { Draw_VLp((int *)list); } #define IN_ROM static inline void set_scale(unsigned int scale) {*(volatile unsigned char *)0xD004 = scale;} #define DEFINE(name) static const int name[] IN_ROM = { #define move_rel_xy(x,y) 0 /*MOVE*/, ((int)y), ((int)x) #define line_rel_xy(x,y) (int)-1 /*LINE*/, ((int)y), ((int)x) #define ENDDEF(name) 1 /*STOP*/ }; static void SHOW_##name(void) { displaylist(name); } DEFINE(MINUS) move_rel_xy(4,8), // to 8,4 line_rel_xy(14,0), // to 8,20 move_rel_xy(4,-8), // to 0,24 ENDDEF(MINUS) DEFINE(N0) move_rel_xy(0,24), // to 0,24 line_rel_xy(16,0), // to 16,24 line_rel_xy(0,-24), // to 16,0 line_rel_xy(-16,0), // to 0,0 line_rel_xy(0,24), // to 0,24 move_rel_xy(24,-24), // to 24,0 ENDDEF(N0) DEFINE(N1) move_rel_xy(8,0), // to 8,0 line_rel_xy(0,24), // to 8,24 move_rel_xy(16,-24), // to 24,0 ENDDEF(N1) DEFINE(N2) move_rel_xy(0,24), // to 0,24 line_rel_xy(16,0), // to 16,24 line_rel_xy(0,-12), // to 16,12 line_rel_xy(-16,0), // to 0,12 line_rel_xy(0,-12), // to 0,0 line_rel_xy(16,0), // to 16,0 move_rel_xy(8,0), // to 24,0 ENDDEF(N2) DEFINE(N3) line_rel_xy(16,0), // to 16,0 move_rel_xy(-8,12), // to 8,12 line_rel_xy(8,0), // to 16,12 move_rel_xy(0,-12), // to 16,0 line_rel_xy(0,24), // to 16,24 line_rel_xy(-16,0), // to 0,24 move_rel_xy(24,-24), // to 24,0 ENDDEF(N3) DEFINE(N4) move_rel_xy(0,24), // to 0,24 line_rel_xy(0,-12), // to 0,12 line_rel_xy(16,0), // to 16,12 move_rel_xy(-8,0), // to 8,12 line_rel_xy(0,-12), // to 8,0 move_rel_xy(16,0), // to 24,0 ENDDEF(N4) DEFINE(N5) line_rel_xy(16,0), // to 16,0 line_rel_xy(0,12), // to 16,12 line_rel_xy(-16,0), // to 0,12 line_rel_xy(0,12), // to 0,24 line_rel_xy(16,0), // to 16,24 move_rel_xy(8,-24), // to 24,0 ENDDEF(N5) DEFINE(N6) move_rel_xy(0,12), // to 0,12 line_rel_xy(16,0), // to 16,12 line_rel_xy(0,-12), // to 16,0 line_rel_xy(-16,0), // to 0,0 line_rel_xy(0,24), // to 0,24 move_rel_xy(24,-24), // to 24,0 ENDDEF(N6) DEFINE(N7) move_rel_xy(8,0), // to 8,0 line_rel_xy(8,24), // to 16,24 line_rel_xy(-16,0), // to 0,24 move_rel_xy(24,-24), // to 24,0 ENDDEF(N7) DEFINE(N8) line_rel_xy(16,0), // to 16,0 move_rel_xy(-16,24), // to 0,24 line_rel_xy(16,0), // to 16,24 move_rel_xy(-16,-12), // to 0,12 line_rel_xy(16,0), // to 16,12 move_rel_xy(0,12), // to 16,24 line_rel_xy(0,-24), // to 16,0 move_rel_xy(-16,24), // to 0,24 line_rel_xy(0,-24), // to 0,0 move_rel_xy(24,0), // to 24,0 ENDDEF(N8) DEFINE(N9) move_rel_xy(16,24), // to 16,24 line_rel_xy(-16,0), // to 0,24 line_rel_xy(0,-12), // to 0,12 line_rel_xy(16,0), // to 16,12 move_rel_xy(0,12), // to 16,24 line_rel_xy(0,-24), // to 16,0 move_rel_xy(4,0), // to 24,0 ENDDEF(N9) static const int *digitlist[10] = { N0, N1, N2, N3, N4, N5, N6, N7, N8, N9 }; static inline void show_digit(int digit) { displaylist(digitlist[(int)digit]); } static void position_and_scale(int absx, int absy, unsigned int scale) { Reset0Ref(); set_scale(0x7f); Moveto_d(absy, absx); set_scale(scale); } static void SHOW_SIGNED_NUM(long int num, int absx, int absy, unsigned int scale) { // left-aligned int digit, zeroes; // If scale is 0, this draws a number starting at the current position, which is at the origin // of the previous number offset by the number width. if (scale) position_and_scale(absx, absy, 0x20); // This replaces code that used divide by 10 and modulo 10. Much faster. // handles full 16 bit range of -32768:32767 - Uses negative numbers to avoid the issue of negating -32768 if (num >= 0) num = -num; else SHOW_MINUS(); // TO DO: add a font character for '-'. digit = 0; zeroes = 1; // CLRing is shorter // max 11 add/subtracts... if (num <= -20000) { num += 20000; digit += 2; zeroes = 0; } if (num <= -10000) { num += 10000; digit += 1; zeroes = 0; } if (!zeroes) show_digit(digit); digit = 0; if (num <= -8000) { num += 8000; digit += 8; zeroes = 0; } else if (num <= -4000) { num += 4000; digit += 4; zeroes = 0; } if (num <= -2000) { num += 2000; digit += 2; zeroes = 0; } if (num <= -1000) { num += 1000; digit += 1; zeroes = 0; } if (!zeroes) show_digit(digit); digit = 0; if (num <= -800) { num += 800; digit += 8; zeroes = 0; } else if (num <= -400) { num += 400; digit += 4; zeroes = 0; } if (num <= -200) { num += 200; digit += 2; zeroes = 0; } if (num <= -100) { num += 100; digit += 1; zeroes = 0; } if (!zeroes) show_digit(digit); digit = 0; if (num <= -80) { num += 80; digit += 8; zeroes = 0; } else if (num <= -40) { num += 40; digit += 4; zeroes = 0; } if (num <= -20) { num += 20; digit += 2; zeroes = 0; } if (num <= -10) { num += 10; digit += 1; zeroes = 0; } if (!zeroes) show_digit(digit); show_digit((int)-num); } static void SHOW_UNSIGNED_NUM(unsigned long int num, int absx, int absy, unsigned int scale) { // left-aligned int digit, zeroes; // If scale is 0, this draws a number starting at the current position, which is at the origin // of the previous number offset by the number width. if (scale) position_and_scale(absx, absy, 0x20); // This replaces code that used divide by 10 and modulo 10. Much faster. // handles full 16 bit range of -32768:32767 - Uses negative numbers to avoid the issue of negating -32768 digit = 0; zeroes = 1; // CLRing is shorter // max 11 add/subtracts... if (num >= 40000) { num -= 40000UL; digit += 4; zeroes = 0; } if (num >= 20000) { num -= 20000UL; digit += 2; zeroes = 0; } if (num >= 10000) { num -= 10000UL; digit += 1; zeroes = 0; } if (!zeroes) show_digit(digit); digit = 0; if (num >= 8000) { num -= 8000; digit += 8; zeroes = 0; } else if (num >= 4000) { num -= 4000; digit += 4; zeroes = 0; } if (num >= 2000) { num -= 2000; digit += 2; zeroes = 0; } if (num >= 1000) { num -= 1000; digit += 1; zeroes = 0; } if (!zeroes) show_digit(digit); digit = 0; if (num >= 800) { num -= 800; digit += 8; zeroes = 0; } else if (num >= 400) { num -= 400; digit += 4; zeroes = 0; } if (num >= 200) { num -= 200; digit += 2; zeroes = 0; } if (num >= 100) { num -= 100; digit += 1; zeroes = 0; } if (!zeroes) show_digit(digit); digit = 0; if (num >= 80) { num -= 80; digit += 8; zeroes = 0; } else if (num >= 40) { num -= 40; digit += 4; zeroes = 0; } if (num >= 20) { num -= 20; digit += 2; zeroes = 0; } if (num >= 10) { num -= 10; digit += 1; zeroes = 0; } if (!zeroes) show_digit(digit); show_digit((int)num); } static unsigned int horiz[8]; // each byte is 8 x's static unsigned int vert[8]; // each byte is 8 y's static const unsigned int bitpos[8] = { 128U, 64U, 32U, 16U, 8U, 4U, 2U, 1U }; static const unsigned int notbitpos[8] = { ~128U, ~64U, ~32U, ~16U, ~8U, ~4U, ~2U, ~1U }; static inline unsigned int vertical(unsigned int x, unsigned int y) { // each byte is 8 y's return (vert[x]&bitpos[y]); // 0 or non-zero } static inline void setvertical(unsigned int x, unsigned int y) { // each byte is 8 y's vert[x] |= bitpos[y]; } static inline void clrvertical(unsigned int x, unsigned int y) { // each byte is 8 y's vert[x] &= notbitpos[y]; } static inline void assignvertical(unsigned int x, unsigned int y, unsigned int bitvalue) { // each byte is 8 y's vert[x] &= notbitpos[y]; if (bitvalue) vert[x] |= bitpos[y]; } static inline unsigned int horizontal(unsigned int x, unsigned int y) { // each byte is 8 x's return horiz[y]&bitpos[x]; // 0 or non-zero } static inline void sethorizontal(unsigned int x, unsigned int y) { // each byte is 8 x's horiz[y] |= bitpos[x]; } static inline void clrhorizontal(unsigned int x, unsigned int y) { // each byte is 8 x's horiz[y] &= notbitpos[x]; } static inline void assignhorizontal(unsigned int x, unsigned int y, unsigned int bitvalue) { // each byte is 8 x's horiz[y] &= notbitpos[x]; if (bitvalue) horiz[y] |= bitpos[x]; } #define XMAG 15 // See https://www.facebook.com/groups/vectrex/posts/1523916847818687/ #define YMAG 13 #define XBASE 16 #define YBASE 8 static inline void DrawHorizontal(int xl, int xr, int y) { // coordinate range is 0:15,0:15 Reset0Ref(); Moveto_d((y-4)*YMAG+YBASE, (xl-4)*XMAG+XBASE);// 0..7 -> -4..3 Draw_Line_d(0, (xr-xl)*XMAG); // 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-4)*YMAG+YBASE, (x-4)*XMAG+XBASE);// 0..7 -> -4..3 Draw_Line_d((yt-yb)*YMAG, 0); } unsigned int _x; unsigned int _a; unsigned int _b; unsigned int _c; 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); } unsigned int random8 (void) { _x++; _a = (_a ^ _c ^ _x); _b = (_b + _a); _c = ((_c + (_b >> 1)) ^ _a); return _c; } static int countdown_timer = 0; #define autorepeat_rate 12 static int dirx = 0, diry = 0; // An arbitrary 8x8 rectangle is found within 4 aligned 8x8 rectangles // The aligned ones use global coordinates aligned on an 8x8 grid to ensure reproducibility. // Scrolling is done by moving the inner rectangle until its origin is no longer // within the master lower-left aligned rectangle, at which point the grid is moved // 8 units at a time // // * * * * * * * * * * * * * * * * // * * * * * * * * * * * * * * * * // * * * * * * * * * * * * * * * * // * * * * * * * * * * * * * * * * // * * * * * * * * * * * * * * * * // * * * * * + - - - - - - + * * * // * * * * * | * * * * * * | * * * // * * * * * | * * * * * * | * * * // // * = = = = | = * * * * * | * * * // # * * * * | * # * * * * | * * * // # * * * * | * # * * * * | * * * // # * * * * | * # * * * * | * * * // # * * * * + - - - - - - + * * * <- yoff=3 // # * * * * * * # * * * * * * * * // # * * * * * * # * * * * * * * * // * = = = = = = * * * * * * * * * // ^ ^ // | xoff=5 // Origin x,y // // The 2x2 rectangles are not stored - they are calculated on // the fly using an idempotent hash. The inner rectangle *is* // stored, and re-generated on any scrolling move. // the _off variables may vary by 1 at a time until they reach 8 or ffff at which // point an 8-bit re-basing is required. // start in the middle of a large virtual world space - I haven't yet // considered what happens at 0/65535 :-) I'm assuming no-one would // ever get there unless they're deliberately trying to crash it, and // frankly who cares of they do or not! static unsigned long global_x = 32768UL; // Y coord of specific bit static unsigned long global_y = 32768UL; // Y coord of *BASE* of Y array #define HORIZONTAL 0x55 #define VERTICAL 0xAA unsigned int hash(unsigned long L1, unsigned long L2, unsigned int i) { unsigned long int HASH; // This part would have to be tweaked for generating any specific type of layout. // 4 is rightmost column 5-6 // 8 is one left of that 4-5 // 16 is one left of that 3-4 // 32 is one left of that 2-3 // 64 is one left of that 1-2 // 128 is leftmost column 0-1 HASH = (L1^L2) >> 5UL; HASH ^= (L1^L2) << 3UL; HASH = (HASH>>8UL) ^ HASH; return (unsigned int)(HASH ^ i); } void assign_bitmap(void) { // use 8x8, simpler than our reduced 7x7 // i.e scrolls on an 8x8 basis, even though // display is 7x7 // uses parameters global_x, global_y which are the bottom left corner of our 8x8 window long unsigned int x, y; unsigned long global_x_origin; // Y coord of leftmost bit if X array unsigned int global_x_off; // offset of specific bit in 8-bit X byte unsigned long global_y_origin; // Y coord of *BASE* of Y array unsigned int global_y_off; // Y coord of *BASE* of Y array unsigned long longi; // we handle x and y totally separately - in one, x is row-major, // and in the other x is column-major... global_x_origin = global_x>>3UL; global_x_off = (unsigned int)global_x&7; global_y_origin = global_y>>3UL; global_y_off = (unsigned int)global_y&7; // we are getting 8 bits of x data at a time, for 8 different y values. // Unless y0 is aligned with the bottom of the base block, we may have // to straddle both the base block and the one above it to get all the data for (y = global_y; y < global_y+8; y++) { // (y-global_y) is 0..7 longi = ((unsigned long)hash(y, global_x_origin, HORIZONTAL)<<8UL) | ((unsigned long)hash(y, global_x_origin+1UL, HORIZONTAL)); // for leftmost bit pos 0..7 we want to rightshift 8..1 horiz[y-global_y] = (unsigned int)(longi >> (unsigned long)(8-global_x_off)); // 128 is leftmost, 4 is rightmost } // now the same with x and y swapped for (x = global_x; x < global_x+8; x++) { // (x-global_x) is 0..7 longi = ((unsigned long)hash(x, global_y_origin, VERTICAL)<<8UL) | ((unsigned long)hash(x, global_y_origin+1UL, VERTICAL)); vert[x-global_x] = (unsigned int)(longi >> (unsigned long)(8-global_y_off)); } // 0 is the leftmost wall, and the bottom wall horiz[0] = 0xfc; horiz[6] = 0xfc; // Add walls. vert[0] = 0xfc; vert[6] = 0xfc; } int main(void) { unsigned int y = 0, x = 0; int i = 0; countdown_timer = 0; enable_controller_1_x(); enable_controller_1_y(); assign_bitmap(); for (;;) { Wait_Recal(); check_joysticks(); check_buttons(); Intensity_a(60); if (button_1_1_held()) { // leftmost button for internal debugging SHOW_SIGNED_NUM((long)countdown_timer, -18,120,255); SHOW_UNSIGNED_NUM(global_x, -88,-100,255); SHOW_UNSIGNED_NUM(global_y, -122,-85,255); } set_scale(255); Intensity_a(80); // Draw horizontal lines first: for (y = 0; y < 7; y++ ) { if (horiz[y] == 0) continue; x = 0; for (;;) { if (horizontal(x,y)) { unsigned int x0=x; do x++; while ((x < 6) && (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 >= 6) break; } } // Then vertical lines for (x = 0; x < 7; x++ ) { if (vert[x] == 0) continue; y = 0; 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 ((y < 6) && (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 >= 6) break; } } // scroll? if (countdown_timer) { countdown_timer -= 1; } else { i = joystick_1_x(); if (i < -40) { dirx = -1; } else if (i > 40) { dirx = 1; } i = joystick_1_y(); if (i < -40) { diry = -1; } else if (i > 40) { diry = 1; } } if (((dirx|diry) != 0) && (countdown_timer == 0)) { global_x += (unsigned long)dirx; global_y += (unsigned long)diry; dirx = diry = 0; countdown_timer = autorepeat_rate; assign_bitmap(); } } return 0; (void) &SHOW_N0; (void) &SHOW_N1; (void) &SHOW_N2; (void) &SHOW_N3; (void) &SHOW_N4; (void) &SHOW_N5; (void) &SHOW_N6; (void) &SHOW_N7; (void) &SHOW_N8; (void) &SHOW_N9; (void) &SHOW_MINUS; (void) &SHOW_SIGNED_NUM; (void) &SHOW_UNSIGNED_NUM; }