/* STAR TERROR! */ // Given that Faw Labo has teased us over the last several years with many // unreleased demos/games, all basically the same same code of a graphical // intro and a star field, // https://www.youtube.com/watch?v=qi0LYXnZ_IU // https://www.youtube.com/watch?v=ZkawFHJf9Fk // https://www.youtube.com/watch?v=pABpAVcFeiI // https://www.youtube.com/watch?v=Felzgc1ynJc // https://www.youtube.com/watch?v=cJIzZnYxnl4 // https://www.youtube.com/watch?v=L0CIdx2Ri3w // https://www.youtube.com/watch?v=qK7plhQTxDc // I thought I might encourage some of our coders to take on those projects, // and finish/release something playable, ... so here is a skeleton for a // similar game, with a firing sight and star movement (attacking or retreating, your choice). // There are plenty of free cycles left for the gameplay. // (I ripped this out of my tailgunner code as you probably guessed) // Just add some bad guys to shoot at and maybe a big banner intro... // Note that a 'star trek' banner could be extracted from: // https://commons.wikimedia.org/wiki/File:Star_Trek_TOS_Logo.svg#/media/File:Star_Trek_TOS_Logo.svg // convert the SVG using https://atariage.com/forums/topic/237191-sinz-v07-vectrex-demo-playing-around-with-sinus/ ... // and you can probably find the star trek or star wars themes online and massage them to work with // the vectrex sound chip, eg http://www.lotro-abc.com/abc/startrek.abc // The code below took me about 2 hours to put together; I'ld go on to make an actual game // out of it if I had more time, but I swore to my colleagues in the PiTrex project that // I would spend this weekend working on the MAME port, which is more urgent than this hack... // Graham. 31 Aug 2019. #include <vectrex.h> static long rseed = 12345L; static inline long urandom16 (void) { return (rseed = (rseed * 2053) + 13849); } static inline unsigned long irand (long max) { // returns int 0..max-1 return (unsigned long) (urandom16 () % max); // probably not very random, but this is a video game, not crypto } static inline long pickrandom (long low, long high) { // We'll just assume that high > low... return (long) irand (high - low + 1) + low; // pick a random integer between low and high inclusive. } static int stardots[8]; // fill with randoms ONCE - could move this into ROM... static int rotated3[8]; // rotate stardots into place static int rotated2[8]; static int rotated1[8]; static int rotated0[8]; static int *rotated[4] = { rotated0, rotated1, rotated2, rotated3 }; static unsigned int starfield_scale; #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 set_scale (unsigned int scale) { *(volatile unsigned char *) 0xD004 = scale; /*VIA_t1_cnt_lo */ } static inline void line (int y, int x) { *(volatile unsigned char *) 0xC823 = 0; Draw_Line_d ((int) y, (int) x); } static inline void dots (unsigned int size, const int *array) { *(volatile char *) 0xC823 = size - 1; Dot_List ((void *) array); } static inline void rotate_vl (unsigned int angle, unsigned int points, const void *original, void *rotated) { *(volatile unsigned int *) 0xC836 = angle; *(volatile unsigned int *) 0xC823 = points - 1; Rot_VL ((int *) original, rotated); } #define joybit Joy_Digital #define tstat (*((volatile unsigned int *) 0xc856)) #define pot0 (*((volatile int *) 0xc81b)) #define pot1 (*((volatile int *) 0xc81c)) #define pot2 (*((volatile int *) 0xc81d)) #define pot3 (*((volatile int *) 0xc81e)) #define epot0 (*((volatile unsigned int *) 0xc81f)) #define epot1 (*((volatile unsigned int *) 0xc820)) #define epot2 (*((volatile unsigned int *) 0xc821)) #define epot3 (*((volatile unsigned int *) 0xc822)) #define t1lolc (*((volatile unsigned int *) 0xd004)) #define read_ram(x) (*((volatile int *) x)) #define joystick1_button1 (int)(read_ram(0xC80F) & 1U) /* A */ #define joystick1_button2 (int)(read_ram(0xC80F) & 2U) /* S */ #define joystick1_button3 (int)(read_ram(0xC80F) & 4U) /* D */ #define joystick1_button4 (int)(read_ram(0xC80F) & 8U) /* F */ static int XJoy, YJoy, button1, button2, button3, button4; static void poll_joystick (void) { joybit (); if (pot0 < -10) { XJoy = -1; // Left } else if (pot0 > 10) { XJoy = 1; // Right } else { XJoy = 0; } if (pot1 < -10) { YJoy = -1; // Down } else if (pot1 > 10) { YJoy = 1; // Up } else { YJoy = 0; } asm ("pshs x,y,u,dp"); asm ("lda #0"); asm ("jsr 0HF1B4 ; read_btns_mask":::"a", "b", "d"); asm ("puls dp,u,y,x"); button1 = joystick1_button1; button2 = joystick1_button2; button3 = joystick1_button3; button4 = joystick1_button4; } static void init_hardware (void) { // setup joystick read function to read only joystick 1 epot0 = 1; epot1 = 3; epot2 = 0; epot3 = 0; } static inline void init_stars (void) { starfield_scale = (unsigned int) 0xFF; // origin at 0,0 stardots[0] = -60; stardots[1] = (int) pickrandom (-45, 45); // LEFT stardots[2] = 60; stardots[3] = (int) pickrandom (-45, 45); // RIGHT stardots[4] = (int) pickrandom (-45, 45); stardots[5] = 60; // TOP stardots[6] = (int) pickrandom (-45, 45); stardots[7] = -60; // BOT stardots[6] -= stardots[4]; stardots[7] -= stardots[5]; // abs to rel stardots[4] -= stardots[2]; stardots[5] -= stardots[3]; stardots[2] -= stardots[0]; stardots[3] -= stardots[1]; rotate_vl ((unsigned int) (pickrandom (0, 15)), 4U, stardots, rotated0); rotate_vl ((unsigned int) (pickrandom (16, 31)), 4U, stardots, rotated1); rotate_vl ((unsigned int) (pickrandom (32, 47)), 4U, stardots, rotated2); rotate_vl ((unsigned int) (pickrandom (48, 63)), 4U, stardots, rotated3); } static void stars (int speed) { // speed must be a power of 2 unsigned int shifted_scale; int i; shifted_scale = starfield_scale - (unsigned int) 0xBF; for (i = 0; i < 4; i++) { if ((unsigned int) shifted_scale > (unsigned int) 0x20) { // disappear here Reset0Ref (); Intensity_a ((shifted_scale >> 1) - 0x10); // fade with distance set_scale (shifted_scale); dots (4, rotated[i]); } else { if (((unsigned int) shifted_scale & (unsigned int) 0x3F) == 0U) { // pull in new ring of stars rotate_vl ((unsigned int) (pickrandom (0, 63)), 4U, stardots, rotated[i]); } } shifted_scale += 0x40; } starfield_scale -= (unsigned int) speed; // must be power of 2 } static const int crosshair[] = { 0, 17, 40, // trying to move it to the center -1, -30, 0, 0, 15, 20, -1, 0, -115, 0, -15, 20, -1, 30, 0, 0, -10, 20, -1, -10, 0, 0, -10, 10, -1, 0, 10, 0, -15, 10, -1, 0, -30, 0, -20, 15, -1, 105, 0, 0, -20, 15, -1, 0, -30, 0, -20, 10, -1, 0, 10, 0, -10, 10, -1, -10, 0, 1 }; static int crosshair_x, crosshair_y; static void follow_crosshair (void) { XJoy += XJoy; XJoy += XJoy; YJoy += YJoy; YJoy += YJoy; // constrain joysticks but carefully avoid integer overflow if ((crosshair_x >= 0) && (XJoy > 0) && (crosshair_x >= 127 - XJoy)) /* ignore */ ; else if ((crosshair_x < 0) && (XJoy < 0) && (crosshair_x < -127 - XJoy)) /* ignore */ ; else crosshair_x += XJoy; if ((crosshair_y >= 0) && (YJoy > 0) && (crosshair_y >= 127 - YJoy)) /* ignore */ ; else if ((crosshair_y < 0) && (YJoy < 0) && (crosshair_y < -127 - YJoy)) /* ignore */ ; else crosshair_y += YJoy; Reset0Ref (); set_scale (0x7f); Moveto_d (crosshair_y, crosshair_x); set_scale (0x18); Draw_VLp (crosshair); } int main (void) { long i; int starspeed = -4; init_hardware (); init_stars (); while (1) { for (i = 0L; i < 400L; i++) { Wait_Recal (); Reset0Ref (); Intensity_a (0x7F); Print_Str_d (0, -50, (starspeed < 0 ? "FORWARD\x80" : "REVERSE\x80")); poll_joystick (); follow_crosshair (); if (button4) { // firing lasers // You could patterns these lines and flicker them off and on for a better effect, like tracer shot... Reset0Ref (); set_scale (0x7fU); Moveto_d (-128, -128); set_scale (0xff); line ((int) (((long) crosshair_y + 128L) / 2L), (int) (((long) crosshair_x + 128L) / 2L)); // draw line to bottom left corner Reset0Ref (); set_scale (0x7fU); Moveto_d (-128, 127); set_scale (0xff); line ((int) (((long) crosshair_y + 128L) / 2L), (int) (((long) crosshair_x - 128L) / 2L)); // draw line to bottom right corner } stars (starspeed); } starspeed = -starspeed; // just for demo. } return 0; }