Vectrex: C (GCC)

As another vectrex related project I tried to be able to program the vectrex in the “C” language.

I drew upon the GCC crosscompiler. The results are somewhat ambigious. It sort of works, but it is not fool proof.
What I mean by that is, that one CAN (with the provided package) do C programs and compile them to run on a vectrex, but if you do something wrong (although the C language allows it), it does not work. Responsible for that is mainly a bug in the crosscompiler of gcc that I was not able to fix (or should I rather say, I was not willing to invest enough time to get to know the gcc sources intimately enough to debug it).

The bug has to to with the (8bit) registers A and B and their combination to the (16bit) register D.

One CAN use the compiler, but if something does not work out of the box, make sure you know enough assembler, to check the generated sources and find a workaround in C so the sources are generated correctly.

Note:
Unlike some “new” C-Compiler packages which enable to program “C” for vectrex, I explicitly configured the gcc version to use “Register Calling Convention” rather than “Stack Calling Convention”. This make this version of “C” for vectrex quite a bit more performant than others.

Included in the package are the sources of the GCC-version I used, all binaries and directories are included. The package does work in the DVE-DOSBox environment you can download on this site (see: DVE). You can copy the directory CC09 to the mounted device “E:”.

To compile a demo (or your own program) just go to the directory and type “..\bin\make”, which should “make” everything that is needed, including the final “bin”.

You can download the C-package: CC09.ZIP

The package also contains compiled versions of all 8 “C” demo programs I made.

Demo1 “Hello World”:

Demo1

/*
 * Include some standard vectrex functions first!
 */
#include <vectrex.h>
/*
 * Some defines, Maximal brightness is $7f, highest bit not set!
 */
#define MAX_BRIGHTNESS (0x7f)
/*
 * Our main function we start of here...
 * we should make sure that we never return from here, or vectrex will
 * be surely bothered!
 */
int main(void)
{
 while (true)                         /* never to return... */
 {
   wait_recal();                      /* recalibrate vectrex */
   intensity(MAX_BRIGHTNESS);         /* set some brightness */
   write_ram(Vec_Text_Width, 100);
   print_str(-70,0,"HELLO WORLD!");   /* print hello world! */
 }                                    /* while (true) */
}
/* END OF FILE */

 

Demo2 “Play Vectrex Music”:

Demo2

/*
 * Include some standard vectrex functions first!
 */
#include <vectrex.h>
/*
 * Some defines, Maximal brightness is $7f, highest not set!
 * Max Scale would be $ff, well here we take only $f0!
 */
#define MAX_BRIGHTNESS (0x7f)
#define MAX_SCALE (0xf0)
/*
 * For variable variables ALLWAYS leave them uninitialized, this way
 * the compiler puts them into the BSS ram section in vectrex ram
 * area from c880 onwards.
 *
 * All non BSS memory should be declared constant!
 * (This is still leaves the option of auto variables in a
 * functiion, which will takes it needed men from the stack)
 *
 */
unsigned char *current_song;
/*
 * If you declare a function 'static inline' it is pretty much sure that
 * the compiler will inline it completely, the function will not
 * appear in any way in the module.
 *
 * If you leave out the static, the compiler assumes it might be used
 * globally, and thus leaves the function in the code, even if
 * it is inlined everywhere!
 */
/*
 * This Funktion handles all startup code, needed for vectrex to work
 * correctly, recallibrating the beam and making sure that some sound
 * is played (if wanted).
 */
static inline void start_one_vectrex_round(void)
{
 set_dp_c8();                     /* vectrex internal... dp must point */
 recalculate_music(current_song); /* to c800, could make a function which */
 wait_recal();                    /* sets this up allright... */
 do_sound();
}
/*
 * This function sets up a piece of music to be played from the start
 * of the next round on onward...
 */
static inline void play_song(unsigned char *song)
{
 write_ram(Vec_Music_Flag, 1);   /* A makro to write to a specific memory */
 current_song = song;            /* address */
}
/*
 * Our main function we start of here...
 * we should make sure that we never return from here, or vectrex will
 * be surely bothered!
 */
int main(void)
{
 unsigned char last_button = 0;   /* one auto variable... remember the */
 read_buttons();                  /* last pressed button */
 while (true)                     /* never to return... */
 {
   start_one_vectrex_round();     /* start 'de round */
   set_scale(MAX_SCALE);          /* set scale factor */
   intensity(MAX_BRIGHTNESS);     /* set some brightness */
   print_str(-20,120, "MUSIC");   /* print on screen */
   print_str(-100,80, "BUTTON 1 SCRAMBLE");
   print_str(-100,40, "BUTTON 2 CRAZY COASTER");
   print_str(-100,00, "BUTTON 3 BERZERK ");
   print_str(-100,-40,"BUTTON 4 SOLAR QUEST");
   if ((joystick1_button1) && (last_button != 1)) /* check the button 1 */
   {
     play_song(SCRAMBLE_MUSIC);   /* play a song */
     last_button = 1;             /* and remember this button */
   } else
   if ((joystick1_button2) && (last_button != 2)) /* check the button 2 */
   {
     play_song(CRAZY_COASTER);    /* play a song */
     last_button = 2;             /* and remember this button */
   } else
   if ((joystick1_button3) && (last_button != 3)) /* check the button 3 */
   {
     play_song(BERZERK);          /* play a song */
     last_button = 3;             /* and remember this button */
   } else
   if ((joystick1_button4) && (last_button != 4)) /* check the button 4 */
   {
     play_song(SOLAR_QUEST);      /* play a song */
     last_button = 4;             /* and remember this button */
   }
   read_buttons();                /* read again for next round */
 }                                /* while (true) */
}
/* END OF FILE */

 

 

Demo 3 “Pacman”:

Demo3

 

...
/*
 * A simple setup routine, enables/disables the joystick, and makes sure
 * that the button state is read correctly...
 */
void setup(void)
{
 enable_joystick_1x();
 enable_joystick_1y();
 disable_joystick_2x();
 disable_joystick_2y();
 joy_digital();
 read_buttons();
 wait_recal();                     /* one round */
}
/*
 * Our main function we start of here...
 * we should make sure that we never return from here, or vectrex will
 * be surely bothered!
 */
int main(void)
{
 unsigned char anim_state;        /* our animation state counter */
 signed char pacman_x;            /* where is the pacman? */
 signed char pacman_y;
 pacman_x = 0;
 pacman_y = 0;
 anim_state = 0;
 setup();                         /* setup our program */
 while (true)                     /* never to return... */
 {
   start_one_vectrex_round();     /* start 'de round */
   intensity(MAX_BRIGHTNESS);     /* set some brightness */
   set_scale(MOVE_SCALE);         /* set scale factor */
   print_str(-128,100, "JOYSTICK 1 TO MOVE PACMAN!"); /* a message! */
   move_to(pacman_x, pacman_y);   /* position pacman */
   set_scale(PACMAN_SCALE);       /* set scale factor for the sprite */
   draw_vector_list(pacman[anim_state]); /* draw the current pacman */
   anim_state++;                  /* next time the next animation */
   if (anim_state == MAX_ANIM)    /* could do a % MAXANIM, but this is */
     anim_state = 0;              /* more optimized */
   if (!read_ram(Vec_Music_Flag)) /* music finished? */
     play_song(SCRAMBLE_MUSIC);   /* if so ... restart */
   if (joystick1_x>0)             /* check the joystick and */
   {                              /* update position */
     pacman_x++;
   }
   else if (joystick1_x<0)
   {
     pacman_x--;
   }
   if (joystick1_y>0)
   {
     pacman_y++;
   }
   else if (joystick1_y<0)
   {
     pacman_y--;
   }
   if (pacman_x>=100) pacman_x = 100; /* make sure pacman is not */
   if (pacman_x<=-100) pacman_x = -100; /* out of bounds */
   if (pacman_y>=100) pacman_y = 100;
   if (pacman_y<=-100) pacman_y = -100;
   joy_digital();                /* call once per round, to insure */
  }                              /* joystick information is up to date */
}
/* END OF FILE */

 

Demo 4 “Scrolltext”:

Demo4

(The scroll routines I used from VFrogger are included as a library in the C-package)

...
const unsigned char text[] =
"A REALLY SMALL VECTREX DEMO. FEATURING A\
 SCROLLER WRITTEN IN ASSEMBLER... ";

static inline void intro(void)
{
 scr_y = -50;                     /* y position of scroller */
 scr_lbnd= -60;                   /* left boundery */
 scr_rbnd= 60;                    /* right boundery */
 scr_sped=-1;                     /* speed, must be negativ, the lower the faster */
 scr_ints=MAX_BRIGHTNESS;         /* brightness of scrolling */
 scroll_init(text);               /* initialize scroller with text */
 do
 {
   start_one_vectrex_round();     /* initialize vectrex for this round */
   intensity(MAX_BRIGHTNESS);     /* set some brightness */
   set_scale(MAX_SCALE);          /* set the scale for movement */
   print_str(-30,100, "DEMO 4");  /* a message! */
   scr_step();                    /* do one scroll step */
   read_buttons();                /* read buttons, if pressed... finish here */
 }
 while (!joystick1_button1);      /* was a button pressed ? */
}

/*
 * Our main function we start of here...
 * we should make sure that we never return from here, or vectrex will
 * be surely bothered!
 */
int main(void)
{
 setup();                         /* setup our program */
 while (true)                     /* never to return... */
 {
   intro();                       /* do the intro over and over again :-( */
 }                               
}
/* END OF FILE */

 

Demo 5 “Shooting stars”:

Demo5

/*
 * Include some standard vectrex functions first!
 */
#include <vectrex.h>

/* NON CONSTANT VARIABLES NOT ALLOWED TO BE INITIALIZED */
/*
 * Some defines, Maximal brightness is $7f, highest not set!
 * Max Scale would be $ff, well here we take only $f0!
 */
#define MAX_BRIGHTNESS (0x7f)

/*
 * Nicer would have been an enum... what the heck... 8 possible directions
 * THREE e.g. mean from left to right...
 */
#define TO_HALF_THREE 0
#define TO_THREE 1
#define TO_HALF_SIX 2
#define TO_SIX 3
#define TO_HALF_NINE 4
#define TO_NINE 5
#define TO_HALF_TWELF 6
#define TO_TWELF 7
#define HIGHEST_DIRECTION (TO_TWELF + 1)

/*
 * Define some constants that determine the output
 */
#define HIGHEST_SPEED 4           /* highest speed of the dots */
#define SHOT_SCALE 120            /* for positioning the dots on the screen */
#define SHOTS 50                  /* how many dots at one time? */
#define DOT_BRIGHTNESS 5          /* dot dwell time... */
#define SHOT_INTERVALL 2          /* how much time between reapearing at a */

/* new location? */
struct shot                       /* one shot... */
{
 signed short shot_counter;       /* time keeper, when should it appear?*/
 signed char direction;           /* ... */
 unsigned char speed;
 unsigned char hunting;           /* not used */
 signed x;
 signed y;
};
struct shot current_shots[SHOTS]; /* all dots bundled */

/*
 * Inlined static... insures this is 'really' inlined completely...
 *
 * This function sets up a random shop, going in one of eight
 * possible directions from one end of the screen to another...
 *
 */
static inline void init_shot(struct shot *current_shot)
{
 unsigned char choice = random() % 4; /* start on which side? */
 unsigned char start = random();      /* start on which position? */
 current_shot->shot_counter = -1;     /* shotcounter negative -> active */
 current_shot->direction = random() % HIGHEST_DIRECTION; /* random direction of shot */
 current_shot->speed = (random() & 3) + 1; /* random speed */
 current_shot->hunting = 0;           /* still not used :-) */
 if (choice == 0)                     /* do the starting */
 {                                    /* coordinates... */
   current_shot->y = -127;
   current_shot->x = start;
 }
 if (choice == 1)
 {
   current_shot->y = 127;
   current_shot->x = start;
 }
 if (choice == 2)
 {
   current_shot->y = start;
   current_shot->x = -127;
 }
 if (choice == 3)
 {
   current_shot->y = start;
   current_shot->x = 127;
 }
}

/*
 * Inlined static... insures this is 'really' inlined completely...
 *
 * Process one dot...
 *
 */
static inline void do_shot(struct shot *current_shot)
{
 zero_beam();                     /* reset beam to middle of screen */
 if (current_shot->shot_counter > 0) /* is this shot active? */
 {
   current_shot->shot_counter--;  /* no?, than reduce counter... */
   if (current_shot->shot_counter == 0) /* if 0... make active and set up */
   {
     init_shot(current_shot);
   }
   return;                        /* next time shot will be active, */
 }                                /* for now... return */
 else
 {
   switch (current_shot->direction) /* process direction flag */
   {
   case TO_HALF_THREE:
   {  
     /* is dot out of bounds? */
     if ((current_shot->x > 120) || (current_shot->y > 120) )
     {
       /* yep, than make inactive and reset 'timer' */
       current_shot->shot_counter = SHOT_INTERVALL;
       /* and bye */
       return;
     }
     current_shot->x += current_shot->speed;
     current_shot->y += current_shot->speed;
     break;
   }
   case TO_THREE:
   {
     /* is dot out of bounds? */
     if (current_shot->x > 120)
     {
       /* yep, than make inactive and reset 'timer' */
       current_shot->shot_counter = SHOT_INTERVALL;
       /* and bye */
       return;
     }
     /* otherwise process coordinated according to direction and speed */
     current_shot->x += current_shot->speed;
     break;
   }
   case TO_HALF_SIX:
   {
     /* is dot out of bounds? */
     if ((current_shot->x > 120) || (current_shot->y < -120) )
     {
       /* yep, than make inactive and reset 'timer' */
       current_shot->shot_counter = SHOT_INTERVALL;
       /* and bye */
       return;
     }
     /* otherwise process coordinated according to direction and speed */
     current_shot->x += current_shot->speed;
     current_shot->y -= current_shot->speed;
     break;
   }
   case TO_SIX:
   {
     /* is dot out of bounds? */
     if (current_shot->y < -120)
     {
       /* yep, than make inactive and reset 'timer' */
       current_shot->shot_counter = SHOT_INTERVALL;
       /* and bye */
       return;
     }
     /* otherwise process coordinated according to direction and speed */
     current_shot->y -= current_shot->speed;
     break;
   }
   case TO_HALF_NINE:
   {
     /* is dot out of bounds? */
     if ((current_shot->x < -120) || (current_shot->y < -120) )
     {
       /* yep, than make inactive and reset 'timer' */
       current_shot->shot_counter = SHOT_INTERVALL;
       /* and bye */
       return;
     }
     /* otherwise process coordinated according to direction and speed */
     current_shot->x -= current_shot->speed;
     current_shot->y -= current_shot->speed;
     break;
   }
   case TO_NINE:
   {
     /* is dot out of bounds? */
     if (current_shot->x < -120)
     {
       /* yep, than make inactive and reset 'timer' */
       current_shot->shot_counter = SHOT_INTERVALL;
       /* and bye */
       return;
     }
     /* otherwise process coordinated according to direction and speed */
     current_shot->x -= current_shot->speed;
     break;
   }
   case TO_HALF_TWELF:
   {
     /* is dot out of bounds? */
     if ((current_shot->x < -120) || (current_shot->y > 120) )
     {
       /* yep, than make inactive and reset 'timer' */
       current_shot->shot_counter = SHOT_INTERVALL;
       /* and bye */
       return;
     }
     /* otherwise process coordinated according to direction and speed */
     current_shot->x -= current_shot->speed;
     current_shot->y += current_shot->speed;
     break;
   }
   case TO_TWELF:
   {
     /* is dot out of bounds? */
     if (current_shot->y > 120)
     {
       /* yep, than make inactive and reset 'timer' */
       current_shot->shot_counter = SHOT_INTERVALL;
       /* and bye */
       return;
     }
     /* otherwise process coordinated according to direction and speed */
     current_shot->y += current_shot->speed;
     break;
   }
   default:
   {
     /* oops... something wrong... make this false dot inactive ... */
     current_shot->shot_counter = SHOT_INTERVALL;
     return;
   }
  }
 }
 /* now draw the dot */
 write_ram(Vec_Dot_Dwell, DOT_BRIGHTNESS);  /* first set up the dot dwell time */
 set_scale(SHOT_SCALE);                      /* set scale for positioning */
 draw_dot(current_shot->x, current_shot->y); /* position and draw the dot */
}

/*
 * Inlined static... insures this is 'really' inlined completely...
 *
 * Initialize all dots to starting defaults...
 *
 */
static inline void init_new_game(void)
{
 unsigned char i;
 for (i=0; i<SHOTS; i++)
 {
   current_shots[i].shot_counter = 10;
 }
}

/*
 * Inlined static... insures this is 'really' inlined completely...
 *
 * oops... a small one :-)
 *
 */
static inline void start_one_vectrex_round(void)
{
 wait_recal();
}

/*
 * Main ... what else to say?
 */
int main(void)
{
 unsigned char i;                 /* a counter */
 init_new_game();                 /* initialize dots ... */
 while (true)                     /* do forever... the following */
 {
   start_one_vectrex_round();     /* start vectrex round */
   intensity(MAX_BRIGHTNESS);     /* set intensity of vector beam... */
   for (i=0; i < SHOTS; i++)      /* and process all dots */
   {
     do_shot(&current_shots[i]);  /* with this function ... */
   }
 }
}
/* END OF FILE */

Demo 6 Play YM-Music:
(YM routines are included as a library for “C”)

Demo6

/*
 * Include some standard vectrex functions first!
 */
#include <vectrex.h>
#include <ymsound.h>

/* NON CONSTANT VARIABLES NOT ALLOWED TO BE INITIALIZED */
/*
 * Some defines, Maximal brightness is $7f, highest not set!
 * Max Scale would be $ff, well here we take only $f0!
 */
#define MAX_BRIGHTNESS (0x7f)
#define LOW_BRIGHTNESS (0x2f)
#define NO_OF_SONGS 9

extern const void pac4_data;
extern const char pac4_name;

extern const void RAINBOW_data;
extern const char RAINBOW_name;

extern const void BEBOP_data;
extern const char BEBOP_name;

extern const void RAINBOW_data;
extern const char RAINBOW_name;

extern const void STARWARS_data;
extern const char STARWARS_name;

extern const void TITLE_data;
extern const char TITLE_name;

extern const void AXELF_data;
extern const char AXELF_name;

extern const void DOH3_data;
extern const char DOH3_name;

extern const void COMNDO2_data;
extern const char COMNDO2_name;

extern const void MM_data;
extern const char MM_name;
/*
 if non volatile compiler error!
 static unsigned char chosen;
 static volatile unsigned char chosen;
*/
/* slsl slsl look at asm *.ss */
static unsigned char chosen;
struct song
{
 int x;
 int y;
 const char *name;
 const void *data;
};
struct song songs[NO_OF_SONGS];

/*
 * Inlined static... insures this is 'really' inlined completely...
 *
 * oops... a small one :-)
 *
 */
static inline void start_one_vectrex_round(void)
{
 ym_sound();
 if (ym_cdata == 0)
 {
   ym_init(songs[chosen].data);   /* 'C' callable init, sound data */
 }
 joy_digital();
 wait_recal();
 intensity(MAX_BRIGHTNESS);       /* set intensity of vector beam... */
}

/*
 * Inlined static... insures this is 'really' inlined completely...
 *
 * Initialize all dots to starting defaults...
 *
 */
static inline void init(void)
{
 songs[0].x= -128;
 songs[0].y= 100;
 songs[0].name= &(pac4_name);
 songs[0].data= &(pac4_data);

 songs[1].x= -128;
 songs[1].y= 80;
 songs[1].name= &(RAINBOW_name);
 songs[1].data= &(RAINBOW_data);

 songs[2].x= -128;
 songs[2].y= 60;
 songs[2].name= &(TITLE_name);
 songs[2].data= &(TITLE_data);

 songs[3].x= -128;
 songs[3].y= 40;
 songs[3].name= &(BEBOP_name);
 songs[3].data= &(BEBOP_data);
 
 songs[4].x= -128;
 songs[4].y= 20;
 songs[4].name= &(STARWARS_name);
 songs[4].data= &(STARWARS_data);
 
 songs[5].x= -128;
 songs[5].y= 0;
 songs[5].name= &(AXELF_name);
 songs[5].data= &(AXELF_data);

 songs[6].x= -128;
 songs[6].y= -20;
 songs[6].name= &(DOH3_name);
 songs[6].data= &(DOH3_data);

 songs[7].x= -128;
 songs[7].y= -40;
 songs[7].name= &(COMNDO2_name);
 songs[7].data= &(COMNDO2_data);

 songs[8].x= -128;
 songs[8].y= -60;
 songs[8].name= &(MM_name);
 songs[8].data= &(MM_data);
 
 chosen = 0;
 ym_init(songs[chosen].data);     /* 'C' callable init, sound data */
}

/*
 * Main ... what else to say?
 */
int main(void)
{
 unsigned char i;                 /* a counter */
 char last;
 init();                          /* initialize ... */
 last=chosen;
 do
 {
   wait_recal();
   intensity(MAX_BRIGHTNESS);     /* set intensity of vector beam... */
   print_str(-128,10, "USE CURSOR TO CHOOSE SONG!");
   joy_digital();
 }
 while (!joystick1_y);

 while (true)                     /* do forever... the following */
 {
   start_one_vectrex_round();     /* start vectrex round */
   print_str(songs[chosen].x, songs[chosen].y, songs[chosen].name);
   if (joystick1_y<0)             /* DOWN */
   {
     if (chosen < NO_OF_SONGS-1)
     {
       if (joystick1_y != last)
       {
         chosen++;
         ym_init(songs[chosen].data); /* 'C' callable init, sound data */
       }
     }
   }
   else if (joystick1_y>0)        /* UP */
   {
     if (chosen > 0)
     {
       if (joystick1_y != last)
       {
         chosen--;
         ym_init(songs[chosen].data); /* 'C' callable init, sound data */
       }
     }
   }
   last = joystick1_y;
 }
}
/* END OF FILE */

Demo 7 3D:

Bildschirmfoto 2015-12-26 um 13.07.48

The source is a bit more complex, look at the archive…

Demo 8 Loderunner:

Demo8

The source is a bit more complex, look at the archive…
The “game” is not finished!

 

Leave a Reply

Your email address will not be published. Required fields are marked *