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