Vectrex tutorial II – starting with BIOS

HELLO WORLD (1)

Well, the usual first, most simple program!

Vectrex is capable of displaying simple texts on its screen. The easiest way to display a simple message is to use the ROM routines. For this example we will only use three BIOS functions.

The text display function we will use is called Print_Str_d ($F37A). It requires the direct page register to be set to $d0. It also requires 3 parameters:

U-reg points to string list
A-reg = relative Y position
B-reg = relative X position

The second used BIOS function is responsible for a clean display. It is called Wait_Recal ($F192). This function should be called every ‘display round’. It waits for a certain period of time, recalibrates the vector hardware, sets the pen to zero position and so on. It also sets DP to D0, so we don’t have to do it. The BIOS default time to wait is 30000 cycles, which will display our vectors 50 times per second (if we have enough time).

So he above function destroys some inner settings, as well as cleaning some others. The ‘Wait_Recal’ call is necessary for example to set the intensity after each round. This is not immediately obvious, since the program runs well on any vectrex – for a minute or so. But slowly the intensity information will ‘drift’ towards zero. After 1 or 2 minutes you won’t see our ‘HELLO WORLD’ any more if we do not set the intensity. There are BIOS functions for this. For the easiness of the example we pick a fixed intensity function, Intensity_5F ($F2A5). This function (quite obviously) sets the intensity to $5f, which is fairly bright, but not maximum ($7f).

We don’t have to worry about uninitialized BIOS stuff, because there is no such thing. When vectrex boots up it first initializes itself and its BIOS, so some default values are always present, like the above timer or a default text size. In this first example we do not change any of the default settings.

Here a screenshot:

Here is a source code to a most simple ‘HELLO WORLD’:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
;***************************************************************************
; DEFINE SECTION
;***************************************************************************
Intensity_5F    EQU     $F2A5                   ; BIOS Intensity routine
Print_Str_d     EQU     $F37A                   ; BIOS print routine
Wait_Recal      EQU     $F192                   ; BIOS recalibration
music1          EQU     $FD0D                   ; address of a (BIOS ROM)
                                                ; music
; start of vectrex memory with cartridge name...
                ORG     0
;***************************************************************************
; HEADER SECTION 
;***************************************************************************
                DB      "g GCE 1998", $80       ; 'g' is copyright sign
                DW      music1                  ; music from the rom
                DB      $F8, $50, $20, -$56     ; height, width, rel y, rel x
                                                ; (from 0,0)
                DB      "HELLO WORLD PROG 1",$80; some game information,
                                                ; ending with $80
                DB      0                       ; end of game header
;***************************************************************************
; CODE SECTION
;***************************************************************************
; here the cartridge program starts off
main:
                JSR     Wait_Recal              ; Vectrex BIOS recalibration
                JSR     Intensity_5F            ; Sets the intensity of the
                                                ; vector beam to $5f
                LDU     #hello_world_string     ; address of string
                LDA     #$10                    ; Text position relative Y
                LDB     #-$50                   ; Text position relative X
                JSR     Print_Str_d             ; Vectrex BIOS print routine
                BRA     main                    ; and repeat forever
;***************************************************************************
; DATA SECTION
;***************************************************************************
hello_world_string:
                DB   "HELLO WORLD"              ; only capital letters
                DB   $80                        ; $80 is end of string
;***************************************************************************
                END  main
;***************************************************************************

OK, let’s start at the top of the source. First thing we do is to set some defines, so the source looks a bit more intuitive. You will probably know such things. Every modern compiler/assembler will allow defining of constants. In the next examples this section will be replaces by an ‘include’ directive, which (as you might have guessed) includes all possible global definitions. In this example we tell the assembler what ROM addresses our function names represent (sorry about being to obvious).

Vectrex ‘programs’ (roms) always start at location 0. The first thing we need is a copyright information of GCE, “g GCE”. This string MUST be present in order for your program to work, the BIOS checks for it. If the above string is not found it will boot with minestorm instead! The string can be followed by any string you like.

NOTE: only capital letters are supported, small letters represent special characters (like the copyright sign) (see appendix E (TODO)). Strings must always be terminates with a $80. Following that $80 is a word pointer to a music structure. I won’t go into music structures at this point. For now it is enough to know that in the vectrex BIOS ROM 13 pieces of music are integrated. Music1 is the power-on music, Crazy Coaster and Narrow Escape. Following is a 4 byte structure describing the format of the following text. First value is the height of the text. This is -8, since the text goes from top to bottom, than the width (positive, from left to right). Followed by the (not relative) coordinates Y, X. Than the string itself follows, terminated by a $80. It would be possible to include some other structures like this one (height, width, Y, X, “STRING”, $80). The game header finishes of with a 0.

The byte following after the 0 is the first byte of code. Since we don’t want to initialize anything we go right into our display loop from the start. The following steps are repeated eternally.

  • At first we do call the BIOS function Wait_Recal, calling this function at the beginning of every display round insures a stable image (amongst other things). Since the function does not expect any values and does not return anything, a call is at this point enough.
  • We than have to make sure our intensity stays the same even after a Wait_Recal call. We call the function Intensity_5F ($F2A5), which does exactly that. It sets the vector intensity (brightness) to $5f.
  • Next thing we do is prepare the call of the text display function Print_Str_d. We load the U register with the address of the to be displayed text. Since we just called Wait_Recal our position on the vectrex screen should currently be known and 0, 0. We load the relative Y coordinate to A. We load the relative X coordinate to B. Since we just called Wait_Recal we also know that DP is pointing to D0, which is required by Print_Str_d. Thus we have prepared all there is for Print_Str_d.
  • Now let us call Print_Str_d. The text is displayed on the screen. We are done with drawing the screen. Since that is all we want our program to do we are finished at this point. The only thing left to do is to make sure that the screen gets repainted constantly, so we
  • jump back to the top, and do everything again. At the end of the source is our “HELLO WORLD” string (again terminated by a $80).

Done! This is your first running vectrex program! Wasn’t hard was it?


Modifying HELLO WORLD

Well, we just did assemble our first vectrex program and let it run (or didn’t you?). What small things can be done to the above program, so that we can learn a bit more about vectrex?

Let us now change the size of the text.

This is fairly simple, since only a changing of a BIOS RAM location is necessary. The ‘variable’ responsible is Vec_Text_HW ($C82A), which is a word pointer. It consists of two byte variables, namely Vec_Text_Height ($C82A) and Vec_Text_Width ($C82B). As mentioned above the height is a negative size. The default size is $F848, which is height=-8 and width=72. Well, these are just numbers they don’t tell me anything. What these numbers actually are is the ‘speed’ of the vector beam as described in ‘Length of vectors / scale’. Just experiment with the numbers to get a feeling for which numbers represent the display size you want.

Before the ‘main:’ line in the ‘HELLO WORLD’ example enter the following lines:

LDA #$F1 ; HEIGHT (big=-14)
LDB #$60 ; WIDTH (big=96)
STA Vec_Text_Height ; store height
STB Vec_Text_Width ; store width


(NOTE, instead of the more obvious code above you can also abbreviate like:

LDD #$F160 ; HEIGTH, WIDTH (-14, 96)
STD Vec_Text_HW ; store to BIOS RAM location

)
This gives you a larger text display. Note, to center the text you might also want to change the Y, X position (see example further below).

We can also change the brightness of the text, there are some other ‘static’ functions for intensity, like:

Intensity_1F ($F29D)
Intensity_3F ($F2A1)
Intensity_5F ($F2A5)
Intensity_7F ($F2A9)

Just call them instead of the Intensity_5F ($F2A5) in the example (you must change the ‘defines’ in the header section too). There also is a ‘variable’ intensity function, Intensity_a ($F2AB). As you might have guessed, this function expects a parameter in the A register. The value in A is the intensity which will be set by the function.

So… by now we can change three settings: position, size and brightness. If you like exercising you might try a HELLO WORLD which bounces the text around the screen, changes text size and brightness at every bounce (or something like that).

You might also want to take a look at the following BIOS functions (Appendix A (Todo)), they all have some relation to string printing. The later functions print a group (list) of strings.

Print_Str_hwyx ($F373)
Print_Str_yx ($F378)
Print_Str_d ($F37A)
Print_Str ($F495)
Print_List_hw ($F385)
Print_List ($F38A)
Print_List_chk ($F38C)

Dot Drawing

Now let us move to something even less complicated, drawing a single dot on the screen. As you might have guessed there are functions for dot drawing provided by the BIOS. These functions are:

Dot_d ($F2C3)
Dot_here ($F2C5)
Dot_ix_b ($F2BE)
Dot_ix ($F2C1)
Dot_List ($F2D5)
Dot_List_Reset ($F2DE)

Look at Appendix A (Todo) for more information on each of the functions.

Let us first plot a single dot at the center of the screen. To achieve that, we might just simply modify the above program. Instead of printing a text, we could just call Dot_here ($F2C5). One might expect the program to work just fine, but there is a catch to it.

Here a screenshot:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
;***************************************************************************
; DEFINE SECTION
;***************************************************************************
                INCLUDE "VECTREX.I"             ; vectrex function includes
; start of vectrex memory with cartridge name...
                ORG     0
;***************************************************************************
; HEADER SECTION
;***************************************************************************
                DB      "g GCE 1998", $80       ; 'g' is copyright sign
                DW      music1                  ; music from the rom
                DB      $F8, $50, $20, -$56     ; height, width, rel y, rel x
                                                ; (from 0,0)
                DB      "PLOT A DOT",$80        ; some game information,
                                                ; ending with $80
                DB      0                       ; end of game header
;***************************************************************************
; CODE SECTION
;***************************************************************************
; here the cartridge program starts off
main:
                JSR     Wait_Recal              ; Vectrex BIOS recalibration
                JSR     Intensity_5F            ; Sets the intensity of the
                                                ; vector beam to $5f
                ; special attention here!!!
                JSR     Dot_here                ; Plot a dot at the center of
                                                ; the screen
                BRA     main                    ; and repeat forever
;***************************************************************************
                END  main
;***************************************************************************

The above program looks correct, and it should really work alright, bad sadly it doesn’t. There is a catch to the ‘Wait_Recal’ function. The last thing it does is position the vector beam to -128, -128 at full vector speed. Than it resets the vector position to zero. That positioning to zero does take some time, I don’t know exactly how long, but about 20-50 cycles or so. The intensity function is not all that long, so not enough cycles have passed when ‘Dot_here’ is called. If you assemble the program as it is above you can watch some electronical RC laws. Vectrex plots the end of an uncharging of capacitors on the screen, an e-function, moving from the lower left to the center!

Actually you don’t need to be concerned by this. I don’t think in a real program a drawing directly after calling the ‘Wait_Recal’ (or the ‘Reset0Ref’ function, which does also reset the vector beam position to 0,0), ever occurs in any time critical way. So usually you don’t need to be concerned about timing after resetting. In this really special case we need to insert a delay in order to only produce a dot, and not an e-function. Please modify the above source after the

“; special attention here!!!”

line with a call to a delay function. The line should be like:

JSR Delay_3 ; delay for 30 cycles

Dot intensities can be varied by two ways (as mentioned somewhere above). First using the, by now known, intensity of vectors and secondly using a BIOS RAM variable Vec_Dot_Dwell ($C828), which is a counter of a loop, for how long the vector beam will be placed on the dot with the current intensity.

(WARNING! high intensity and long Vec_Dot_Dwell might result in a burn in on your vectrex monitor, be carefull while experimenting with this!)

Now let us move on to something more interesting (?), let us plot a whole series of dots :-).

Here a screenshot:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
;***************************************************************************
; DEFINE SECTION
;***************************************************************************
                INCLUDE "VECTREX.I"             ; vectrex function includes
; start of vectrex memory with cartridge name...
                ORG     0
;***************************************************************************
; HEADER SECTION
;***************************************************************************
                DB      "g GCE 1998", $80       ; 'g' is copyright sign
                DW      music1                  ; music from the rom
                DB      $F8, $50, $20, -$70     ; height, width, rel y, rel x
                                                ; (from 0,0)
                DB      "PLOT A LIST OF DOTS",$80; some game information,
                                                ; ending with $80
                DB      0                       ; end of game header
;***************************************************************************
; CODE SECTION
;***************************************************************************
; here the cartridge program starts off
main:
                JSR     Wait_Recal              ; Vectrex BIOS recalibration
                JSR     Intensity_5F            ; Sets the intensity of the
                                                ; vector beam to $5f
                JSR     Delay_3                 ; delay for 30 cycles
                LDA     #50                     ; load 50
                STA     VIA_t1_cnt_lo           ; 50 as scaling
                LDA     #6                      ; load A with 6, dots - 1
                STA     Vec_Misc_Count          ; set it as counter for dots
                LDX     #dot_list               ; load the address of dot_list
                JSR     Dot_List                ; Plot a series of dots
                BRA     main                    ; and repeat forever
;***************************************************************************
; DATA SECTION
;***************************************************************************
dot_list:
                DB       30,-70                 ; seven dots, relative
                DB      -40, 10                 ; position, Y, X
                DB        0, 30
                DB       40, 10
                DB       10, 30
                DB        5, 30
                DB       -10,40
;***************************************************************************
                END  main
;**********************************************************************

There is not really much to say about the above program. We use a new function called Dot_List ($F2D5), which expects as a parameter the address of a list of dots in the X register. The list consists of pairs of coordinates. These coordinates are each relative to the last position, and as usual the Y coordinate is first, X second. The number of dots to draw is specified in a BIOS RAM location Vec_Misc_Count ($C823). The value you load to that location must be one less than the number of dots you want to plot.

Scaling

The above program also sets the scaling value to 50, which is still a pretty small scaling value. Vectrex achieves scaling with a timer. The time for how long a vector is drawn (or just positioned) is the scale value. Thus (as also mentioned befor) using high scale factors result in a ‘waste’ of time. The scaling factor is set by loading the value directly to the timer of the VIA 6522 chip, namely register VIA_t1_cnt_lo ($D004). Below you can find some functions that will set the scaling value while (befor) postioning the vectorbeam (further below).

Vector beam positioning functions

There are quite a few positioning functions available, some absolut, some relative and some reset functions. Here is a program that will plot some dots using positioning functions.

Here a screenshot:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
;***************************************************************************
; DEFINE SECTION
;***************************************************************************
                INCLUDE "VECTREX.I"             ; vectrex function includes
; start of vectrex memory with cartridge name...
                ORG     0
;***************************************************************************
; HEADER SECTION
;***************************************************************************
                DB      "g GCE 1998", $80       ; 'g' is copyright sign
                DW      music1                  ; music from the rom
                DB      $F8, $50, $20, -$70     ; height, width, rel y, rel x
                                                ; (from 0,0)
                DB      "POSITION SOME DOTS",$80; some game information,
                                                ; ending with $80
                DB      0                       ; end of game header
;***************************************************************************
; CODE SECTION
;***************************************************************************
; here the cartridge program starts off
main:
                JSR     Wait_Recal              ; Vectrex BIOS recalibration

                ; prepare dot 0
                JSR     Intensity_5F            ; Sets the intensity of the
                                                ; vector beam to $5f
                JSR     Delay_3                 ; delay for 30 cycles
                JSR     Dot_here                ; Plot a dot here
                ; end of dot 0

                ; prepare dot 1
                LDA     #100                    ; load 100
                STA     VIA_t1_cnt_lo           ; 100 as scaling
                LDA     #-100                   ; relative Y position = -100
                LDB     #-50                    ; relative X position = -50
                                                ; register D = 256*A+B
                JSR     Moveto_d                ; move to position specified
                                                ; in D register
                JSR     Dot_here                ; Plot a dot here
                ; end of dot 1

                ; prepare dot 2
                LDA     #50                     ; load 50
                STA     VIA_t1_cnt_lo           ; 50 as scaling
                LDA     #100                    ; relative Y position = 100
                LDB     #50                     ; relative X position = 50
                                                ; register D = 256*A+B
                JSR     Moveto_d                ; move to position specified
                                                ; in D register
                JSR     Dot_here                ; Plot a dot here
                ; end of dot 2

                ; prepare dot 3
                LDB     #200                    ; scale factor of 200
                LDX     #position               ; load address of position
                JSR     Moveto_ix_b             ; move to position specified
                                                ; in address pointed to by X
                                                ; and set scaling factor found
                                                ; register B
                                                ; (befor positioning)
                JSR     Dot_here                ; Plot a dot here
                ; end of dot 3
                BRA     main                    ; and repeat forever
;***************************************************************************
; DATA SECTION
;***************************************************************************
position:
                DB      100, 50                 ; relative Y, X position
;***************************************************************************
                END  main
;***************************************************************************

First we draw a dot at the center, like we used to in one of the above examples. Than we set a scaling factor of 100. Next we prepare for the function call of Moveto_d ($F312). This functions positions our vector beam relative to the current position. The coordinates are expected in the A (Y position) and B (X position) registers (that is the D register). After calling the function we plot the dot. Notice, this is the dot on the screen on the bottom left side, the coordinates we used for positioning were (-100, -50) (Y, X).

Now we take the invers position (100, 50) (Y, X) and only half the scale factor. Notice, that with the same positioning information and half the scale factor (50 instead of 100), we move exactly half the way back!

The last position example in the above program is via indexed positioning. The function we will call is named Moveto_ix_b ($F30E). The function expects an address to a position information in the X register and a scaling factor in the B register. We have stored another (100, 50) (Y, X) at the address of ‘position’, the X register is pointing to that address. Furthermore we moved 200 to B register for scaling information. Notice this dot is the one on the top right of the screen.

Further information about positioning functions can be found in appendix A (todo):

Moveto_d ($F312)
Moveto_d_7F ($F2FC)
Moveto_ix ($F310)
Moveto_ix_7F ($F30C)
Moveto_ix_b ($F30E)
Moveto_ix_FF ($F308)
Moveto_x_7F ($F2F2)

Line Drawing

By now you’ll probably have noticed, that vectrex programming (using the BIOS) is not all that complicated. Drawing lines follows the same scheme as above examples. We think of some coordinates, put them into appropriate registers and call a function every display round. Drawing figures made of vectors is exactly the same. We keep a list of vector coordinates in memory and again call a function for displaying them. The length of vectors, as already described above can be varied by two different parameters, the strength of a vector and the time of drawing (scaling).

There are different kinds of vector display functions. Mainly these functions can be sorted by their ‘supportiveness’.

1. drawing only one vector
2. drawing a list of vectors
a. including positioning information
b. excluding positioning information
i. including scaling information
ii. excluding scaling information
#1. including brightness information
#2. excluding brightness information
#a. including mode information *
#b. excluding mode information *

* mode means here, that a line can be draw using e.g. patterns and other conditional information

For more information on each function look at the appropriate function description in Appendix A (todo). In the following examples I’ll first introduce a single line program, and later a program for displaying a vector list.

Following BIOS functions support line drawing:

Draw_Grid_VL ($FF9F)
Draw_Line_d ($F3DF)
Draw_Pat_VL ($F437)
Draw_Pat_VL_a ($F434)
Draw_Pat_VL_d ($F439)
Draw_VL ($F3DD)
Draw_VL_a ($F3DA)
Draw_VL_ab ($F3D8)
Draw_VL_b ($F3D2)
Draw_VL_mode ($F46E)
Draw_VLc ($F3CE)
Draw_VLcs ($F3D6)
Draw_VLp ($F410)
Draw_VLp_7F ($F408)
Draw_VLp_b ($F40E)
Draw_VLp_FF ($F404)
Draw_VLp_scale ($F40C)
Mov_Draw_VL ($F3BC)
Mov_Draw_VL_ab ($F3B7)
Mov_Draw_VL_a ($F3B9)
Mov_Draw_VL_d ($F3BE)
Mov_Draw_VLc_a ($F3AD)
Mov_Draw_VLc_b ($F3B1)
Mov_Draw_VLcs ($F3B5)

Now the first example:

Here a screenshot:

;***************************************************************************
; DEFINE SECTION
;***************************************************************************
                INCLUDE "VECTREX.I"
; start of vectrex memory with cartridge name...
                ORG     0
;***************************************************************************
; HEADER SECTION
;***************************************************************************
                DB      "g GCE 1998", $80       ; 'g' is copyright sign
                DW      music1                  ; music from the rom
                DB      $F8, $50, $20, -$45     ; height, width, rel y, rel x
                                                ; (from 0,0)
                DB      "SINGLE LINE",$80       ; some game information,
                                                ; ending with $80
                DB      0                       ; end of game header
;***************************************************************************
; CODE SECTION
;***************************************************************************
; here the cartridge program starts off
main:
                JSR     Wait_Recal              ; Vectrex BIOS recalibration
                LDA     #$80                    ; scaling factor of $80 to A
                STA     VIA_t1_cnt_lo           ; move to time 1 lo, this
                                                ; means scaling
                LDA     #0                      ; to 0 (y)
                LDB     #0                      ; to 0 (x)
                JSR     Moveto_d                ; move the vector beam the
                                                ; relative position
                JSR     Intensity_5F            ; Sets the intensity of the
                                                ; vector beam to $5f
                CLR     Vec_Misc_Count          ; in order for drawing only 1
                                                ; vector, this must be set to
                                                ; 0
                LDA     #100                    ; to 100 (y)
                LDB     #50                     ; to 50 (x)
                JSR     Draw_Line_d             ; draw the line now
                BRA     main                    ; and repeat forever
;***************************************************************************
                END main
;***************************************************************************

In the example we assume (correctly) that the Wait_Recal sets DP to D0. First we set a scale factor we want to use for our vector ($80). As we already know, scale factor is a VIA register, timer1 low byte. Than we set the start coordinate, actually this is a relative coordinate. We know that the Wait_Recal routine sets the beam to the (0,0) position. We use that position as our starting position. NOTE: You must set some starting position with a positioning function. You can not use the (0,0) from Wait_Recal directly. Wait_Recal does some weird stuff, that doesn’t allow us to use the positioning given by it directly (when drawing with a standard line function)!

After positioning the vector beam to (0,0) we set the intensity (brightness) to $5f (reason… see dot plotting :-)). From (Fred’s) description of the Draw_Line_d routine we know that the routine is actually part of the Draw_VL subroutine, which is used to plot a number of vectors. That number is stored in the Vec_Misc_Count RAM location. Therefore we must insure that only one vector is plotted. We do that by poking 0 to that location

(Note, the BIOS always ensures this location is 0 before returning, if you don’t change that location in your program, than there is no need to clear this location).

Than we set the end position of the vector (relative to the position we are at now) (the start position is already known (0,0)). Register A stores the Y position and register B the X position. These positions are the ‘translated’ strength of the vector. The beam internally is drawn for a fixed amount of time (the scale value) in the direction specified by the Y, X position. The position is internally translated to a voltage with which the electron beam is deflected from the center of the tube.

Anyway, after setting the end position we are ready to call the ‘Draw_Line_d’ subroutine. After that we are already finished with everything we need to do for drawing a single line, so we go back to the beginning for repainting!

(NOTE:

What strange ‘thing’ the Wait_Recal does:

It calls right at the end ‘Reset0Ref’!

Reset0Ref:

LDD #$00CC
STB VIA_cntl ;/BLANK low, /ZERO low

...

That means, that both /BLANK AND /ZERO are enabled (0). This means that the vector beam is always on center (zero). This is true until the VIA_cntl is modified so that the /ZERO flag is high again. The simple ‘Draw_Line_d’ function does not do this, ‘Moveto_d’ does however, thus the above construct works ok.

You could instead of the ‘Moveto_d’ function call also insert the following two lines, which do only the stuff we need:

LDA #$CE ;/Blank low, /ZERO high
STA VIA_cntl

NOTE 2:

/Blank, /ZERO are both low active.)

The second example:

Now let us draw a couple of lines. Most programs for vectrex will sooner or later require ‘combined’ line objects. We call these combined lines – vector lists. There are a couple of list types, as you might imagine looking at the above listed assortment of vector plotting routines. The next example will introduce the most simple form of vector lists. The others are not really much more complicated, but it would be a waste of effort to dig to deeply into them with this document, so they are left out as an exercise for the reader, to go out and explore the BIOS function description. Here comes the next example:

Here a screenshot:

;***************************************************************************
; DEFINE SECTION
;***************************************************************************
                INCLUDE "VECTREX.I"
; start of vectrex memory with cartridge name...
                ORG     0
;***************************************************************************
; HEADER SECTION
;***************************************************************************
                DB      "g GCE 1998", $80       ; 'g' is copyright sign
                DW      music1                  ; music from the rom
                DB      $F8, $50, $20, -$55     ; height, width, rel y, rel x
                                                ; (from 0,0)
                DB      "VECTOR LIST TEST",$80  ; some game information,
                                                ; ending with $80
                DB      0                       ; end of game header
;***************************************************************************
; CODE SECTION
;***************************************************************************
; here the cartridge program starts off
main:
                JSR     Wait_Recal              ; Vectrex BIOS recalibration
                LDA     #$10                    ; scaling factor of $80 to A
                STA     VIA_t1_cnt_lo           ; move to time 1 lo, this
                                                ; means scaling
                LDA     #0                      ; to 0 (y)
                LDB     #0                      ; to 0 (x)
                JSR     Moveto_d                ; move the vector beam the
                                                ; relative position
                JSR     Intensity_5F            ; Sets the intensity of the
                                                ; vector beam to $5f
                LDX     #turtle_line_list       ; load the address of the to be
                                                ; drawn vector list to X
                JSR     Draw_VLc                ; draw the line now
                BRA     main                    ; and repeat forever
;***************************************************************************
SPRITE_BLOW_UP EQU 25
turtle_line_list:
                DB 23                           ; number of vectors - 1
                DB  2*SPRITE_BLOW_UP,  2*SPRITE_BLOW_UP
                DB -1*SPRITE_BLOW_UP,  2*SPRITE_BLOW_UP
                DB  2*SPRITE_BLOW_UP,  1*SPRITE_BLOW_UP
                DB  2*SPRITE_BLOW_UP, -2*SPRITE_BLOW_UP
                DB  0*SPRITE_BLOW_UP,  2*SPRITE_BLOW_UP
                DB -1*SPRITE_BLOW_UP,  1*SPRITE_BLOW_UP
                DB  1*SPRITE_BLOW_UP,  3*SPRITE_BLOW_UP
                DB -1*SPRITE_BLOW_UP,  4*SPRITE_BLOW_UP
                DB  1*SPRITE_BLOW_UP,  0*SPRITE_BLOW_UP
                DB -1*SPRITE_BLOW_UP,  1*SPRITE_BLOW_UP
                DB -1*SPRITE_BLOW_UP,  0*SPRITE_BLOW_UP
                DB -3*SPRITE_BLOW_UP,  2*SPRITE_BLOW_UP
                DB -3*SPRITE_BLOW_UP, -2*SPRITE_BLOW_UP
                DB -1*SPRITE_BLOW_UP,  0*SPRITE_BLOW_UP
                DB -1*SPRITE_BLOW_UP, -1*SPRITE_BLOW_UP
                DB  1*SPRITE_BLOW_UP,  0*SPRITE_BLOW_UP
                DB -1*SPRITE_BLOW_UP, -4*SPRITE_BLOW_UP
                DB  1*SPRITE_BLOW_UP, -3*SPRITE_BLOW_UP
                DB -1*SPRITE_BLOW_UP, -1*SPRITE_BLOW_UP
                DB  0*SPRITE_BLOW_UP, -2*SPRITE_BLOW_UP
                DB  2*SPRITE_BLOW_UP,  2*SPRITE_BLOW_UP
                DB  2*SPRITE_BLOW_UP, -1*SPRITE_BLOW_UP
                DB -1*SPRITE_BLOW_UP, -2*SPRITE_BLOW_UP
                DB  2*SPRITE_BLOW_UP, -2*SPRITE_BLOW_UP
;***************************************************************************
                END main
;***************************************************************************

OK… here we go again. You’ll immediately see, that the program follows the same scheme as all other programs before. It is nearly the same as the last program. Just one function call is different, with different parameters, and of course the data of the vector list. Therefore I’ll from now on only describe the differences to the last program, not the whole program anymore. The new function in this example is Draw_VL ($F3DD). The function name is a short form of ‘Draw Vector List’. This is exactly what it does. It requires just one parameter which is passed in the X register. This is a pointer to the address of the vector list to be drawn. We pass at this stage the address of our list, which is ‘turtle_line_list’ (the vector list is taken from Vectrex Frogger). The list is very easily constructed, the first byte found at that address is taken as the count, how many vectors make up this list, MINUS 1! (the routine tests for how long the decrement of the count is positive, thus we need a minus 1 here) After the count follow the line ‘coordinates’. These ‘coordinates’ are the same ‘vector strength`s’ we encountered in the one line version. All of these ‘coordinates’ are relative to the last relevant position. Thus in the above example the first line will be drawn from 0, 0 (from the Moveto_d, btw the Wait_Recal function) to 2, 2 (equally to the upper right). The next line is drawn from 2, 2 to relative -1, 2. To absolute these coordinates you must add the coordinates. The line`s ‘real’ coordinates would thus be:

line 1 start 0, 0 end: 2, 2
line 2 start: 2, 2 end: (2-1), (2+2) this is 1, 4
line 3 start: 1, 4 end: (1+2), (4+1) this is 3, 5
...

To visualize that:

Ooops, that looks pretty bad, but perhaps you get the point. In the above example you encounter a constant factor in the vector list, here called SPRITE_BLOW_UP. This is just an idea I had at one time to examine the effect of scale factors and vector strengths. Using a definable constant helps to change the strengths of the vectors very easily. In the above example the vectors actually have strengths of factors of 25 (-125, -100, -75, -50, -25 0, 25, 50, 75, 100, 125). We can pretty easily control the size of our objects without using the scale factor. For the above program I ran a little test. The program as listed above, SPRITE_BLOW_UP = 25 and scale factor of $10, uses per update round exactly 3158 cycles. If we change the scale factor to $50 and set the SPRITE_BLOW_UP to 5, which gives us an object of exactly the same size, we need already 4705 cycles! This does not look like much, but keep in mind, that with just one object you will not be able to create great programs. And that you will also want a pretty fast round, below 30,000 cycles to have a steady image.

Note:

There are also vector list routines, which have a ‘mode’ or ‘pattern’ byte. With these routines you are able to draw invisible lines, therefore objects are possible, that are not drawn in one go.

Note:

There are also functions, that provide collision detection of vector lists. And there are functions with which you can rotate and scale your vectors as need arise. I won’t go into these, ’cause they go (IMHO) beyond the scale of a little introduction to vectrex programming. The day you might need them will be the day you don’t need an introduction anymore :-).

Sound Playing

Now you know (with a little experimenting on your side) how to draw vectors, dots and whole objects of vectors. You know how to move them, how to scale them and give them different brightnesses. All these things done via a couple of BIOS functions. Now let us go on to something completely different. Let us enter the universe of sound! To keep one thing clear in mind, I am no musician, I can’t read a single note, and I won’t go into any depths on this subject, I’ll tell you what BIOS routines are available, a bit how to use them – that’s all!

A little introduction nonetheless before we start. The soundchip of the vectrex is a Programmable Sound Generator (short PSG), it’s correct name is AY-3-8912. Appendix D (todo) is an extract of the reference manual of that chip. The vectrex uses the PSG not only for sound generation, but also for communicating with the outside world. Register 16 of the PSG is connected to the joypad electronic. The PSG itself cannot be addressed directly, only by sending the correct byte sequences to the VIA chip. As you might imagine it is cumbersome to program that thing yourself. The original vectrex programmers thought so too, that is why a whole series of functions exist that make life easier. There are functions to acquire joystick positions, button stati, sending bytes and streams of bytes. There are even functions to play songs and noises. The only sound functions I used so far are the functions needed to play some notes. These will therefore be the only (sound) functions I’ll explain a little, the other sound related functions are again left as an exercise for the reader. 
  
 

Get on with it…

First, let me shock you a little with the available sound functions:

Clear_Sound ($F272) 
Do_Sound ($F289) 
Do_Sound_x ($F28C) 
Init_Music ($F68D) 
Init_Music_Buf ($F533) 
Init_Music_chk ($F687) 
Init_Music_dft ($F692) 
Explosion_Snd ($F92E) 
Sound_Byte ($F256) 
Sound_Byte_x ($F259) 
Sound_Byte_raw ($F25B) 
Sound_Bytes ($F27D) 
Sound_Bytes_x ($F284)

Of the above functions we will look a bit closer only at the following:

Init_Music_Buf ($F533) 
Init_Music_chk ($F687) 
Do_Sound ($F289)

Of these only the last two are needed to play a piece of music. There is also a very interesting RAM location, Vec_Music_Flag ($C856), but later more on that.

Before we play our first piece of music, again a little (second) introduction. The BIOS designers really cared about programmers who wanted to play music which knew nothing about the PSG. They designed routines, which allow us to create pieces of music as they come to our mind and writing down the notes like we learn them (or don’t :-() in school. The PSG supports 3 voices at the time, so our music can also be played utilizing three voices. Furthermore it has the ability to create noises, so we can not only play music, but can also have some shooting or explosion or the like sounds (snare drums…).

The music playing routines the BIOS provides us with, enables us to use 64 notes, ranging from G2 to AS7 (S=Sharp). As usual (?) in each octave we have 12 different notes, look at Appendix F(todo) for a list of all notes. If you absolutely hate music and do not even want any further information, the BIOS has several pieces of music built into (13 I think), you can use them in your programs for several usages, they range from simple songs, to some noise and bonus sounds. Look at the Appendix A (todo) other ROM addresses as to where these are located. Actually now that I think of it, we could start playing one of them, just to show you, how to set the system up and going, before we move on to how the song structure looks like.

;***************************************************************************
; DEFINE SECTION
;***************************************************************************
                INCLUDE "VECTREX.I"
; start of vectrex memory with cartridge name...
                ORG     0
;***************************************************************************
; HEADER SECTION
;***************************************************************************
                DB      "g GCE 1998", $80       ; 'g' is copyright sign
                DW      music1                  ; music from the rom
                DB      $F8, $50, $20, -$55     ; height, width, rel y, rel x
                                                ; (from 0,0)
                DB      "VECTOR LIST TEST",$80  ; some game information,
                                                ; ending with $80
                DB      0                       ; end of game header
;***************************************************************************
; CODE SECTION
;***************************************************************************
; here the cartridge program starts off
main:
                LDA     #1                      ; one means, we are about to
                                                ; start a piece of music
                STA     Vec_Music_Flag          ; store it in appropriate RAM
                                                ; location
main_loop:
                JSR     DP_to_C8                ; DP to RAM
                LDU     #music1                 ; get some music, here music1
                JSR     Init_Music_chk          ; and init new notes
                JSR     Wait_Recal              ; Vectrex BIOS recalibration
                JSR     Do_Sound                ; ROM function that does the
                                                ; sound playing
                BRA     main_loop               ; and repeat forever
;***************************************************************************
                END main
;***************************************************************************

This time we have to set up something before we enter our main loop. In order for the music routines to work they must be initialized. This is rather easy, because they are sort of self initializing. All we have to do is make sure that they know, that a new piece of music is about to begin. The before mentioned BIOS RAM variable Vec_Music_Flag keeps track of the current state of music playing routines. Three states are possible, 0 means no music playing. 1 means about to start a new piece of music, and $80 finally means, that a piece of music is currently playing. Since we want to start a new piece of music we store a 1 in that location and with that we are already done with initializing. We enter the main loop. Right before we call the Wait_Recal function the sound update routine is usually called, this routine is called Init_Music_chk ($F687). This routine checks and updates the state of all PSG shadow registers. These shadow registers are copies the music routines use in order to easily keep track of the PSG state. The first time we call the routine it initializes the shadow registers of the PSG with the first set of notes our piece of music consists of. It than changes the state from 1 to $80, from ‘about to play’ to ‘I’m currently playing’. This routine only updates and calculates the shadow registers, it does not access the PSG. Before calling the routine we must ensure two things, first that DP is pointing to $C8, and second that we put the address of the music we want to play into register U. This we do. The music we want to play is a piece of BIOS music, here called (highly unimaginative) music1. This is the all known startup music. After vector hardware recalibration we call another music routine, Do_Sound ($F289), this routine looks whether the shadow registers have changed, if so it copies the changed ones into the PSG chip. In order for that to work correctly the DP register must be pointing to $D0, having just called the Wait_Recal routine that is insured. Done. We loop through that eternally. What we hear is the startup music once played. Only once, since upon finishing Vec_Music_Flag contains a zero, thus the Init_Music_chk knows the music is finished and nothing more is done. If we wanted to construct a program that played the startup music continuously, we would have need to check the above location, and if we at some stage encounter a 0 we would have to fill it with another initializing 1. That’s all.

Now that we know how to play a piece of music let us go one step further, and look what the piece of music is made of. Following text is simply copied from the BIOS description done by Bruce Tomlin:

; Music data format: ; 
; ; 
; header word -> $C84F 32 nibble ADSR table ; 
; header word -> $C851 8-byte "twang" table ; 
; data bytes ; 
; ; 
; The ADSR table is simply 32 nibbles (16 bytes) of amplitude values. ; 
; ; 
; The twang table is 8 signed bytes to modify the base frequency of ; 
; each note being played. Each channel has a different limit to its ; 
; twang table index (6-8) to keep them out of phase to each other. ; 
; ; 
; Music data bytes: ; 
; Bits 0-5 = frequency ; 
; Bit 6 clear = tone ; 
; Bit 6 set = noise ; 
; Bit 7 set = next music data byte is for next channel ; 
; Bit 7 clear, play note with duration in next music data byte: ; 
; bits 0-5 = duration ; 
; bit 6 = unused ; 
; bit 7 set = end of music ;

It describes very well what a piece of music looks like. Usually you will use a BIOS ADSR and TWANG table. But if you like experimenting you can set up your own. There is no real magic about them.

The ADSR table sets up a ‘fading’ of the notes. If the fading is abrupt it sounds very much like an old PONG game. If it last a bit longer you can make it somewhat ‘organ’ like. If you are pretty clever with this you can make it sound like different instruments.

The Twang table is for all pieces of music I know of for all bytes 0. The Twang table is also called Vibe table, it sets up some sort of vibrato. Actually I have not done much testing with that table but you sure can come up with some weird sounds.

As you see, there is no magic about these values, and for most pieces of music the BIOS provided once will be sufficient. Look at the ROM listing and/or at Appendix A(todo) other ROM addresses, as to where they are located, Minestorm including there are at least 6 different to be found. The actual piece of music and how it is made up from the above byte settings looks a bit confusing at first, but it really is pretty straight forward. If you ever did some bit fiddling before you won’t have any difficulties. Let us give another small example, this time taken again from Vectrex Frogger:

;***************************************************************************
; DEFINE SECTION
;***************************************************************************
                INCLUDE "VECTREX.I"
; start of vectrex memory with cartridge name...
                ORG     0
;***************************************************************************
; HEADER SECTION
;***************************************************************************
                DB      "g GCE 1998", $80       ; 'g' is copyright sign
                DW      music1                  ; music from the rom
                DB      $F8, $50, $20, -$55     ; height, width, rel y, rel x
                                                ; (from 0,0)
                DB      "VECTOR LIST TEST",$80  ; some game information,
                                                ; ending with $80
                DB      0                       ; end of game header
;***************************************************************************
; CODE SECTION
;***************************************************************************
; here the cartridge program starts off
main:
                LDA     #1                      ; one means, we are about to
                                                ; start a piece of music
                STA     Vec_Music_Flag          ; store it in appropriate RAM
                                                ; location
main_loop:
                JSR     DP_to_C8                ; DP to RAM
                LDU     #yankee                 ; get some music, here yankee
                JSR     Init_Music_chk          ; and init new notes
                JSR     Wait_Recal              ; Vectrex BIOS recalibration
                JSR     Do_Sound                ; ROM function that does the
                                                ; sound playing
                BRA     main_loop               ; and repeat forever
;***************************************************************************
yankee:
                FDB     $FEE8, $FEB6            ; ADSR and twang address tables, in Vectrex ROM
                FCB     2,12                    ;;;;;;;;
                FCB     0,12                    ; first byte is a note, to be
                FCB     2,12                    ; found in vectrex rom, is a
                FCB     0,12                    ; 64 byte table...
                FCB     2,6                     ; last byte is length of note
                FCB     0,6
                FCB     2,6
                FCB     0,6
                FCB     2,6
                FCB     0,6
                FCB     2,12
                FCB     0,12                    ;;;;;;;;
                FCB     2,12
                FCB     0,12
                FCB     2,12
                FCB     0,12                    ;;;;;;;;
                FCB     2,6
                FCB     0,6
                FCB     2,6
                FCB     0,6
                FCB     2,6
                FCB     0,6
                FCB     2,6                     ;;;;;;;;
                FCB     0,6
                FCB     2,12
                FCB     0,12
                FCB     128+2,128+26,26-12, 12  ;
                FCB     128+0,128+31,31-12, 12  ;;;;;;;;
                FCB     128+2,128+31,31-12, 12  ; a 128 means the next byte is
                FCB     128+0,128+33,33-12, 12  ; a note for the next channel
                FCB     128+2,128+35,35-12, 12  ; channel...
                FCB     128+0,128+31,31-12, 12  ;;;;;;;;
                FCB     128+2,128+35,35-12, 12
                FCB     128+0,128+33,33-12, 12
                FCB     128+2,128+26,26-12, 12
                FCB     128+0,128+31,31-12, 12  ;;;;;;;;
                FCB     128+2,128+31,31-12, 12
                FCB     128+0,128+33,33-12, 12
                FCB     128+2,128+35,35-12, 12
                FCB     128+0,128+31,31-12, 12  ;;;;;;;;
                FCB     2,12
                FCB     128+0,128+30,30-12, 12
                FCB     128+2,128+26,26-12, 12
                FCB     128+0,128+31,31-12, 12  ;;;;;;;;
                FCB     128+2,128+31,31-12, 12
                FCB     128+0,128+33,33-12, 12
                FCB     128+2,128+35,35-12, 12
                FCB     128+0,128+36,36-12, 12  ;;;;;;;;
                FCB     128+2,128+35,35-12, 12
                FCB     128+0,128+33,33-12, 12
                FCB     128+2,128+31,31-12, 12
                FCB     128+0,128+30,30-12, 12  ;;;;;;;;
                FCB     128+2,128+26,26-12, 12
                FCB     128+0,128+28,28-12, 12
                FCB     128+2,128+30,30-12, 12
                FCB     128+0,128+31,31-12, 12  ;;;;;;;;
                FCB     2, 12
                FCB     128+0,128+31,31-12, 12
                FCB     2, 12
                FCB     128+0,128+28,28-12, 18  ;;;;;;;;
                FCB     128+30,30-12, 06
                FCB     128+2,128+28,28-12, 12
                FCB     128+0,128+26,26-12, 12
                FCB     128+2,128+28,28-12, 12  ;;;;;;;;
                FCB     128+0,128+30,30-12, 12
                FCB     128+2,128+31,31-12, 12
                FCB     0, 12
                FCB     128+0,128+26,26-12, 18  ;;;;;;;;
                FCB     128+28,28-12, 06
                FCB     128+2,128+26,26-12, 12
                FCB     128+0,128+24,24-12, 12
                FCB     128+2,128+23,23-12, 12  ;;;;;;;;
                FCB     0, 12
                FCB     128+2,128+26,26-12, 12
                FCB     0, 12
                FCB     128+2,128+28,28-12, 18  ;;;;;;;;
                FCB     128+30,30-12, 06
                FCB     128+0,128+28,28-12, 12
                FCB     128+2,128+26,26-12, 12
                FCB     128+0,128+28,28-12, 12  ;;;;;;;;
                FCB     128+2,128+30,30-12, 12
                FCB     128+0,128+31,31-12, 12
                FCB     128+2,128+28,28-12, 12
                FCB     128+0,128+26,26-12, 12  ;;;;;;;;
                FCB     128+2,128+31,31-12, 12
                FCB     128+0,128+30,30-12, 12
                FCB     128+2,128+33,33-12, 12
                FCB     128+0,128+31,31-12, 12  ;;;;;;;;
                FCB     2, 12
                FCB     128+0,128+31,31-12, 12
                FCB     2, 12
                FCB     19, $80                 ; $80 is end marker for music
                                                ; (high byte set)
;***************************************************************************
                END main
;***************************************************************************

All that is changed from the program before is the pointer of the music, from music1 to yankee, and I added the yankee doodle music data.

The first two words of the data are BIOS ROM addresses of an ADSR and a TWANG table. The TWANG table is as noted above, all zero, the ADSR table looks like:

FEE8 FDB $EEFF,$FFEE,$EEDD,$CCBB,$AA99,$8888,$8888,$8888

As you can see there is not really all that much fading. It sounds a bit organ like. Following are all notes. The first 26 notes are one voice notes pretty low notes, actually they were supposed to sound a bit like a drum, but using noise, sounded pretty bad, so I used ‘tones’ instead, but took low notes. If you look at Appendix F you can easily translate the values to notes, here a small extract:

G2 EQU $00 G = 1.5 8ves below middle C 
GS2 EQU $01 G sharp (second 8ve) , etc. 
A2 EQU $02 
AS2 EQU $03

Thus the 2 means we are playing an A2 note, and the 0 means we are playing a G2. The second byte in each line (when there are only two bytes) means the length, for how long the note is played. The time is measured in milliseconds.

I wouldn’t know how long a note must last, for it to be a quarter or an eighth, but you probably would, I guess quarter about 16, eighth about 8 and so on. For the yankee doodle I took whatever came to my mind, and I don’t think it is very well done. Further down you will notice lines that are somewhat more occupied. That’s where I started using more than one voice. Since we have only 64 notes, that means that the 7th (and 6) bit of our note index is not used. Well it IS used after all, if it is set it means, that the following byte is not the time our note is supposed to last, but a value which note the second (or third) voice should play. There are a maximum of 3 voices. The missing sixth bit is used to indicate whether the note should be played with a noise generator (bit 6 set) or via a tone generator (bit 6 clear). The yankee doodle above does not make use of the noise generator. The introduction music for armor attack (music3 ($FD81)) is made up solely using the noise generator, it is supposed to be snare drum pattern). After the data for the third voice a length (timing) information must be given. The timing information also only has a length of 6bit. The 6th bit is unused. If the 7th bit is set, than the music is over. 
  

Joystick/Button input

This is going to be the last chapter, and as the chapters before, no in depth view and programming information will be given. I’ll only tell you about the BIOS routines. And I don’t think any sane person will need any more information, as I already mentioned in my babblings about sound, the joypads are (indirectly) connected to the PSG soundchip, which in turn can only be accessed using some weird pokes to the VIA chip. So I dearly recommend any programmer to only use the available functions. These functions are:

Joy_Analog ($F1F5) 
Joy_Digital ($F1F8) 
Read_Btns_Mask ($F1B4) 
Read_Btns ($F1BA)

All of these routines require the DP register set to $D0! Furthermore there are some very interesting BIOS RAM locations, here is a list of them, but read on for some more information about them:

Vec_Btn_State ($C80F) Current state of all joystick buttons 
Vec_Prev_Btns ($C810) Previous state of all joystick buttons 
Vec_Buttons ($C811) Current toggle state of all buttons 
Vec_Button_1_1 ($C812) Current toggle state of stick 1 button 1 
Vec_Button_1_2 ($C813) Current toggle state of stick 1 button 2 
Vec_Button_1_3 ($C814) Current toggle state of stick 1 button 3 
Vec_Button_1_4 ($C815) Current toggle state of stick 1 button 4 
Vec_Button_2_1 ($C816) Current toggle state of stick 2 button 1 
Vec_Button_2_2 ($C817) Current toggle state of stick 2 button 2 
Vec_Button_2_3 ($C818) Current toggle state of stick 2 button 3 
Vec_Button_2_4 ($C819) Current toggle state of stick 2 button 4 
Vec_Joy_Resltn ($C81A) Joystick A/D resolution ($80=min $00=max) 
Vec_Joy_1_X ($C81B) Joystick 1 left/right 
Vec_Joy_1_Y ($C81C) Joystick 1 up/down 
Vec_Joy_2_X ($C81D) Joystick 2 left/right 
Vec_Joy_2_Y ($C81E) Joystick 2 up/down 
Vec_Joy_Mux ($C81F) Joystick enable/mux flags (4 bytes) 
Vec_Joy_Mux_1_X ($C81F) Joystick 1 X enable/mux flag (=1) 
Vec_Joy_Mux_1_Y ($C820) Joystick 1 Y enable/mux flag (=3) 
Vec_Joy_Mux_2_X ($C821) Joystick 2 X enable/mux flag (=5) 
Vec_Joy_Mux_2_Y ($C822) Joystick 2 Y enable/mux flag (=7)

Vec_Misc_Count ($C823) misc counter/flag byte, zero when not in use

One small note in advance, in order for the joypad routines to work correctly, you must again assure that the Vec_Misc_Count ($C823) BIOS RAM location is zero, since it is used for analogous testing. As said before, if you don’t change that variable somewhere, than everything is ok, since BIOS functions always return leaving that location zero.

Again I will not describe all functions and all variables, only two actually, probably the two you’ll need most. The other functions can again be easily worked out by looking at the appropriate BIOS disassembly (where you can actually find everything (nearly) I say in this document), or Appendix A.

Let us first do the most easy things, as usual. Let us have a look at the buttons. Here is a small example:

;***************************************************************************
; DEFINE SECTION
;***************************************************************************
                INCLUDE "VECTREX.I"
; start of vectrex memory with cartridge name...
                ORG     0
;***************************************************************************
; HEADER SECTION
;***************************************************************************
                DB      "g GCE 1998", $80       ; 'g' is copyright sign
                DW      music1                  ; music from the rom
                DB      $F8, $50, $20, -$55     ; height, width, rel y, rel x
                                                ; (from 0,0)
                DB      "BUTTON FLASH TEST",$80  ; some game information,
                                                ; ending with $80
                DB      0                       ; end of game header
;***************************************************************************
; CODE SECTION
;***************************************************************************
; here the cartridge program starts off
                LDD     #$FC20                  ; HEIGTH, WIDTH (-4, 32)
                STD     Vec_Text_HW             ; store to BIOS RAM location
main:
                JSR     Read_Btns               ; get one status first, for
                                                ; the difference
main_loop:
                JSR     Wait_Recal              ; Vectrex BIOS recalibration
                JSR     Intensity_5F            ; Sets the intensity of the
                                                ; vector beam to $5f
                JSR     Read_Btns               ; get button status
                CMPA    #$00                    ; is a button pressed?
                BEQ     no_button               ; no, than go on
                BITA    #$01                    ; test for button 1 1
                BEQ     button_1_1_not          ; if not pressed jump
                LDU     #button_1_1_string      ; otherwise display the
                PSHS    A                       ; store A
                JSR     Print_Str_yx            ; string using string function
                PULS    A                       ; restore A
button_1_1_not:
                BITA    #$02                    ; test for button 1 2
                BEQ     button_1_2_not          ; if not pressed jump
                LDU     #button_1_2_string      ; otherwise display the
                PSHS    A                       ; store A
                JSR     Print_Str_yx            ; string using string function
                PULS    A                       ; restore A
button_1_2_not:
                BITA    #$04                    ; test for button 1 3
                BEQ     button_1_3_not          ; if not pressed jump
                LDU     #button_1_3_string      ; otherwise display the
                PSHS    A                       ; store A
                JSR     Print_Str_yx            ; string using string function
                PULS    A                       ; restore A
button_1_3_not:
                BITA    #$08                    ; test for button 1 4
                BEQ     button_1_4_not          ; if not pressed jump
                LDU     #button_1_4_string      ; otherwise display the
                PSHS    A                       ; store A
                JSR     Print_Str_yx            ; string using string function
                PULS    A                       ; restore A
button_1_4_not:
                BITA    #$10                    ; test for button 2 1
                BEQ     button_2_1_not          ; if not pressed jump
                LDU     #button_2_1_string      ; otherwise display the
                PSHS    A                       ; store A
                JSR     Print_Str_yx            ; string using string function
                PULS    A                       ; restore A
button_2_1_not:
                BITA    #$20                    ; test for button 2 2
                BEQ     button_2_2_not          ; if not pressed jump
                LDU     #button_2_2_string      ; otherwise display the
                PSHS    A                       ; store A
                JSR     Print_Str_yx            ; string using string function
                PULS    A                       ; restore A
button_2_2_not:
                BITA    #$40                    ; test for button 2 3
                BEQ     button_2_3_not          ; if not pressed jump
                LDU     #button_2_3_string      ; otherwise display the
                PSHS    A                       ; store A
                JSR     Print_Str_yx            ; string using string function
                PULS    A                       ; restore A
button_2_3_not:
                BITA    #$80                    ; test for button 2 4
                BEQ     button_2_4_not          ; if not pressed jump
                LDU     #button_2_4_string      ; otherwise display the
                JSR     Print_Str_yx            ; string using string function
button_2_4_not:
                BRA     main_loop               ; go on, repeat...
no_button:
                LDU     #no_button_string
                JSR     Print_Str_yx
                BRA     main_loop               ; and repeat forever
;***************************************************************************
no_button_string:
                DB 50,-50,"NO BUTTON CHANGED", $80
button_1_1_string:
                DB 40,-50,"JOYPAD 1 BUTTON 1", $80
button_1_2_string:
                DB 30,-50,"JOYPAD 1 BUTTON 2", $80
button_1_3_string:
                DB 20,-50,"JOYPAD 1 BUTTON 3", $80
button_1_4_string:
                DB 10,-50,"JOYPAD 1 BUTTON 4", $80
button_2_1_string:
                DB 0,-50,"JOYPAD 2 BUTTON 1", $80
button_2_2_string:
                DB -10,-50,"JOYPAD 2 BUTTON 2", $80
button_2_3_string:
                DB -20,-50,"JOYPAD 2 BUTTON 3", $80
button_2_4_string:
                DB -30,-50,"JOYPAD 2 BUTTON 4", $80
;***************************************************************************
                END main
;***************************************************************************

The above function calls the Read_Btns ($F1BA) function. This function does a couple of things. But the only thing we use in the above example is the information given to us in register A. In that register a button transition information is given. That means a bit is set at a button specific position, if it’s state has changed. But only half the transition is registered, the transition from unpressed to pressed. If such a transition occurred than for the next call of Read_Btns the appropriate bit will be set. Most of the time that little information is enough. Especially if you don’t want continues fire or the like. I guess I don’t have to go through the program line by line, as I did before, since (pardon me) it IS fairly obvious. The Read_Btns function has some other uses (for an exact description look at the disassembled ROM listing or Appendix A), further down you will find a program that makes use of the RAM locations mentioned above, that are set to the current button state by the Read_Btns function. The function does following other things:

  • Vec_Buttons ($C811) Current toggle state of all buttons 
  • Contains the same information as register A.
  • Vec_Btn_State ($C80F) Current state of all joystick buttons 
  • Contains a bitmap whether a button is pressed (1) or not (0).
  • Vec_Prev_Btns ($C810) Previous state of all joystick buttons 
  • Contains a bitmap whether a button was pressed (1) or not (0) before the function call.
  • Vec_Button_1_1 ($C812) Current toggle state of stick 1 button 1 
  • Vec_Button_1_2 ($C813) Current toggle state of stick 1 button 2 
  • Vec_Button_1_3 ($C814) Current toggle state of stick 1 button 3 
  • Vec_Button_1_4 ($C815) Current toggle state of stick 1 button 4 
  • Vec_Button_2_1 ($C816) Current toggle state of stick 2 button 1 
  • Vec_Button_2_2 ($C817) Current toggle state of stick 2 button 2 
  • Vec_Button_2_3 ($C818) Current toggle state of stick 2 button 3 
  • Vec_Button_2_4 ($C819) Current toggle state of stick 2 button 4

The bitmap used in all above BIOS RAM locations is as in the above program:

joystick 1
button 1: $01 
button 2: $02 
button 3: $04 
button 4: $08
joystick 2
button 1: $10 
button 2: $20 
button 3: $40 
button 4: $80

With the that information we can easily change our above program to print a steady message whether a button is currently pressed or not. From the at least two different approaches we take the (IMHO) easier one:

;***************************************************************************
; DEFINE SECTION
;***************************************************************************
                INCLUDE "VECTREX.I"
; start of vectrex memory with cartridge name...
                ORG     0
;***************************************************************************
; HEADER SECTION
;***************************************************************************
                DB      "g GCE 1998", $80       ; 'g' is copyright sign
                DW      music1                  ; music from the rom
                DB      $F8, $50, $20, -$55     ; height, width, rel y, rel x
                                                ; (from 0,0)
                DB      "BUTTON TEST",$80       ; some game information,
                                                ; ending with $80
                DB      0                       ; end of game header
;***************************************************************************
; CODE SECTION
;***************************************************************************
; here the cartridge program starts off
                LDD     #$FC20                  ; HEIGTH, WIDTH (-4, 32)
                STD     Vec_Text_HW             ; store to BIOS RAM location
main:
main_loop:
                JSR     Wait_Recal              ; Vectrex BIOS recalibration
                JSR     Intensity_5F            ; Sets the intensity of the
                                                ; vector beam to $5f
                JSR     Read_Btns               ; get button status
                LDA     Vec_Btn_State           ; get the current state of all
                                                ; buttons
                CMPA    #$00                    ; is a button pressed?
                BEQ     no_button               ; no, than go on
                BITA    #$01                    ; test for button 1 1
                BEQ     button_1_1_not          ; if not pressed jump
                LDU     #button_1_1_string      ; otherwise display the
                PSHS    A                       ; store A
                JSR     Print_Str_yx            ; string using string function
                PULS    A                       ; restore A
button_1_1_not:
                BITA    #$02                    ; test for button 1 2
                BEQ     button_1_2_not          ; if not pressed jump
                LDU     #button_1_2_string      ; otherwise display the
                PSHS    A                       ; store A
                JSR     Print_Str_yx            ; string using string function
                PULS    A                       ; restore A
button_1_2_not:
                BITA    #$04                    ; test for button 1 3
                BEQ     button_1_3_not          ; if not pressed jump
                LDU     #button_1_3_string      ; otherwise display the
                PSHS    A                       ; store A
                JSR     Print_Str_yx            ; string using string function
                PULS    A                       ; restore A
button_1_3_not:
                BITA    #$08                    ; test for button 1 4
                BEQ     button_1_4_not          ; if not pressed jump
                LDU     #button_1_4_string      ; otherwise display the
                PSHS    A                       ; store A
                JSR     Print_Str_yx            ; string using string function
                PULS    A                       ; restore A
button_1_4_not:
                BITA    #$10                    ; test for button 2 1
                BEQ     button_2_1_not          ; if not pressed jump
                LDU     #button_2_1_string      ; otherwise display the
                PSHS    A                       ; store A
                JSR     Print_Str_yx            ; string using string function
                PULS    A                       ; restore A
button_2_1_not:
                BITA    #$20                    ; test for button 2 2
                BEQ     button_2_2_not          ; if not pressed jump
                LDU     #button_2_2_string      ; otherwise display the
                PSHS    A                       ; store A
                JSR     Print_Str_yx            ; string using string function
                PULS    A                       ; restore A
button_2_2_not:
                BITA    #$40                    ; test for button 2 3
                BEQ     button_2_3_not          ; if not pressed jump
                LDU     #button_2_3_string      ; otherwise display the
                PSHS    A                       ; store A
                JSR     Print_Str_yx            ; string using string function
                PULS    A                       ; restore A
button_2_3_not:
                BITA    #$80                    ; test for button 2 4
                BEQ     button_2_4_not          ; if not pressed jump
                LDU     #button_2_4_string      ; otherwise display the
                JSR     Print_Str_yx            ; string using string function
button_2_4_not:
                BRA     main_loop               ; go on, repeat...
no_button:
                LDU     #no_button_string
                JSR     Print_Str_yx
                BRA     main_loop               ; and repeat forever
;***************************************************************************
no_button_string:
                DB 50,-50,"NO BUTTON PRESSED", $80
button_1_1_string:
                DB 40,-50,"JOYPAD 1 BUTTON 1", $80
button_1_2_string:
                DB 30,-50,"JOYPAD 1 BUTTON 2", $80
button_1_3_string:
                DB 20,-50,"JOYPAD 1 BUTTON 3", $80
button_1_4_string:
                DB 10,-50,"JOYPAD 1 BUTTON 4", $80
button_2_1_string:
                DB 0,-50,"JOYPAD 2 BUTTON 1", $80
button_2_2_string:
                DB -10,-50,"JOYPAD 2 BUTTON 2", $80
button_2_3_string:
                DB -20,-50,"JOYPAD 2 BUTTON 3", $80
button_2_4_string:
                DB -30,-50,"JOYPAD 2 BUTTON 4", $80
;***************************************************************************
                END main
;***************************************************************************

I think we pretty much exhausted the possibilities of the buttons with the above two examples (leave alone the masked button requests, but they are not really that different) let us go on to the joystick(s).

As you have seen above there are two functions for joystick information gathering. I will only explain a bit of Joy_Digital ($F1F8), since analogous is (again IMHO) not all that useful, and takes considerable time to calculate and is even a bit unstable.

Some preliminaries first. When you program a game you’ll probably at one stage know what kind of joystick routine you need (one or two joysticks, digital or analogous), once you know that, you should set up the BIOS joystick routines for your needs, because (and I really mean it) these routines are cycle wasters, and every bit they have to calculate to much is really a waste. There are the following BIOS RAM locations:

Vec_Joy_Mux_1_X ($C81F) Joystick 1 X enable/mux flag (=1) 
Vec_Joy_Mux_1_Y ($C820) Joystick 1 Y enable/mux flag (=3) 
Vec_Joy_Mux_2_X ($C821) Joystick 2 X enable/mux flag (=5) 
Vec_Joy_Mux_2_Y ($C822) Joystick 2 Y enable/mux flag (=7)

Into these you should pass the information what you want to know. 0 means not interested and the ‘flag’ numbers mean that you are interested. In the example program below we are only interested in joystick one, but both X and Y position.

;***************************************************************************
; DEFINE SECTION
;***************************************************************************
                INCLUDE "VECTREX.I"
; start of vectrex memory with cartridge name...
                ORG     0
;***************************************************************************
; HEADER SECTION
;***************************************************************************
                DB      "g GCE 1998", $80       ; 'g' is copyright sign
                DW      music1                  ; music from the rom
                DB      $F8, $50, $20, -$55     ; height, width, rel y, rel x
                                                ; (from 0,0)
                DB      "JOYSTICK 1 TEST",$80   ; some game information,
                                                ; ending with $80
                DB      0                       ; end of game header
;***************************************************************************
; CODE SECTION
;***************************************************************************
; here the cartridge program starts off
                LDD     #$FC20                  ; HEIGTH, WIDTH (-4, 32)
                STD     Vec_Text_HW             ; store to BIOS RAM location
                LDA     #1                      ; these set up the joystick
                STA     Vec_Joy_Mux_1_X         ; enquiries
                LDA     #3                      ; allowing only all directions
                STA     Vec_Joy_Mux_1_Y         ; for joystick one
                LDA     #0                      ; this setting up saves a few
                STA     Vec_Joy_Mux_2_X         ; hundred cycles
                STA     Vec_Joy_Mux_2_Y         ; don't miss it, if you don't
                                                ; need the second joystick!
main:
main_loop:
                JSR     Wait_Recal              ; Vectrex BIOS recalibration
                JSR     Intensity_5F            ; Sets the intensity of the
                                                ; vector beam to $5f
                JSR     Joy_Digital             ; read joystick positions
                LDA     Vec_Joy_1_X             ; load joystick 1 position
                                                ; X to A
                BEQ     no_x_movement           ; if zero, than no x position
                BMI     left_move               ; if negative, than left
                                                ; otherwise right
right_move:
                LDU     #joypad_right_string    ; display right string
                BRA     x_done                  ; goto x done
left_move:
                LDU     #joypad_left_string     ; display left string
                BRA     x_done                  ; goto x done
no_x_movement:
                LDU     #no_joypad_x_string     ; display no x string
x_done:
                JSR     Print_Str_yx            ; using string function
                LDA     Vec_Joy_1_Y             ; load joystick 1 position
                                                ; Y to A
                BEQ     no_y_movement           ; if zero, than no y position
                BMI     down_move               ; if negative, than down
                                                ; otherwise up
up_move:
                LDU     #joypad_up_string       ; display up string
                BRA     y_done                  ; goto y done
down_move:
                LDU     #joypad_down_string     ; display down string
                BRA     y_done                  ; goto y done
no_y_movement:
                LDU     #no_joypad_y_string     ; display no y string
y_done:
                JSR     Print_Str_yx            ; using string function
                BRA     main_loop               ; and repeat forever
;***************************************************************************
no_joypad_x_string:
                DB 40,-50,"NO JOYPAD X INPUT", $80
joypad_right_string:
                DB 40,-50,"JOYPAD 1 RIGHT", $80
joypad_left_string:
                DB 40,-50,"JOYPAD 1 LEFT", $80
no_joypad_y_string:
                DB 20,-50,"NO JOYPAD Y INPUT", $80
joypad_up_string:
                DB 20,-50,"JOYPAD 1 UP", $80
joypad_down_string:
                DB 20,-50,"JOYPAD 1 DOWN", $80
;***************************************************************************
                END main
;***************************************************************************

The above example is (hopefully understandable) again straight forward. A simple example for a joypad inquiry. The above tested BIOS RAM locations contain always the current position of the joypad (current means last set by the Joy_Digital function). Negative values stand for left/down, positive for right/up and zero for no movement detected. Following are again the BIOS RAM locations, where these will be set, both for joypad 1 and joypad 2.

Vec_Joy_1_X ($C81B) Joystick 1 left/right 
Vec_Joy_1_Y ($C81C) Joystick 1 up/down 
Vec_Joy_2_X ($C81D) Joystick 2 left/right 
Vec_Joy_2_Y ($C81E) Joystick 2 up/down

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.