How to program Vectrex
----------------------

Preface
-------
I didn't really plan this text, it just grew while writing it. So forgive me
if it is not all well structured. There are no page numbers, and no real
chapters in a common sense. The contents only gives you the headings found
further below.

Contents
--------
Preface
Contents
Introduction
Conventions
Basics
Coordinates
Screen updates
Structure of program
The Vectrex OS and its functions
Timing
Vectors
Brightness
Positions
Length of vectors / Scale
HELLO WORLD (1)
Modifying HELLO WORLD
Dot Drawing
Scaling
Vector beam positioning functions
Line Drawing
Sound Playing
Joystick/Button input
Vector display
Why wait a moment? (Optimization warning)
Vector positioning optimization thoughts
Optimization for Vectrex Programs
Using the emulator to program vectrex programs

Appendix A: List of Vectrex ROM functions
Appendix B: BIOS RAM locations
Appendix C: THE 6522 VIA CHIP
Appendix D: Programmable Sound Generator (AY-3-8912)
Appendix E: Bit dump of characters in BIOS ROM
Appendix F: Notes supported by the BIOS sound functions
Appendix G: Links to Vectrex sites


Introduction
------------
This document is supposed to give people a start in vectrex programming. For
my sake I will assume that you are familiar with non 'vectrex special' related
hardware and software used. Such as programming a 6809 cpu in assembler, what
hex, bin and dec numbers are, what a 256byte page is and so on.
I will try to cover many aspects, but mainly will cover 'standard' procedures
offered by the Vectrex BIOS.
Actually this is more a course on vectrex BIOS than about vectrex. Perhaps in
the future I'll do some chapters on the actual hardware programming, but let's
start of easy, ok?

Notice:
Most things in this document can be found out looking at other sources avail-
able. Like the great dissassembled ROM images done by Fred Taft, the BIOS dis-
assmblies by also Fred Taft and Bruce Tomlin. Many sources of homebrewn soft-
ware, from 'simple' pieces of music, to full blown up games, rivaling the
original commercial ones. I can't take credit for what you find in here, be-
cause in many parts it is just a compilation of stuff found elsewhere. If you
look for further information, be it something as yet uncovered by this text or
some further going explanations, be sure to look at these other sources as
well.

Notice also:
I'm in no way responsible what you do with/to your vectrex. I don't think much
harm can be done by just programming the machine (apart from burning the
tube), but whatever comes of it, it is your own risk. I tried all programs
covered in this documentation with my own vectrex, so they should work all
right, but to cover my back... again, I take no responsibility whatsoever.

This was written in April 1998 by Malban the document is public
domain (at least the parts written by me). Comments and vectrex talk are wel-
come. (email: malban@email.de)
All code within this document was successfully assembled using 'Frank A. Vor-
stenbosch, Kingswood Software' Crossassmbler. It can be found at his homepage
at http://www.falstaff.demon.co.uk/cross.html. I used version as09 v1.11.
Following command line was used to assemble:
    as09.exe -w200 -h0 -l -mcti progname.asm >error

Conventions
-----------
Functions we will use are named like Print_Str_d ($F37A). The hex number is
the ROM address of the function. Most of the time the function name also gives
a hint as to which registers are used in passing information. The above
function uses the D register to pass position information (A=ypos, B=Xpos,
with D=A*256+B).

Basics
------
Memory map (hardware):
(taken from Keith Wilkins 'internal.txt')

    0000-7fff   Cartridge ROM Space, the CART line is also gated with
                ~E to produce a signal that can be fed directly to the
                output enable of a ROM. This address range is not gated
                with the read-write line but this line is fed to the
                cartridge connector. (r/w)

    8000-C7FF   Unmapped space.

    C800-CFFF   Vectrex RAM Space 1Kx8, shadowed twice. (r/w)

    D000-D7FF   6522VIA shadowed 128 times (r/w)

    D800-DFFF   Dont use this area. Both the 6522 and RAM are selected
                in any reads/writes to this area.

    E000-FFFF   System ROM Space 8Kx8 (r/w)

$C800-$C880 is RAM used by the vectrex BIOS for housekeeping. (see appendix B)
$CBEA-$CBFE is RAM used by the vectrex BIOS for housekeeping. (see appendix B)
$C880-$CBEA is RAM that can be used by the programmer!
$E000-$EFFF is ROM, the built in game MINE STORM.

Coordinates
-----------
Vectrex display is structured like a cartesian coordinate system:
(NOTE: 0, 0 is in the middle of the screen)
(Vectrex Monitor)
--------------------------------------------------
I                                                I
I                                                I
I                                                I
I                                                I
I                                                I
I                      +Y                        I
I                      /I\                       I
I                       I                        I
I                       I                        I
I                       I                        I
I                       I 0,0                    I
I      -X <-------------+-------------> +X       I
I                       I                        I
I                       I                        I
I                       I                        I
I                       I                        I
I                      \I/                       I
I                      -Y                        I
I                                                I
I                                                I
I                                                I
I                                                I
I                                                I
--------------------------------------------------

Screen updates
--------------
Since you are looking at this text, I might assume that you have allready some
programming experience. You most probably collected your experience program-
ming raster screen 'machines'. Be it computers, arcades, consoles, handhelds
or whatever. If you have allready programmed vector screen 'machines' you will
find nothing new in this small section. If you on the other hand have no exp-
erience programming vector displays, let me do a little introduction first,
and be ready for a whole new experience in screen updates :-).

There are nice and not so nice aspects of vector screens, as you will see.
You as a programmer have total control over the vector beam (or you let the
BIOS do the job for you -> recomended!). If you want to draw a vector you have
to place the beam at the position you want the vector to start. 'Switch the
light on', move the beam and 'switch the light off'. Due to the persistence of
the screen, the vector will than be displayed for some milliseconds (I don't
exactly know for how long). Than the vector will fade away. Most of the time
you will not be satisfied with a vector being displayed only for some milli-
seconds, so sooner or later you will start thinking about how to draw a stable
image. Well, there is no secret to it, you just have to keep drawing... .Draw
the vector you want to appear stable, about 50 times per second and you can
happily view a stable vector image! While the BIOS has routines for vector
drawing, even vector lists or string drawing, it does not update the screen
automatically. You as a programmer are responsible for updating the screen
regularily. If you lose to much time between these updates the display will
surely flicker. As a rule of thumb the above mentioned value of 50Hz is suff-
icient for a stable image. The nice effect of this is, that you don't have to
worry about garbage on your screen, if you move a 'sprite' you don't have to
keep track of the background, since in the next round everything is 'clean'
again.

Structure of program
--------------------
Keeping the above said in mind, a vectrex program most of the time has follow-
ing basic structure:
        -init_code-
main_loop:
        -sound update-
        -recalibration-
        -sound output-
        -input handling-
        -some other stuff-
        -screen update-
        repeat main_loop until exit

The Vectrex OS and its functions
--------------------------------
There is no 'official' ROM listing, and therefor there are no official func-
tion names. I learned the inner workings of the vectrex ROM from a documented
dissassembled ROM. As far as I know there are two such dissassembled listings,
one done by Fred Taft and one by Bruce Tomlin (based on Freds work). Through-
out this document I use the function names given in Bruce's version.
Many thanks for the nice job you both have done.
The OS is located at memory address $F000 to $FFFF (well nearly, the last few
bytes are reserved vectors of the 6809 CPU).
Most functions require parameters, some functions return some values. This
information is most of the time passed via registers of the 6809 micropro-
ccessor. There is no 'standard' in what registers are used. Look at the func-
tions for the information which registers are used.
As you will know the 6809 CPU has a register called DP (direct page register),
this register is used to point to 256byte pages within the memory. The 6809
can address 256*256byte pages, dp therefor can have values from 0-255. The
dp register if used wisely can shorten and speedup programs, because direct
page accesses are faster and shorter than extended addressing. There are
two values that you will encounter frequently while programming vectrex, as
$c8 and $d0. You can see, eyeing the memory map above, these are direct page
pointers to 1. ($c8) RAM used by the BIOS (or the programmer) and 2. ($d0) the
base address of the VIA 6522 (VIA Vercatile Interface Adapter, sometimes
called PIA Periphal Interface Adapter). All output and timer functionality are
regulated/accessed by that chip, it is pretty important :-). For a detailed
describtion of that chip see Appendix C. Actually input is also done via VIA,
but indirectly, by accessing the AY-3-8912 soundchip, see Appendix D for a
detailed describtion of that chip.

Timing
------
Vectrex uses a Motorola 68A09 microprocessor at 1.5Mhz. Thus one cycle is
1/1,500,000 seconds. In order to to have a 50Hz stable display (which is most
desireable), we must do everything we want within a limit of 30,000 cycles.
The BIOS uses Timer 2 to keep track of 'system time'. The Wait_Recal routine
waits for that timer to expire before continuing and recalibrating the vector
system. This BIOS timing information can be set at BIOS RAM location Vec_Rfrsh
($C83D), which is a word pointer, $C83D is the timer low byte, $C83E is the
timer high byte. Thus a value of the above 30,000=$7530 would be stored to the
BIOS RAM location with a sequence like:
     LDD #$3075
     STD Vec_Rfrsh
My experience: If you change the timer value, most of the time the vectors
will 'shake' a bit, especially if you don't zero the beam frequently.
The game speed will vary, and non moving vectors (fixed, like a court or so)
will annoyingly 'shake'!

The second (there are only two timers) timer (actually timer 1) is used for
drawing vectors within BIOS vector drawing routines. Actually while drawing
vectors the timer is set with the SCALE factor (which IS the low byte of the
timer 1, a VIA register, VIA_t1_cnt_lo ($D004)). Thus scaling DIRECTLY relates
to timing. Using a large scale factor allways takes 'a lot' of time. If at all
possible use small scaling values. See also the next Vectors->length!

Vectors
-------
Vectors in vectrex relation consist of following parts.
- Brightness
- Y position start
- X position start
- length in Y direction
- length in X direction
- scaling factor

Brightness
----------
There are two ways to control brightness. First the 'Intensity', which is a
value from 0-$7f (if the 7th bit is set there is no intensity), 0 means black,
$7f means full brightness. Apart from the intensity you can vary brightness
of vectors by drawing vectors more than ones or for dots leaving the vector
beam for a longer period of time on the same dot. For the last there is a BIOS
variable Vec_Dot_Dwell ($C828), which sets a counter for how long the vector
beam will illuminate the dot.

Positions
---------
The position of vectors are most of the time relative to the last known posi-
tion. Somtimes the position is not interesting when drawing a vector, since it
was set some other time, and now drawing just continues. Positioning with any
function is done in relation to the present scaling factor (TIME). The pro-
gramer must keep track of the position, and should zero it now and than. If
the set position is not 'used' within a certain time, the position will drift
(my vectrex drifts allways to +,+ (cartesian coordinates), a few centimenters
per 10,000 cycles!).
Position is given in values from -128 to +127. Most position information is
set with first Y than X postion.

Length of vectors / Scale
-------------------------
The length of vectors here means something like the speed with which the beam
will travel across the screen. The scaling is something like the time, for how
long the travelling will be done. Thus it is easy to see, that travelling with
a speed of 10 for a time of 5, which would equal a travelled space of 50, is
about the same as travelling with a speed of 50 for a time of 1, which also
results in a space of 50 to be covered. The only difference is, that the first
will take five times as long as the second!
Length is given in values from -128 to +127. Most length information is set
with first Y than X length.

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 para-
meters:
      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).

The above function destroys some inner settings, as well as cleaning up. So it
is neccessary for example to set the intensity after each 'Wait_Recal' call.
This is not immediatly 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 easyness
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 maxinum ($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 allways present, like the above timer or a default
text size. In this first example we do not change any of the default settings.

Here is a source code to a most simple 'HELLO WORLD':
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                               CODE START
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;***************************************************************************
; 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
;***************************************************************************
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                                 CODE END
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

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) allways 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).
Strings must allways 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 finnishes 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 finnished 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 neccessary. The 'variable' responsible is
Vec_Text_HW ($C82A), which is a word pointer. It consists of two byte var-
iables, namely Vec_Text_Height ($C82A) and Vec_Text_Width ($C82B). As men-
tioned above the height is a negative size. The default size is $F848,
which is heigth=-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                    ; HEIGTH (big=-14)
                LDB     #$60                    ; WIDTH (big=96)
                STA     Vec_Text_Height         ; store heigth
                STB     Vec_Text_Width          ; store width

(NOTE, instead of the more obvious code above you can also abriviate 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 exercizing 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), 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 pro-
vided 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 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.
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                               CODE START
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;***************************************************************************
; 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
;***************************************************************************
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                                 CODE END
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

The above program looks correct, and it should really work allright, 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 inten-
sity 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 capaci-
tors 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 pro-
gram 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 timecritical way. So ususally 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 :-).

<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                               CODE START
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;***************************************************************************
; 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
;***************************************************************************
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                                 CODE END
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

There is not really much to say about the above program. We use a new funct-
ion 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 coodinate 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 vector-
beam (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.
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                               CODE START
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;***************************************************************************
; 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
;***************************************************************************
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                                 CODE END
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
First we draw a dot at the center, like we used to in one of the above exam-
ples. 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 func-
tion 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 fac-
tor. 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 positoning functions can be found in appendix A:
 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 excactly the same. We keep a list of vector
coordinates in memory and again call a function for displaying them. The len-
gth of vectors, as allready 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 descr-
ibtion in Appendix A. 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:

<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                               CODE START
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;***************************************************************************
; 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
;***************************************************************************
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                                 CODE END
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

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 all-
ready 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) describtion 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. Therefor we must insure that only one
vector is plotted. We do that by poking 0 to that location (Note, the BIOS
allways 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 (relativ to the position we are at now)
(the start position is allready 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 endposition we are ready to call the 'Draw_Line_d'
subroutine. After that we are allready finnished 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 allways 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' func-
 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 sortiment 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 describtion. Here comes the next example:

<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                               CODE START
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;***************************************************************************
; 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
;***************************************************************************
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                                 CODE END
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

OK... here we go again. You'll immediatly see, that the program follows the
same scheme as all other programs befor. 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. Therefor 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 'coordi-
nates' 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 absolut 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:
---------------------------------------
I                                     I
I                 +Y         *        I
I                 /I\        I line3  I
I                 2+  *     /         I
I                  I  /\_  I          I
I                 1+ /   \_*line2     I
I                  I/line1            I
I -X <-------------0-+-+-+-+-+---> +X I
I                  I 1 2 3 4 5        I
I                -1+                  I
I                  I                  I
I                -2+                  I
I                 \I/                 I
I                 -Y                  I
---------------------------------------
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 defineable 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 allready 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, therefor objects
are possible, that are not drawn in one go.

Note:
There are also functions, that provide collision detection of vectorlists.
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 :-).


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 completly 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 nonethless befor we start. The soundchip of the vectrex
is a Programmable Sound Generator (short PSG), it's correct name is AY-3-8912.
Appendix D 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 electr-
onic. 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 aquire 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 therefor be the only (sound) functions I'll explain a
little, the other sound related functions are again left as an exercize for
the reader.


Sound Playing
-------------
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.
Befor 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 for a list of all notes.
If you absolutly 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 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.

<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                               CODE START
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;***************************************************************************
; 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
;***************************************************************************
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                                 CODE END
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

This time we have to set up something befor 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 ment-
ioned 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 allready 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.
Befor 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 thru that eternally.
What we hear is the startup music once played. Only once, since upon fin-
nishing Vec_Music_Flag contains a zero, thus the Init_Music_chk knows the
music is finnished and nothing more is done. If we wanted to construct a
program that played the startup music continuesly, 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 describtion 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 other ROM addresses, as to where they are
located, Minstorm 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:

<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                               CODE START
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;***************************************************************************
; 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
;***************************************************************************
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                                 CODE END
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

All that is changed from the program befor 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 milli-
seconds.
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 afterall, 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
unsused. 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 befor, 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 info-
rmation, as I allready mentioned in my babblings about sound, the joypads are
(indirectly) connected to the PSG soundchip, which in turn can only be acces-
sed using some werid pokes to the VIA chip. So I dearly recommed any pro-
grammer 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 asure that the Vec_Misc_Count ($C823) BIOS RAM location is
zero, since it is used for analog testing. As said befor, if you don't change
that variable somewhere, than everything is ok, since BIOS functions allways
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 dissassembly (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 but-
tons. Here is a small example:

<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                               CODE START
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;***************************************************************************
; 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
;***************************************************************************
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                                 CODE END
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

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 occured 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 befor, since (pardon me) it IS fairly obvious. The
Read_Btns function has some other uses (for an exact describtion 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) befor the func-
tion 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:

<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                               CODE START
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;***************************************************************************
; 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
;***************************************************************************
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                                 CODE END
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

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 ga-
thering. I will only explain a bit of Joy_Digital ($F1F8), since analog is
(again IMHO) not all that usefull, 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
analog), 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.

<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                               CODE START
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;***************************************************************************
; 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
;***************************************************************************
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                                 CODE END
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

The above example is (hopefully understandable) again straight forward. A
simple example for a joypad inquiry. The above tested BIOS RAM locations con-
tain allways 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


Vector display
--------------
Just let me drop a few words on how vectrex positions the beam on the monitor.
How the BIOS does it, and how one can use that information to tickle a bit
more speed out of the whole system.

I won't really go into the hardware level, if you want a good describtion of
a general way how a vector is drawn/positioned look at the 'internal.txt' by
Keith Wilkins.

There are a couple of highly important things to know in order to understand
the following. Look at the schematics, at the 'internal.txt' and at the
reference sheet (or the documentation of Vectrex Frogger) when I lose you.

~ZERO (==VIA CA2)
~BLANK (==VIA CB2)
~RAMP (==VIA PB7)
VIA Timer 1 (One shot mode, PB7 enabled)
VIA Shift Register Mode 4 (Shift Out Under T2 Control)
VIA Interrupts
VIA Auxiliary Control Register
VIA Control Register
Integrators (look at 'internal.txt' good introduction!)

If the above mentioned don't tell you anything, you are probably better off
learning a bit more an come back later :-)!

First I'll describe how the BIOS positions the beam, than how it draws
vectors and after that... well, let's see.

The BIOS upon initialization sets some VIA registers, if the programmer does
not change them, they will be relevant until you switch your vectrex off. The
BIOS most of the time assumes some of the settings, if they should not be as
expected your vectors probably won't be drawn correctly anymore.

BIOS initializes the data direction registers of VIA port A all for output and
port B all for output except bit 5 and 6 (which are input).
It than pokes $98 to VIA_aux_cntl.
Bitmask $98:               Meaning in register VIA_aux_cntl

bit 7             1       t1 enable PB7 output
bit 6             0       0=t1 one shot
bit 5             0       0=t2 one shot
bit 4             1       \   shift register control
bit 3             1        >  110=output to CB2 under control of phase 2 clock
bit 2             0       /
bit 1             0       PB latch disable
bit 0             0       PA latch disable

There are mainly two aspects of interest in that setting, first the shift
mode, which is shift mode 4. Which means, that shifting is enabled by poking
something to the shift register, and that the bit shifted out from bit 7 is
circulated back to bit 0. Shifting continues even after 8 shifts. Shifting is
done at 1/2 the system clock speed. The bit that is shifted out is put into
CB2 (~BLANK). ~BLANK is zero active, that means if the bit is zero, output is
switched off.
The other interesting bit is the one that enables PB7 output when timer1 is
active. PB7 is the ~RAMP signal which is zero active, but becomes inverted.
What it comes to is that the integrators are switched on, when the bit
PB7 (== ~RAMP) is set. Integrators on means, that the vectorbeam is moving.

Perhaps you can allready at this point see where this is leading us. The beam
will be positioned for a certain time, the time is stored to timer 1. While
timer 1 is active the beam is moving. Depending on the value in the Shift
register, we have this or that output. If there is %00000000 in the Shift
register we have no output at all, since at each shift a zero will be
stored to the ~BLANK line, which disables all output. If a %10101010 would be
stored to the Shift register we would see something like a dotted line. Depen-
ding on the 'strength' of the vectors we would see short 'dots' or wider
'dots'(!). If a %11111111 was set we would see a solid line!

Following is some part of the BIOS Moveto_d function:

Moveto_d:       STA     <VIA_port_a     ;Store Y in D/A register
                CLR     <VIA_port_b     ;Enable mux
                PSHS    D               ;Save D-register on stack
LF318:          LDA     #$CE            ;Blank low, zero high?
                STA     <VIA_cntl
                CLR     <VIA_shift_reg  ;Clear shift regigster
                INC     <VIA_port_b     ;Disable mux
                STB     <VIA_port_a     ;Store X in D/A register
                CLR     <VIA_t1_cnt_hi  ;timer 1 count high
                PULS    D               ;Get back D-reg
                JSR     Abs_a_b
                STB     -1,S
                ORA     -1,S
                LDB     #$40
                CMPA    #$40
                BLS     LF345
                CMPA    #$64
                BLS     LF33B
                LDA     #$08
                BRA     LF33D

LF33B:          LDA     #$04            ;Wait for timer 1
LF33D:          BITB    <VIA_int_flags
                BEQ     LF33D
LF341:          DECA                    ;Delay a moment
                BNE     LF341
                RTS
:HERE
LF345:          BITB    <VIA_int_flags  ;Wait for timer 1
                BEQ     LF345
                RTS

In D is the Y, X position, the first thing the function does is send the Y
strength to Port A of the VIA, which also is input to the DAC.
The DAC output can be distributed VIA a MUX to different destinations
since this is the Y coordinate we enable the MUX with MUX settings which
send the DAC data to the Y integration data storage (the strength which
will be added at each cycle to the Y integrators, once additions are allowed),
the MUX settings which enable sending to Y is 00 (two bits of VIA Port B).
Enabling the MUX itself is also a bit of VIA Port B, bit 0, MUX is enabled by
clearing that bit.

Here a small describtion of the bits of VIA port B:

VIA_port_b  $D000  : VIA port B data I/O register

Bit         Meaning

bit 7      ~RAMP
bit 6      external device (slot pin 35) initialized to input
bit 5      comparator input
bit 4      sound BDIR
bit 3      sound BC1
bit 2      \ mux sel: 0-Y integrator, 1-X, Y integrator offset
bit 1      /          2-Z brightness, 3-sound output
bit 0      sample/hold (0=enable  mux 1=disable mux)

Than the functions stores register D on stack, which also serves as a
short delay befor disabling the MUX again.
Right after that we load register A with $CE, which as a bitmask is:

Bitmask $CE:               Meaning in register VIA_cntl

bit 7             1       \
bit 6             1        > CB2 control  CB2 -> ~BLANK 110=low 111=high
bit 5             0       /
bit 4             0       CB1 control     CB1 -> NC     0=IRQ on low
bit 3             1       \
bit 2             1        > CA2 control  CA2 -> ~ZERO  110=low 111=high
bit 1             1       /
bit 0             0       CA1 control CA1 -> SW7 0=IRQ on low

This sets the ~BLANK to low, which disables output and ~ZERO to high, which
disables zeroing. If zeroing is enabled, the vector beam can not be moved,
since it is 'grounded', it remains in zero position. The settings thus allow
the vector beam to be moved.

Next we clear the Shift register, that means, when the integration starts,
the vector moves, there will be no output, since at every shift out a zero
will be moved to ~BLANK. Integration will start as soon as ~RAMP is zero
(which as said before will be negated befor reaching the integration circuit).

After that we disable the MUX, remember, the enable bit for that was bit 0 of
VIA port B. It gets disabled when the bit is set. This means that the DAC
value now only gets to the X integration storage (which allways gets the DAC
value, the MUX setting doesn't change that).

Than we store the X strength to VIA port A and thus to the DAC register.
The next step is to enable the timer by writing to the hi byte.
The high byte is cleared, since we integrate at most to $ff time, which is
the highest scale value. The scale value was set befor even calling the move
function into the low byte of the timer.

The next lines are IMHO not neccessary, all this computing of some extra
cycles to wait. And actually I don't understand it. Since the additional
waiting is done AFTER the timer reaches zero, that means the ~RAMP is dis-
abled anyway, so no change to any vector position will occur.

Anyway, let us assume that we reach the point marked ':HERE'.
Register B is loaded with $40, which is the bitmask for timer 1 interrupt
testing. The loop's only purpose is to wait for the time to expire. After
the time has gone by the ~RAMP will be again not active, no integration is
done and position of the beam will not change anymore (presumable), since
~RAMP is disabled, as soon as the timer reaches zero.
Why on earth the BIOS programmers wait for some more cycles to go by - I don't
understand.
Apart from that - you see that once you know where to put the correct timers,
and what registers are used for what... it is sort of understandable how a
vector is positioned.

The Vector drawing routines are essentially the same, apart from the
Shift register, which is not cleared, but set to $ff = %11111111, which
when shifted results in ~BLANK to be non active, thus the beam is illuminated.

Here is a simple BIOS vector plot routine:

Draw_Line_d     STA     <VIA_port_a     ;Send Y to A/D
                CLR     <VIA_port_b     ;Enable mux
                LEAX    2,X             ;Point to next coordinate pair
                NOP                     ;Wait a moment
                INC     <VIA_port_b     ;Disable mux
                STB     <VIA_port_a     ;Send X to A/D
                LDD     #$FF00          ;Shift reg=$FF (solid line), T1H=0
LF3ED:          STA     <VIA_shift_reg  ;Put pattern in shift register
                STB     <VIA_t1_cnt_hi  ;Set T1H (scale factor?)
                LDD     #$0040          ;B-reg = T1 interrupt bit
LF3F4:          BITB    <VIA_int_flags  ;Wait for T1 to time out
                BEQ     LF3F4
                NOP                     ;Wait a moment more
                STA     <VIA_shift_reg  ;Clear shift register (blank output)
                LDA     $C823           ;Decrement line count
                DECA
                BPL     Draw_VL_a       ;Go back for more points
                JMP     Check0Ref       ;Reset zero reference if necessary

That routine is part of some other routines, because of that some things are
done, which are not directly related to vector drawing. The corresponding
source lines do not interest us here.
As you can see the routine starts of exactly as the Moveto routine, by setting the
DAC to the Y strength. The MUX becomes enabled, we wait a moment, than we
disable the MUX again. This time the routine assumes, that the ~ZERO flag is
allready set correctly, so we don't bother with it. Then we poke the X
strength to the DAC. When integration starts we have the correct strengths set
to the integrators. Now we prepare the shift register, we poke a $ff to it,
which means we will be plotting a solid line. Notice, that we are not integ-
rating yet. But the light is allready switched on (so to say), since shifting
starts the moment we set a new shifting value. This is one of the reasons,
why all lines we draw have a bright spot at the beginning. The light is always
switched on a few cycles to early! After that we clear the hi value of timer
one, this enabled the ~RAMP, integration, now the vector beam is moving with
the light switched on, that means we are actually drawing a vector now.
This drawing will last till timer one expires. The movement will cease the
moment the timer reaches zero. Unfortunately the light is not switched off
automatically. We therefore wait in a loop, till we get a message, that
timer one has expired ($40 (bit 6) in the VIA_int_flags register).
After that we switch the light off by clearing the shift register. Done, the
complete line is drawn now.
As you can see there is no fiddling with any strange additional wait loops,
while drawing lines, but otherwise it is exactly the same thing as positioning
the vectors (apart from $ff in the Shift register instead of $00).
This sort of confirms my suspicion, that these strange loops in the position-
ing functions are sort of useless. In my experiments I have not seen any dif-
ferences, if I left them out.


Why wait a moment? (Optimization warning)
-----------------------------------------
I'm not really sure. But I suspect the hardware not to react immediatly to
changes in VIA registers/bits. VIA itself needs 1-2 cycles to initiate shift-
ing and/or timing. Some things go through the MUX and others are negated along
the way to their final destination (~RAMP/~ZERO).
I experienced some timing problems optimizing Vectrex Frogger.
I changed the above line routine to be a bit faster for my purposes. Since in
Vectrex Frogger all 'sprites' are drawn using a scale factor of 6, which is
the timer low counter, I thought that after 6 cycles from switching the timer
on I could switch the light off, without a request loop, and the line would be
drawn all right. This is not so. I think somewhere in the vectrex hardware
there is a delay of a couple of cycles befor the signals reach their final
destination (the integration hardware, in opposite to their first destination,
the VIA registers/flags). And furthermore, that these delays are not the same
for all operations, since everything would again be allright.
I guess, for example, the ~RAMP enabling somehow takes more time than ~BLANK
enabling. I have not done really many tests on that, perhaps some day I will.
When optimizing vectrex programs keep this in mind and test timing critical
routines carefully. There are some situations where you can easily change
some registers to early, theoretically the results would be ok, but in reality
vectrex will draw something weirdly different. The vector drawing and pos-
itioning routines have great potential for optimization, but be carefull!


Vector positioning optimization thoughts
----------------------------------------
For Vectrex Frogger I did in many cases not use BIOS functions, but ripped
them and fitted them for my purposes. This was neccessary to fit the program
into the tiny timescale of 30,000 cycles for one update round. One major cycle
saving trick was done at a late stage of optimizing. It utilizes the idle
phase during vector positioning. For some more information about this look
at the documentation of Vectrex Frogger, the sourcecode and the 'progger.txt'.
Here I will give another brief introduction to the idea I had.
In the above documented BIOS function Moveto_d the last relevant lines of
code are:

LF345:          BITB    <VIA_int_flags  ;Wait for timer 1
                BEQ     LF345

The positioning is usually done with quite a large scale factor. In Vectrex
Frogger I often used $90 (or so). The above loop does nothing but check for
the timer to expire. It is an iddle loop, that waits for nearly $90 cycles.
After the loop is finnished not a single thing is done anymore, the routine
is finnished and returns to its caller. You will see quickly, that a wait
loop like that can be used to do more than just nothing. In a tightly packed
program there are always some things to do, like collision checking, preparing
the next vector drawing, positioning updating, decrementing timers or prepar-
ing sound output. All these things can be done during that waiting. HALT!
Not all things, you are not allowed to do anything that changes vector drawing
relevant registers in the VIA chip, that applies to VIA_Port A, B, Shift reg-
ister, Timer 1 and possibly a few others (control registers). That generally
means, that you are not allowed to access VIA at all, that again means, no
sound output, and no joystick inquiries, no new scaling value...
But I experienced that, with clever programming MANY things can be done in
that idle time. You do not have to worry about taking to long either, since
the timer will expire all by itself. The only thing you have to watch out for
is, that you don't finnish to early, since if you start drawing the vector
when you have not finnished positioning the beam... well, you probably can
imagine what happens. But making sure, that positioning has correctly fin-
nished - all you need to do is put that little 'idle'-loop from above at the
end of your intermediate section. Voila, you saved some really valuable time
here!

The same thing can theoretically be applied to vector drawing. With drawing
vectors you must make sure, that you finnish you intermediate stuff in time,
so that you can switch the beam off in time, otherwise you will always see
a bright dot at the end of your vectors.
But generally speaking, the above optimization is not really such a good idea
while drawing vectors. Since you usually have quite small timing figures (or
at least SHOULD have). As mentioned above, Vectrex Frogger draws all 'sprites'
using a scale factor of six. There is not all that much iddle time left. What
I did is to skip the loop entirely, since ALL vectors I draw have the same
scale value, I was able to put some fixed code into the drawing 'idle' time,
so I DID save a few cycles here as well. But this is a field where you have to
experiment individually for your program. I had to change my routines quite a
few times, till I got them right for fitting in that 'idle' time. One major
hog was the above mentioned uncertain time for some vector drawing related
flags to arrive at the integration hardware.
On the other hand, cycles saved in the routine for drawing single vectors
is really, really, REALLY!!! important. Since you usually draw 'sprites'
consisting of many vectors. And you'll probably draw many sprites. Imagine
drawing 10 sprites, each using 20 vectors, that'll make 200 vectors. Now
imaging your optiminzed code takes for each drawn vector 10 cycles less than
the non optimized code. That would save about (or exactly in this example)
2,000 cycles. If you do a game where you will have not much time left you will
appreciate these saved cycles very much!

For examples of the above mentioned routines look at Vectrex Frogger.


Optimization for Vectrex Programs
---------------------------------
First, for some further information on this subject look at the 'vectors'
section, the source for Vectrex Frogger and at the 'progger.txt' file included
with Vectrex Frogger.

This will not be a full blown section of its own. I'll just give some tips and
tricks I encountered/used. This is by no means complete. Perhaps some of it
is even wrong, who knows, I'm not a profi, remember, I just write down some
thoughts I have/had.

General optimization techniques apply, as searching for good algorithms to do
something instead of making up something on the fly. General assembler
techniques are not really looked into in depth. Look at an assembler book for
that.

In all programming ever done you will encounter two ways of optimizing. One
way for speed, the other for size. Both ways are usually mutual exclusive.
I'll just go into the speed area a bit. The first vectrex programmers probably
had to look after the size of their programs as well as the speed. But at
RAM (ROM/EPROM that is) prices as they are today we don't really need to be
concerned about space. We have 32KB we can fill with stuff. 32KB assembler
programs for a 6809 processor really take some time to code.

First thing you might want to look for in your program are what sections need
optimizing. There is, for example, no need to speed optimize the initializ-
ation code. It probably doesn't matter if you initialize the next level in
a hundreths or a tenth of a second. This is usually a place where you might
think of space optimization instead of speed.

The part of the code which has to run 50 times per second is another thing,
here we are entering code that might be worth a second look. Even more inter-
esting are sections of code which run many times per update round. Like
vector positioning/drawing code. Perhaps collision detection stuff. Well,
there is no general describtion of what code might be executed that is worth
optimizing, you'll need some feeling for it. Don't worry, you'll know where
to optimize once you start with your program.

The way I usually go about writting a program is:
- think about it
- program it in the most easy and intuitive way possibly
- test/change till it works allright
- start optimizing
- repeat the last two steps, till satisfied

There are probably many people who'll say, do it the right way (optimized)
right at start. I don't think so, I find that very cumbersome.

Anyway, do what you want, as you will do so anyway.
Here are some points I would like to share:

- once the code is stable remove JSR/RTS, especially where called
  many times per update round, each JSR/RTS takes 12 cycles

- use makros instead of subroutines, they are equal in easy changeability,
  but faster

- at the end of your program check if there might be some loop unroling be
  done, at places this might save you many cycles (vector lists, look at
  Vectrex Frogger for an example)

- if the last thing in a subroutine is the calling of another subroutine,
  you can as well jump there, this saves a couple of cycles (and space)

- if you can, avoid long branches, they take longer and take two bytes more
  of space, since their are second page opcodes

- if you can, avoid Y register usage, cause this one is too, a second
  page opcode, it takes longer and uses more space

- when finalizing your code, sort you code section, look where you access
  what RAM/VIA area most, and set the DP register accordingly, you can really
  save cycles this way and SPACE.
  I found it convenient to use a makro for changing the DP register, since
  you can place a 'direct' pseudo opcode within that makro

- if you can, do a LDD instead of LDA, LDB, it saves space and cycles,
  this often occurs when loading Y, X coordinates, and different values for
  PIA registers

- look at the *.lst file the assembler creates (with the correct options),
  you will cleary see where you waste cycles and space

- create a makro library of BIOS routines you use often, perhaps optimize
  the routines for your needs, the BIOS routines are quite broad in their
  functionality, you usually use only some parts of them, remove the unused
  part in you makros (look at the other documents, and Vectrex Frogger makros)

- at all costs, if possible avoid BIOS 'print_str' routines, they are born
  cycle wasters! One string printed can easily take 10,000 cycles!

- remember to switch of unused joystick stuff, each joystick direction test
  takes a few hundred cycles (or so)

- if not really neccesary, use digital joytick settings instead of analog


Using the emulator to program vectrex programs
----------------------------------------------
In general, the vectrex emulator does it's job fairly well. BUT you can't
rely entirely on it. Most of the time if you do things like you are supposed
to, you'll be allright. If you use the BIOS routines you won't get into any
trouble (unless you really ask for it).
For one BIOS trouble maker, look at the dot-plotting section, and why to
insert a delay loop.

What the emulator can not cope with:

- time critical programming of VIA registers, vector hardware
  (this probably only applies to hardcore optimized vector routines)
  (might try recompiling the emulator, with different analog section
   enabled, this gives you <some> more accuracy)
  (But :-) the right things will allways be right on the emulator too)

- e-function style integrator stuff, watchable with a real vectrex mainly
  by switching the ~BLANK to enable while zeroing

- 3D imager programming, if done in no timecritical way, using the timing
  parameters for a known 3d game (Narrow Escape preferably), should be ok,
  on the other side, perhaps not, I have no Imager, so I can't test it very
  well...

- using different VIA Shift register settings than 4, which is now the
  only used one

- Integrator offset stuff implemented, but never tested, probably not 100%
  correct

- fixed vector wobbling if not drawn in a 30,000 cycle update round

- flicker not corresponding to vectrex update frequency (might change
  DVE update frequency though)

- analog hardware failures/differences because of age (not all anyway)

- some parts of vectrex test program are not correctly handled

- weird stuff I can't think of now


This sounds as if the emulator is of no use. WRONG.
As I said, the emulator is quite good. It runs by now every available com-
mercial and non commercial program nearly perfectly, this includes >weird<
ones, like lightpen using ArtMaster (with RAM expension), Pole Position (which
changes DAC values while drawing a vector), Spike (digital sound), Clean Sweep
(different vector drawing routines)...

Vectrex Frogger was programmed using the emulator till I got my vectrex hooked
up. There was only one stupid mistake I (not the emulator) made. It caused
a small offset of all vectors, that was all.
Well one other thing, the wobble effect, when not updated in a 30,000 cycle
round.
But in many respects the emulator can be of invaluable help. Cycle counting,
there is a function in the monitor, that calculates the cycles per update
round (when using Wait_Recal function), with it you can test the speed of
your program, it should be under all circumstances under 30,000 in order to
have a solid image. You have a debugger, can set breakpoints, watch all regi-
sters, change values of registers and ROM/RAM locations online. Set watch
points for memory locations and and and.
If you use the assembler I use, you can create automatically control files
for use with DVE. This enables you to use all variables, function names, and
constants while in the DVE monitor! Just write something like:

WATCH frog_x

And a window will pop up, which will allways show the RAM location frog_x,
which might be something like $c8f5 (or whatever???).

IMHO, the vectrex emulator is not perfect, nor will it ever be perfect,
since there are many analog parts that cannot be (easily) emulated, but it is
of great use for programming vectrex.


Appendix A:
List of Vectrex ROM functions:
(by Bruce Tomlin, modified by Malban)

------------------------------------------------------------------------------
Functionally ordered:

Overview of functional classes:

1. Calibration/vector reset functions
2. Counter handling functions
3. Day to day functions
4. Delay functions
5. Drawing functions
  5.1 Dot functions
  5.2 String functions
  5.3 Vector functions
    5.3.1 Drawing only
    5.3.2 Drawing and moving
6. Mathematical functions
7. Memory managment
  7.1 Memory clear functions
  7.2 Memory copy functions
  7.3 Memory fill functions
8. Joystick handling functions
9. Player option functions
10. Reset and initializition functions
11. Score functions
12. Sound functions
13. Vector beam positioning functions
14. Vector brightness functions
15. Vector object handling functions
  15.1. Object collision detection functions
  15.2  Rotating functions

1. Calibration/vector reset functions
 Check0Ref       ($F34F)
 Recalibrate     ($F2E6)
 Reset_Pen       ($F35B)
 Reset0Int       ($F36B)
 Reset0Ref       ($F354)
 Reset0Ref_D0    ($F34A)
 Set_Refresh     ($F1A2)
 Wait_Recal      ($F192)

2. Counter handling functions
 Dec_3_Counters  ($F55A)
 Dec_6_Counters  ($F55E)
 Dec_Counters    ($F563)

3. Day to day functions
 Bitmask_a       ($F57E)
 Dec_3_Counters  ($F55A)
 Dec_6_Counters  ($F55E)
 Dec_Counters    ($F563)
 DP_to_C8        ($F1AF)
 DP_to_D0        ($F1AA)
 Print_Ships_x   ($F391)
 Print_Ships     ($F393)
 Random          ($F517)
 Random_3        ($F511)

4. Delay functions
 Delay_0         ($F579)
 Delay_1         ($F575)
 Delay_2         ($F571)
 Delay_3         ($F56D)
 Delay_b         ($F57A)
 Delay_RTS       ($F57D)

5. Drawing functions
 5.1 Dot functions
  Dot_d           ($F2C3)
  Dot_here        ($F2C5)
  Dot_ix_b        ($F2BE)
  Dot_ix          ($F2C1)
  Dot_List        ($F2D5)
  Dot_List_Reset  ($F2DE)
 5.2 String functions
  Print_Str_d     ($F37A)
  Print_List      ($F38A)
  Print_List_chk  ($F38C)
  Print_List_hw   ($F385)
  Print_Str       ($F495)
  Print_Str_hwyx  ($F373)
  Print_Str_yx    ($F378)
 5.3 Vector functions
  5.3.1 Drawing only
   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)
  5.3.2 Drawing and moving
   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)

6. Mathematical functions:
 Abs_a_b         ($F584)
 Abs_b           ($F58B)
 Bitmask_a       ($F57E)

7. Memory managment
 7.1 Memory clear functions
  Clear_C8_RAM    ($F542)
  Clear_x_b       ($F53F)
  Clear_x_d       ($F548)
  Clear_x_256     ($F545)
  Clear_Score     ($F84F)
  Clear_Sound     ($F272)
 7.2 Memory copy functions
  Move_Mem_a      ($F683)
  Move_Mem_a_1    ($F67F)
 7.3 Memory fill functions
  Clear_x_b_80    ($F550)
  Clear_x_b_a     ($F552)

8. Joystick handling functions
 Joy_Analog      ($F1F5)
 Joy_Digital     ($F1F8)
 Read_Btns_Mask  ($F1B4)
 Read_Btns       ($F1BA)

9. Player option functions
 Display_Option  ($F835)
 Select_Game     ($F7A9)

10. Reset and initializition functions
 Cold_Start      ($F000)
 Init_OS         ($F18B)
 Init_OS_RAM     ($F164)
 Init_VIA        ($F14C)
 Recalibrate     ($F2E6)
 Reset_Pen       ($F35B)
 Reset0Int       ($F36B)
 Reset0Ref       ($F354)
 Reset0Ref_D0    ($F34A)
 Wait_Recal      ($F192)
 Warm_Start      ($F06C)

11. Score functions
 Add_Score_a     ($F85E)
 Add_Score_d     ($F87C)
 Clear_Score     ($F84F)
 Compare_Score   ($F8C7)
 New_High_Score  ($F8D8)
 Strip_Zeros     ($F8B7)

12. 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)

13. Vector beam positioning functions
 Check0Ref       ($F34F)
 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)
 Reset_Pen       ($F35B)
 Reset0Int       ($F36B)
 Reset0Ref       ($F354)
 Reset0Ref_D0    ($F34A)
 Wait_Recal      ($F192)

14. Vector brightness functions
 Intensity_1F    ($F29D)
 Intensity_3F    ($F2A1)
 Intensity_5F    ($F2A5)
 Intensity_7F    ($F2A9)
 Intensity_a     ($F2AB)
 Reset_Pen       ($F35B)
 Wait_Recal      ($F192)

15. Vector object handling functions
 15.1 Object collision detection functions
  Obj_Hit         ($F8FF)
  Obj_Will_Hit    ($F8F3)
  Obj_Will_Hit_u  ($F8E5)
 15.2 Rotating functions
  Get_Rise_Idx    ($F5D9)
  Get_Run_Idx     ($F5DB)
  Rise_Run_Angle  ($F593)
  Rise_Run_Idx    ($F5EF)
  Rise_Run_Len    ($F603)
  Rise_Run_X      ($F5FF)
  Rise_Run_Y      ($F601)
  Rot_VL          ($F616)
  Rot_VL_ab       ($F610)
  Rot_VL_M_dft    ($F62B)
  Rot_VL_Mode     ($F61F)
  Xform_Rise      ($F663)
  Xform_Rise_a    ($F661)
  Xform_Run       ($F65D)
  Xform_Run_a     ($F65B)

------------------------------------------------------------------------------

Alphabetically ordered:

Abs_a_b         ($F584)
Abs_b           ($F58B)
Add_Score_a     ($F85E)
Add_Score_d     ($F87C)
Bitmask_a       ($F57E)
Check0Ref       ($F34F)
Clear_C8_RAM    ($F542)
Clear_Score     ($F84F)
Clear_Sound     ($F272)
Clear_x_256     ($F545)
Clear_x_b       ($F53F)
Clear_x_b_80    ($F550)
Clear_x_b_a     ($F552)
Clear_x_d       ($F548)
Cold_Start      ($F000)
Compare_Score   ($F8C7)
Dec_3_Counters  ($F55A)
Dec_6_Counters  ($F55E)
Dec_Counters    ($F563)
Delay_0         ($F579)
Delay_1         ($F575)
Delay_2         ($F571)
Delay_3         ($F56D)
Delay_b         ($F57A)
Delay_RTS       ($F57D)
Display_Option  ($F835)
Do_Sound        ($F289)
Do_Sound_x      ($F28C)
Dot_d           ($F2C3)
Dot_here        ($F2C5)
Dot_ix_b        ($F2BE)
Dot_ix          ($F2C1)
Dot_List        ($F2D5)
Dot_List_Reset  ($F2DE)
DP_to_C8        ($F1AF)
DP_to_D0        ($F1AA)
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)
Explosion_Snd   ($F92E)
Get_Rise_Idx    ($F5D9)
Get_Run_Idx     ($F5DB)
Init_Music      ($F68D)
Init_Music_Buf  ($F533)
Init_Music_chk  ($F687)
Init_Music_dft  ($F692)
Init_OS         ($F18B)
Init_OS_RAM     ($F164)
Init_VIA        ($F14C)
Intensity_1F    ($F29D)
Intensity_3F    ($F2A1)
Intensity_5F    ($F2A5)
Intensity_7F    ($F2A9)
Intensity_a     ($F2AB)
Joy_Analog      ($F1F5)
Joy_Digital     ($F1F8)
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)
Move_Mem_a      ($F683)
Move_Mem_a_1    ($F67F)
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)
New_High_Score  ($F8D8)
Obj_Hit         ($F8FF)
Obj_Will_Hit    ($F8F3)
Obj_Will_Hit_u  ($F8E5)
Print_Str_d     ($F37A)
Print_List      ($F38A)
Print_List_chk  ($F38C)
Print_List_hw   ($F385)
Print_Ships_x   ($F391)
Print_Ships     ($F393)
Print_Str       ($F495)
Print_Str_hwyx  ($F373)
Print_Str_yx    ($F378)
Random          ($F517)
Random_3        ($F511)
Read_Btns_Mask  ($F1B4)
Read_Btns       ($F1BA)
Recalibrate     ($F2E6)
Reset_Pen       ($F35B)
Reset0Int       ($F36B)
Reset0Ref       ($F354)
Reset0Ref_D0    ($F34A)
Rise_Run_Angle  ($F593)
Rise_Run_Idx    ($F5EF)
Rise_Run_Len    ($F603)
Rise_Run_X      ($F5FF)
Rise_Run_Y      ($F601)
Rot_VL          ($F616)
Rot_VL_ab       ($F610)
Rot_VL_M_dft    ($F62B)
Rot_VL_Mode     ($F61F)
Select_Game     ($F7A9)
Set_Refresh     ($F1A2)
Sound_Byte      ($F256)
Sound_Byte_x    ($F259)
Sound_Byte_raw  ($F25B)
Sound_Bytes     ($F27D)
Sound_Bytes_x   ($F284)
Strip_Zeros     ($F8B7)
Wait_Recal      ($F192)
Warm_Start      ($F06C)
Xform_Rise      ($F663)
Xform_Rise_a    ($F661)
Xform_Run       ($F65D)
Xform_Run_a     ($F65B)

------------------------------------------------------------------------------

Full describtion of functions:
(alphabetically ordered)

----------------------------------------------------------------------
I                             Abs_a_b                                I
----------------------------------------------------------------------
Abs_a_b ($F584)
->Abs_b ($F58B)
----------------------------------------------------------------------
I                              Abs_b                                 I
----------------------------------------------------------------------
Abs_b ($F58B)

This routine returns the absolute value of the two single byte
numbers passed in in the A and B registers.  Abs_b only uses the B
register.  There is a special case: 0x80 is returned as 0x7F.

ENTRY A-reg contains first value
      B-reg contains second value (Abs_a_b only)

EXIT: A-reg contains absolute value of first value
      B-reg contains absolute value of second value (Abs_a_b only)

      All other registers preserved.
----------------------------------------------------------------------
I                           Add_Score_a                              I
----------------------------------------------------------------------
Add_Score_a ($F85E)
->Add_Score_d ($F87C)
----------------------------------------------------------------------
I                           Add_Score_d                              I
----------------------------------------------------------------------
Add_Score_d ($F87C)

These routines take the BCD value in the D-register or the binary
value in the A-register, and add it to the 6-byte ASCII number
pointed by the X-register.

ENTRY A-reg = binary value (Add_Score_a only)
      D-reg = BCD value (Add_Score_d only)
      U-reg = BCD conversion of A-reg (Add_Score_a only)
      X-reg points to six byte ASCII score accumulator

      D-reg trashed
----------------------------------------------------------------------
I                            Bitmask_a                               I
----------------------------------------------------------------------
Bitmask_a ($F57E)

This routine takes a bit number, specified in the A register, and
returns a bit mask with only the specified bit set.

ENTRY A-reg contains the bit number

EXIT: A-reg contains the bit mask

      X-reg trashed
----------------------------------------------------------------------
I                            Check0Ref                               I
----------------------------------------------------------------------
Check0Ref ($F34F)

This routine will check to see if the Reset0Ref enable flag ($C824)
is set, and if it is, then it will reset the integrators by calling
Reset0Ref.

ENTRY DP = $D0
      $C824 = enable flag

      D-reg trashed
----------------------------------------------------------------------
I                         Clear_C8_RAM                               I
----------------------------------------------------------------------
Clear_C8_RAM ($F542)

This routine clears to 0 the block of memory in the range
$C800-$C8FF.

EXIT: X-reg = $C800
      D-reg = $FFFF
----------------------------------------------------------------------
I                          Clear_Score                               I
----------------------------------------------------------------------
Clear_Score ($F84F)

This routine will initialize the passed-in score string (pointed to
by the X-register) to the following value:

      "     0",0x80

ENTRY X-reg points to seven byte score accumulator

      D-reg trashed
----------------------------------------------------------------------
I                          Clear_Sound                               I
----------------------------------------------------------------------
Clear_Sound ($F272)

This routine clears the 15 registers on the music chip and the soft
copy of their values (C800-C80E), by writing a byte of 0 to each
register. This causes the sound chip to not make any sounds.

ENTRY DP = $D0

      D-reg, X-reg trashed
----------------------------------------------------------------------
I                           Clear_x_256                              I
----------------------------------------------------------------------
Clear_x_256 ($F545)
->Clear_x_d ($F548)
----------------------------------------------------------------------
I                            Clear_x_b                               I
----------------------------------------------------------------------
Clear_x_b ($F53F)

This routine clears to 0 the block of memory starting at the
address contained in the X register, and continuing for the number
of bytes specified by B+1.

ENTRY X-reg points to the start of the RAM to be cleared.
      B-reg = number of bytes minus 1 to clear.

EXIT: D-reg = $FFFF
----------------------------------------------------------------------
I                           Clear_x_b_80                             I
----------------------------------------------------------------------
Clear_x_b_80 ($F550)
->Clear_x_b_a ($F552)
----------------------------------------------------------------------
I                          Clear_x_b_a                               I
----------------------------------------------------------------------
Clear_x_b_a ($F552)

This routine sets the block of memory pointed to by the X register
to $80 or the A register.  The B register specifies the number of
bytes to be cleared.

ENTRY A-reg = byte to be stored (Clear_x_b_a only)
      B-reg = number of bytes to clear ($00 = 256)
      X-reg points to start of memory block to clear

EXIT: A-reg = $80 (Clear_x_b_80 only)
      B-reg = $00

      All other registers preserved.
----------------------------------------------------------------------
I                           Clear_x_d                                I
----------------------------------------------------------------------
Clear_x_d ($F548)

This routine clears the block of memory starting at the contained
in the X register to zero.

ENTRY X-reg points to the start of RAM to be cleared
      D-reg = number of bytes to clear minus 1 (Clear_x_d only)

EXIT: D-reg = $FFFF
----------------------------------------------------------------------
I                           Cold_Start                               I
----------------------------------------------------------------------
Cold_Start ($F000)

Jump here to restart the Vectrex and re-initialize the OS.  If the
cold start flag is correct (it should be unless you just turned the
Vectrex on), the cold start code is skipped.

On cold start, the high score is cleared, and the power-on screen
is displayed with the power-on music.

----------------------------------------------------------------------
I                          Compare_Score                             I
----------------------------------------------------------------------
Compare_Score ($F8C7)

This routine will compare two BCD score strings, to determine which
one is higher.  The two strings are pointed to by the U and X
registers.  Depending upon how the scores compare, one of the
following values will be returned in the A-register:

      1) The scores are the same: a = 0
      2) X score > U score:   a = 1
      3) U score > X score:   a = 2

ENTRY X-reg points to first score string (terminated with $80)
      U-reg points to second score string

EXIT: A-reg returns result of the compare

      B-reg trashed
----------------------------------------------------------------------
I                         Dec_3_Counters                             I
----------------------------------------------------------------------
Dec_3_Counters ($F55A)
->Dec_6_Counters ($F55E)
----------------------------------------------------------------------
I                         Dec_6_Counters                             I
----------------------------------------------------------------------
Dec_6_Counters ($F55E)

These routines check either the first three or all six of the
default counters at $C82E-$C833 and decrements those which are not
already zero.

EXIT: X-reg points to the default counters at $C82E
      B-reg = $FF

      All other registers preserved.
----------------------------------------------------------------------
I                         Dec_Counters                               I
----------------------------------------------------------------------
Dec_Counters ($F563)

This routine checks the counters pointed to by the X register and
decrements those which are not already zero.

ENTRY B-reg = number of counters minus 1
      X-reg points to counter bytes

EXIT: B-reg = $FF

      All other registers preserved.
----------------------------------------------------------------------
I                             Delay_0                                I
----------------------------------------------------------------------
Delay_0 ($F579)
12 cycles
->Delay_RTS ($F57D)
----------------------------------------------------------------------
I                             Delay_1                                I
----------------------------------------------------------------------
Delay_1 ($F575)
20 cycles
->Delay_RTS ($F57D)
----------------------------------------------------------------------
I                             Delay_2                                I
----------------------------------------------------------------------
Delay_2 ($F571)
25 cycles
->Delay_RTS ($F57D)
----------------------------------------------------------------------
I                             Delay_3                                I
----------------------------------------------------------------------
Delay_3 ($F56D)
30 cycles
->Delay_RTS ($F57D)
----------------------------------------------------------------------
I                             Delay_b                                I
----------------------------------------------------------------------
Delay_b ($F57A)
5*B + 10 cycles
->Delay_RTS ($F57D)
----------------------------------------------------------------------
I                             Delay_RTS                              I
----------------------------------------------------------------------
Delay_RTS ($F57D)
5 cycles

Each of these routines loads the B-register with the indicated
value, and then loops until the B register value has decremented
below zero.  Delay_RTS is just an RTS instruction, but at least
one GCE cartridge calls it.

Cycle counts do not include timing of the instructions used to
call the delay routines.

ENTRY B-reg = delay count (Delay_b only)

EXIT: B-reg = $FF (except Delay_RTS)
----------------------------------------------------------------------
I                         Display_Option                              I
----------------------------------------------------------------------
Display_Option ($F835)

This routine displays the player or game option string, along with
the current value for that option.  The A-register contains the
value of the option, while the Y-register points to a block of the
following form:

      rel y, rel x,      ( for value )
      rel y, rel x,      ( for option string)
      option string,
      0x80

ENTRY DP = $D0
      A-reg=the option value.
      Y-reg points to the string block.

      D-reg, U-reg, X-reg trashed
----------------------------------------------------------------------
I                            Do_Sound                                I
----------------------------------------------------------------------
Do_Sound ($F289)
->Do_Sound_x ($F28C)
----------------------------------------------------------------------
I                          Do_Sound_x                                I
----------------------------------------------------------------------
Do_Sound_x ($F28C)

This routine will start/continue making the sound which was first
set up by your call to Init_Music.  This routine should normally
be called right after your call to Wait_Recal.  It takes the next
music information, contained in the music buffer $C83F-$C84C, and
updates only those registers which differ from the last data written
to the sound chip.

ENTRY DP = $D0

      D-reg, X-reg, U-reg trashed
----------------------------------------------------------------------
I                             Dot_d                                  I
----------------------------------------------------------------------
Dot_d ($F2C3)

This routine draws a dot at the relative y and relative x position
contained in the D register.  The intensity used is the value
already stored in $C828.

ENTRY DP = $D0
      A-reg = relative Y coordinate
      B-reg = relative X coordinate

      D-reg trashed
----------------------------------------------------------------------
I                           Dot_here                                 I
----------------------------------------------------------------------
Dot_here ($F2C5)

This routine draws a dot at the current pen position.
The intensity used is the value already stored in $C828.

ENTRY DP = $D0

      D-reg trashed
----------------------------------------------------------------------
I                           Dot_ix_b                                 I
----------------------------------------------------------------------
Dot_ix_b ($F2BE)
->Dot_ix ($F2C1)
----------------------------------------------------------------------
I                            Dot_ix                                  I
----------------------------------------------------------------------
Dot_ix ($F2C1)

These routines draw a dot at the relative y and relative x
position pointed to by the X register.  Afterwards, the X register
is incremented by 2.

ENTRY DP = $D0
      X-reg points to the (y,x) coordinate pair
      B-reg contains the intensity (Dot_ix_b only)
      $C828 contains the intensity (Dot_ix only)

EXIT  X-reg incremented by 2

      D-reg trashed
----------------------------------------------------------------------
I                            Dot_List                                I
----------------------------------------------------------------------
Dot_List ($F2D5)

This routine draws a series of dots, using the intensity already
set up in $C828.  The format for the dot list, which is pointed to
by the X register, is:

    ( rel y, rel x), (rel y, rel x), .....

The number of dots-1 to draw is specified in $C823.

ENTRY DP = $D0
      X-reg points to the list of dot coordinates
      $C823 specifies the number of dots to draw

EXIT: X-reg points to next byte after list
      $C823 cleared

      D-reg trashed
----------------------------------------------------------------------
I                        Dot_List_Reset                              I
----------------------------------------------------------------------
Dot_List_Reset ($F2DE)

This routine draws a series of dots, specified by the list pointed
to by the X register.  The list has the following format:

    mode, relative y, relative x,
    mode, relative y, relative x,
      .      .           .
      .      .           .
    mode, relative y, relative x
    0x01

This routine will continue to traverse the list, until a mode > 0
is encountered; at that point, it will reset the zero reference
(the integrators).

ENTRY DP = $D0
      X-reg points to the dot list

EXIT: X-reg points to next byte after the terminator

      D-reg trashed
----------------------------------------------------------------------
I                            DP_to_D0                                I
----------------------------------------------------------------------
DP_to_D0 ($F1AA)

Sets the DP register to $D0, so that all direct page addressing will
start at $D000 (the hardware I/O area).

EXIT: DP = $D0
      A-reg = $D0
----------------------------------------------------------------------
I                            DP_to_C8                                I
----------------------------------------------------------------------
DP_to_C8 ($F1AF)

Sets the DP register to $C8, so that all direct page addressing will
start at $C800 (OS RAM area).


EXIT: DP = $C8
      A-reg = $C8
----------------------------------------------------------------------
I                          Draw_Grid_VL                              I
----------------------------------------------------------------------
Draw_Grid_VL ($FF9F)

This routine apparently will draw a vector list using a 16x16 grid,
and occasionally using regular vector lists too.  This could possibly
be useful for drawing gridded things like a chess board and all of
its pieces at the same time.

The master vector list contains multiple sublists that start with
a flag byte:
      Bit 7 = draw the next regular vector list (from X-reg) first
      Bit 6 = this is the last sublist in the master vector list
      Bits 5,4 = unused
      Bits 3-0 = number of points in this sublist (1-16)

The points are stored as a pair of nibbles:
      Bits 7-4 = Y coordinate (?)
      Bits 3-0 = X coordinate (?)

ENTRY DP = $D0
      X-reg points to regular vector lists
      Y-reg points to master vector list

EXIT: X-reg points to next byte after last regular vector list used
      Y-reg points to next byte after end of master vector list

      D-reg trashed
----------------------------------------------------------------------
I                          Draw_Line_d                               I
----------------------------------------------------------------------
Draw_Line_d ($F3DF)

This routine will draw a line from the current pen position, to the
point specified by the (y,x) pair specified in the D register.  The
current scale factor is used.  Before calling this routine, $C823
should be = 0, so that only the one vector will be drawn.

ENTRY DP = $D0
      A-reg = relative y position
      B-reg = relative x position

EXIT: X-reg is incremented by 2

      D-reg trashed
----------------------------------------------------------------------
I                           Draw_Pat_VL                              I
----------------------------------------------------------------------
Draw_Pat_VL ($F437)
->Draw_Pat_VL_d ($F439)
----------------------------------------------------------------------
I                          Draw_Pat_VL_a                             I
----------------------------------------------------------------------
Draw_Pat_VL_a ($F434)
->Draw_Pat_VL_d ($F439)
----------------------------------------------------------------------
I                         Draw_Pat_VL_d                              I
----------------------------------------------------------------------
Draw_Pat_VL_d ($F439)

All of these routines draw a series of patterned vectors.  The
pattern to use must already be specified in $C829.  When using
Draw_Pat_VL or Draw_Pat_VL_d, the number of vectors to draw minus 1
must be specified in $C823; when using Draw_Pat_VL_a, the number of
vectors to draw minus 1 must be passed in in the A register.
The vector list, pointed to by the X register, has the following
format:

      rel y, rel x, rel y, rel x, ...

Draw_Pat_VL_d starts at the (y,x) coordinates specified in the
D register and ignores the first pair of coordinates in the vector
list.

ENTRY DP = $D0
      X-reg points to the vector list
      A-reg = the number of vectors to draw (Draw_Pat_VL_a only)
      D-reg = start (Y,X) coordinate (Draw_Pat_VL_d only)
      $C829 contains the line pattern.

EXIT: X-reg points to next byte after list

      D-reg trashed
----------------------------------------------------------------------
I                             Draw_VL                                I
----------------------------------------------------------------------
Draw_VL ($F3DD)

This routine draws vectors between the set of (y,x) points pointed
to by the X register.  The number of vectors to draw must already be
specified in $C823.  The current scale factor is used.  The vector
list has the following format:

      rel y, rel x, rel y, rel x, ...

ENTRY DP = $D0
      X-reg points to the vector list

EXIT: X-reg points to next byte after list

      D-reg trashed
----------------------------------------------------------------------
I                           Draw_VL_a                                I
----------------------------------------------------------------------
Draw_VL_a ($F3DA)

This routine draws vectors between the set of (y,x) points pointed
to by the register.  The current scale factor is used.  The vector
list has the following format:

      rel y, rel x, rel y, rel x, ...

ENTRY DP = $D0
      A-reg = the number of vectors to draw
      X-reg points to the vector list

EXIT: X-reg points to next byte after list

      D-reg trashed
----------------------------------------------------------------------
I                          Draw_VL_ab                                I
----------------------------------------------------------------------
Draw_VL_ab ($F3D8)

This routine draws vectors between the set of (y,x) points pointed
to by the X register.  The vector list has the following format:

      rel y, rel x, rel y, rel x, ...

ENTRY DP = $D0
      A-reg = the number of vectors to draw
      B-reg = the scale factor
      X-reg points to the vector list

EXIT: X-reg points to next byte after list

      D-reg trashed
----------------------------------------------------------------------
I                           Draw_VL_b                                I
----------------------------------------------------------------------
Draw_VL_b ($F3D2)

This routine draws vectors between the set of (y,x) points pointed to
by the X register.  The vector list has the following format:

      rel y, rel x, rel y, rel x, ...

ENTRY DP = $D0
      B-reg = the scale factor
      X-reg points to the vector list

EXIT: X-reg points to next byte after list

      D-reg trashed
----------------------------------------------------------------------
I                          Draw_VL_mode                              I
----------------------------------------------------------------------
Draw_VL_mode ($F46E)

This routine processes the vector list pointed to by the X register.
The current scale factor is used.  The vector list has the following
format:

      mode, rel y, rel x,
      mode, rel y, rel x,
       .      .      .
       .      .      .
      mode, rel y, rel x,
      0x01

where mode has the following meaning:

      < 0  use the pattern in $C829
      = 0  move to specified endpoint
      = 1  end of list, so return
      > 1  draw to specified endpoint

ENTRY DP = $D0
      X-reg points to the vector list
      $C829 contains the line pattern.

EXIT: X-reg points to next byte after terminator

      D-reg trashed
----------------------------------------------------------------------
I                            Draw_VLc                                I
----------------------------------------------------------------------
Draw_VLc ($F3CE)

This routine draws vectors between the set of (y,x) points pointed
to by the X register.  The number of vectors to draw is specified
as the first byte in the vector list.  The current scale factor is
used.  The vector list has the following format:

      count, rel y, rel x, rel y, rel x, ...

ENTRY DP = $D0
      X-reg points to the vector list

EXIT: X-reg points to next byte after list

      D-reg trashed
----------------------------------------------------------------------
I                           Draw_VLcs                                I
----------------------------------------------------------------------
Draw_VLcs ($F3D6)

This routine draws vectors between the set of (y,x) points pointed
to by the X register.  The number of vectors to draw is specified
as the first byte in the vector list.  The scale factor is specified
as the second byte in the vector list. The vector list has the
following format:

      count, scale, rel y, rel x, rel y, rel x, ...

ENTRY DP = $D0
      X-reg points to the vector list

EXIT: X-reg points to next byte after list

      D-reg trashed
----------------------------------------------------------------------
I                           Draw_VLp_7F                              I
----------------------------------------------------------------------
Draw_VLp_7F ($F408)

These routines force the scale factor to 0xFF or 0x7F, and then
process the vector list pointed to by the X register.  The vector
list has the following format:

      pattern, rel y, rel x
      pattern, rel y, rel x
         .      .      .
         .      .      .
      pattern, rel y, rel x
      0x01

The list is terminated by a pattern byte with the high bit cleared.

ENTRY DP = $D0
      X-reg points to the vector list

EXIT: X-reg points to the terminator byte

      D-reg trashed
----------------------------------------------------------------------
I                             Draw_VLp                               I
----------------------------------------------------------------------
Draw_VLp ($F410)

This routine draws patterned lines using the vector list pointed to
by the X-register.  The current scale factor is used.  The vector
ist has the following format:

      pattern, rel y, rel x
      pattern, rel y, rel x
         .      .      .
         .      .      .
      pattern, rel y, rel x
      0x01

The list is terminated by a pattern byte with the high bit cleared.

ENTRY DP = $D0
      X-reg points to the vector list

EXIT: X-reg points to the terminator byte

      D-reg trashed
----------------------------------------------------------------------
I                            Draw_VLp_b                              I
----------------------------------------------------------------------
Draw_VLp_b ($F40E)

This routine draws patterned lines using the vector list pointed to
by the X register.  The vector list has the following format:

      pattern, rel y, rel x
      pattern, rel y, rel x
         .      .      .
         .      .      .
      pattern, rel y, rel x
      0x01

The list is terminated by a pattern byte with the high bit cleared.

ENTRY DP = $D0
      B-reg = the scale factor
      X-reg points to the vector list

EXIT: X-reg points to the terminator byte

      D-reg trashed
----------------------------------------------------------------------
I                          Draw_VLp_FF                               I
----------------------------------------------------------------------
Draw_VLp_FF ($F404)
->Draw_VLp_7F ($F408)
----------------------------------------------------------------------
I                          Draw_VLp_scale                            I
----------------------------------------------------------------------
Draw_VLp_scale ($F40C)

This routine processes the vector list pointed to by the X register.
The first byte in the vector list is the scale factor.  The vector
list has the following format:

      scale
      pattern, rel y, rel x
      pattern, rel y, rel x
         .      .      .
         .      .      .
      pattern, rel y, rel x
      0x01

The list is terminated by a pattern byte with the high bit cleared.

ENTRY DP = $D0
      X-reg points to the vector list

EXIT: X-reg points to the terminator byte

      D-reg trashed
----------------------------------------------------------------------
I                        Explosion_Snd                               I
----------------------------------------------------------------------
Explosion_Snd ($F92E)

This routine appears to generate some type of an explosion sound,
dependent upon the 4 bytes which are pointed to by the U register.
You will probably need to call Do_Sound for this to do anything.

The format of the 4-byte block is:
      1)      Bits 0-2 = ?    Stored in $C85D
              Bits 3-5 = ?    Stored in $C853
              Bits 6-7 = 0
              Bits 0-2 and 3-5 are ORed and stored in bits 0-2 of
                                                              $C854
      2)      <0 = ?          Something to do with register 6
              =0 = ?
              >0 = ?
      3)      <0 = ?
              =0 = ?
              >0 = ?
      4)      Speed?  Higher values = lower duration?

ENTRY DP = $C8
      U-reg points to 4-byte block of data if $C867 high bit set

      D-reg, X-reg trashed
----------------------------------------------------------------------
I                         Get_Rise_Idx                               I
----------------------------------------------------------------------
Get_Rise_Idx ($F5D9)
->Get_Run_Idx ($F5DB)
----------------------------------------------------------------------
I                          Get_Run_Idx                               I
----------------------------------------------------------------------
Get_Run_Idx ($F5DB)

These routines are responsible for generating the two index pairs
which are required by the rest of the rotation routines.  Each index
pair is two bytes long, and has the following format:

      The high byte is obtained by masking the anglewith 0x1F (this
      forces the angle to be between 0 and 180 degrees), and then
      using this value to index into the multiplier table.

      The lower byte contains information about whether the angle
      lies along either the x or y axis, and whether the rise/run
      will be positive or negative.

              0 => positive rise, not on an axis, or
                   negative run, not on an axis.
           0x80 => negative rise, not on an axis, or
                   positive run, not on an axis.
              1 => positive rise, on an axis, or
                   negative run, on an axis.
           0x81 => negative rise, on an axis, or
                   positive run, on an axis.

ENTRY A-reg = the angle value

EXIT: A-reg = slope?
      B-reg = slope direction?

      X-reg trashed
----------------------------------------------------------------------
I                          Init_Music                                I
----------------------------------------------------------------------
Init_Music ($F68D)
->Init_Music_dft ($F692)
----------------------------------------------------------------------
I                        Init_Music_Buf                              I
----------------------------------------------------------------------
Init_Music_Buf ($F533)

This routine clears out the music work buffer, located at
$C83F-$C84C.

      X-reg, D-reg trashed
----------------------------------------------------------------------
I                        Init_Music_chk                              I
----------------------------------------------------------------------
Init_Music_chk ($F687)
->Init_Music_dft ($F692)
----------------------------------------------------------------------
I                        Init_Music_dft                              I
----------------------------------------------------------------------
Init_Music_dft ($F692)

These routines are responsible for filling the music work buffer
while a sound is being made.  It should be called once during each
refresh cycle.  If you want to start a new sound, then you must set
$C856 to 0x01, and point the U-register to the sound block.  If no
sound is in progress ($C856 = 0), then it returns immediately
(unless you called Init_Music or Init_Music_dft, which do not make
this check).  When a sound is in progress, $C856 will be set to 0x80.

These routines process a single note at a time, and calculate the
amplitude and course/fine tuning values for the 3 sound channels.
The values calculated are stored in the music work buffer, at
$C83F-$C84C.

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

ENTRY DP = $C8
      U-reg points to the start of the music data
      $C84D points to frequency table (Init_Music_dft only)
      $C856 may need to be set.

      D-reg, X-reg, Y-reg, U-reg trashed
----------------------------------------------------------------------
I                            Init_OS                                 I
----------------------------------------------------------------------
Init_OS ($F18B)

This routine is responsible for setting up the initial system state,
each time the system is either reset or powered up.  It will
initialize the OS RAM area, initialize the VIA chip, and then clear
all the registers on the sound chip.

EXIT: DP = $D0

      D-reg, X-reg trashed
----------------------------------------------------------------------
I                          Init_OS_RAM                               I
----------------------------------------------------------------------
Init_OS_RAM ($F164)

This routine first clears the block of RAM in the range $C800 to
$C87A, and then it initializes the dot dwell time, the refresh time,
and the joystick enable flags.

EXIT: DP = $C8

      D-reg, X-reg trashed
----------------------------------------------------------------------
I                            Init_VIA                                I
----------------------------------------------------------------------
Init_VIA ($F14C)

This routine is invoked during powerup, to initialize the VIA chip.
Among other things, it initializes the scale factor to 0x7F, and
sets up the direction for the port A and B data lines.

EXIT: DP = $D0

      D-reg, X-reg trashed
----------------------------------------------------------------------
I                          Intensity_1F                              I
----------------------------------------------------------------------
Intensity_1F ($F29D)
->Intensity_a ($F2AB)
----------------------------------------------------------------------
I                          Intensity_3F                              I
----------------------------------------------------------------------
Intensity_3F ($F2A1)
->Intensity_a ($F2AB)
----------------------------------------------------------------------
I                          Intensity_5F                              I
----------------------------------------------------------------------
Intensity_5F ($F2A5)
->Intensity_a ($F2AB)
----------------------------------------------------------------------
I                          Intensity_7F                              I
----------------------------------------------------------------------
Intensity_7F ($F2A9)
->Intensity_a ($F2AB)
----------------------------------------------------------------------
I                          Intensity_a                               I
----------------------------------------------------------------------
Intensity_a ($F2AB)

Each of these routines are responsible for setting the vector/dot
intensity (commonly used to denote the z axis) to a specific value.
0x00 is the lowest intensity, and 0x7F is the brightest intensity.
A negative intensity is also lowest intensity (7th bit set).
The intensity must be reset to the desired value after each call
to Wait_Recal; however, it can also be changed at any other time.
A copy of the new intensity value is saved in $C827.

ENTRY DP = $D0
      A-reg = intensity (Intensity_a only)

      D-reg trashed
----------------------------------------------------------------------
I                           Joy_Analog                               I
----------------------------------------------------------------------
Joy_Analog ($F1F5)
->Joy_Digital ($F1F8)
----------------------------------------------------------------------
I                          Joy_Digital                               I
----------------------------------------------------------------------
Joy_Digital ($F1F8)

These routines read the current positions of the two joysticks.

The joystick enable flags (C81F-C822) must be initialized to one of
the following values:

      0 - ignore; return no value.
      1 - return state of console 1 left/right position.
      3 - return state of console 1 up/down position.
      5 - return state of console 2 left/right position.
      7 - return state of console 2 up/down position.

The joystick values are returned in $C81B-$C81E, where the value
returned in $C81B corresponds to the mask set in in $C81F, and so
on and so forth.

The joystick conversion is dependent on which routine is called.
Results for each routine are:

      Joy_Digital:
              The return value will be:
                < 0 if joystick is left of down of center.
                = 0 if joystick is centered.
                > 0 if joystick is right or up of center.

      Joy_Analog:
              A successive approximation algorithm is used to read
              the actual value of the joystick pot, a signed value.
              In this case, $C81A must be set to a power of 2, to
              to control conversion resolution; 0x80 is least
              accurate, and 0x00is most accurate.

ENTRY DP = $D0

      D-reg, X-reg trashed
----------------------------------------------------------------------
I                            Mov_Draw_VL                             I
----------------------------------------------------------------------
Mov_Draw_VL ($F3BC)
->Mov_Draw_VL_d ($F3BE)
----------------------------------------------------------------------
I                           Mov_Draw_VL_a                            I
----------------------------------------------------------------------
Mov_Draw_VL_a ($F3B9)

This routine moves to the first location specified in vector list,
and then draws lines between the rest of coordinates in the list.
The vector list has the  following format:

      rel y, rel x, rel y, rel x, ...

ENTRY DP = $D0
      A-reg = number of vectors to draw
      B-reg = scale factor to use (Draw_VL_ab only)
      X-reg points to the vector list

EXIT: X-reg points to next byte after list

      D-reg trashed
----------------------------------------------------------------------
I                          Mov_Draw_VL_ab                            I
----------------------------------------------------------------------
Mov_Draw_VL_ab ($F3B7)
->Mov_Draw_VL_a ($F3B9)
----------------------------------------------------------------------
I                          Mov_Draw_VL_d                             I
----------------------------------------------------------------------
Mov_Draw_VL_d ($F3BE)

This routine moves to the first location specified in vector list,
and then draws lines between the rest of coordinates in the list.
The vector list has the following format:

      rel y, rel x, rel y, rel x, ...

Draw_VL_d starts at the (y,x) coordinates specified in the D register
and ignores the first pair of coordinates in the vector list.

ENTRY DP = $D0
      $C823 = number of vectors to draw
      D-reg = start coordinate (Draw_VL_d only)
      X-reg points to the vector list (2,X for Mov_Draw_VL_d)

EXIT: $C823 is cleared

EXIT: X-reg points to next byte after list

      D-reg trashed
----------------------------------------------------------------------
I                          Mov_Draw_VLc_a                            I
----------------------------------------------------------------------
Mov_Draw_VLc_a ($F3AD)

This routine moves to the first location specified in vector list,
and then draws lines between the rest of coordinates in the list.
The number of vectors to draw is specified as the first byte in the
vector list.  The current scale factor is used.  The vector list has
the following format:

      count, rel y, rel x, rel y, rel x, ...

ENTRY DP = $D0
      X-reg points to the vector list

EXIT: X-reg points to next byte after list

      D-reg trashed
----------------------------------------------------------------------
I                          Mov_Draw_VLc_b                            I
----------------------------------------------------------------------
Mov_Draw_VLc_b ($F3B1)

This routine moves to the first location specified in vector list,
and then draws lines between the rest of coordinates in the list.
The vector list has the following format:

      rel y, rel x, rel y, rel x, ...

ENTRY DP = $D0
      B-reg = scale factor
      $C823 = number of vectors to draw
      X-reg points to the vector list

EXIT: $C823 is cleared

EXIT: X-reg points to next byte after list

      D-reg trashed
----------------------------------------------------------------------
I                         Mov_Draw_VLcs                              I
----------------------------------------------------------------------
Mov_Draw_VLcs ($F3B5)

This routine moves to the first location specified in vector list,
and then draws lines between the rest of coordinates in the list.
The number of vectors to draw is specified as the first byte in the
vector list, and the scale factor is the second byte in the vector
list.  The vector list has the following format:

      count, scale, rel y, rel x, rel y, rel x, ...

ENTRY DP = $D0
      X-reg points to the vector list

EXIT: X-reg points to next byte after list

      D-reg trashed
----------------------------------------------------------------------
I                           Move_Mem_a                               I
----------------------------------------------------------------------
Move_Mem_a ($F683)
->Move_Mem_a_1 ($F67F)
----------------------------------------------------------------------
I                          Move_Mem_a_1                              I
----------------------------------------------------------------------
Move_Mem_a_1 ($F67F)

This routine copies a block of memory, starting at the hi address,
and working down to the low address.  The base of the source address
is specified in the U register, and the base of the destination
address is  specified in the X register.  The A register contains
the number of bytes to copy; 0x80 is the maximum value which can
be specified.

ENTRY A-reg = byte count (Move_Mem_a only)
      A-reg = byte count minus 1 (Move_Mem_a_1 only)
      X-reg points to the destination
      U-reg points to the source

EXIT  A-reg = $FF
      B-reg = first byte of source
----------------------------------------------------------------------
I                             Moveto_d                               I
----------------------------------------------------------------------
Moveto_d ($F312)

This routine uses the current scale factor, and moves the pen to the
(y,x) position specified in D register.

ENTRY DP = $D0
      A-reg = Y coordinate
      B-reg = X coordinate

      D-reg trashed
----------------------------------------------------------------------
I                           Moveto_d_7F                              I
----------------------------------------------------------------------
Moveto_d_7F ($F2FC)

This routine forces the scale factor to 0x7F, and then moves the
pen to the position specified in the D register.

ENTRY DP = $D0
      A-reg = relative Y coordinate
      B-reg = relative X coordinate

      D-reg trashed
----------------------------------------------------------------------
I                            Moveto_ix                               I
----------------------------------------------------------------------
Moveto_ix ($F310)

This routine uses the current scale factor, and moves the pen to the
(y,x) position pointed to by the X register.  The X register is then
incremented by 2.

ENTRY DP = $D0
      X-reg points to the (y,x) coordinate pair

EXIT: X-reg has been incremented by 2

      D-reg trashed
----------------------------------------------------------------------
I                          Moveto_ix_7F                              I
----------------------------------------------------------------------
Moveto_ix_7F ($F30C)
->Moveto_ix_b ($F30E)
----------------------------------------------------------------------
I                           Moveto_ix_b                              I
----------------------------------------------------------------------
Moveto_ix_b ($F30E)

These routines force the scale factor to 0xFF, 0X7F, or the
A register, and then move the pen to the (y,x) position pointed to
by the X-register.  The X-register is then incremented by 2.

ENTRY DP = $D0
      X-reg points to the (y,x) coordinate pair
      B-reg contains the scale factor (Moveto_ix_b only)

EXIT: X-reg has been incremented by 2

      D-reg trashed
----------------------------------------------------------------------
I                          Moveto_ix_FF                              I
----------------------------------------------------------------------
Moveto_ix_FF ($F308)
->Moveto_ix_b ($F30E)
----------------------------------------------------------------------
I                           Moveto_x_7F                              I
----------------------------------------------------------------------
Moveto_x_7F ($F2F2)

This routine forces the scale factor to 0x7F, and then moves the
pen to the location pointed to by the X register.  The relative y
and relative x coordinates are both 2 byte quantities; however,
only the most signicant byte of each is of any interest.  The values
pointed to by the X register have the following format:

    X => (rel y hi),(rel y lo), (rel x hi), (rel x lo)

The position moved to is obtained by y=(0,x) & x=(2,x).

ENTRY DP = $D0
      X-reg points to double-sized coordinate pair

      D-reg trashed
----------------------------------------------------------------------
I                       New_High_Score                               I
----------------------------------------------------------------------
New_High_Score ($F8D8)

This routine compares a players score string, pointed to by the
X register, to the current hi score, pointed by the U register.  If
the player's score is higher than the currently saved hi score, then
the player's score will be copied into the hi score buffer pointed
to by the U register.

ENTRY X-reg points to a player's score string
      U-reg points to the high score string (usually $CBEB?)

      X-reg, U-reg, D-reg trashed
----------------------------------------------------------------------
I                             Obj_Hit                                I
----------------------------------------------------------------------
Obj_Hit ($F8FF)

Thit routine checks to see if a missile hashit an object.  If the
missile has hit the object, then the carry bit will be set;
otherwise, the carry bit will be cleared.  A hit is checked for in
the following fashion:

  if (object y-height/2) <= missile y <= (object y+height/2)
                              and
     (object x-width/2)  <= missile x <= (object x+width/x)

  then the missile hit, otherwise it missed.

ENTRY Y-reg = (y,x) position of the object
      X-reg = (y,x) position of the missile
      D-reg = (h/2,w/2) size of object

EXIT: Carry bit set if the object & missile have collided

      All registers preserved.
----------------------------------------------------------------------
I                          Obj_Will_Hit                              I
----------------------------------------------------------------------
Obj_Will_Hit ($F8F3)
->Obj_Will_Hit_u ($F8E5)
----------------------------------------------------------------------
I                        Obj_Will_Hit_u                              I
----------------------------------------------------------------------
Obj_Will_Hit_u ($F8E5)

This routine first modifies the position of the object, and then it
checks to see if the missile has hit the object.  The Y register
contains the (y,x) position of the object, the U register contains
a pointer to the (y,x) modification values, the X register contains
the missile (y,x) position, and the D register contains the
(height/2, width/2) of the object.

(0,u) is temporarily added to the y position of the object, and
(1,u) is temporarily added to the x position.

ENTRY Y-reg = (y,x) position of the object
      X-reg = (y,x) position of the missile
      U-reg points to movement (y,x) (Mov_Obj_Hit_u only)
      U-reg = movement (y,x) (Mov_Obj_Hit only)
      D-reg = (h/2,w/2) size of object

EXIT: Carry bit set if the object & missile have collided

      ALL registers saved.  Even the original Y-register.
----------------------------------------------------------------------
I                          Print_Str                                 I
----------------------------------------------------------------------
Print_Str ($F495)

This is the routine which does the actual printing of a string.  The
U register points to the start of the string, while $C82A contains
the height of the character, cell, and $C82B contains the width of
the character cell.  The string is terminated with an 0x80.

The string is displayed by drawing 7 horizontal rows of dots.  The
first row is drawn for each character, then the second, etc.  The
character generation table is located at ($F9D4 + $20).  Only
characters 0x20-0x6F (upper case) are defined; the lower case
characters a-o produce special icons.

ENTRY DP = $D0
      U-reg points to the start of the string

EXIT: U-reg points to next byte after terminator

      D-reg, X-reg trashed
----------------------------------------------------------------------
I                            Print_Str_d                             I
----------------------------------------------------------------------
Print_Str_d ($F37A)

This routine prints a single string (up to an 0x80), using the
default height and width, as stored in $C82A, and at the pen position
specified in the D register.  The parameter block describing the
string is pointed to by the U register.  The format for the
parameter block is as follows:

    string, 0x80

ENTRY DP = $D0
      U-reg points to string list
      A-reg = relative Y position
      B-reg = relative X position

EXIT: U-reg points to the byte after the terminating 0x80

      D-reg, X-reg trashed

----------------------------------------------------------------------
I                          Print_List                                I
----------------------------------------------------------------------
Print_List ($F38A)
->Print_List_chk ($F38C)
----------------------------------------------------------------------
I                        Print_List_chk                              I
----------------------------------------------------------------------
Print_List_chk ($F38C)

This displays the group of strings described by the parameter block
which is pointed to by the U register.  The string parameter block
has the following format:

      rel y, rel x, string, 0x80,
      rel y, rel x, string, 0x80,
      0x00

The current string height and width to which the hardware is set will
be used.

Print_List routine will first print the passed-in string, and THEN
check for the end of the string list.  Print_List_Chk will check for
the end of the string list first.

ENTRY DP = $D0
      U-reg points to string list

EXIT: U-reg points to null terminator byte

      D-reg, X-reg trashed
----------------------------------------------------------------------
I                         Print_List_hw                              I
----------------------------------------------------------------------
Print_List_hw ($F385)

This displays the group of strings described by the parameter block
which is pointed to by the U register.  The string parameter block
has the following format:

    height, width, rel y, rel x, string, 0x80,
    height, width, rel y, rel x, string, 0x80,
    0x00

ENTRY DP = $D0
      U-reg points to string list

EXIT: U-reg points to null terminator byte

      D-reg, X-reg trashed
----------------------------------------------------------------------
I                          Print_Ships                               I
----------------------------------------------------------------------
Print_Ships ($F393)

This routine displays the number of ships passed in the B register
followed by a minus sign and the ship icon character passed in the
A register at the (y,x) coordinates passed in the X register.  If
the B-register > 9, then the infinity symbol is displayed.

Note: This routine uses bytes at a negative offset from the stack as
      temporary storage, so hopefully an IRQ won't happen until the
      string is finished bring printed!

ENTRY DP = $D0
      A-reg = ship icon character
      B-reg = number of ships
      X-reg = (y,x) coordinates (Print_Ships only)
      X-reg points to (y,x) coordinates (Print_Ships_x only)

      D-reg, X-reg, U-reg trashed
----------------------------------------------------------------------
I                         Print_Ships_x                              I
----------------------------------------------------------------------
Print_Ships_x ($F391)
->Print_Ships ($F393)
----------------------------------------------------------------------
I                         Print_Str_hwyx                             I
----------------------------------------------------------------------
Print_Str_hwyx ($F373)

This routine prints a single string (up to an 0x80).  The parameter
block describing the string is pointed to by the U register.  The
format for the parameter block is as follows:

      height, width, rel y, rel x, string, 0x80

ENTRY DP = $D0
      U-reg points to the string list

EXIT: U-reg points to the byte after the terminating 0x80

      D-reg, X-reg trashed
----------------------------------------------------------------------
I                          Print_Str_yx                              I
----------------------------------------------------------------------
Print_Str_yx ($F378)

This routine prints a single string (up to an 0x80), using the
default height and width, as stored in $C82A.  The parameter block
describing the string is pointed to by the U register.  The format
for the parameter block is as follows:

      rel y, rel x, string, 0x80

ENTRY DP = $D0
      U-reg points to the string list

EXIT: U-reg points to the byte after the terminating 0x80

      D-reg, X-reg trashed
----------------------------------------------------------------------
I                             Random                                 I
----------------------------------------------------------------------
Random ($F517)
->Random_3 ($F511)
----------------------------------------------------------------------
I                            Random_3                                I
----------------------------------------------------------------------
Random_3 ($F511)

This routine generates a random 1-byte number, and places it in the
A register.  Random_3 runs through the random number generator
algorithm three times.  The random number seed is stored in the
three bytes pointed to by $C87B.

EXIT: A-reg contains the generated random number

      All other registers are preserved.
----------------------------------------------------------------------
I                         Read_Btns_Mask                             I
----------------------------------------------------------------------
Read_Btns_Mask ($F1B4)
->Read_Btns ($F1BA)
----------------------------------------------------------------------
I                           Read_Btns                                I
----------------------------------------------------------------------
Read_Btns ($F1BA)

Both of these routines read the button states on the two joysticks,
and return their state in the following RAM locations:

      joystick 1, button 1:  $C812 = $01
                  button 2:  $C813 = $02
                  button 3:  $C814 = $04
                  button 4:  $C815 = $08
      joystick 2, button 1:  $C816 = $10
                  button 2:  $C817 = $20
                  button 3:  $C818 = $40
                  button 4:  $C819 = $80

      C80F: Contains current state of all buttons;
            1 = depressed, 0 = not depressed

      C810: Contains state of all buttons from LAST time these
            routines were called; if Read_Btns_Mask was called,
            then this is AND'ed with the passed in mask.

      C811: Contains the same information as $C812-$C819

       Bit 7                                     Bit 0
      +-----+-----+-----+-----+-----+-----+-----+-----+
      | 2.4 | 2.3 | 2.2 | 2.1 | 1.4 | 2.3 | 1.2 | 1.1 |
      +-----+-----+-----+-----+-----+-----+-----+-----+

If Read_Btns is called, the result will be the same as Read_Btns_Mask
with a mask of $FF1, and a 1 will only be returned if the button
has transitioned to being pressed.

If Read_Btns_Mask is called, then a mask, passed in in the A-reg
will be used to determine how the button state info is returned:

If a bit is 0, then the current state of the button is to be returned
in the appropriate RAM location; 0 = not pressed, and 1 = pressed.

If a bit is 1, then the appropriate RAM location is set to 1 only
on the depression transition of the button; additional calls will
return 0, until the button is released and then depressed again.

ENTRY DP = $D0
      A-reg = mask (for Read_Btns_Mask only)

Exit: A-reg = button transition state (same as $C811)

      B-reg, X-reg trashed
----------------------------------------------------------------------
I                         Recalibrate                                I
----------------------------------------------------------------------
Recalibrate ($F2E6)

      Recalibrate the vector generators.

ENTRY DP = $D0

      D-reg, X-reg trashed
----------------------------------------------------------------------
I                          Reset_Pen                                 I
----------------------------------------------------------------------
Reset_Pen ($F35B)

      Reset the pen to the origin.

ENTRY DP = $D0

      D-reg trashed
----------------------------------------------------------------------
I                          Reset0Int                                 I
----------------------------------------------------------------------
Reset0Int ($F36B)

This routine resets the integrators to zero.  It leaves the
integrators in zero mode, so nothing can be drawn until a move is
done, or D00C is set to 0xCE.

ENTRY DP = $D0

      D-reg trashed
----------------------------------------------------------------------
I                          Reset0Ref                                 I
----------------------------------------------------------------------
Reset0Ref ($F354)

This routine zeros the integrators, and resets the pen back to the
origin.  It leaves the integrators in zero mode, so nothing can be
drawn until a move is done, or $D00C is set to 0xCE to bring /ZERO
high.  This routine must be called every so often, to prevent your
vectors from getting out of whack.

ENTRY DP = $D0

      D-reg trashed
----------------------------------------------------------------------
I                         Reset0Ref_D0                               I
----------------------------------------------------------------------
Reset0Ref_D0 ($F34A)

This routine sets the DP register to D0, and then resets the
integrators.

EXIT: DP = $D0

      D-reg trashed
----------------------------------------------------------------------
I                       Rise_Run_Angle                               I
----------------------------------------------------------------------
Rise_Run_Angle ($F593)

Given a (rise,run) pair, this routine calculates the angle which
corresponds to that (rise,run) pair.  The returned angle is relative
to the x-axis (+ is CCW), so to convert it to a Vectrex angle
(relative to the y-axis, + is CCW), you must subtract the number 0x10
(90 degrees) from the returned value.

ENTRY DP = $C8
      A-reg = rise value
      B-reg = run value

EXIT: A-reg = the angle from the x-axis
      B-reg = the angle from the x-axis

      All other registers preserved.
----------------------------------------------------------------------
I                        Rise_Run_Idx                                I
----------------------------------------------------------------------
Rise_Run_Idx ($F5EF)

This routine gets the index pair for both the rise and run, using
the passed-in angle value.

ENTRY DP = $C8
      $C836 contains the angle value

EXIT: $C837-$C838 contains the index pair for the run
      $C839-$C83A contains the index pair for the rise

      D-reg trashed
----------------------------------------------------------------------
I                        Rise_Run_Len                                I
----------------------------------------------------------------------
Rise_Run_Len ($F603)
->Rise_Run_Y ($F601)
----------------------------------------------------------------------
I                         Rise_Run_X                                 I
----------------------------------------------------------------------
Rise_Run_X ($F5FF)
->Rise_Run_Y ($F601)
----------------------------------------------------------------------
I                         Rise_Run_Y                                 I
----------------------------------------------------------------------
Rise_Run_Y ($F601)

This routine takes an angle value which is relative to the x- or
y-axis, and calculates the rise and run for that angle, relative to a
passed-in scalar velocity value.  A large scalar value will cause an
object to move quickly, while a small scalar value will cause an
object to move more slowly.

Keep in mind that most games store x & y coordinates as 2 bytes each,
with the upper byte being the actual coordinate, and the lower byte
being that which is usually added to the rise/run value; when the
lower byte overflows into the hi byte, then the object will 'move'.
The rise/run values returned here are meant to be added to the low
byte -- NOT the hi byte!!

ENTRY DP = $C8
      A-reg = the scalar velocity value (except Rise_Run_Len)
      B-reg = the Vectrex angle value

EXIT: A-reg = the rise value
      B-reg = the run value

      All other registers are saved.
----------------------------------------------------------------------
I                           Rot_VL                                   I
----------------------------------------------------------------------
Rot_VL ($F616)
->Rot_VL_ab ($F610)
----------------------------------------------------------------------
I                          Rot_VL_ab                                 I
----------------------------------------------------------------------
Rot_VL_ab ($F610)

This routine rotates a vector list of length 'n+1', where 'n' is
specified by the value in the B register.  The A register contains
the rotation value, and the X contains a pointer to the vector list.
The U register contains a pointer to a buffer into which the
transformed points are to be saved.  The vector list has the
following format:

      rel y, rel x, rel y, rel x, ...

ENTRY A-reg = rotation angle value (Rot_VL_ab only)
      $C836 = rotation angle value (Rot_VL only)
      B-reg = number of points - 1 (Rot_VL_ab only)
      $C823 = number of points - 1 (Rot_VL only)
      X-reg points to original vector list
      U-reg points to rotated vector list

EXIT: DP = $C8
      X-reg points to next byte after list
      U-reg points to next byte after rotated list

      D-reg trashed
----------------------------------------------------------------------
I                         Rot_VL_M_dft                               I
----------------------------------------------------------------------
Rot_VL_M_dft ($F62B)
->Rot_VL_Mode ($F61F)
----------------------------------------------------------------------
I                         Rot_VL_Mode                                I
----------------------------------------------------------------------
Rot_VL_Mode ($F61F)

This routine rotates a vector list having the following format:

      mode, rel y, rel x,
      mode, rel y, rel x,
        .     .      .
        .     .      .
      mode, rel y, rel x,
      0x01

The A register contains the rotation value, and the X contains a
pointer to the vector list.  The U register contains a pointer to a
buffer into which the transformed points are to be saved.

ENTRY DP = $C8
      A-reg = rotation angle value (Rot_VL_Mode only)
      X-reg points to original vector list
      U-reg points to rotated vector list

EXIT: X-reg points to next byte after list
      U-reg points to next byte after rotated list

      D-reg trashed
----------------------------------------------------------------------
I                          Select_Game                               I
----------------------------------------------------------------------
Select_Game ($F7A9)

This routine provides a game with the means for allowing the player
to choose the game number he would like to play, and the number of
players.  The game indicates the number of game versions available,
by placing the  value in the B register.  The number of players
allowed is specified in the A register.  If a parameter is passed in
with a value of 0, then the corresponding question will not be asked.
The number of players selected is returned in $C879, while the game
number selected is returned in $C87A.

This routine performs most of the work involved in allowing the
player to select a game version and the number of players.  It
displays the game # and player options, and allows the player a
certain amount of time to modify their values.  Anytime one of the
buttons is used to modify a value, the timer will be restarted.
When a button is pressed, the associated value is modified,
and then redisplayed on the screen.  This routine will return when
either the timer expires, or button 4 is pressed.

ENTRY A-reg = maximum number of players allowed
      B-reg = number of game versions available

EXIT: DP = $C8
      $C879 contains number of players selected
      $C87A contains the game version selected

      D-reg, X-reg, Y-reg trashed
----------------------------------------------------------------------
I                          Set_Refresh                               I
----------------------------------------------------------------------
Set_Refresh ($F1A2)

This routine loads the refresh timer (t2) with the value in $C83D-
$C83E, and recalibrates the vector generators, thus causing the pen
to be left at the origin (0,0).  The high order byte for the timer
is loaded from $C83E, and the low order byte is loaded from $C83D.
The refresh rate is calculated as follows:

  rate = (C83E)(C83D) / 1.5 mhz

ENTRY DP = $D0

      D-reg trashed                                           *
----------------------------------------------------------------------
I                           Sound_Byte                               I
----------------------------------------------------------------------
Sound_Byte ($F256)
->Sound_Byte_raw ($F25B)
----------------------------------------------------------------------
I                          Sound_Byte_x                              I
----------------------------------------------------------------------
Sound_Byte_x ($F259)
->Sound_Byte_raw ($F25B)
----------------------------------------------------------------------
I                         Sound_Byte_raw                             I
----------------------------------------------------------------------
Sound_Byte_raw ($F25B)

All of these routines cause a byte of music data to be written to
the music chip.  Sound_Byte stores a shadow copy of the data into
$C800-$C80E, and Sound_Byte_x stores a shadow copy into a 15 byte
area pointed to by the X register.  Sound_Byte_raw does not store a
shadow copy of the data at all.

ENTRY DP = $D0
      A-reg = which of the 15 sound chip registers to modify
      B-reg = the byte of sound data
      X-reg = 15 byte shadow area (Sound_Byte_x only)

EXIT: X-reg = $C800 (Sound_Byte only)

      D-reg trashed
----------------------------------------------------------------------
I                          Sound_Bytes                               I
----------------------------------------------------------------------
Sound_Bytes ($F27D)
->Sound_Bytes_x ($F25B)
----------------------------------------------------------------------
I                          Sound_Bytes_x                              I
----------------------------------------------------------------------
Sound_Bytes_x ($F284)

This routine copies a block of sound information into the sound
chip buffer (at $C800-$C80E) and into the registers on the music
chip.  The format for the block of sound data is as follows:

  (register number), (music data),
  (register number), (music data),
        .                  .
        .                  .
      0xFF

As long as the register number is >= 0, then the music data will be
copied; however, as soon as a register number < 0 is encountered,
the copy will stop.

ENTRY DP = $D0
      U-reg = pointer to the block of sound data

      D-reg, X-reg, U-reg trashed
----------------------------------------------------------------------
I                          Strip_Zeros                               I
----------------------------------------------------------------------
Strip_Zeros ($F8B7)

This routine strips the leading zeros from a score accumulator.

ENTRY B-reg = first digit to start with (usually zero)
      X-reg points to six byte ASCII score accumulator

      D-reg trashed
----------------------------------------------------------------------
I                           Wait_Recal                               I
----------------------------------------------------------------------
Wait_Recal ($F192)

Wait for t2 (the refresh timer) to timeout, then restart it using
the value in $C83D.  then, recalibrate the vector generators to the
origin (0,0).  This routine MUST be called once every refresh
cycle, or your vectors will get out of whack.  This routine calls
Reset0Ref, so the integrators are left in zero mode.

EXIT: DP = $D0

      D-reg, X-reg trashed
----------------------------------------------------------------------
I                           Warm_Start                               I
----------------------------------------------------------------------
Warm_Start ($F06C)

Jump here to restart the Vectrex without re-initializing the OS.

----------------------------------------------------------------------
I                           Xform_Rise                               I
----------------------------------------------------------------------
Xform_Rise ($F663)
->Xform_Rise_a ($F661)
----------------------------------------------------------------------
I                           Xform_Rise_a                             I
----------------------------------------------------------------------
Xform_Rise_a ($F661)

These two routines generate a rise value, using the rise index pair
in $C839-$C83A.  For Xform_Rise_a the scalar value is passed in the
A register, while for Xform_Rise, the scalar value must already be
in $C83B.  The transformed value is return in the A register.

ENTRY DP = $C8
      A-reg = length for rise/run (Xform_Run_a only)

EXIT: A-reg = rise value

      B-reg trashed
----------------------------------------------------------------------
I                             Xform_Run                              I
----------------------------------------------------------------------
Xform_Run ($F65D)
->Xform_Run_a ($F65B)
----------------------------------------------------------------------
I                             Xform_Run_a                            I
----------------------------------------------------------------------
Xform_Run_a ($F65B)

These two routines generate a run value, using the run index pair in
$C837-$C838.  For Xform_Run_a the scalar value is passed in the
A register, while for Xform_Run, the scalar value must already be in
$C83B.  The transformed value is return in the A register.

ENTRY DP = $C8
      A-reg = length for rise/run (Xform_Rise_a only)

EXIT: A-reg = run value

      B-reg trashed

----------------------------------------------------------------------
I                      Other Rom Addresses                           I
----------------------------------------------------------------------
$F9DC Bit_Masks
$F9E4 Music_Table_1
$F9EA Music_Table_2
$F9F0 Recal_Points
$F9F4 Char_Table
$FC24 angle_data1
$FC2C angle_data2
$FC6D rotation_pair_table
$FC8D Freq_Table
$FD0D power-on music and music for Crazy Coaster and Narrow Escape
$FD1D music for Berzerk
$FD69 ADSR table for Berzerk and FF7A
$FD79 twang table for Berzerk and Scramble
$FD81 music
$FDC3 ADSR table for FD81 and FF8F
$FDD3 music for Scramble
$FE28 ADSR table for Scramble, FF26, FF44, FF62
$FE38 music for Solar Quest
$FE66 ADSR table for Solar Quest
$FE76 music
$FEB2 ADSR table for FE76
$FEB6 "flat" twang table
$FEC6 music
$FEE8 ADSR table for FEC6
$FEF8 music for Melody Master
$FF16 ADSR table for FEF8
$FF26 music
$FF44 music
$FF62 music
$FF7A music
$FF8F music

Appendix B:
BIOS RAM locations
(by Bruce Tomlin, modified by Malban)

$C800: Vec_Snd_Shadow    Shadow of sound chip registers (15 bytes)
$C80F: Vec_Btn_State     Current state of all joystick buttons
$C810: Vec_Prev_Btns     Previous state of all joystick buttons
$C811: Vec_Buttons       Current toggle state of all buttons
$C812: Vec_Button_1_1    Current toggle state of stick 1 button 1
$C813: Vec_Button_1_2    Current toggle state of stick 1 button 2
$C814: Vec_Button_1_3    Current toggle state of stick 1 button 3
$C815: Vec_Button_1_4    Current toggle state of stick 1 button 4
$C816: Vec_Button_2_1    Current toggle state of stick 2 button 1
$C817: Vec_Button_2_2    Current toggle state of stick 2 button 2
$C818: Vec_Button_2_3    Current toggle state of stick 2 button 3
$C819: Vec_Button_2_4    Current toggle state of stick 2 button 4
$C81A: Vec_Joy_Resltn    Joystick A/D resolution ($80=min $00=max)
$C81B: Vec_Joy_1_X       Joystick 1 left/right
$C81C: Vec_Joy_1_Y       Joystick 1 up/down
$C81D: Vec_Joy_2_X       Joystick 2 left/right
$C81E: Vec_Joy_2_Y       Joystick 2 up/down
$C81F: Vec_Joy_Mux       Joystick enable/mux flags (4 bytes)
$C81F: Vec_Joy_Mux_1_X   Joystick 1 X enable/mux flag (=1)
$C820: Vec_Joy_Mux_1_Y   Joystick 1 Y enable/mux flag (=3)
$C821: Vec_Joy_Mux_2_X   Joystick 2 X enable/mux flag (=5)
$C822: Vec_Joy_Mux_2_Y   Joystick 2 Y enable/mux flag (=7)
$C823: Vec_Misc_Count    Misc counter/flag byte, zero when not in use
$C824: Vec_0Ref_Enable   Check0Ref enable flag
$C825: Vec_Loop_Count    Loop counter word (incremented in Wait_Recal)
$C827: Vec_Brightness    Default brightness
$C828: Vec_Dot_Dwell     Dot dwell time?
$C829: Vec_Pattern       Dot pattern (bits)
$C82A: Vec_Text_HW       Default text height and width
$C82A: Vec_Text_Height   Default text height
$C82B: Vec_Text_Width    Default text width
$C82C: Vec_Str_Ptr       Temporary string pointer for Print_Str
$C82E: Vec_Counters      Six bytes of counters
$C82E: Vec_Counter_1     First  counter byte
$C82F: Vec_Counter_2     Second counter byte
$C830: Vec_Counter_3     Third  counter byte
$C831: Vec_Counter_4     Fourth counter byte
$C832: Vec_Counter_5     Fifth  counter byte
$C833: Vec_Counter_6     Sixth  counter byte
$C834: Vec_RiseRun_Tmp   Temp storage word for rise/run
$C836: Vec_Angle         Angle for rise/run and rotation calculations
$C837: Vec_Run_Index     Index pair for run
$C839: *                 Pointer to copyright string during startup
$C839: Vec_Rise_Index    Index pair for rise
$C83B: *                 High score cold-start flag (=0 if valid)
$C83B: Vec_RiseRun_Len   length for rise/run
$C83C: *                 temp byte
$C83D: Vec_Rfrsh         Refresh time (divided by 1.5MHz)
$C83D: Vec_Rfrsh_lo      Refresh time low byte
$C83E: Vec_Rfrsh_hi      Refresh time high byte
$C83F: Vec_Music_Work    Music work buffer (14 bytes, backwards?)
$C842: Vec_Music_Wk_A            register 10
$C843: *                         register 9
$C844: *                         register 8
$C845: Vec_Music_Wk_7            register 7
$C846: Vec_Music_Wk_6            register 6
$C847: Vec_Music_Wk_5            register 5
$C848: *                         register 4
$C849: *                         register 3
$C84A: *                         register 2
$C84B: Vec_Music_Wk_1            register 1
$C84C: *                         register 0
$C84D: Vec_Freq_Table    Pointer to note-to-frequency table (normally $FC8D)
$C84F: Vec_Max_Players   Maximum number of players for Select_Game
$C850: Vec_Max_Games     Maximum number of games for Select_Game
$C84F: Vec_ADSR_Table    Storage for first music header word (ADSR table)
$C851: Vec_Twang_Table   Storage for second music header word ('twang' table)
$C853: Vec_Music_Ptr     Music data pointer
$C853: Vec_Expl_ChanA    Used by Explosion_Snd - bit for first channel used?
$C854: Vec_Expl_Chans    Used by Explosion_Snd - bits for all channels used?
$C855: Vec_Music_Chan    Current sound channel number for Init_Music
$C856: Vec_Music_Flag    Music active flag ($00=off $01=start $80=on)
$C857: Vec_Duration      Duration counter for Init_Music
$C858: Vec_Music_Twang   3 word 'twang' table used by Init_Music
$C858: Vec_Expl_1        4 bytes copied from Explosion_Snd's U-reg parameters
$C859: Vec_Expl_2
$C85A: Vec_Expl_3
$C85B: Vec_Expl_4
$C85C: Vec_Expl_Chan     Used by Explosion_Snd - channel number in use?
$C85D: Vec_Expl_ChanB    Used by Explosion_Snd - bit for second channel used?
$C85E: Vec_ADSR_Timers   ADSR timers for each sound channel (3 bytes)
$C861: Vec_Music_Freq    Storage for base frequency of each channel (3 words)
$C85E: *                 Scratch 'score' storage for Display_Option (7 bytes)
$C867: Vec_Expl_Flag     Explosion_Snd initialization flag?
$C868: Unused?
$C869: Unused?
$C86a: Unused?
$C86b: Unused?
$C86c: Unused?
$C86d: Unused?
$C86e: Unused?
$C86f: Unused?
$C870: Unused?
$C871: Unused?
$C872: Unused?
$C873: Unused?
$C874: Unused?
$C875: Unused?
$C876: Unused?
$C877: Vec_Expl_Timer    Used by Explosion_Snd
$C878: Unused?
$C879: Vec_Num_Players   Number of players selected in Select_Game
$C87A: Vec_Num_Game      Game number selected in Select_Game
$C87B: Vec_Seed_Ptr      Pointer to 3-byte random number seed (=$C87D)
$C87D: Vec_Random_Seed   Default 3-byte random number seed
                                :
*    $C880 - $CBEA is user RAM  :
                                :
$CBEA: Vec_Default_Stk   Default top-of-stack
$CBEB: Vec_High_Score    High score storage (7 bytes)
$CBF2: Vec_SWI3_Vector   SWI2/SWI3 interrupt vector (3 bytes)
$CBF2: Vec_SWI2_Vector   SWI2/SWI3 interrupt vector (3 bytes)
$CBF5: Vec_FIRQ_Vector   FIRQ interrupt vector (3 bytes)
$CBF8: Vec_IRQ_Vector    IRQ interrupt vector (3 bytes)
$CBFB: Vec_SWI_Vector    SWI/NMI interrupt vector (3 bytes)
$CBFB: Vec_NMI_Vector    SWI/NMI interrupt vector (3 bytes)
$CBFE: Vec_Cold_Flag     Cold start flag (warm start if = $7321)


Appendix C:

1.
--

THE 6522 VIA CHIP
Versatile Interface Adapter (VIA)

 It is essential to gain a full understanding of the 6522 chip if it is to
be emulated as in the recent BBC and VIC-20 emulators. At first it all seems
a bit complicated but after a while things will start to make sense and
you'll appreciate how powerful and versatile this chip was in it's day. I
have included all the information I have on the 6522 in this document.

DESCRIPTION

 The R6522 VIA adds two powerful, flexible interval timers, a serial-to
-parallel:parallel-to-serial shift register and input latching on the
peripheral ports. Handshaking capability is expanded to allow control of
bidirectional data transfers between VIAs in multiple processor systems and
between periherals.

 Control of peripherals is primarily through two 8-bit bidirectional ports.
Each of these ports can be programmed to act as an input or and output.
Peripheral I/O lines can be selectively controlled by the Interval Timers
to generate programmable frequency square waves and/or to count externally
generated pulses. Positive control of VIA functions is gained through its
internal register organisation: Interrupt Flag Register, Interrupt Enable
Register, and two Funtion Control Registers.

FEATURES

* Organised for simplified software control of many functions.
* Compatible with the RS650x and RS651x family of microprocesors (CPUs)
* Bi-directional, 8-bit data bus for communication with microprocessor.
* Two Bi-directional, 8-bit input/output ports for interface with
  peripheral devies.
* CMOS and TTL compatible input/output periheral ports.
* Data Direction Registers allow each peripheral pin to act as either
  an input or an output.
* Interrupt Flag Register allows the microprocessor to readily determine
  the source of an interrupt and provides convenient control of the
  interrupts within the chip.
* Handshake control logic for input/output peripheral data transfer
  operations.
* Data latching on peripheral input/output ports.
* Two fully-programmable interval timers/counters.
* Eight-bit Shift Register for serial intergace.
* Forty-pin plastic or ceramic DIP package.

PIN CONFIGURATION
                ___________________
               |                   |
    (0V)  VSS -|1                40|- CA1
               |                   |
          PA0 -|2                39|- CA2
               |                   |
          PA1 -|3                38|- RS0
               |                   |
          PA2 -|4                37|- RS1
               |                   |
          PA3 -|5                36|- RS2
               |                   |
          PA4 -|6                35|- RS3
               |                   |  ___
          PA5 -|7                34|- RES
               |                   |
          PA6 -|8                33|- D0
               |                   |
          PA7 -|9                32|- D1
               |                   |
          PB0 -|10               31|- D2
               |                   |
          PB1 -|11               30|- D3
               |                   |
          PB2 -|12               29|- D4
               |                   |
          PB3 -|13               28|- D5
               |                   |
          PB4 -|14               27|- D6
               |                   |
          PB5 -|15               26|- D7
               |                   |
          PB6 -|16               25|- P2C (Phase Two Clock from CPU)
               |                   |
          PB7 -|17               24|- CS1
               |                   |  ___
          CB1 -|18               23|- CS2
               |                   |
          CB2 -|19               22|- R/W
               |                   |  ___
    (5V)  VCC -|20               21|- IRQ
               |___________________|


Register Select Lines (RS0 - RS3) : select the internal VIA register that
the processor is accessing.

Port Pins (PA0 - PA7, PB0 - PB7) : are the input and output pins which
connect to external peripheral and make up the two bi-directional ports.

Data Bus (D0 - D7) : used to transmit and receive data, from and into the
VIAs internal registers.
                        ___
Chip Select Lines (CS1, CS2) : used by the microprocessor to select the chip.

Read/Write Line (R/W) : selects whether the microprocessor is reading from a
VIA register or writing into a VIA register. LOW = Write, HIGH = Read.

Control Lines (CA1, CA2, CB1, CB2) : are the control lines which are used
for a wide range of control functions, handshaking, and initiating interrupts.


CONTROL LINES

 The control lines are now discussed in more detail. These lines can act as
interrupt inputs or handshake outputs. They can perform a number of
different functions determined by the Peripheral Control Register (PCR).
CA2 and CB2 can act as inputs or outputs in a number or modes discussed
later, whereas CA1 and CB1 can only act as inputs. As an example, the VIC-20
uses the controls lines for events such as the restore key being pushed,
received serial data, and outputs such as turning the cassette motor on.

VIA INTERNAL REGISTERS

 One VIA chip has 16 internal registers used to control its many features.
These registers occupy 16 consecutive addresses in RAM. The following table
gives the offsets of each VIA register. All registers are 8-bit:

Register        Designation     Function
offset no.

  0             ORA or IRB      Output or input register B
  1             ORA or IRA      Output or input register A
  2             DDRB            Data direction register B
  3             DDRA            Data direction register A
  4             T1C-L           T1 low-byte latch or T1 low-byte counter
  5             T1C-H           T1 high-byte counter
  6             T1L-L           T1 low-byte latch
  7             T1L-H           T1 high-byte latch
  8             T2C-L           T2 low-byte latch or T2 low-byte counter
  9             T2C-H           T2 high-byte counter
 10             SR              Shift Register
 11             ACR             Auxiliary Control Register
 12             PCR             Peripheral Control Register
 13             IFR             Interrupt Flag Register
 14             IER             Interrupt Enable Register
 15             ORA or IRA      Identical to offset 1 but no handshake

 We will now go through each of these registers in turn.

ORB/IRB : OUTPUT REGISTER B/INPUT REGISTER B

 This is the first of the peripheral ports called port B. Most communication
to external peripherals is done through one of the two 8-bit ports. Each of
the 8 pins making up the ORB/IRA port can be set to be an input or an output.
DDRB, discussed later, defines the directivity of ORB/IRB. If a data line is
programmed as an output in the DDRB, then the corresponding bit in the ORB
IRB register decides the logic on the data line. On the otherhand, if a line
is programmed as an input, the logic state received from a device on that
line is entred into the IRB. If a line is programmed as an input, then any
attempt to write into that line via ORB is ignored.

ORA/IRA : OUTPUT REGISTER A/INPUT REGISTER A

 Smilar to ORB/IRB above.

DDRB/DDRA : DATA DIRECTION REGISTERS

 These registers determine the direction of each port pin. The eight bits
in a data direction register correspond to the eight bits in the port
registers and are interpreted as follows:

 A '1' defines the corresponding pin as an ouput.
 A '0' defines the corresponding pin as an input.


THE TIMERS AND COUNTERS

 It is always possible to generate delays (time intervals) by loading one or
more of the 6502 instruction registers with the desired delay number and
counting down to zero. This is not always satisfactory because it ties up
the computer. To provide for independent delays and various other pulse-
counting operations, the VIA is equipped with a variety of timers, counters
and latches. They are useful for generating interrupts at regular intervals,
triggering external devices or simulating a real-time environment. The two
timers, T1 and T2, are essentially 16-bit counters. Each counter occupies two
consecutive addresses (low and hign byte) but T1, the more complex of the two,
has an associated 16-bit latch, consequently occupying a further two
addresses, Before treating the tiners in detail, it is useful to begin with an
overview of the possibilities on offer.

(1) They may be read or written into as six memory locations, four for T1 and
two for T2. (See the VIA addresses given earlier in this chapter).
(2) Their respective operation modes are governed by bits 5, 6 and 7 of the
Auxiliary Control Register.
(3) Their status, at any time during the counting phase, is obtainable by
examining bits 5 and 6 in the IFR. By 'status' we mean whether or not
programmed interval has ended (time out).
(4) To generate a single time interval, a timer is loaded with the number of
clock pulses required (to generate that interval).
(5) Pulses arriving on PB6 can be counted until they compare with a
previously loaded number (T2 only). The normal use of PB6 as one of the
eight data lines is, of course, suspended.
(6) T1 can be used to provide continuous time intervals. The time interval
between pulses will depend on a previously loaded number.
(7) A single, or continuous series of pulses can be produced on PB7; the
pulse width will be dependent on a previously loaded number. In this mode,
PB7 will not be available as a normal data line.


THE T2 TIMER

 It is clear from the above overview that T2 is more simple than T1. It can
only generate simple time intervals or count pulses arriving on PB6. Bit 5
of the ACR determines whether the counter is decremented by the 6502 system
clock or input pulses arriving on PB6.

          Counter High : T2C-H            Counter Low : T2C-L
     ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___
    |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
    | 15| 14| 13| 12| 11| 10| 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
    |___|___|___|___|___|___|___|___|___|___|___|___|___|___|___|___|


                                             Latch Low : T2L-L
                                     ___ ___ ___ ___ ___ ___ ___ ___
                                    |   |   |   |   |   |   |   |   |
                                    | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
                                    |___|___|___|___|___|___|___|___|


 The low-byte address of T2 is used to write or read the low-order byte of
the delay number. The T2 interrupt flag is automatically cleared on a read
action. The high-byte address is used to write or read the high-byte of the
number. Writing to this address completes the timer loading, clears the T2
interrupt flag and start the timing operation. On completion of the timing
interval, the T2 interrupt flag is set (bit 5 of IFR).

   Offset 8: Write T2L-L.
             Read T2C-L.
               Clear Interrupt Flag.

   Offset 9: Write T2C-H.
               Transfer T2L-L to T2C-L.
               Clear Interrupt Flag.
               Start the timer.
             Read T2C-H.


THE T1 TIMER

 This timer has a 16-bit latch as well as the normal 16-bit counter. It is
also possible to generate an output on PB7. There are four different operating
modes, depending on bits 6 and 7 in the ACR. The choice is single-shot or
free-running mode (bit 6) and enable or disable PB7 (bit 7).
Bit 7=0 will disable PB7 output. Bit 7=1 will enable PB7 output.
Bit 6=0 is one shot mode. Bit 6=1 is free-running mode.
 The addressing details and the start and finish of timing are virtually the
same as described under T2 apart form the different addresses and bit 6,
instead of bit 5, for the interrupt flag in the IFR. The free-running mode is
made possible by the provision of a separate 16-bit latch in the usual
low-byte (T1L), high-byte (T1H) form. These occupy two separate addresses. It
is possible, therefore, to read or write into the latches without affecting
the associated timer count. In the free-running mode, the number in the
latches is automatically re-entered into the timer again and the count
restarted. This makes it possible to generate a wave form of any mark to
space ratio on PB7. This is because the logic level (HIGH or LOW) on PB7
remains fixed within a timing interval but inverts to the opposite state
during the next interval and so on.

   Offset 4: Write T1L-L;
             Read T1C-L.
               Clear interrupt flag.
   Offset 5: Write T1L-H and T1C-H.
               Transfer T1L-L to T1C-L.
               Clear interrupt flag.
               Start thetimer.
             Read T1C-H.
   Offset 6: Write T1L-L.
             Read T1L-L.
   Offset 7: Write T1L-H.
               Clear interrupt flag.
             Read T1L-H.


SR : SHIFT REGISTER

 This register is more suited for serial data transmission. Bits 2, 3, and 4
in the ACR determine the behaviour of the SR as follows:

          ACR Bits          Effect on Register

            000             Disable SR
            001             Shift in at Counter 2 rate
            010             Shift in at system clock rate
            011             Shift in at external clock rate
            100             Free running output at Counter 2 rate
            101             Shift out at Counter 2 rate
            110             Shift out at system clock rate
            111             Shift out at external clock rate


ACR : AUXILIARY CONTROL REGISTER

                                          ___ ___ ___ ___ ___ ___ ___ ___
                                         |   |   |   |   |   |   |   |   |
                                         | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
 T1 control                              |___|___|___|___|___|___|___|___|
 0 = Disable PB7 output ___________________|   |   |   |_______|   |   |
 1 = Enable PB7 output                         |   |       |       |   |
                                               |   |       |       |   |
 T1 control                                    |   |       |       |   |
 0 = one-shot mode ____________________________|   |       |       |   |
 1 = free-running mode                             |       |       |   |
                                                   |       |       |   |
 T2 control                                        |       |       |   |
 0 = decrement by 6502 system clock   _____________|       |       |   |
 1 = decrement by input pulses on PB6.                     |       |   |
                                                           |       |   |
 Used for controlling Shift-register ______________________|       |   |
 (treated separately)                                              |   |
                                                                   |   |
 Port B input latch  ______________________________________________|   |
 0 = disable latch, 1 = enable latch                                   |
                                                                       |
 Port A input latch ___________________________________________________|_
 0 = disable latch, 1 = enable latch

 The only feature of the ACR that hasn't been mentioned yet is the latching
mentioned above for bit 0 and bit 1. There are two variations of input
behaviour for Port A and Port B depending on whether latching is enabled or
disabled. If latching is disabled, the level present at an input (i.e. the
relevant PB or PA pin) is read into IRB or IRA respecitively. If the latch
is enabled, the level read into IRB is that which existed after the 'last
active transition' arriving  on CB1 (when a pulse of the correct phasing and
shape hit CB1 input). In other words, if the conditions existing now are
required, then latch must be in the disabled condition. We only enable
latching if we require CB1 to act as a data-valid signal and we wish to
ignore levels arriving after the latching.


PCR : PERIPHERAL CONTROL REGISTER

 The function of the various bits in the PCR depends on whether CB2, and
CA2 are inputs or outputs. This is the reason we have two diagrams below.
Note: CB1 and CA1 are always inputs.

 When CB2 or CA2 is an input (bit 7=0 or bit 3=0)
                                         ___ ___ ___ ___ ___ ___ ___ ___
                                        |   |   |   |   |   |   |   |   |
                                        | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
                                        |___|___|___|___|___|___|___|___|
 Determines CB2 direction ________________|   |   |   |   |   |   |   |
 0 = input, 1 = output                        |   |   |   |___|___|___|
                                              |   |   |         |
 Active edge of CB2 when it is an input ______|   |   |         |
 0 = active low, 1 = active high                  |   |         |
                                                  |   |         |
 When CB2 is an input, decides if it is           |   |         |
 normal or independent       _____________________|   |         |
 0 = normal, 1 = independent                          |         |
                                                      |         |
 Active edge of CB1              _____________________|         |
 0 = active low, 1 = active high                                |
                                                                |
 The same as bits 4-7 but for CA2 ______________________________|
 instead of CB2


 When CB2 or CA2 is an input (bit 7=1 or bit 3=1)
                                         ___ ___ ___ ___ ___ ___ ___ ___
                                        |   |   |   |   |   |   |   |   |
                                        | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
                                        |___|___|___|___|___|___|___|___|
 Determines CB2 direction ________________|   |   |   |   |   |   |   |
 0 = input, 1 = output                        |___|   |   |___|___|___|
                                                |     |         |
 Output Mode  __________________________________|     |         |
 Bit 6  Bit 5                                         |         |
                                                      |         |
   0     0     Handshake mode                         |         |
   0     1     Pulse output mode                      |         |
   1     0     Manual mode : CB2 = LOW                |         |
   1     1     Manual mode : CB2 = HIGH               |         |
                                                      |         |
 Active edge of CB1              _____________________|         |
 0 = active low, 1 = active high                                |
                                                                |
 The same as bits 4-7 but for CA2 ______________________________|
 instead of CB2

PCR IN MORE DETAIL

 We will now define a few of the terms used above. When mention is made of
the 'active edge', it refers to the setting of the appropriate flag in
another register (IFR). That is to say, the only indication that an
acceptable pulse has appeared on CB1, CB2, CA1 or CA2 input is the setting
of the appropriate flag.
 The terms 'normal' and 'independent' apply only to CB2 and CA2 and, even
then, only when they are inputs. These terms concern the conditions under
which the CB2/CA2 flag is reset after it has been set. In normal mode, the
flag remains set until a READ or WRITE instruction on the relevant data
input/output port. For example, for CB2 it would be ORB/IRB. This is most
likely down with an LDA or STA. In the independent mode, once a flag is set,
READ or WRITE does not reset it.

 We will now discuss the behaviour of CB2 and CA2 when they are outputs. For
simplicity I will refer to CB2 only since CA2 operates identically. Clearly
the significance of bits 6 and 5 is completely different as can be seen in
the diagrams above.

 Handshake mode: CB2 goes LOW by a write operation on ORB and goes HIGH
 again on an active transition of the CB1 input signal.

 Pulse output mode: A negative-going pulse (goes from HIGH down to LOW then
 back again) is emitted following a write operation on ORB. Ideal for
 gadgetry which is activated by a negative-going pulse.

 Manual mode: So called because both levels output on CB2 must be directly
 programmed. The level on CB2 depends on bit 5. If bit 5 is 0, CB2 remains
 LOW. If bit 5 is 1, then CB2 is HIGH. In other words, providing bit 6
 remains at 1, CB2 mirrors the state of bit 5.


IFR : INTERRUPT FLAG REGISTER

 When a signal arrives on CB1, CB2, CA1, or CA2 (when CB2 and CA2 are inputs)
it sets the appropriate flag to 1 in the IFR. The significance of each bit in
the IFR is as follows:
                                         ___ ___ ___ ___ ___ ___ ___ ___
                                        |   |   |   |   |   |   |   |   |
                                        | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
                                        |___|___|___|___|___|___|___|___|
                                          |   |   |   |   |   |   |   |
 General interrupt status bit             |   |   |   |   |   |   |   |
 1 if any interrupt active and enabled ___|   |   |   |   |   |   |   |
 0 when interrupt condition cleared           |   |   |   |   |   |   |
                                              |   |   |   |   |   |   |
 Timer 1 flag                                 |   |   |   |   |   |   |
 1 when time out                        ______|   |   |   |   |   |   |
 0 after reading T1 low-byte counter or           |   |   |   |   |   |
 writing T1 high-byte latch                       |   |   |   |   |   |
                                                  |   |   |   |   |   |
 Timer 2 flag                _____________________|   |   |   |   |   |
 Behaves similar to T1 above                          |   |   |   |   |
                                                      |   |   |   |   |
 CB1 flag                          ___________________|   |   |   |   |
 Cleared by a read or write of ORB                        |   |   |   |
                                                          |   |   |   |
 CB2 flag                          _______________________|   |   |   |
 Cleared by a read or write of ORB                            |   |   |
                                                              |   |   |
 Shift register flag                                          |   |   |
 1 at end of 8 shifts           ______________________________|   |   |
 Cleared by read or write of SR                                   |   |
                                                                  |   |
 CA1 flag.                         _______________________________|   |
 Cleared by a read or write of ORA                                    |
                                                                      |
 CA2 flag.                         ___________________________________|
 Cleared by a read or write of ORA

 It is possible, and sometimes desirable, to clear directly one or more of
the flags in the IFR. This is done (rather strangely) by writing '1's into
the flag positions to be cleared. Direct clearing in this manner will
normally be required when the control line inputs are being used for purposes
other than 6502 involvement - inparticular, when using the manual mode (refer
back to PCR).
 Bit 7 is the general interrupt status and is the only flag which cannot be
reset (or set) directly.


IER : INTERRUPT ENABLE REGISTER

 The bits in this register correspond exactly as describe above for the IFR
register. It represents a last ditch stand between the various interrupt
request sources and the 6502 IRQ input pin. For example, there may have
been and active signal arriving on, say, CB1. This will have set the CB1 flag
in the IFR. However, there may already be another flag or flags set. The
6502 can only accept one interrupt at a time so there is clearly a need for
higher status register which can be programmed to select which flag is to
be recognised (enabled). This is the role of the IER. It operates as
follows:

Bit 7:
Like bit 7 in the IFR, this bit is special.
When bit 7 = 0: Each 1 in a bit poisition is cleared (disabled).
When bit 7 = 1: Each 1 in a bit position enables that bit.
(Zeros in bit positions are left unchanged)

 This isn't so easy to understand. Here are some examples which may help:

 (1) Enable CB1 interrupt and disable all others:

     LDA #&6F    \0110 1111
     STA IER     \Bit 7 = 0 so 1's disabled
     LDA #&90    \1001 0000
     STA IER     \Bit 7 = 1 so bit-4 enabled

 Note that the second pattern is the logical complement of the first. This
 is not a coincidence.

 (2) Enable Timer 1, disable the rest and then clear the T1 flag bit in the
     IFR.

     LDA #&3F    \Disable all others
     STA IER
     LDA #&C0    \1100 0000 to enable T1
     STA IER
     STA IFR     \Reset T1 flag in IFR


ORA\IRA : NO HANDSHAKE

 This means that writing or reading to this address has no affect on the
control lines as it would do for the normal ORA\IRA. Apart from that the
two addresses behave identically.

##############################################################################

2.
--
This   document  covers  all  programming  information  about  Rockwell  R6522
Versatile  Interface  Adapter  (VIA)  chip  used  in  many  Commodore devices.
Figures  and  tables  in  this  file  were  drawn  with  +,  -,  | characters.
For clear description, some of the figures are bit wider than 80 characters.

This file was created from scannings by Frank Kontros <jeno@kontr.uzhgorod.ua>
Many thanks for corrections to Wolfgang Lorenz <pc64@compuserve.com>

If  you  have  any  questions, comments or suggestions concerning this file or
6522 chip, please contact me (Frank).


------------------------------------------------------------------------------
                                      R6522
                               VERSATILE INTERFACE
                                     ADAPTER
------------------------------------------------------------------------------


DESCRIPTION

The  R6522  Versatile  Interface  Adapter (VIA) is a very flexible I/O control
device.  In  addition,  this  device  contains  a pair of very powerful 16-bit
interval  timers,  a  serial-to-parallel/parallel-to-serial shift register and
input  data  latching on the peripheral ports. Expanded handshaking capability
allows  control  of  bidirectional  data  transfers  between VIA's in multiple
processor systems.

Control   of  peripheral  devices  is  handled  primarily  through  two  8-bit
bidirectional  ports.  Each  line  can  be programmed as either an input or an
output.  Several  peripheral  I/O  lines  can  be controlled directly from the
interval  timers  for  generating  programmable  frequency square waves or for
counting  externally  generated  pulses.  To  facilitate  control  of the many
powerful  features  of  this  chip,  an  interrupt flag register, an interrupt
enable register and a pair of function control registers are provided.


FEATURES

o Two 8-bit bidirectional I/O ports
o Two 16-bit programmable timer/counters
o Serial data port
o TTL compatible
o CMOS compatible peripheral control lines
o Expanded "handshake" capability allows positive control
  data transfers between processor and peripheral devices
o Latched output and input registers
o 1 MHz and 2 Mhz operation
o Single +5V power supply


  ORDERING INFORMATION                                +---------------+
                                                 Vss =|  1         40 |= CA1
                                                 PA0 =|  2         39 |= CA2
   Part Number:                                  PA1 =|  3         38 |= RS0
   R6522 _ _ _                                   PA2 =|  4         37 |= RS1
         | | |                                   PA3 =|  5         36 |= RS2
         | | |                                   PA4 =|  6         35 |= RS3
         | | |                                   PA5 =|  7         34 |= RES
         | | |                                   PA6 =|  8         33 |= D0
         | | |                                   PA7 =|  9         32 |= D1
         | | +------ Temperature Range           PB0 =| 10         31 |= D2
         | |         Blank = 40C to +70C       PB1 =| 11         30 |= D3
         | |             E = 40C to +85C       PB2 =| 12         29 |= D4
         | |                                     PB3 =| 13         28 |= D5
         | |                                     PB4 =| 14         27 |= D6
         | +-------- Package                     PB5 =| 15         26 |= D7
         |               C = Ceramic             PB6 =| 16         25 |= 02
         |               P = Plastic             PB7 =| 17         24 |= CS1
         |                                       CB1 =| 18         23 |= CS2
         |                                       CB2 =| 19         22 |= R/W
         +---------- Frequency                   Vcc =| 20         21 |= IRQ
                     No Letter = 1 MHz                +---------------+
                             A = 2 MHz             R6522 Pin Configuration


INTERFACE SIGNALS

                               +---------------+
          /                    |               |                     \
         |           /-------\ |               | /-------\            |
   M B   |    D0-D7 <    8    >|               |<   (8)   > PA0-PA7   |
   I U   |           \-------/ |               | \-------/            |
   C S   |                     |               |                      |   P I
   R     |       02 ---------->|               |<---------- CA1       |   E N
 R O I   |        _            |               |                      |   R T
 6 P N   |      R/W ---------->|     R6522     |<---------> CA2       |   I E
 5 R T  <       ___       2    |      VIA      |                       >  P R
 0 O E   |  CS1,CS2 -----/---->|               |<---------> CB1       |   H F
 0 C R   |                4    |               |                      |   E A
   E F   |  RS0-RS3 -----/---->|               |<---------> CB2       |   R C
   S A   |      ___            |               |                      |   A E
   S C   |      RES ---------->|               | /-------\            |   L
   O E   |      ___            |               |<   (8)   > PB0-PB7   |
   R     |      IRQ ---------->|               | \-------/            |
          \                    |               |                     /
                               +---------------+

                     Figure 1. R6522 VIA interface Signals

       ___
RESET (RES)
                ___
A  low  reset  (RES)  input  clears  all  R6522  internal registers to logic 0
(except  T1  and  T2  latches  and  counters  and  the  Shift  Register). This
places  all  peripheral  interface  lines  in  the  input  state, disables the
timers, shift register, etc. and disables interrupting from the chip.

INPUT CLOCK (PHASE 2)

The  input  clock  is  the  system  02  clock  and triggers all data transfers
between processor bus and the R6522.
              _
READ/WRITE (R/W)

The direction of the data transfers between the R6522 and the system processor
is controlled by the R/W line in conjunction with the CS1 and CS2 inputs. When
R/W is low (write operation),  and the R6522 is selected,  data is transferred
from  the  processor  bus  into the selected R6522 register.  When R/W is high
(read  operation),  and  the  R6522 is selected,  data is transferred from the
selected R6522 register to the processor bus.

DATA BUS (D0-D7)

The  eight  bidirectional  data  bus lines transfer data between the R6522 and
the  system  processor  bus.  During read cycles, the contents of the selected
R6522  register  are  placed on the data bus lines. During write cycles, these
lines  are  high-impedance  inputs  and data is transferred from the processor
bus  into  the  selected  register.  When  the R6522 is not selected, the data
bus lines are high impedance.
                   ___
CHIP SELECTS (CS1, CS2)

The  two  chip select inputs are normally connected to processor address lines
either  directly or through decoding.  The selected R6522 register is accessed
when CS1 is high and CS2 is low.

REGISTER SELECTS (RS0-RS3)

The  coding  of  the four Register Select inputs select one of the 16 internal
registers of the R6522, as shown in Table 1.


                       Table 1. R6522 Register Addressing

+----+---------------+---------+---------------------------------------------+
|Reg.|   RS Coding   |Register |            Register/Description             |
| #  +---+---+---+---+ Desig.  +----------------------+----------------------+
|    |RS3|RS2|RS1|RS0|         | Write (R/W = L)      | Read (R/W = H)       |
+----+---+---+---+---+---------+----------------------+----------------------+
|  0 | 0 | 0 | 0 | 0 | ORB/IRB | Output Register B    | Input Register B     |
+----+---+---+---+---+---------+----------------------+----------------------+
|  1 | 0 | 0 | 0 | 1 | ORA/IRA | Output Register A    | Input Register A     |
+----+---+---+---+---+---------+----------------------+----------------------+
|  2 | 0 | 0 | 1 | 0 |  DDRB   | Data Direction Register B                   |
+----+---+---+---+---+---------+---------------------------------------------+
|  3 | 0 | 0 | 1 | 1 |  DDRA   | Data Direction Register A                   |
+----+---+---+---+---+---------+----------------------+----------------------+
|  4 | 0 | 1 | 0 | 0 |  T1C-L  | T1 Low-Order Latches | T1 Low-Order Counter |
+----+---+---+---+---+---------+----------------------+----------------------+
|  5 | 0 | 1 | 0 | 1 |  T1C-H  | T1 High-Order Counter| T1 High-Order Counter|
+----+---+---+---+---+---------+---------------------------------------------+
|  6 | 0 | 1 | 1 | 0 |  T1L-L  | T1 Low-Order Latches                        |
+----+---+---+---+---+---------+---------------------------------------------+
|  7 | 0 | 1 | 1 | 1 |  T1L-H  | T1 High-Order Latches                       |
+----+---+---+---+---+---------+----------------------+----------------------+
|  8 | 1 | 0 | 0 | 0 |  T2C-L  | T2 Low-Order Latches | T2 Low-Order Counter |
+----+---+---+---+---+---------+----------------------+----------------------+
|  9 | 1 | 0 | 0 | 1 |  T2C-H  | T2 High-Order Counter                       |
+----+---+---+---+---+---------+---------------------------------------------+
| 10 | 1 | 0 | 1 | 0 |  SR     | Shift Register                              |
+----+---+---+---+---+---------+---------------------------------------------+
| 11 | 1 | 0 | 1 | 1 |  ACR    | Auxiliary Control Register                  |
+----+---+---+---+---+---------+---------------------------------------------+
| 12 | 1 | 1 | 0 | 0 |  PCR    | Peripheral Control Register                 |
+----+---+---+---+---+---------+---------------------------------------------+
| 13 | 1 | 1 | 0 | 1 |  IFR    | Interrupt Flag Register                     |
+----+---+---+---+---+---------+---------------------------------------------+
| 14 | 1 | 1 | 1 | 0 |  IER    | Interrupt Enable Register                   |
+----+---+---+---+---+---------+----------------------+----------------------+
| 15 | 1 | 1 | 1 | 1 | ORA/IRA | Output Register A*   | Input Register A*    |
+----+---+---+---+---+---------+----------------------+----------------------+
| NOTE: * Same as Register 1 except no handshake.                            |
+----------------------------------------------------------------------------+

                   ___
INTERRUPT REQUEST (IRQ)

The  Interrupt  Request output goes low whenever an internal interrupt flag is
set  and  the corresponding interrupt enable bit is a logic 1.  This output is
open-drain  to  allow the interrupt request signal to be wire-OR'ed with other
equivalent signals in the system.

PERIPHERAL PORT A (PA0-PA7)

Port  A  consists  of  eight lines which can be individually programmed to act
as  inputs  or  outputs  under  control  of  a  Data Direction Register A. The
polarity  of  output  pins  is controlled by an Output Register and input data
may  be  latched  into an internal register under control of the CA1 line. All
of  these  modes  of  operation are controlled by the system processor through
the  internal  control  registers.  These  lines  represents  one standard TTL
load  in  the  input  mode  and will drive one standard TTL load in the output
mode. Figure 2 illustrates the output circuit.


                                            +5V
                                             o
                                             |
                                              >
                                             <
                                              >
                                             <
                                             |
                                             *-----*---> PA0-PA7,
                                             |     |     CA2
                             +-----+      +--+     |
             I/O CONTROL ----+     |     ||        |
                             | NOR |o----+|        |
             OUTPUT DATA ----+     |     ||        |
                             +-----+      +--+     |
                                           __|__   |
                                            ---    |
                                             ~     |
              INPUT DATA <-------------------------+

                       Figure 2. Port A Output Circuit


PORT A CONTROL LINES (CA1,CA2)

The  two  Port  A  control  lines  act  as  interrupt  inputs  or as handshake
outputs.  Each  line  controls an internal interrupt flag with a corresponding
interrupt  enable  bit.  In  addition,  CA1  controls  the latching of data on
Port  A  input  lines. CA1 is a high-impedance input only while CA2 represents
one  standard  TTL  load  in  the  input mode. CA2 will drive one standard TTL
load in the output mode.

PORT B (PB0-PB7)

Peripheral   Port   B   consists  of  eight  bi-directional  lines  which  are
controlled  by  an  output  register and a data direction register in much the
same  manner  as  the  Port  A.  In  addition,  the polarity of the PB7 output
signal  can  be  controlled  by  one  of  the interval timers while the second
timer  can  be  programmed  to  count  pulses  on  the  PB6  pin. Port B lines
represent  one  standard  TTL  load  in  the  input  mode  and  will drive one
standard  TTL  load  in  the  output  mode.  In  addition, they are capable of
sourcing  1.0  mA  at  1.5  Vdc  in  the  output  mode to allow the outputs to
directly  drive  Darlington  transistor  circuits.  Figure  3  is  the circuit
schematic.

                                                +5V
                                                 o
                                                 |
                                                 *------+
                                                 |      |
          INPUT                  +-----+      +--+       >
         OUTPUT -------------*---+     |     ||         <
        CONTROL        |\    |   | NOR |o----+|          >
                   +--o| >---+---+     |     ||         <
                   |   |/    |   +-----+      +--+      |
                   |         |                   |      |
                   |         |                   *------*---> PB0-PB7,
                   |         |                   |      |     CB1,CB2
                   |         |   +-----+      +--+      |
                   |         +---+     |     ||         |
         OUTPUT    |             | NOR |o----+|         |
           DATA ---*-------------+     |     ||         |
                                 +-----+      +--+      |
                                               __|__    |
                                                ---     |
                                                 ~      |
     INPUT DATA <---------------------------------------+

                       Figure 3. Port B Output Circuit


PORT B CONTROL LINES (CB1,CB2)

The  Port  B  control  lines  act as interrupt inputs or as handshake outputs.
As   with   CA1   and  CA2,  each  line  controls  an  interrupt  flag  with a
corresponding  interrupt  enable  bit.  In  addition,  these  lines  act  as a
serial  port  under  control  of the Shift Register. These lines represent one
standard  TTL  load  in the input mode and will drive one standard TTL load in
the  output  mode.  CB2  can  also  drive  a  Darlington  transistor  circuit;
however, CB1 cannot.


FUNCTIONAL DESCRIPTION

The internal organization of the R6522 VIA is illustrated in Figure 4.


                         INTERRUPT                                         ___
                          CONTROL     +----------------------------------> IRQ
                       +------------+ |      +-----------+    +-------+
                       |   FLAGS    | |      |INPUT LATCH|    |       |
                       |   (IFR)    | |      |   (IRA)   |    |       |     P
                   +--\+------------+ |      +-----------+    |       |     O
                   | +/|   ENABLE   +-+  +--\|  OUTPUT   |/--\|BUFFERS|/--\ R
         +-------+ | | |   (IER)    |    | +/|   (ORA)   |\--/| (PA)  |\--/ T
         |       | | | +------------+    | | +-----------+    |       |
DATA /--\|  DATA +-+ +-------------------+ | | DATA DIR  |    |       |     A
BUS  \--/|  BUS  +-+ +-------------------+ | |  (DDRA)   |    |       |
         |BUFFERS| | | +------------+    | | +-----------+    +-------+
         |       | | | | PERIPHERAL |    | |PORT A REGISTER
         +-------+ | | |   (PCR)    |    | |
                   | +\+------------+    | | +-----------+<--------------- CA1
                   | +/| AUXILIARY  |    | +\|  PORT A   |<--------------> CA2
                   | | |   (ACR)    |    | +/+-----------+
                   | | +------------+    | | |  PORT B   |<--------+
                   | |    FUNCTION       | | +-----------+<------+ |
                   | |    CONTROL        | |   HANDSHAKE         | |
                   | | +-------+-------+ | |    CONTROL          | |
___      +-------+ | | | LATCH | LATCH | | | +-----------+       | |
RES ---->|       | | | |(T1L-H)|(T1L-L)| | +\| SHIFT REG |<------+-*-----> CB1
  _      |       | | +\+-------+-------+ | +/|    (SR)   |<------*-------> CB2
R/W ---->|       | | +/|COUNTER|COUNTER| | | +-----------+
02  ---->|       | | | |(T1C-H)|(T1C-L)| | |
___      |  CHIP | | | +-------+-------+ | |PORT B REGISTER
CS2 ---->| ACCESS| | |      TIMER 1      | | +-----------+    +-------+
CS1 ---->|CONTROL| | |         +-------+ | | |INPUT LATCH|    |       |
         |       | | |         | LATCH | | | |   (IRB)   |    |       |     P
RS0 ---->|       | | |         |(T2L-L)| | | +-----------+    |       |     O
RS1 ---->|       | | | +-------+-------+ | +\|  OUTPUT   |/--\|BUFFERS|/--\ R
RS2 ---->|       | | +\|COUNTER|COUNTER| +--/|   (ORB)   |\--/| (PB)  |\--/ T
RS3 ---->|       | +--/|(T2C-H)|(T2C-L)|     +-----------+    |       |
         +-------+     +-------+-------+     | DATA DIR  |    |       |     B
                            TIMER 2          |  (DDRB)   |    |       |
                                             +-----------+    +-------+

                      Figure 4. R6522 VIA Block Diagram


PORT A AND PORT B OPERATION

The  R6522  VIA  has  two  8-bit  bidirectional  I/O ports (Port A and Port B)
and each port has two associated control lines.

Each  8-bit  peripheral  port  has  a Data Direction Register (DDRA, DDRB) for
specifying  whether  the  peripheral pins are to act as inputs or outputs. A 0
in  a  bit  of the Data Direction Register causes the corresponding peripheral
pin to act as an input. A 1 causes the pin to act as an output.

Each  peripheral  pin  is  also  controlled  by  a  bit in the Output Register
(ORA,  ORB)  and  the Input Register (IRA, IRB). When the pin is programmed as
an  output,  the  voltage on the pin is controlled by the corresponding bit of
the  Output  Register.  A  1  in  the  Output Register causes the output to go
high,  and  a  0  causes the output to go low. Data may be written into Output
Register  bits  corresponding  to pins which are programmed as inputs. In this
case, however, the output signal is unaffected.

Reading  a  peripheral  port  causes  the contents of the Input Register (IRA,
IRB)  to  be  transferred onto the Data Bus. With input latching disabled, IRA
will  always  reflect  the levels on the PA pins. With input latching enabled,
IRA  will  reflect  the  levels  on  the PA pins at time the latching occurred
(via CA1).

The  IRB  register  operates  similar  to  the IRA register. However, for pins
programmed  as  outputs  there is a difference. When reading IRA, the level on
the  pin  determines  whether a 0 or a 1 is sensed. When reading IRB, however,
the  bit  stored  in  the  output  register, ORB, is the bit sensed. Thus, for
outputs  which  have  large  loading effects and which pull an output "1" down
or  which  pull  an  output  "0"  up,  reading IRA may result in reading a "0"
when  a  "1"  was  actually  programmed,  and  reading  a  "1"  when a "0" was
programmed.  Reading  IRB,  on  the other hand, will read the "1" or "0" level
actually programmed, no matter what the loading on the pin.

Figures  5  through  8  illustrate  the  formats  of  the  port  registers. In
addition,  the  input  latching  modes  are  selected by the Auxiliary Control
Register (Figure 12).


              REG 0 -- ORB/IRB
     +---+---+---+---+---+---+---+---+
     | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+       -+
       |   |   |   |   |   |   |   +----- PB0 |
       |   |   |   |   |   |   |              |
       |   |   |   |   |   |   +--------- PB1 |
       |   |   |   |   |   |                  |
       |   |   |   |   |   +------------- PB2 |  OUTPUT REGISTER
       |   |   |   |   |                      |  "B" (ORB)
       |   |   |   |   +----------------- PB3 |
       |   |   |   |                          +-       OR
       |   |   |   +--------------------- PB4 |
       |   |   |                              |  INPUT REGISTER
       |   |   +------------------------- PB5 |  "B" (IRB)
       |   |                                  |
       |   +----------------------------- PB6 |
       |                                      |
       +--------------------------------- PB7 |
                                             -+

+-----------------------+-----------------------+---------------------------+
|       PIN             |                       |                           |
|  DATA DIRECTION       |        WRITE          |           READ            |
|    SELECTION          |                       |                           |
+-----------------------+-----------------------+---------------------------+
|DDRB = 1  OUTPUT       |MPU WRITES OUTPUT LEVEL|MPU READS OUTPUT REGISTER  |
|                       |ORB                    |BIT, ORB PIN LEVEL HAS NO  |
|                       |                       |AFFECT                     |
+-----------------------+-----------------------+---------------------------+
|DDRB = 0  INPUT        |MPU WRITES INTO ORB BUT|MPU READS INPUT LEVEL ON PB|
|INPUT LATCHING DISABLED|NO AFFECT ON PIN LEVEL |PIN                        |
|                       |UNTIL DDRB CHANGED     |                           |
+-----------------------+                       +---------------------------+
|DDRB = 0  INPUT        |                       |MPU READS IRB BIT WHICH IS |
|INPUT LATCHING ENABLED |                       |THE LEVEL OF THE PB PIN AT |
|                       |                       |THE TIME OF THE LAST CB1   |
|                       |                       |ACTIVE TRANSITION          |
+-----------------------+-----------------------+---------------------------+

          Figure 5. Output Register B (ORB), Input Register B (IRB)


              REG 1 -- ORA/IRA
     +---+---+---+---+---+---+---+---+
     | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+       -+
       |   |   |   |   |   |   |   +----- PA0 |
       |   |   |   |   |   |   |              |
       |   |   |   |   |   |   +--------- PA1 |
       |   |   |   |   |   |                  |
       |   |   |   |   |   +------------- PA2 |  OUTPUT REGISTER
       |   |   |   |   |                      |  "A" (ORA)
       |   |   |   |   +----------------- PA3 |
       |   |   |   |                          +-       OR
       |   |   |   +--------------------- PA4 |
       |   |   |                              |  INPUT REGISTER
       |   |   +------------------------- PA5 |  "A" (IRA)
       |   |                                  |
       |   +----------------------------- PA6 |
       |                                      |
       +--------------------------------- PA7 |
                                             -+
+-----------------------+-----------------------+---------------------------+
|       PIN             |                       |                           |
|  DATA DIRECTION       |        WRITE          |           READ            |
|    SELECTION          |                       |                           |
+-----------------------+-----------------------+---------------------------+
|DDRA = 1  OUTPUT       |MPU WRITES OUTPUT LEVEL|MPU READS LEVEL ON PA PIN  |
|INPUT LATCHING DISABLED|ORA                    |                           |
+-----------------------+                       +---------------------------+
|DDRA = 1  INPUT        |                       |MPU READS IRA BIT WHICH IS |
|INPUT LATCHING ENABLED |                       |THE LEVEL OF THE PA PIN AT |
|                       |                       |THE TIME OF THE LAST CA1   |
|                       |                       |ACTIVE TRANSITION          |
+-----------------------+-----------------------+---------------------------+
|DDRA = 0  INPUT        |MPU WRITES INTO ORA BUT|MPU READS LEVEL ON PA PIN  |
|INPUT LATCHING DISABLED|NO AFFECT ON PIN LEVEL |                           |
|                       |UNTIL DDRA CHANGED     |                           |
+-----------------------+                       +---------------------------+
|DDRA = 0  INPUT        |                       |MPU READS IRA BIT WHICH IS |
|INPUT LATCHING ENABLED |                       |THE LEVEL OF THE PA PIN AT |
|                       |                       |THE TIME OF THE LAST CA1   |
|                       |                       |ACTIVE TRANSITION          |
+-----------------------+-----------------------+---------------------------+

          Figure 6. Output Register A (ORA), Input Register A (IRA)


   REG 2 -- DDRB                          REG 3 -- DDRA
 +-+-+-+-+-+-+-+-+                      +-+-+-+-+-+-+-+-+
 |7|6|5|4|3|2|1|0|                      |7|6|5|4|3|2|1|0|
 +-+-+-+-+-+-+-+-+      -+              +-+-+-+-+-+-+-+-+      -+
  | | | | | | | +--- PB0 |               | | | | | | | +--- PA0 |
  | | | | | | +----- PB1 |               | | | | | | +----- PA1 |
  | | | | | +------- PB2 |  DATA         | | | | | +------- PA2 |  DATA
  | | | | +--------- PB3 |_ DIRECTION    | | | | +--------- PA3 |_ DIRECTION
  | | | +----------- PB4 |  REGISTER     | | | +----------- PA4 |  REGISTER
  | | +------------- PB5 |  "B" (DDRB)   | | +------------- PA5 |  "A" (DDRA)
  | +--------------- PB6 |               | +--------------- PA6 |
  +----------------- PB7 |               +----------------- PA7 |
                        -+                                     -+

  "0"  ASSOCIATED PB PIN IS AN INPUT     "0"  ASSOCIATED PA PIN IS AN INPUT
       (HIGH IMPEDANCE)                       (HIGH IMPEDANCE)
  "1"  ASSOCIATED PB PIN IS AN OUTPUT    "1"  ASSOCIATED PA PIN IS AN OUTPUT
       WHOSE LEVEL IS DETERMINED BY           WHOSE LEVEL IS DETERMINED BY
       ORB REGISTER BIT                       ORA REGISTER BIT

  Figure 7. Data Direction Register B    Figure 8. Data Direction Register A
            (DDRB)                                 (DDRA)



HANDSHAKE CONTROL OF DATA TRANSFERS

The  R6522  allows  positive  control  of  data  transfers  between the system
processor  and  peripheral devices through the operation of "handshake" lines.
Port  A  lines  (CA1, CA2) handshake data on both a read and a write operation
while the Port B lines (CB1, CB2) handshake on a write operation only.

READ HANDSHAKE

Positive  control  of  data  transfers from peripheral devices into the system
processor  can  be  accomplished  very  effectively using Read Handshaking. In
this  case,  the  peripheral  device  must  generate the equivalent of a "Data
Ready"  signal  to  the processor signifying that valid data is present on the
peripheral  port.  This  signal  normally interrupts the processor, which then
reads  the  data,  causing generation of a "Data Taken" signal. The peripheral
device  responds  by  making  new data available. This process continues until
the data transfer is complete.

In  the  R6522,  automatic  "Read Handshaking" is possible on the Peripheral A
Port  only.  The  CA1  interrupt input pin accepts the "Data Ready" signal and
CA2  generates  the  "Data  Taken" signal. The "Data Ready" signal will set an
internal  flag  which  may  interrupt  the  processor  or  which may be polled
under  program  control.  The  "Data  Taken" signal can either be a pulse or a
level  which  is  set  low by the system processor and is cleared by the "Data
Ready"  signal.  These  options  are  shown  in Figure 9 which illustrates the
normal Read Handshake sequence.


                     +-+ +-+ +-+ +-+   +-+ +-+ +-+ +-+ +-+   +-+ +-+ +-+
                02 --+ +-+ +-+ +-+ +-#-+ +-+ +-+ +-+ +-+ +-#-+ +-+ +-+ +-
                                                 |   |
    "DATA READY"   ---------+   +----#-------+-------------#------+
     CA1)                   +---+----#-------+   |   |            +------
    ___                     |                    |   |            |
    IRQ OUTPUT     ---------+                    +---------#------+
                            +--------#-----------+   |            +------
    READ IRA                                 +---+   |            |
    OPERATION      ------------------#-------+   +---------#-------------
                                                 |   |            |
    "DATA TAKEN"   ------------------------------+   |            +------
    HANDSHAKE MODE                               +---------#------+
    (CA2)                                        |   |
                                                 |   |
    "DATA TAKEN"   ------------------#-----------+   +-----#-------------
    PULSE MODE                                   +---+
    (CA2)

                Figure 9. Read Handshake Timing (Port A, Only)


WRITE HANDSHAKE

The  sequence  of  operations  which  allows  handshaking data from the system
processor  to  a  peripheral device is very similar to that described for Read
Handshaking.  However,  for Write  Handshaking,  the R6522 generates the "Data
Ready"  signal  and  the  peripheral device must respond with the "Data Taken"
signal.  This  can  be accomplished on both the PA port and the PB port on the
R6522.  CA2  or  CB2 act as a "Data Ready" output in either the handshake mode
or  pulse  mode  and  CA1  or  CB1  accept  the  "Data  Taken" signal from the
peripheral  device,  setting  the interrupt flag and cleaning the "Data Ready"
output. This sequence is shown in Figure 10.


                     +-+ +-+ +-+ +-+   +-+ +-+ +-+ +-+ +-+   +-+ +-+ +-+
                02 --+ +-+ +-+ +-+ +-#-+ +-+ +-+ +-+ +-+ +-#-+ +-+ +-+ +-
                       |   |                                   |   |
                       +---+                                   +---+
    WRITE ORA,ORB  ----+   +---------#---------------------#---+   +-----
    OPERATION                                                  |
                                                               |
    "DATA READY"   ----------+                +----------------------+
    HANDSHAKE MODE           +-------#--------+                |     +---
    (CA2,CB2)                |                |                |
                             |                |                |
    "DATA READY"   ----------+   +---#---------------------#---------+
    PULSE MODE               +---+            |                |     +---
    (CA2,CB2)                                 |                |     |
                                              |                |     |
    "DATA TAKEN"   ---------------------------+   +--------#---+---------
    (CA1,CB1)                                 +---+--------#---+     |
    ___                                       |                      |
    IRQ OUTPUT     ------------------#--------+                      +---
                                              +------------#---------+

                      Figure 10. Write Handshake Timing


Selection  of  operating  modes  for  CA1, CA2, CB1 and CB2 is accomplished by
the Peripheral Control Register (Figure 11).


                  REG 12 -- PERIPHERAL CONTROL REGISTER
                    +---+---+---+---+---+---+---+---+
                    | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
                    +---+---+---+---+---+---+---+---+
                     |         |  |  |         |  |
                     +----+----+  |  +----+----+  |
                          |       |       |       |
         CB2 CONTROL -----+       |       |       +- CA1 INTERRUPT CONTROL
+-+-+-+------------------------+  |       |   +--------------------------+
|7|6|5| OPERATION              |  |       |   | 0 = NEGATIVE ACTIVE EDGE |
+-+-+-+------------------------+  |       |   | 1 = POSITIVE ACTIVE EDGE |
|0|0|0| INPUT NEG. ACTIVE EDGE |  |       |   +--------------------------+
+-+-+-+------------------------+  |       +---- CA2 INTERRUPT CONTROL
|0|0|1| INDEPENDENT INTERRUPT  |  |       +-+-+-+------------------------+
| | | | INPUT NEGATIVE EDGE    |  |       |3|2|1| OPERATION              |
+-+-+-+------------------------+  |       +-+-+-+------------------------+
|0|1|0| INPUT POS. ACTIVE EDGE |  |       |0|0|0| INPUT NEG. ACTIVE EDGE |
+-+-+-+------------------------+  |       +-+-+-+------------------------+
|0|1|1| INDEPENDENT INTERRUPT  |  |       |0|0|1| INDEPENDENT INTERRUPT  |
| | | | INPUT POSITIVE EDGE    |  |       | | | | INPUT NEGATIVE EDGE    |
+-+-+-+------------------------+  |       +-+-+-+------------------------+
|1|0|0| HANDSHAKE OUTPUT       |  |       |0|1|0| INPUT POS. ACTIVE EDGE |
+-+-+-+------------------------+  |       +-+-+-+------------------------+
|1|0|1| PULSE OUTPUT           |  |       |0|1|1| INDEPENDENT INTERRUPT  |
+-+-+-+------------------------+  |       | | | | INPUT POSITIVE EDGE    |
|1|1|0| LOW OUTPUT             |  |       +-+-+-+------------------------+
+-+-+-+------------------------+  |       |1|0|0| HANDSHAKE OUTPUT       |
|1|1|1| HIGH OUTPUT            |  |       +-+-+-+------------------------+
+-+-+-+------------------------+  |       |1|0|1| PULSE OUTPUT           |
    CB1 INTERRUPT CONTROL --------+       +-+-+-+------------------------+
+--------------------------+              |1|1|0| LOW OUTPUT             |
| 0 = NEGATIVE ACTIVE EDGE |              +-+-+-+------------------------+
| 1 = POSITIVE ACTIVE EDGE |              |1|1|1| HIGH OUTPUT            |
+--------------------------+              +-+-+-+------------------------+

                 Figure 11. Peripheral Control Register (PCR)


COUNTERS/TIMERS

There  are  two  independent  16-bit-counter/timers  (called Timer 1 and Timer
2)  in  the R6522. Each timer is controlled by writing bits into the Auxiliary
Control Register (ACR) to select this mode of operation (Figure 12).


                    REG 11 -- AUXILIARY CONTROL REGISTER
                      +---+---+---+---+---+---+---+---+
                      | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
                      +---+---+---+---+---+---+---+---+
                       |     |  |  |         |  |   |
                       +--+--+  |  +----+----+  |   +------ PA
                          |     |       |       |
      T1 TIMER CONTROL ---+     |       |       +---------- PB
+-+-+----------------+-------+  |       |
|7|6|OPERATION       | PB7   |  |       |              LATCHING ENABLE/DISABLE
+-+-+----------------+-------+  |       |              +---------------------+
|0|0|TIMED INTERRUPT |       |  |       |              | 0 = DISABLE LATCHING|
| | |EACH TIME T1 IS |       |  |       |              | 1 = ENABLE LATCHING |
| | |LOADED          |DISABLE|  |       |              +---------------------+
+-+-+----------------+       |  |       |
|0|1|CONTINUOUS      |       |  |       |
| | |INTERRUPTS      |       |  |       +---- SHIFT REGISTER CONTROL
+-+-+----------------+-------+  |  +-+-+-+-----------------------------------+
|1|0|TIMED INTERRUPT |ONE-   |  |  |4|3|2| OPERATION                         |
| | |EACH TIME T1 IS |SHOT   |  |  +-+-+-+-----------------------------------+
| | |LOADED          |OUTPUT |  |  |0|0|0| DISABLED                          |
+-+-+----------------+-------+  |  +-+-+-+-----------------------------------+
|1|1|CONTINUOUS      |SQUARE |  |  |0|0|1| SHIFT IN UNDER COMTROL OF T2      |
| | |INTERRUPTS      |WAVE   |  |  +-+-+-+-----------------------------------+
| | |                |OUTPUT |  |  |0|1|0| SHIFT IN UNDER CONTROL OF 02      |
+-+-+----------------+-------+  |  +-+-+-+-----------------------------------+
                                |  |0|1|1| SHIFT IN UNDER CONTROL OF EXT.CLK |
   T2 TIMER CONTROL ------------+  +-+-+-+-----------------------------------+
+-+-----------------+              |1|0|0| SHIFT OUT FREE-RUNNING AT T2 RATE |
|5| OPERATION       |              +-+-+-+-----------------------------------+
+-+-----------------+              |1|0|1| SHIFT OUT UNDER CONTROL OF T2     |
|0| TIMED INTERRUPT |              +-+-+-+-----------------------------------+
+-+-----------------+              |1|1|0| SHIFT OUT UNDER CONTROL OF 02     |
|1| COUNT DOWN WITH |              +-+-+-+-----------------------------------+
| | PULSES ON PB6   |              |1|1|1| SHIFT OUT UNDER CONTROL OF EXT.CLK|
+-+-----------------+              +-+-+-+-----------------------------------+

                 Figure 12. Auxiliary Control Register (ACR)


Timer 1 Operation

Interval  Timer  T1  consists  of  two  8-bit latches (Figure 13) and a 16-bit
counter  (Figure  14).  The  latches store data which is to be loaded into the
counter.  After  loading,  the  counter  decrements  at  02  clock  rate. Upon
reaching  zero,  an  interrupt  flag  is  set,  and  IRQ  goes  low  if the T1
interrupt  is  enabled.  Timer  1  then  disables  any  further  interrupts or
automatically  transfers  the  contents  of  the  latches into the counter and
continues  to  decrement.  In  addition, the timer may be programmed to invert
the  output  signal  on  a peripheral pin (PB7) each time it "times-out". Each
of these modes is discussed separately below.

Note  that  the  processor  does not write directly into the low-order counter
(T1C-L).  Instead,  this  half of the counter is loaded automatically from the
low  order  latch  (T1L-L)  when  the  processor  writes  into  the high order
counter  (T1C-H).  In  fact, it may not be necessary to write to the low order
counter  in  some  applications  since  the  timing  operation is triggered by
writing to the high order latch.


  REG 6 -- T1 LOW-ORDER LATCH             REG 7 -- T1 HIGH-ORDER LATCH
  +-+-+-+-+-+-+-+-+                       +-+-+-+-+-+-+-+-+
  |7|6|5|4|3|2|1|0|                       |7|6|5|4|3|2|1|0|
  +-+-+-+-+-+-+-+-+      -+               +-+-+-+-+-+-+-+-+        -+
   | | | | | | | +--- 1   |                | | | | | | | +--- 256   |
   | | | | | | +----- 2   |                | | | | | | +----- 512   |
   | | | | | +------- 4   |                | | | | | +------- 1024  |
   | | | | +--------- 8   |_ COUNT         | | | | +--------- 2048  |_ COUNT
   | | | +----------- 16  |  VALUE         | | | +----------- 4096  |  VALUE
   | | +------------- 32  |                | | +------------- 8192  |
   | +--------------- 64  |                | +--------------- 16384 |
   +----------------- 128 |                +----------------- 32768 |
                         -+                                        -+

 WRITE - 8 BITS LOADED INTO T1           WRITE - 8 BITS LOADED INTO T1 HIGH-
         LOW-ORDER LATCHES. THIS                 ORDER LATCHES. UNLIKE REG 4
         OPERATION IS NO DIFFERENT               OPERATION NO LATCH TO
         THAN A WRITE INTO REG 4                 COUNTER TRANSFERS TAKE PLACE

 READ  - 8 BITS FROM T1 LOW ORDER-       READ  - 8 BITS FROM T1 HIGH-ORDER
         LATCHES TRANSFERRED TO MPU.             LATCHES TRANSFERRED TO MPU
         UNLIKE REG 4 OPERATION,
         THIS DOES NOT CAUSE RESET
         OF T1 INTERRUPT FLAG

                   Figure 13. Timer 1 (T1) Latch Registers


 REG 4 -- T1 LOW-ORDER COUNTER           REG 5 -- T1 HIGH-ORDER COUNTER
  +-+-+-+-+-+-+-+-+                       +-+-+-+-+-+-+-+-+
  |7|6|5|4|3|2|1|0|                       |7|6|5|4|3|2|1|0|
  +-+-+-+-+-+-+-+-+      -+               +-+-+-+-+-+-+-+-+        -+
   | | | | | | | +--- 1   |                | | | | | | | +--- 256   |
   | | | | | | +----- 2   |                | | | | | | +----- 512   |
   | | | | | +------- 4   |                | | | | | +------- 1024  |
   | | | | +--------- 8   |_ COUNT         | | | | +--------- 2048  |_ COUNT
   | | | +----------- 16  |  VALUE         | | | +----------- 4096  |  VALUE
   | | +------------- 32  |                | | +------------- 8192  |
   | +--------------- 64  |                | +--------------- 16384 |
   +----------------- 128 |                +----------------- 32768 |
                         -+                                        -+

 WRITE - 8 BITS LOADED INTO T1           WRITE - 8 BITS LOADED INTO T1
         LOW-ORDER LATCHES. LATCH                HIGH-ORDER LATCHES. ALSO
         CONTENTS ARE TRANSFERRED                AT THIS TIME BOTH HIGH- AND
         INTO LOW-ORDER COUNTER AT               LOW-ORDER LATCHES TRANSFERRED
         THE TIME THE HIGH-ORDER                 INTO T1 COUNTER. T1 INTERRUPT
         COUNTER IS LOADED (REG 5)               FLAG ALSO IS RESET

 READ  - 8 BITS FROM T1 LOW-ORDER        READ  - 8 BITS FROM T1 HIGH-ORDER
         COUNTER TRANSFERRED TO MPU.             COUNTER TRANSFERRED TO MPU
         IN ADDITION T1 INTERRUPT FLAG
         IS RESET (BIT 6 IN INTERRUPT
         FLAG REGISTER)

                  Figure 14. Timer 1 (T1) Counter Registers


Timer 1 One-Shot Mode

The  Timer  1  one-shot  mode generates a single interrupt for each timer load
operation.  As  with  any  interval timer, the delay between the "write T1C-H"
operation  and  generation  of the processor interrupt is a direct function of
the  data  loaded  into the timing counter. In addition to generating a single
interrupt,  Timer  1  can  be programmed to produce a single negative pulse on
the  PB7  peripheral  pin.  With  the  output enabled (ACR7=1) a "write T1C-H"
operation  will  cause  PB7 to go low. PB7 will return high when Timer 1 times
out. The result is a single programmable width pulse.
                                           ___
T1   interrupt  flag  will  be  set,  the  IRQ  pin  will  go  low  (interrupt
enabled),  and  the  signal on PB7 will go high. At this time the counter will
continue   to   decrement  at  system  clock  rate.  This  allows  the  system
processor  to  read  the  contents  of the counter to determine the time since
interrupt.  However,  the  T1 interrupt flag cannot be set again unless it has
been cleared as described in this specification.

Timing for the R6522 interval timer one-shot mode is shown in Figure 15.


                     +-+ +-+ +-+ +-+ +-+ +-+   +-+ +-+ +-+ +-+ +-+ +-+
                02 --+ +-+ +-+ +-+ +-+ +-+ +-#-+ +-+ +-+ +-+ +-+ +-+ +-
                       |   |                           |
                       +---+                           |
       WRITE T1C-H ----+   +-----------------#-------------------------
        ___                |                           |
        IRQ OUTPUT --------------------------#---------+
                           |                           +---------------
                           |                           |
        PB7 OUTPUT --------+                           +---------------
                           +-----------------#---------+
                           | N |N-1|N-2|N-3|     | 0 |N| |N-1|N-2|N-3|
                           |                           |
                           |<---- N + 1.5 CYCLES ----->|

                   Figure 15. Timer 1 One-Shot Mode Timing


In   the  one-shot  mode,  writing  into  the  T1L-H  has  no  effect  on  the
operation  of  Timer  1.  However, it will be necessary to assure that the low
order  latch  contains  the  proper data before initiating the count-down with
a  "write  T1C-H"  operation.  When  the  processor writes into the high order
counter  (T1C-H),  the  T1 interrupt flag will be cleared, the contents of the
low  order  latch  will  be  transferred  into  the low order counter, and the
timer  will  begin  to  decrement  at  system clock rate. If the PB7 output is
enabled,  this  signal  will  go  low on the 02 following the write operation.
When  the  counter  reaches  zero,  the T1 interrupt flag will be set, the IRQ
pin  will  go  low (interrupt enabled), and the signal on PB7 will go high. At
this  time  the  counter will continue to decrement at system clock rate. This
allows   the  system  processor  to  read  the  contents  of  the  counter  to
determine  the  time  since  interrupt.  However, the T1 interrupt flag cannot
be set again unless it has been cleared as described in this specification.

Timer 1 Free-Run Mode

The  most  important  advantage  associated  with  the  latches  in  T1 is the
ability  to  produce  a  continuous series of evenly spaced interrupts and the
ability  to  produce  a  square wave on PB7 whose frequency is not affected by
variations  in  the  processor  interrupt  response time. This is accomplished
in the "free-running" mode.

In  the  free-running  mode,  the  interrupt flag is set and the signal on PB7
is   inverted  each  time  the  counter  reaches  zero.  However,  instead  of
continuing  to  decrement  from zero after a time-out, the timer automatically
transfers   the  contents  of  the  latch  into  the  counter  (16  bits)  and
continues  to  decrement  from  there.  The  interrupt  flag can be cleared by
writing  T1C-H,  by  reading  T1C-L  or  by  writing directly into the flag as
described  later.  However,  it  is  not  necessary  to  rewrite  the timer to
enable setting the interrupt flag on the next time-out.

All   interval  timers  in  the  R6522  are  "re-triggerable".  Rewriting  the
counter   will   always  re-initialize  the  time-out  period.  In  fact,  the
time-out  can  be  prevented  completely if the processor continues to rewrite
the  timer  before  it  reaches  zero.  Timer 1 will operate in this manner if
the  processor  writes  into  the  high  order  counter  (T1C-H).  However, by
loading  the  latches  only,  the  processor  can access the timer during each
down-counting  operation  without  affecting the time-out in process. Instead,
the  data  loaded  into  the  latches  will  determine  the length of the next
time-out   period.   This   capability   is   particularly   valuable  in  the
free-running  mode  with  the  output enabled. In this mode, the signal on PB7
is  inverted  and  the interrupt flag is set with each time-out. By responding
to   the  interrupts  with  new  data  for  the  latches,  the  processor  can
determine  the  period  of  the  next half cycle during each half cycle of the
output  signal  on  PB7.  In  this  manner,  very  complex  waveforms  can  be
generated.

A  precaution  to  take  in  the  use  of PB7 as the timer output concerns the
Data  Direction  Register  contents  for  PB7.  Both  DDRB bit 7 and ACR bit 7
must  be  1  for  PB7  to  function  as  the timer output. If one is 1 and the
other  is  0,  then  PB7  functions  as a normal output pin, controlled by ORB
bit 7.

                    +-+ +-+ +-+   +-+ +-+ +-+ +-+ +-+   +-+ +-+ +-+ +-+
               02 --+ +-+ +-+ +-#-+ +-+ +-+ +-+ +-+ +-#-+ +-+ +-+ +-+ +-
                          |               |                     |
      WRITE T1C-H     +---+               |                     |
      OPERATION   ----+   +-----#---------------------#-----------------
       ___                |               |                     |
       IRQ OUTPUT --------------#---------+           #---------+
                          |               +-----------#         +-------
                          |               |                     |
       PB7 OUTPUT --------+               +-----------#---------+
                          +-----#---------+                     +-------
                          |               |                     |
                          |< N+1.5 CYCLES>|<---- N+2 CYCLES --->|

                   Figure 16. Timer 1 Free-Run Mode Timing


Timer 2 Operation

Timer  2  operates  as  an interval timer (in the "one-shot" mode only), or as
a  counter  for  counting  negative pulses on the PB6 peripheral pin. A single
control  bit  in  the  Auxiliary  Control  Register  selects between these two
modes.  This  timer  is comprised of a "write-only" low-order latch (T2L-L), a
"read-only"  low-order  counter  (T2C-L)  and  a read/write high order counter
(T2C-H).  The  counter  registers  act as a 16-bit counter which decrements at
02 rate. Figure 17 illustrates the T2 Latch/Counter Registers.


  REG 8 - T2 LOW-ORDER LATCH/COUNTER      REG 9 - T2 HIGH-ORDER COUNTER
  +-+-+-+-+-+-+-+-+                       +-+-+-+-+-+-+-+-+
  |7|6|5|4|3|2|1|0|                       |7|6|5|4|3|2|1|0|
  +-+-+-+-+-+-+-+-+      -+               +-+-+-+-+-+-+-+-+        -+
   | | | | | | | +--- 1   |                | | | | | | | +--- 256   |
   | | | | | | +----- 2   |                | | | | | | +----- 512   |
   | | | | | +------- 4   |                | | | | | +------- 1024  |
   | | | | +--------- 8   |_ COUNT         | | | | +--------- 2048  |_ COUNT
   | | | +----------- 16  |  VALUE         | | | +----------- 4096  |  VALUE
   | | +------------- 32  |                | | +------------- 8192  |
   | +--------------- 64  |                | +--------------- 16384 |
   +----------------- 128 |                +----------------- 32768 |
                         -+                                        -+


 WRITE - 8 BITS LOADED INTO T2           WRITE - 8 BITS LOADED INTO T2
         LOW-ORDER LATCH                         HIGH-ORDER COUNTER. ALSO,
                                                 LOW-ORDER LATCH TRANSFERRED
 READ  - 8 BITS FROM T2 LOW-ORDER                TO LOW-ORDER COUNTER. IN
         COUNTER TRANSFERRED TO MPU.             ADDITION T2 INTERRUPT FLAG
         T2 INTERRUPT FLAG IS RESET              IS RESET

                                         READ  - 8 BITS FROM T2 HIGH-ORDER
                                                 COUNTER TRANSFERRED TO MPU

               Figure 17. Timer 2 (T2) Latch/Counter Registers


Timer 2 One-Shot Mode

As  an interval timer,  T2 operates in the "one-shot" mode similar to Timer 1.
In  this  mode,  T2  provides  a  single  interrupt  for  each  "write  T2C-H"
operation.   After  timing  out,  the  counter  will  continue  to  decrement.
However,  setting  of  the  interrupt  flag is disabled after initial time-out
so  that  it  will  not be set by the counter decrementing again through zero.
The  processor  must  rewrite  T2C-H  to enable setting of the interrupt flag.
The  interrupt  flag  is  cleared by reading T2C-L or by writing T2C-H. Timing
for this operation is shown in Figure 18.


                     +-+ +-+ +-+ +-+ +-+ +-+   +-+ +-+ +-+ +-+ +-+ +-+
                02 --+ +-+ +-+ +-+ +-+ +-+ +-#-+ +-+ +-+ +-+ +-+ +-+ +-
                       |   |                           |
                       +---+                           |
       WRITE T2C-H ----+   +-----------------#-------------------------
        ___                |                           |
        IRQ OUTPUT --------------------------#---------+
                           |                           +---------------
                           |                           |
                           | N |N-1|N-2|N-3|     | 0 |N| |N-1|N-2|N-3|
                           |<------ N+1.5 CYCLES ----->|

                          Figure 18. Timer 2 One-Shot Mode Timing


Timer 2 Pulse Counting Mode

In   the   pulse   counting   mode,   T2  counts  a  predetermined  number  of
negative-going  pulses  on  PB6.  This  is  accomplished  by  first  loading a
number  into  Timer  2.  Writing  into  T2C-H  clears  the  interrupt flag and
allows  the  counter  to  decrement  each  time a pulse is applied to PB6. The
interrupt  flag  is  set  when T2 counts down past zero. The counter will then
continue  to  decrement  with  each  pulse on PB6. However, it is necessary to
rewrite  T2C-H  to  allow  the interrupt flag to set on a subsequent time-out.
Timing  for  this  mode  is  shown  in Figure 19. The pulse must be low on the
leading edge of 02.


    WRITE T2C-H     +---+
    OPERATION   ----+   +------------------------------------------------
                ------------------+ +-----+ +-----#-----+ +-----+ +------
      PB6 INPUT                   +-+     +-+           +-+     +-+
     ___        ------------------------------------------------+
     IRQ OUTPUT                   |       |             |       +--------
                               N  |  N-1  |  N-2     1  |   0   |  -1

                    Figure 19. Timer 2 Pulse Counting Mode


SHIFT REGISTER OPERATION

The  Shift  Register  (SR)  performs serial data transfers into and out of the
CB2  pin  under  control  of an internal modulo-8 counter. Shift pulses can be
applied  to  the  CB1  pin  from  an  external source or, with the proper mode
selection,  shift  pulses  generated internally will appear on the CB1 pin for
controlling external devices.

The  control  bits  which  select  the  various shift register operating modes
are  located  in  the  Auxiliary  Control  Register. Figure 20 illustrates the
configuration  of  the  SR  data  bits and Figure 21 shows the SR control bits
of the ACR.


 REG 10 -- SHIFT REGISTER              REG 11 -- AUXILIARY CONTROL REGISTER
 +-+-+-+-+-+-+-+-+                               +-+-+-+-+-+-+-+-+
 |7|6|5|4|3|2|1|0|                               |7|6|5|4|3|2|1|0|
 +-+-+-+-+-+-+-+-+   -+                          +-+-+-+-+-+-+-+-+
  | | | | | | | +---- |                                 |   |
  | | | | | | +------ |                                 +-+-+
  | | | | | +-------- |  SHIFT                            |
  | | | | +---------- |_ REGISTER                         |  SHIFT REGISTER
  | | | +------------ |  BITS                             +- MODE CONTROL
  | | +-------------- |              +-+-+-+---------------------------------+
  | +---------------- |              |4|3|2|OPERATION                        |
  +------------------ |              +-+-+-+---------------------------------+
                     -+              |0|0|0|DISABLED                         |
                                     |0|0|1|SHIFT IN UNDER CONTROL OF T2     |
 NOTES                               |0|1|0|SHIFT IN UNDER CONTROL OF 02     |
 1  WHEN SHIFTING OUT BIT 7 IS THE   |0|1|1|SHIFT IN UNDER CONT. OF EXT.CLK  |
    FIRST BIT OUT AND SIMULTANEOUSLY |1|0|0|SHIFT OUT FREE RUNNING AT T2 RATE|
    IS ROTATED BACK INTO BIT 0       |1|0|1|SHIFT OUT UNDER CONTROL OF T2    |
 2  WHEN SHIFTING IN BITS INITIALLY  |1|1|0|SHIFT OUT UNDER CONTROL OF 02    |
    ENTER BIT 0 AND ARE SHIFTED      |1|1|1|SHIFT OUT UNDER CONT. OF EXT.CLK |
    TOWARDS BIT 7                    +-+-+-+---------------------------------+

     Figure 20. Shift registers           Figure 21. Shift Register Modes


SR Mode 0 -- Disabled

Mode  0  disables  the  Shift  Register.  In  this mode the microprocessor can
write  or  read  the  SR  and  the  SR  will  shift  on each CB1 positive edge
shifting  in  the  value  on  CB2.  In  this  mode  the  SR  Interrupt Flag is
disabled (held to a logic 0).


SR Mode 1 -- Shift in Under Control of T2

In  mode  1,  the  shifting  rate  is controlled by the low order 8 bits of T2
(Figure  22).  Shift  pulses  are generated on the CB1 pin to control shifting
in  external  devices.  The time between transitions of this output clock is a
function  of  the  system  clock  period  and the contents of the low order T2
latch (N).


         02  +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+
            -+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-
WRITE OR READ  +---+         |           |           |           |                           |
SHIFT REG   ---+   +------------------------------------------------------#----------------------
                  N+2 CYCLES |<--------->|<--------->| N+2 CYCLES                            |
CB1 OUTPUT  -----------------+     1     +-----------+     2     +--------#--+     8     +-------
SHIFT CLOCK                  +-----------+           +-----------+           +-----------+   |
                                                                                             |
  CB2 INPUT -----------------------\/---1---\/-------------\/---2---\/----#--------\/---8---\/---
  DATA      -----------------------/\-------/\-------------/\-------/\----#--------/\-------/\---
                                                                                             |
        ___    +-----------------------------------------------------------------------------+
        IRQ ---+                                                                             +---

              Figure 22. SR Mode 1 -- Shift In Under T2 Control


The  shifting  operation  is  triggered  by the read or write of the SR if the
SR  flag  is  set in the IFR. Otherwise the first shift will occur at the next
time-out  of  T2  after  a read or write of the SR. Data is shifted first into
the  low  order  bit  of SR and is then shifted into the next higher order bit
of  the  shift  register  on  the negative-going edge of each clock pulse. The
input  data  should  change  before  the  positive-going edge of the CB1 clock
pulse.  This  data  is  shifted  into shift register during the 02 clock cycle
following  the  positive-going  edge of the CB1 clock pulse. After 8 CB1 clock
pulses, the shift register interrupt flag will set and IRQ will go low.


SR Mode 2 -- Shift in Under 02 Control

In  mode  2,  the  shift  rate  is  a  direct  function  of  the  system clock
frequency  (Figure  23).  CB1  becomes  an output which generates shift pulses
for   controlling  external  devices.  Timer  2  operates  as  an  independent
interval  timer  and  has no effect on SR. The shifting operation is triggered
by  reading  or  writing the Shift Register. Data is shifted, first into bit 0
and  is  then  shifted into the next higher order bit of the shift register on
the  trailing  edge  of  each  02 clock pulse. After 8 clock pulses, the shift
register  interrupt  flag  will  be  set,  and  the output clock pulses on CB1
will stop.


             +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+
         02 -+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-
               +---+         |   |                                                       |   |
    READ SR ---+   +-----------------------------------------------------------------------------
                             |   |                                                       |   |
CB1 OUTPUT  -----------------+   +---+   +---+   +---+   +---+   +---+   +---+   +---+   +-------
SHIFT CLOCK                  +---+   +---+   +---+   +---+   +---+   +---+   +---+   +---+   |
                                                                                         |
  CB2 INPUT -----------------\/---1--\/---2--\/---3--\/---4--\/---5--\/---6--\/---7--\/---8--\/--
  DATA      -----------------/\------/\------/\------/\------/\------/\------/\------/\------/\--
                                                                                             |
        ___ ---------------------------------------------------------------------------------+
        IRQ                                                                                  +---

              Figure 23. SR Mode 2 -- Shift In Under 02 Control


SR Mode 3 -- Shift in Under CB1 Control

In  mode  3,  external  pin  CB1  becomes an input (Figure 24). This allows an
external  device  to  load  the  shift  register  at  its  own pace. The shift
register  counter  will  interrupt  the  processor  each time 8 bits have been
shifted  in.  However,  the  shift register counter does not stop the shifting
operation;  it  acts  simply  as a pulse counter. Reading or writing the Shift
Register  resets  the  Interrupt  Flag and initializes the SR counter to count
another 8 pulses.


             +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+
         02 -+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-
                                                                         |
CB1 OUTPUT  ---+    1    +---------+    2    +--------#----+    8    +-------
SHIFT CLOCK    +---------+         +---------+             +---------+
                                                                         |
  CB2 INPUT -------\/----1---\/--------\/----2---\/---#--------\/----8---\/--
  DATA      -------/\--------/\--------/\--------/\---#--------/\--------/\--
        ___                                                              |
        IRQ -------------------------------------------------------------+
                                                                         +---

                Figure 24. SR Mode 3 -- Shift In Under CB1 Control


Note   that   the  data  is  shifted  during  the  first  system  clock  cycle
following  the  positive-going  edge  of the CB1 shift pulse. For this reason,
data  must  be  held  stable  during  the first full cycle following CB1 going
high.


SR Mode 4 -- Shift Out Under T2 Control (Free-Run)

Mode  4  is  very  similar  to mode 5 in which the shifting rate is set by T2.
However,  in  mode  4  the  SR  Counter  does  not stop the shifting operation
(Figure  25).  Since  the Shift Register bit 7 (SR7) is recirculated back into
bit  0,  the  8  bits  loaded into the Shift Register will be clocked onto CB2
repetitively. In this mode the Shift Register Counter is disabled.


        02  +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+
           -+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-
              +---+         |           |           |           |           |           |
WRITE SR   ---+   +---------------------------------------------------#---------------------------------------------
                 N+2 CYCLES |<--------->|<--------->| N+2 CYCLES|           |           |
CB1 OUTPUT -----------------+     1     +-----------+     2     +-----#-----+     8     +-----------+     9     +---
SHIFT CLOCK                 +-----------+           +-----------+           +-----------+           +-----------+

CB2 OUTPUT -------------------\/-----------1----------\/-------2------#-------\/-----------8----------\/-----1------
DATA       -------------------/\----------------------/\--------------#-------/\----------------------/\------------

        Figure 25. SR Mode 4 -- Shift Out Under T2 Control (Free-Run)


SR Mode 5 -- Shift Out Under T2 Control

In  mode  5,  the  shift rate is controlled by T2 (as in mode 4). The shifting
operation  is  triggered  by the read or write of the SR if the SR flag is set
in  the  IFR  (Figure  26).  Otherwise  the first shift will occur at the next
time-out  of  T2  after  a read or write of the SR. However, with each read or
write  of  the  shift  register the SR Counter is reset and 8 bits are shifted
onto  CB2.  At  the  same time, 8 shift pulses are generated on CB1 to control
shifting  in  external  devices.  After  the  8  shift pulses, the shifting is
disabled,  the  SR  Interrupt  Flag  is  set  and CB2 remains at the last data
level.


            +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+   +-+ +-+ +-+ +-+ +-+
         02 + +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-#-+ +-+ +-+ +-+ +-+ +
              +---+         |       |       |       |         |       |
   WRITE SR --+   +----------------------------------------------------------
                 N+2 CYCLES |<----->|<----->| N+2 CYCLES      |       |
 CB1 OUTPUT ----------------+   1   +-------+   2   +---#-----+   8   +------
 SHIFT CLOCK                +-------+       +-------+         +-------+
                                                                      |
 CB2 OUTPUT --------------------\/------1------\/---2---#--------\/-----8----
 DATA       --------------------/\-------------/\-------#--------/\----------
        ___                                                           |
        IRQ ----------------------------------------------------------+
                                                                      +------

              Figure 26. SR Mode 5 -- Shift Out Under T2 Control


SR Mode 6 -- Shift Out Under 02 Control

In mode 6, the shift rate is controlled by the 02 system clock (Figure 27).


            +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-#-+ +-+ +-+ +-+ +-+ +-+
         02 + +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+   +-+ +-+ +-+ +-+ +-+ +
              +---+     |   |   |   |   |   |   |   |     |   |   |   |
   WRITE SR --+   +----------------------------------------------------------
                        |   |   |   |   |   |   |   |     |   |   |   |
 CB1 OUTPUT ------------+ 1 +---+ 2 +---+ 3 +---+ 4 +--#--+ 7 +---+ 8 +------
 SHIFT CLOCK            +---+   +---+   +---+   +---+     +---+   +---+
                                                                  |   |
 CB2 OUTPUT -------------\/---1--\/---2--\/---3--\/--4-#---\/---7--\/---8----
 DATA       -------------/\------/\------/\------/\----#---/\------/\--------
        ___                                                           |
        IRQ ----------------------------------------------------------+
                                                                      +------

              Figure 27. SR Mode 6 -- Shift Out Under 02 Control


SR Mode 7 -- Shift Out Under CB1 Control

In  mode  7,  shifting  is  controlled  by pulses applied to the CB1 pin by an
external  device  (Figure  28). The SR counter sets the SR Interrupt Flag each
time  it  counts  8 pulses but it does not disable the shifting function. Each
time   the  microprocessor,  writes  or  reads  the  shift  register,  the  SR
Interrupt  Flag  is  reset and the SR counter is initialized to begin counting
the  next  8  shift  pulses  on pin CB1.  After 8 shift pulses,  the Interrupt
Flag  is  set.  The  microprocessor  can then load the shift register with the
next byte of data.


            +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-#-+ +-+ +-+ +-+
         02 + +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+   +-+ +-+ +-+ +
              +---+                                                   |
   WRITE SR --+   +----------------------------------------------------------
                                                                      |
 CB1 INPUT  --------------+   1   +---------------+   2   +---#----+  |  8  +
 SHIFT CLOCK              +-------+               +-------+        +--------+
                                                                      |
 CB2 OUTPUT --------------\/-----------1----------\/-----2----#----\/---8----
 DATA       --------------/\----------------------/\----------#----/\--------
        ___                                                           |
        IRQ ----------------------------------------------------------+
                                                                      +------

             Figure 28. SR Mode 7 -- Shift Out Under CB1 Control



INTERRUPT OPERATION

Controlling    interrupts   within   the  R6522   involves   three   principal
operations.  These  are  flagging  the  interrupts,  enabling  interrupts  and
signaling  to  the  processor that an active interrupt exists within the chip.
Interrupt  flags  are  set  in the Interrupt Flag Register (IFR) by conditions
detected  within  the  R6522  or  on inputs to the R6522. These flags normally
remain  set  until  the  interrupt  has been serviced. To determine the source
of  an  interrupt,  the microprocessor must examine these flags in order, from
highest to lowest priority.

Associated  with  each  interrupt  flag  is  an  interrupt  enable  bit in the
Interrupt   Enable  Register  (IER).  This  can  be  set  or  cleared  by  the
processor   to  enable  interrupting  the  processor  from  the  corresponding
interrupt  flag.  If  an interrupt flag is set to a logic 1 by an interrupting
condition,  and  the  corresponding  interrupt  enable  bit is set to a 1, the
Interrupt  Request  (IRQ)  output  will  go  low.  IRQ  is an "open-collector"
output  which  can  be  "wire-OR'ed"  with  other  devices  in  the  system to
interrupt the processor.


Interrupt Flag Register (IFR)

In  the  R6522,  all  the interrupt flags are contained in one register, i.e.,
the  IFR  (Figure  29).  In addition, bit 7 of this register will be read as a
logic   1  when  an  interrupt  exists  within  the  chip.  This  allows  very
convenient  polling  of  several  devices within a system to locate the source
of an interrupt.


      REG 13 -- INTERRUPT FLAG REGISTER
+-+-+-+-+-+-+-+-+
|7|6|5|4|3|2|1|0|             SET BY                    CLEARED BY
+-+-+-+-+-+-+-+-+    +-----------------------+------------------------------+
 | | | | | | | +--CA2| CA2 ACTIVE EDGE       | READ OR WRITE REG 1 (ORA)*   |
 | | | | | | |       +-----------------------+------------------------------+
 | | | | | | +--CA1--| CA1 ACTIVE EDGE       | READ OR WRITE REG 1 (ORA)    |
 | | | | | |         +-----------------------+------------------------------+
 | | | | | +SHIFT REG| COMPLETE 8 SHIFTS     | READ OR WRITE SHIFT REG      |
 | | | | |           +-----------------------+------------------------------+
 | | | | +-CB2-------| CB2 ACTIVE EDGE       | READ OR WRITE ORB*           |
 | | | |             +-----------------------+------------------------------+
 | | | +-CB1---------| CB1 ACTIVE EDGE       | READ OR WRITE ORB            |
 | | |               +-----------------------+------------------------------+
 | | +-TIMER 2-------| TIME-OUT OF T2        | READ T2 LOW OR WRITE T2 HIGH |
 | |                 +-----------------------+------------------------------+
 | +-TIMER 1---------| TIME-OUT OF T1        | READ T1 LOW OR WRITE T1 HIGH |
 |                   +-----------------------+------------------------------+
 +-IRQ---------------| ANY ENABLED INTERRUPT | CLEAR ALL INTERRUPTS         |
                     +-----------------------+------------------------------+

  * IF THE CA2/CB2 CONTROL IN THE PCR IS SELECTED AS "INDEPENDENT"
    INTERRUPT INPUT, THEN READING OR WRITING THE OUTPUT REGISTER
    ORA/ORB WILL NOT CLEAR THE FLAG BIT. INSTEAD, THE BIT MUST BE
    CLEARED BY WRITING INTO THE IFR, AS DESCRIBED PREVIOUSLY.

                   Figure 29. Interrupt Flag Register (IFR)


The  Interrupt  Flag  Register  (IFR)  may  be read directly by the processor.
In  addition,  individual  flag  bits may be cleared by writing a "1" into the
appropriate  bit  of  the  IFR.  When  the  proper  chip  select  and register
signals  are  applied  to  the  chip, the contents of this register are placed
on  the  data  bus.  Bit  7  indicates  the status of the IRQ output. This bit
corresponds  to the logic function:  IRQ = IFR6xIER6 + IFR5xIER5 + IFR4xIER4 +
+ IFR3xIER3 + IFR2xIER2 + IFR1xIER1 + IFR0xIER0

                                    Note:

                         x = logic AND, + = logic OR

The  IFR  bit  7  is  not  a flag. Therefore, this bit is not directly cleared
by  writing  a  logic  1  into  it. It can only be cleared by clearing all the
flags   in  the  register  or  by  disabling  all  the  active  interrupts  as
discussed in next section.


Interrupt Enable Register (IER)

For  each  interrupt  flag  in  IFR,  there  is  a  corresponding  bit  in the
Interrupt  Enable  Register  (IER) (Figure 30). Individual bits in the IER can
be  set  or  cleared  to  facilitate controlling individual interrupts without
affecting  others.  This  is  accomplished  by  writing to the IER after bit 7
set  or  cleared  to,  in turn, set or clear selected enable bits. If bit 7 of
the  data  placed  on  the system data bus during this write operation is a 0,
each  1  in  bits  6  through  0  clears the corresponding bit in the IER. For
each zero in bits 6 through 0, the corresponding bit is unaffected.


                 REG 14 -- INTERRUPT ENABLE REGISTER
                          +-+-+-+-+-+-+-+-+
                          |7|6|5|4|3|2|1|0|
                          +-+-+-+-+-+-+-+-+            -+
                           | | | | | | | +--- CA2       |
                           | | | | | | +----- CA1       |  0 = INTERRUPT
                           | | | | | +------- SHIFT REG |      DISABLED
                           | | | | +--------- CB2       |_
                           | | | +----------- CB1       |
                           | | +------------- TIMER 2   |  1 = INTERRUPT
                           | +--------------- TIMER 1   |      ENABLED
                           +----------------- SET/CLEAR |
                                                       -+

         NOTES:
         1  IF BIT 7 IS A "0", THEN EACH "1" IN BITS 0-6 DISABLES THE
            CORRESPONDING INTERRUPT.
         2  IF BIT 7 IS A "1",  THEN EACH "1" IN BITS 0-6 ENABLES THE
            CORRESPONDING INTERRUPT.
         3  IF A READ OF THIS REGISTER IS DONE, BIT 7 WILL BE "1" AND
            ALL OTHER BITS WILL REFLECT THEIR ENABLE/DISABLE STATE.

                  Figure 30. Interrupt Enable Register (IER)


Selected  bits  in  the  IER  can  be  set by writing to the IER with bit 7 in
the  data  word  set  to  a  logic 1. In this case, each 1 in bits 6 through 0
will  set  the  corresponding  bit.  For each zero, the corresponding bit will
be   unaffected.   This   individual  control  of  the  setting  and  clearing
operations  allows  very  convenient  control  of the interrupts during system
operation.

In   addition  to  setting  and  clearing  IER  bits,  the  contents  of  this
register can be read at any time. Bit 7 will be read as a logic 1, however.

##############################################################################

3.
--
From a Vectrex point of view:
(internal.txt)
6522A
~~~~~

This device is used to control all of the vectrex peripheral devices, such
as the Keypads, Vector generator, DAC, Sound chip, etc.

The A port is used as a BUS and goes directly to the DAC input and sound
chip input pins D0-D7. The DAC will output whatever value is on this
port.

The B Port is used in the following way:

    PB0 - SWITCH    Switch Control, enables/disables the analog multiplexer,
                    see Vector drawing hardware description

    PB1 - SEL0      Controls multiplexer channel select, see section on
    PB2 - SEL1      Vector drawing.

    PB3 - BC1       Chip Select Signal for the AY-3-8192 Sound Chip
    PB4 - BDIR      Read/Write Signal for the AY-3-8192 Sound Chip

    PB5 - COMPARE   Feedback from the OP-AMP that does the comparison
                    for calculation of analog joystick positions.

    PB6 - ???       This line is fed to the cartridge connector. It is
                    likely that this line was to have been used as a ROM
                    bank select for cartridges greater than 32K.

    PB7 - ~RAMP     This line controls part of the vector drawing
                    hardware, see later. It is an active LOW signal.

The 6522PIA Has a number of control lines that are sometimes used as
handshake lines as the can generate interrupts, but on the vectrex they
are used to control the vector drawing hardware

    CA1 - IO7       This line is connected to the IO7 line of the AY-3-8192
                    sound chip. My guess it is used to generate an
                    interrupt to the CPU. See the AY-3-8192 description
                    for more details on IO7.

    CA2 - ~ZERO     Connected to the integrators that form part of the
                    vector drawing hardware. This line will cause them
                    to be zero'd (both X and Y) and has the effect of
                    bringing the beam back to the centre of the CRT. It
                    is an active LOW signal. See Vector hardware section
                    for more info.

    CB1 - Not Connected

    CB2 - ~BLANK    This Active LOW signal is the BEAM ON/OFF signal
                    to the Vector drawing hardware, and is used to hide
                    the beam when it is being positioned for re-draw.
                    See Vector hardware section for more info.

Some Vectrex defines:

VIA_port_b      EQU     $D000   ;VIA port B data I/O register
*       0 sample/hold (0=enable  mux 1=disable mux)
*       1 mux sel 0
*       2 mux sel 1
*       3 sound BC1
*       4 sound BDIR
*       5 comparator input
*       6 external device (slot pin 35) initialized to input
*       7 /RAMP
VIA_port_a      EQU     $D001   ;VIA port A data I/O register (handshaking)
VIA_DDR_b       EQU     $D002   ;VIA port B data direction register (0=input 1=output)
VIA_DDR_a       EQU     $D003   ;VIA port A data direction register (0=input 1=output)
VIA_t1_cnt_lo   EQU     $D004   ;VIA timer 1 count register lo (scale factor)
VIA_t1_cnt_hi   EQU     $D005   ;VIA timer 1 count register hi
VIA_t1_lch_lo   EQU     $D006   ;VIA timer 1 latch register lo
VIA_t1_lch_hi   EQU     $D007   ;VIA timer 1 latch register hi
VIA_t2_lo       EQU     $D008   ;VIA timer 2 count/latch register lo (refresh)
VIA_t2_hi       EQU     $D009   ;VIA timer 2 count/latch register hi
VIA_shift_reg   EQU     $D00A   ;VIA shift register
VIA_aux_cntl    EQU     $D00B   ;VIA auxiliary control register
*       0 PA latch enable
*       1 PB latch enable
*       2 \                     110=output to CB2 under control of phase 2 clock
*       3  > shift register control     (110 is the only mode used by the Vectrex ROM)
*       4 /
*       5 0=t2 one shot                 1=t2 free running
*       6 0=t1 one shot                 1=t1 free running
*       7 0=t1 disable PB7 output       1=t1 enable PB7 output
VIA_cntl        EQU     $D00C   ;VIA control register
*       0 CA1 control     CA1 -> SW7    0=IRQ on low 1=IRQ on high
*       1 \
*       2  > CA2 control  CA2 -> /ZERO  110=low 111=high
*       3 /
*       4 CB1 control     CB1 -> NC     0=IRQ on low 1=IRQ on high
*       5 \
*       6  > CB2 control  CB2 -> /BLANK 110=low 111=high
*       7 /
VIA_int_flags   EQU     $D00D   ;VIA interrupt flags register
*               bit                             cleared by
*       0 CA2 interrupt flag            reading or writing port A I/O
*       1 CA1 interrupt flag            reading or writing port A I/O
*       2 shift register interrupt flag reading or writing shift register
*       3 CB2 interrupt flag            reading or writing port B I/O
*       4 CB1 interrupt flag            reading or writing port A I/O
*       5 timer 2 interrupt flag        read t2 low or write t2 high
*       6 timer 1 interrupt flag        read t1 count low or write t1 high
*       7 IRQ status flag               write logic 0 to IER or IFR bit
VIA_int_enable  EQU     $D00E   ;VIA interrupt enable register
*       0 CA2 interrupt enable
*       1 CA1 interrupt enable
*       2 shift register interrupt enable
*       3 CB2 interrupt enable
*       4 CB1 interrupt enable
*       5 timer 2 interrupt enable
*       6 timer 1 interrupt enable
*       7 IER set/clear control
VIA_port_a_nohs EQU     $D00F   ;VIA port A data I/O register (no handshaking)


Appendix D:

1.
--

Programmable Sound Generator (AY-3-8912)

Features
* Full Software Control of Sound Generation
* Interface to Most 8-Bit and 16-Bit Microprocessors
* Three Independently Programmed Analog Outputs
* One 8-Bit General Purpose I/O Port

Description
The AY-3-8912 Programmable Sound Generator (PSG) is
a LSI Circuit which can produce a wide variety of complex sounds
under software control. The AY-3-8912 is manufactured in
the General Instrument N-Channel Ion Implant Process. Operation
requires a single +5V power supply a TTL compatible clock, and a
microprocessor controller such as the General Instrument 16-bit
CP1610 or one of the PIC1650 series of 8-bit microcomputers.

The PSG is easily interfaced to any bus oriented system. Its flexibility
makes it useful in applications such as music systems, sound
effects generation, audible alarms, tone signalling and FSK modems.
The analog sound outputs can provide 4 bits of logarithmic
digital to analog conversion, greatly enhancing the dynamic range of
the sounds produced.

In order to perform sound effects while allowing the processor to
continue its other tasks, the PSG can continue to produce sound
after the initial commads have been given by the control processor.
The fact that realistic sound production often involves more than one
effect is satisfied by the three independently controllable channels
available in the PSG.

All of the circuit control signals are digital in nature and intended to
be provided directly by a microprocessor/microcomputer. This
means that one PSG can produce the full range of required sounds
with no change in external circuitry. Since the frequency response of
the PSG ranges from sub-audible at its lowest frequency to post-
audible at its highest frequency, there are a few sounds which are
beyond reproduction with only the simplest electrical connections.

Since most applications of a microprecessor/PSG system would also
require interfacing between outside world and the microprocessor
this facility has been designed into the PSG. The AY-3-8912 has
one port and 28 leads.

PIN CONFIGURATION
28 LEAD DUAL IN LINE
AY-3-8912
                            Top View
                       ___________________
                      |       \___/       |
    ANALOG CHANNEL C -|1                28|- DA0
                      |                   |
              TEST 1 -|2                27|- DA1
                      |                   |
           Vcc (+5V) -|3                26|- DA2
                      |                   |
    ANALOG CHANNEL B -|4                25|- DA3
                      |                   |
    ANALOG CHANNEL A -|5                24|- DA4
                      |                   |
            Vss(GND) -|6                23|- DA5
                      |                   |
                IOA7 -|7                22|- DA6
                      |                   |
                IOA6 -|8                21|- DA7
                      |                   |
                IOA5 -|9                20|- BC1
                      |                   |
                IOA4 -|10               19|- BC2
                      |                   |
                IOA3 -|11               18|- BDIR
                      |                   |
                IOA2 -|12               17|- A8
                      |                   |  _____
                IOA1 -|13               16|- RESET
                      |                   |
                IOA0 -|14               15|- CLOCK
                      |___________________|

PIN FUNCTIONS

DA7-DA0 (input/output/high impedance)
Data/Address 7--0:  pins 21--28
These 8 lines comprise the 8-bit bidirectional bus used by the
microprocessor to send both data and addresses to the PSG and to
recieve data from the PSG. In the data mode, DA7--DA0 correspond
to Register Array bits B7-B0. In the address mode, Da3--Da0 select
the register number (0--17) and a DA7--DA4 in conjunction with
address inputs A8 for high order address (chip select).

A8 (input): pin 17
Address 8
These "extra" address bit is made available to enable the positioning
of the PSG (assigning a 16 word memory space) in a total 512
word memory area rather than a 256 word memory area as defined
by address bits DA7--DA0 alone. If the memory size does not require
the use of this extra address line it may be left unconnected as
it is provided with an on-chip pull-up resistor. In "noisy" environments,
however, it is recommended that A8 is tied to +5V, if it is not to be
used.
_____
RESET (input): pin 16
For initialization/power-on purposes, applying a logic "0" (ground)
to the reset pin will reset all registers to "0". The reset pin is provided
with an on-chip pull-up resistor.

CLOCK (input): pin 15
This TTL-compatible input sipplies the timing reference for the
Tone, Noise and Envelope Generators.

BDIR, BC2, BC1 (inputs: pins 18, 19, 20
Bus DIRection, Bus Control 2, 1
These bus control signals are generated directly by the CP1610
series of microprocessors to control all external and internal bus
operations in the PSG. When using a processor other than the
CP1610, these signals can be provided either by comparable bus
signals or by simulating the signals on I/O lines or the processor. The
PSG decodes these signals as illustrated in the follwing:

BDIR BC2 BC1   CP1610         PSG
              FUNCTION      FUNCTION
  0   0   0     NACT        INACTIVE. See 010 (IAB).
  0   0   1     ADAR        LATCH ADDRESS, See 111 (INTAK).
  0   1   0     IAB         INACTIVE. The PSG/CPU bus is inactive DA7--DA0
                            are in high impedance state.
  0   1   1     DTS         READ FROM PSG. This signal causes the contents
                            of the register which is currently addressed to
                            appear on the PSG/CPU bus. DA7--DA0 are in the
                            output mode.
  1   0   0     BAR         LATCH ADDRESS. See 111 (INTAK).
  1   0   1     DW          INACTIVE. See 010 (IAB).
  1   1   0     DWS         WRITE TO PSG. This signal indicates that the bus
                            contains register data which should be latched into
                            the currently addressed register. DA7--DA0 are in
                            the input mode.
  1   1   1     INTAK       LATCH ADDRESS. This signal indicates that the bus
                            contains a register address which should be latched
                            in the PSG. DA7--DA0 are in input mode.

While interfacing to a processor other than the CP1610 would simply
require simulating the above decoding, the redundancies in the PSG
functions vs bus control signals can be used to advantage in that
only four of the eight possible decoded bus functions are required by
the PSG. This could simplify the programming of the bus control
signals to the following, which would only require that the processor
generate two bus control signals (BDIR and BC1, with BC2 tied to
+5V).

BDIR BC2 BC1      PSG
                FUNCTION
  0   1   0     INACTIVE.
  0   1   1     READ FROM PSG.
  1   1   0     WRITE TO PSG.
  1   1   1     LATCH ADDRESS.

ANALOG CHANNEL A, B, C (outputs): pins 5, 4, 1
Each of these signals is the output of its corresponding D/A
Converter, and provides an up to 1V peak-peak signal representing
the complex sound waveshape generated by the PSG.

IOA7--IOA0 (input/output): pins 7--14
Input/Output A7--A0
This parallel input/output port provides 8 bits of
prarllel data to/from the PSG/CPU bus from/to any external devices
connected to the IOA pins. Each pin is provided with an on-chip
pull-up resistor, so that when in the "input" mode, all pins will
read normally high. Therefore, the recommended method for scanning
external switches would be to ground the input bit.

TEST 1: pin 2
This pin is for General Instrument test purposes only and should
be left open - do not use as tie-point.

Vcc: pin 3
Nominal +5Volt power supply to the PSG.

Vss: pin 6
Ground reference for the PSG.


ARCHITECTURE

The AY-3-8912 is a register oriented Programmable Sound
Generator (PSG). Communication between the processor and the
PSG is based on the concept of memory-mapped I/O. Control
commands are issued to the PSG by writing to 16 memory-mapped
rigisters. Each of the 16 registers within the PSG is also readable
so that the mircroprocessor can determine, as necessary, present
states or stored data values.

All functions of the PSG are controlled through the 16 registers which
once programmed, generate and sustain the sounds, thus freeing the
system processor for other tasks.

REGISTER ARRAY
The principle element of the PSG is the array of 16 read/write control
registers. These 16 registers lock to the CPU as a block of memory
and as such occupy a 16 word block out of 512 possible addresses.
The 9 address bits (8 bits on the common data/address bus, and 1
separate address bit A8) are decoded as follows:

   ___ ___ ___ ___ ___ ___ ___ ___ ___
  |   |   |   |   |   |   |   |   |   |
  | A8|DA7|DA6|DA5|DA4|DA3|DA2|DA1|DA0|
  |___|___|___|___|___|___|___|___|___|
  |   |   |   |   |   |   |   |   |   |
  | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
  |___|___|___|___|___|___|___|___|___|
                THRU
   ___ ___ ___ ___ ___ ___ ___ ___ ___
  |   |   |   |   |   |   |   |   |   |
  | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 |
  |___|___|___|___|___|___|___|___|___|
   \_________________/ \_____________/
          \/                 \/
        HIGH ORDER        LOW ORDER
      (Chip Select)     (Register No.)

The four low order address bits select one of the 16 registers
(R0--R17). The 5 high order address bits function as "chip selects"
to control the tri state bidirectional buffers (when the high order
address bits are "incorrect", the bidirectional buffers are forced to a
high impedance state). High order address bit A8 is fixed in the
PSG design to recognize a 1 code; high order address bits DA7--DA4
may be maskk-programmed to any 4-bit code by a special order
factory mask modification. Unless otherwise specified, address bits
DA7--DA4 are programmed to recognize only a 0000 code. A valid
high order address latches the register address (the low order 4 bits)
in the Register Address Latch/Decode block. A latched address will
remain valid until the receipt of a new address, enabling multiple
reads and writes of the same register contents without the need for
redundant re-addressing.

Conditioning of the Register Address Latch/Decode and Bidirectional
Buffers to recognize the bus function required (anactive,
latch address, write data, or read) is accomplished by the Bus Control
Decode block.

SOUND GENERATING BLOCKS
The basic blocks in the PSG which produce the programmed sounds
include:
Tone Generator          produce the basic square tone frequencies
                        for each channel (A, B, C)

Noise Generator         produces a frequency modulated pseudo
                        random pulse width square wave output.

Mixers                  combine the outputs of the Tone Generators
                        and the Noise Generator. One for each channel
                        (A, B, C)

Amplitude Control       provides the D/A Converters with either a
                        fixed or variable amplitude pattern. The fixed
                        amplitude is under direct CPU control; the
                        variable amplitude is accomplished by using
                        the output of the Envelope Generator.

Envelope Generator      Produces an envelope pattern which can be
                        used to amplitude modulate the output of
                        each Mixer

D/A Converters          the three D/A Converters each produce up to
                        a 16 level output signal as determined by the
                        Amplitude Control

I/O PORT
One additional blcoks is shown in the PSG Block Diagramm which
has nothing directly to do with the production of sound - this is
an I/O Port (A). Since virtually all uses or microprocessor-based
sound would require interfacing between the outside world and the
processor, this facility has been included in the PSG.
Data to/from the CPU bus may be read/written to the 8-bit I/O Port
without affecting any other function of the PSG. The I/O
Port is TTL-compatible and is provided with internal pull-ups on
each pin.

OPERATION

Since all functions of the PSG are controlled by the processor
via a series of regiser loads, a detailed description of the PSG
operation can best be accomplished by relating each PSG function to
the control of its corresponding register. The function of creating or
programming a specific sound or sound effect logically follows the
control sequence listed:

        Operation               Registers     Function
Tone Generator Control           R0--R5       Program tone peroids.

Noise Generator Control            R6         Program noise peroid.

Mixer Control                      R7         Enable tone and/or noise
                                              on selected channels.

Amplitude Control               R10--R12      Select "fixed" or "envelope-
                                              variable" amplitudes.

Envelope Generator Control      R13--R15      Program envelope period
                                              and select envelope pattern.

Tone Generator Control
(Registers R0, R1, R2, R3, R4, R5)
The frequency of each square wave generated by the three Tone
Generators (one each for Channels A, B, and C) is obtained in the
PSG by first counting down the input clock by 16, the by further
counting down the result by the programmed 12-bit Tone Period
value. Each 12-bit value is obtained in the PSG by combining the
contents of the relative Coarse and Fine Tune registers, as illustrated
in the following:

   Coarse Tune Registers     Channel            Fine Tune Register
          R1                    A                      R0
          R3                    B                      R2
          R5                    C                      R4

   B7 B6 B5 B4 B3 B2 B1 B0                      B7 B6 B5 B4 B3 B2 B1 B0
   \_________/ I          \                    /                      /
       \/      I            \                /                      /
    NOT USED   I             I ____________/                      /
             /               I/                              ___/
            TP11 TP10 TP9 TP8 TP7 TP6 TP5 TP4 TP3 TP2 TP1 TP0
                12-bit Tone Period (TP) to Tone Generator

Noise Generator Control
(Register R6)
The frequency of the noise source is obtained in the PSG by first
counting down the input clock by 16, then by further counting down
the result by the programmed 5-bit Noise Period value. This 5-bit
value consists of the lower 5-bits (B4-B0) of register R6, as
illustrated in the following:

               Noise Period Register R6

               B7 B6 B5 B4 B3 B2 B1 B0
               \______/ \___________/
                  \/         \/
               NOT USED   5-bit Noise Period (NP)
                          to Noise Generator

Mixer Control-I/O Enable
(Register R7)                     ______
Register R7 is a multi functional Enable register which controls the
three Noise/Tone Mixers and the general purpose I/O Port.

The Mixers, as previously described, combine the noise and tone
frequencies for each of the three channels. The determination of
combining neither/either/both noise and tone frequencies on each
channel is made by the stae of bits B5-B0 or R7.

The direction (input or output) of the general purpose I/O Port
(IOA) is determined by the state of bit B6 or R7.

These functions are illustrated in the following:

               Mixer Control-U/O Enable Register R7

               B7 B6 B5 B4 B3 B2 B1 B0
   NOT USED____/  I  \______/ \______/
                  I   __\/       \/_______
    ____________ /   I___________        I__________
    Input Enable     Noise Enable        Tone Enable  <-- Function
    I/O Port A        C   B   A           C   B   A   <-- Channel

Amplitude Control
(Registers R10, R11, R12)
The amplitudes of the signals generated by each of the three D/A
Converters (one each for Channels A, B, and C) is determined by the
contents of the lower 5 bits (B4--B0) of registers R10, R11, and R12 as
illustrated in the follwing:

       Amplitude Control Register    Channel
                 R10                   A
                 R11                   B
                 R12                   C

               B7 B6 B5 B4 B3 B2 B1 B0
               \______/  I \________/
                  \/     I     \/
               NOT USED  I     L3 L2 L1 L0
                         I     4-bit "fixed" amplitude Level.
                         I
                         M
                   amplitude "Mode"

Envelope Generator Control
(Registers R13, R14, R15)
To accomplish the generation of fairly complex envelope patterns,
two independent methods of control are provided in the PSG: first, it
is possible to vary the frequency of the envelope using registers R13
and R14; and second, the relative shape and cycle pattern of the
envelope can be varied using register R15. The following paragraphs
explain the details of the envelope control functions, describing
first the envelope period control and then the envelope shape/cycle
control.

ENVELOPE PERIOD CONTROL (Registers R13, R14)
The frequency of the envelope is obtained in the PSG by first
counting down the input clock by 256, then by further counting down
the result by the programmed 16-bit Envelope Peroid value. This
16-bit value is obtained in the PSG by combining the contents of the
Envelope Coarse and Fine Tune registers, as illustrated in the
following:

       Envelope                                     Envelope
   Coarse Tune Registers                        Fine Tune Register
          R14                                          R13

   B7 B6 B5 B4 B3 B2 B1 B0 ____                B7 B6 B5 B4 B3 B2 B1 B0
  /                            \              /                      I
 I                               \          /                        I
 I                                 \      /                          I
 I                                   \  /                            I
 TP15 TP14 TP13 TP12 TP11 TP10 TP9 TP8 TP7 TP6 TP5 TP4 TP3 TP2 TP1 TP0
            16-bit Envelope Period (EP) to Envelope Generator

ENVELOPE SHAPE/CYCLE CONTROL (Register R15)
The Envelope Generator further counts down the envelope frequency
by 16, producing a 16-state per cycle envelope pattern as
defined by its 4-bit counter output, E3, E2, E1, E0. The particular shape
and cycle pattern of any desired envelope is accomplished by
controlling the count pattern (count up/count down) of the 4-bit
counter and by defining a single-cycle or repeat-cycle pattern.

This envelope shape/cycle control is contained in the lower 4 bits
(B3--B0) of register R15. Each of these 4 bits controls a function in
the envelope generator, as illustrated in the following:

        Envelope Shape/Cycle Control Register (R15)

               B7 B6 B5 B4 B3 B2 B1 B0     Function
               \_________/ I  I  I  I_____ Hold      \
                  \/       I  I  I________ Alternate I To Envelope
               NOT USED    I  I___________ Attack    I Generator
                           I______________ Continue  /

I/O Port Data Store
(Register R16)
Register R16 functions as intermediate data storage register
between PSG/CPU data bus (DA0--DA7) and the I/O port (IOA7--IOA0).
Using register 16 for the transfer of I/O data has no effect on
sound generation.

D/A Converter Operation
Since the primary use of the PSG is to produce sound for the highly
imperfect amplitude detection mechanism of the human ear, the D/A
conversion is performed in logarithmic steps with a normalized
voltage range of from 0 to 1 Volt. The specific amplitude control of
each of the three D/A Converters is accomplished by the three sets of
4-bit outputs of the Amplitude Control block, while the Mixer outputs
provide the base signal frequency (Noise and/or Tone).

##############################################################################

2.
--

A2. Programmable Sound Generator AY-3-8912 (from General Instruments data
    sheet)
------------------------------------------

Interfacing
     Bus control signals BDIR (Bus DIRection), BC2, BC1 (Bus Control 2,1) are
     generated directly by the CP1610 series of microprocessors to control
     all external and internal bus operations in the PSG. While interfacing
     to a processor other than the CP1610 would simply require simulating
     these signals, the redundancies in the PSG functions vs. bus control
     signals can be used to advantage in that only four of the eight possible
     decoded bus functions are required by the PSG. This could simplify the
     programming of the bus control signals to the following, which would
     only require that the processor generate two bus control signals (BDIR
     and BC1, with BC2 tied to +5V).

             BDIR BC1        PSG Function
              0    0         Inactive
              0    1         Read from PSG
              1    0         Write to PSG
              1    1         Latch address

Architecture
      The AY-3-8910/8912/8913 is a register oriented Programmable Sound
      Generator. Control commands are issued to the PSG by writing to 16
      registers (register addresses are expressed in octal base). Each of the
      16 registers is also readable so that the
      microprocessor can determine present states or stored data values. All
      functions of the PSG are controlled through the 16 registers which once
      programmed, generate and sustain the sounds, thus freeing the system
      processor for other tasks.
      The basic blocks in the PSG which produce the programmed sounds include:
              Tone Generators         produce the basic square wave tone
                                      frequencies for each channel (A,B,C)
              Noise Generator         produces a frequency modulated pseudo
                                      random pulse width square wave output
              Mixers                  combine the outputs of the Tone
                                      Generators and the Noise Generator. One
                                      for each channel (A,B,C)
              Amplitude Control       provides the D/A converters with either
                                      fixed or variable amplitude pattern. The
                                      fixed amplitude is under direct CPU
                                      control; the variable amplitude is
                                      accomplished by using the output of the
                                      Envelope Generator.
              Envelope Generator      produces an envelope pattern which can
                                      be used to amplitude modulate the output
                                      of each mixer
              D/A Converters          the three D/A converters each produce up
                                      to a 16 level output signal as
                                      determined by the Amplitude Control
      In addition, there are two IO ports (A and B) to interface with the
      outside world. Both ports are available on the AY-3-8910; only IO port A
      is available on the AY-3-8912; no ports are available on the AY-3-8913.

Operation
      The function of creating or programming a specific sound or sound effect
      logically follows the control sequence listed:

Tone Generator Control (registers R0,R1,R2,R3,R4,R5)
      The frequency of each square wave generated by the three Tone Generators
      (one each for Channels A, B and C) is obtained in the PSG by first
      counting down the input clock by 16, then by further counting down
      the result by the programmed 12-bit Tone Period value.
      Each 12-bit value is obtained in the PSG by combining the contents of
      the relative Coarse and Fine Tune registers: bits 0-3 of the Coarse Tune
      registers give the Most Significant bits, bits 0-7 of the Fine Tune
      registers give the Least Significant bits.

              Channel         Coarse Tune Register    Fine Tune Register
                 A                    R1                      R0
                 B                    R3                      R2
                 C                    R5                      R4

Noise Generator Control (register R6)
      The frequency of the noise source is obtained by first counting down the
      input clock by 16, then by further counting down the result by the
      programmed 5-bit Noise Period value. This 5-bit value consists of the
      lower 5 bits (b4-b0) of register R6.

Mixer Control - IO Enable (register R7)
      The determination of combining neither/either/both noise and tone
      frequencies on each channel is made by the state of bits b5-b0 of R7.
      The direction (input or output) of the two general purpose IO ports is
      determined by the state of bits b7 and b6.
      These bits are active low, so a 1 disables, and a 0 enables the function

              b7 input enable IO port A
              b6 input enable IO port B
              b5 noise enable channel C
              b4 noise enable channel B
              b3 noise enable channel A
              b2 tone enable channel C
              b1 tone enable channel B
              b0 tone enable channel A

Amplitude Control (registers R10,R11,R12)
      The amplitudes of the signals generated by each of the three D/A
      converters is determined by the contents of the lower 5 bits (b4-b0) of
      registers R10, R11 and R12.

              b5 Amplitude Mode (0: fixed, 1: envelope-variable)
              b4-b0 4-bit fixed amplitude level

Envelope Period Control (registers R13, R14)
      The frequency of the envelope is obtained by first counting down the
      input clock by 256, then by further counting down the result by the
      programmed 16-bit envelope period. This 16-bit value is obtained by
      combining the contents of the Envelope Coarse (R14) and Fine (R13) Tune
      registers.

Envelope Shape/Cycle Control (register R15)
      The envelope generator further counts down the envelope frequency by 16,
      producing a 16-state per cycle envelope pattern as defined by its 4-bit
      counter output. The particular shape and cycle pattern of any desired
      envelope is accomplished by controlling the count pattern (count up/
      count down) of the 4-bit counter and by defining a single-cycle or
      repeat-cycle pattern. This envelope shape/cycle control is contained in
      the lower 4 bits of register R15. Each of these 4 bits controls a
      function in the envelope generator:

              b3 Continue
              b2 Attack
              b1 Alternate
              b0 Hold

      R15 bits        Graphic representation of envelope generator output 8-)

        00xx          \__________________________________

        01xx          /|_________________________________

        1000          \|\|\|\|\|\|\|\|\|\|\|\|\|\|\|\|\|\

        1001          \__________________________________

        1010          \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
                        _________________________________
        1011          \|

        1100          /|/|/|/|/|/|/|/|/|/|/|/|/|/|/|/|/|/
                       __________________________________
        1101          /

        1110          /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/

        1111          /|_________________________________

D/A Converter operation
      The D/A conversion is performed in logarithmic steps with a normalized
      voltage range of from 0 to 1 Volt.


Appendix E:
Bit dump of characters in BIOS ROM.
(by Bruce Tomlin)

F9F4          F9F5   #      F9F6  # #     F9F7  # #     F9F8   #
FA44          FA45  ###     FA46  # #     FA47  # #     FA48  ####
FA94          FA95  ###     FA96  # #     FA97 #####    FA98 # #
FAE4          FAE5   #      FAE6          FAE7  # #     FAE8  ###
FB34          FB35          FB36          FB37 #####    FB38  ###
FB84          FB85          FB86          FB87  # #     FB88   # #
FBD4          FBD5   #      FBD6          FBD7  # #     FBD8 #####
       20            21            22            23            24

F9F9 ##  #    F9FA   #      F9FB    #     F9FC    #     F9FD  #
FA49 ##  #    FA4A  # #     FA4B   #      FA4C   #      FA4D   #
FA99    #     FA9A  # #     FA9B  #       FA9C  #       FA9D    #
FAE9   #      FAEA  ##      FAEB          FAEC  #       FAED    #
FB39  #       FB3A # # #    FB3B          FB3C  #       FB3D    #
FB89 #  ##    FB8A #  #     FB8B          FB8C   #      FB8D   #
FBD9 #  ##    FBDA  ## #    FBDB          FBDC    #     FBDD  #
       25            26            27            28            29

F9FE   #      F9FF          FA00          FA01          FA02
FA4E # # #    FA4F   #      FA50          FA51          FA52
FA9E  ###     FA9F   #      FAA0          FAA1          FAA2
FAEE # # #    FAEF #####    FAF0          FAF1  ###     FAF2
FB3E # # #    FB3F   #      FB40  #       FB41          FB42
FB8E          FB8F   #      FB90  #       FB91          FB92
FBDE          FBDF          FBE0 #        FBE1          FBE2 #
       2A            2B            2C            2D            2E

FA03     #    FA04   ##     FA05   #      FA06  ###     FA07  ###
FA53     #    FA54  #  #    FA55  ##      FA56 #   #    FA57 #   #
FAA3    #     FAA4  #  #    FAA5   #      FAA6     #    FAA7     #
FAF3   #      FAF4  #  #    FAF5   #      FAF6  ###     FAF7   ##
FB43  #       FB44  #  #    FB45   #      FB46 #        FB47     #
FB93 #        FB94  #  #    FB95   #      FB96 #        FB97 #   #
FBE3 #        FBE4   ##     FBE5  ###     FBE6 #####    FBE7  ###
       2F            30            31            32            33

FA08    #     FA09 #####    FA0A   ##     FA0B #####    FA0C  ###
FA58   ##     FA59 #        FA5A  #       FA5B     #    FA5C #   #
FAA8  # #     FAA9 ####     FAAA #        FAAB    #     FAAC #   #
FAF8 #  #     FAF9     #    FAFA ####     FAFB   #      FAFC  ###
FB48 #####    FB49     #    FB4A #   #    FB4B  #       FB4C #   #
FB98    #     FB99 #   #    FB9A #   #    FB9B #        FB9C #   #
FBE8    #     FBE9  ###     FBEA  ###     FBEB #        FBEC  ###
       34            35            36            37            38

FA0D  ###     FA0E          FA0F  ##      FA10          FA11
FA5D #   #    FA5E  ##      FA5F  ##      FA60    #     FA61
FAAD #   #    FAAE  ##      FAAF          FAB0   #      FAB1  ####
FAFD  ####    FAFE          FAFF  ##      FB00  #       FB01
FB4D     #    FB4E  ##      FB4F  ##      FB50   #      FB51  ####
FB9D    #     FB9E  ##      FB9F   #      FBA0    #     FBA1
FBED  ##      FBEE          FBEF  #       FBF0          FBF1
       39            3A            3B            3C            3D

FA12          FA13  ###     FA14  ###     FA15   #      FA16 ####
FA62  #       FA63 #   #    FA64 #   #    FA65  # #     FA66  #  #
FAB2   #      FAB3     #    FAB4 # # #    FAB5 #   #    FAB6  #  #
FB02    #     FB03    #     FB04 # ###    FB05 #   #    FB06  ###
FB52   #      FB53   #      FB54 # ##     FB55 #####    FB56  #  #
FBA2  #       FBA3          FBA4 #        FBA5 #   #    FBA6  #  #
FBF2          FBF3   #      FBF4  ####    FBF5 #   #    FBF6 ####
       3E            3F            40            41            42

FA17  ###     FA18 ####     FA19 #####    FA1A #####    FA1B  ####
FA67 #   #    FA68  #  #    FA69 #        FA6A #        FA6B #
FAB7 #        FAB8  #  #    FAB9 #        FABA #        FABB #
FB07 #        FB08  #  #    FB09 ###      FB0A ###      FB0B #  ##
FB57 #        FB58  #  #    FB59 #        FB5A #        FB5B #   #
FBA7 #   #    FBA8  #  #    FBA9 #        FBAA #        FBAB #   #
FBF7  ###     FBF8 ####     FBF9 #####    FBFA #        FBFB  ####
       43            44            45            46            47

FA1C #   #    FA1D  ###     FA1E     #    FA1F #   #    FA20 #
FA6C #   #    FA6D   #      FA6E     #    FA6F #  #     FA70 #
FABC #   #    FABD   #      FABE     #    FABF # #      FAC0 #
FB0C #####    FB0D   #      FB0E     #    FB0F ##       FB10 #
FB5C #   #    FB5D   #      FB5E     #    FB5F # #      FB60 #
FBAC #   #    FBAD   #      FBAE #   #    FBAF #  #     FBB0 #   #
FBFC #   #    FBFD  ###     FBFE  ###     FBFF #   #    FC00 #####
       48            49            4A            4B            4C

FA21 #   #    FA22 #   #    FA23 #####    FA24 ####     FA25  ###
FA71 ## ##    FA72 ##  #    FA73 #   #    FA74 #   #    FA75 #   #
FAC1 # # #    FAC2 # # #    FAC3 #   #    FAC4 #   #    FAC5 #   #
FB11 # # #    FB12 #  ##    FB13 #   #    FB14 ####     FB15 #   #
FB61 #   #    FB62 #   #    FB63 #   #    FB64 #        FB65 # # #
FBB1 #   #    FBB2 #   #    FBB3 #   #    FBB4 #        FBB5 #  #
FC01 #   #    FC02 #   #    FC03 #####    FC04 #        FC05  ## #
       4D            4E            4F            50            51

FA26 ####     FA27  ###     FA28 #####    FA29 #   #    FA2A #   #
FA76 #   #    FA77 #   #    FA78 # # #    FA79 #   #    FA7A #   #
FAC6 #   #    FAC7  #       FAC8   #      FAC9 #   #    FACA #   #
FB16 ####     FB17   #      FB18   #      FB19 #   #    FB1A  # #
FB66 # #      FB67    #     FB68   #      FB69 #   #    FB6A  # #
FBB6 #  #     FBB7 #   #    FBB8   #      FBB9 #   #    FBBA   #
FC06 #   #    FC07  ###     FC08   #      FC09  ###     FC0A   #
       52            53            54            55            56

FA2B #   #    FA2C #   #    FA2D #   #    FA2E #####    FA2F  ###
FA7B #   #    FA7C #   #    FA7D #   #    FA7E     #    FA7F  #
FACB #   #    FACC  # #     FACD  # #     FACE    #     FACF  #
FB1B # # #    FB1C   #      FB1D   #      FB1E   #      FB1F  #
FB6B # # #    FB6C  # #     FB6D   #      FB6E  #       FB6F  #
FBBB # # #    FBBC #   #    FBBD   #      FBBE #        FBBF  #
FC0B  # #     FC0C #   #    FC0D   #      FC0E #####    FC0F  ###
       57            58            59            5A            5B

FA30 #        FA31  ###     FA32   #      FA33          FA34
FA80 #        FA81     #    FA82  # #     FA83          FA84
FAD0  #       FAD1     #    FAD2 #   #    FAD3          FAD4  ###
FB20   #      FB21     #    FB22          FB23          FB24 #######
FB70    #     FB71     #    FB72          FB73          FB74 #######
FBC0     #    FBC1     #    FBC2          FBC3          FBC4  #  #
FC10     #    FC11  ###     FC12          FC13 #####    FC14
       5C            5D            5E            5F            60

FA35   #      FA36     #    FA37   #      FA38          FA39
FA85  ###     FA86     ##   FA87   #      FA88  ###     FA89  ###
FAD5 # # #    FAD6     # #  FAD7   #      FAD8 #   #    FAD9 #####
FB25   #      FB26     #    FB27   #      FB28 #   #    FB29 #####
FB75   #      FB76  ####    FB77 # # #    FB78 #   #    FB79 #####
FBC5   #      FBC6 ####     FBC7  ###     FBC8  ###     FBC9  ###
FC15   #      FC16  ##      FC17   #      FC18          FC19
       61            62            63            64            65

FA3A          FA3B   ###    FA3C    #     FA3D   #      FA3E  #   #
FA8A          FA8B  #   #   FA8C    #     FA8D  ###     FA8E
FADA  ##      FADB # ### #  FADC   ###    FADD   #      FADE
FB2A ####     FB2B # #   #  FB2C   ###    FB2D #####    FB2E #     #
FB7A ####     FB7B # ### #  FB7C  #####   FB7D   #      FB7E  #   #
FBCA  ##      FBCB  #   #   FBCC  ## ##   FBCD  # #     FBCE   ###
FC1A          FC1B   ###    FC1C #     #  FC1D #   #    FC1E
       66            67            68            69            6A

FA3F  #   #   FA40          FA41 #######  FA42 ######## FA43 #######
FA8F          FA90  ## ##   FA91 #     #  FA92 ######## FA93 #######
FADF          FAE0 #  #  #  FAE1 #     #  FAE2 ######## FAE3 #######
FB2F   ###    FB30 #  #  #  FB31 #     #  FB32 ######## FB33 #######
FB7F  #   #   FB80  ## ##   FB81 #     #  FB82 ######## FB83 #######
FBCF #     #  FBD0          FBD1 #     #  FBD2 ######## FBD3 #######
FC1F          FC20          FC21 #######  FC22 ######## FC23 #######
       6B            6C            6D            6E            6F


Appendix F:
Notes supported by the BIOS sound functions:

G2      EQU     $00               G = 1.5 8ves below middle C
GS2     EQU     $01               G sharp (second 8ve) , etc.
A2      EQU     $02
AS2     EQU     $03
B2      EQU     $04
C3      EQU     $05
CS3     EQU     $06
D3      EQU     $07
DS3     EQU     $08
E3      EQU     $09
F3      EQU     $0A
FS3     EQU     $0B
G3      EQU     $0C
GS3     EQU     $0D
A3      EQU     $0E
AS3     EQU     $0F
B3      EQU     $10
C4      EQU     $11
CS4     EQU     $12
D4      EQU     $13
DS4     EQU     $14
E4      EQU     $15
F4      EQU     $16
FS4     EQU     $17
G4      EQU     $18
GS4     EQU     $19
A4      EQU     $1A
AS4     EQU     $1B
B4      EQU     $1C
C5      EQU     $1D
CS5     EQU     $1E
D5      EQU     $1F
DS5     EQU     $20
E5      EQU     $21
F5      EQU     $22
FS5     EQU     $23
G5      EQU     $24
GS5     EQU     $25
A5      EQU     $26
AS5     EQU     $27
B5      EQU     $28
C6      EQU     $29
CS6     EQU     $2A
D6      EQU     $2B
DS6     EQU     $2C
E6      EQU     $2D
F6      EQU     $2E
FS6     EQU     $2F
G6      EQU     $30
GS6     EQU     $31
A6      EQU     $32
AS6     EQU     $33
B6      EQU     $34
C7      EQU     $35
CS7     EQU     $36
D7      EQU     $37
DS7     EQU     $38
E7      EQU     $39
F7      EQU     $3A
FS7     EQU     $3B
G7      EQU     $3C
GS7     EQU     $3D
A7      EQU     $3E
AS7     EQU     $3F

Appendix G:
Links to Vectrex sites (April 1998):

My (Malban) vectrex page. Including information, DVE (newest version),
VPONG and VFROG + source, links and other
http://members.aol.com/vectrexcs

The Raven's Retro Nest
Great general vectrex site
http://website.lineone.net/~raven


The Egress: Vectrex
Omega Chase source and general programming information
http://www.freenet.carleton.ca/~aa993/vectrex.htm
http://www.freenet.carleton.ca/~aa993/tutorial.txt

The Unofficial Vectrex Page
PC Johns Vectrex home, many things... First new vectrex programmer, much
information, pics, source, development stuff, and news!
http://www.monmouth.com/~pcjohn

The Vectrex Overlay Home Page
New overlays for vectrexs
http://home.earthlink.net/~mfmurdock/Vectrex/vectrex.htm

The Vectrex
A small vectrex page, with some information, value guide, and game review
http://home1.gte.net/bstanton/vectrex.html

Clay Cowgill Vectrex page
Some information, and source code to stuff he has done
http://raptor.e-volve.net/~clay/Vectrex.html

Digital Press Vectrex emulator page
http://www.digitpress.com/emu/vect_idx.htm

Digital Press Vectrex reviews
http://www.digitpress.com/frconten.htm#Vectrex

Greg Chance History of Home Video Games Homepage!
Vectrex page, general
http://www.videogames.org/VectrexStuff/VectrexIndex.html

Classic Home Video Games Museum
http://ns2.apmtech.com/dbrown/museum/history/gen3/vectrex/vectrex.html

Multi Cards from Sean Kelly
http://www.xnet.com/~skelly/multis.htm

Dos Vectrex Emulator US Mirror
http://gator.naples.net/~saturn/vectrex/dve/

CVG Nexus
Vectrex, many links
http://home.hiwaay.net/~lkseitz/cvg/nexus/Home/Vectrex/

Lunar lander for vectrex + source (unfinnished)
http://www.worldnet.net/~tomsoft/Vectrex.html

Vectrex Overlays
for the emulator
http://members.aol.com/WaltDG/waltvol.html

Viva Vectrex
Game reviews...
http://value.net/~bpacula/vectrex.html

http://www.videogames.org/VectrexStuff/VectrexIndex.html

http://www.net-link.net/~cknape/vectrex.html

http://www.primenet.com/~rworne/vectrex.shtml

Vectrex ftp site
ftp://ftp.csus.edu/pub/vectrex/

Usenet:
news:rec.games.vectrex

