wje@sii.UUCP (Bill Ezell) (04/26/84)
b
Here is almost everything that you ever wanted to know about the
Vectrex display. First, some introductory information.
The processor used in the display is a 6mhz 68A09, which is a
pretty good processor for an 8-bitter.
The address space is split up as follows:
0000
.
C7FF address space available to external rom or ram
C800
.
CBFF internal ram (1k)
D004 PIA timer 1 low order byte (sets magnification factor)
D008 PIA timer 2 low order byte
D009 PIA timer 2 hi order byte
D00A PIA shift register
E000
.
EFFF Mine Storm game (4k)
F000
.
FFFF 'executive' routines (4k)
Several locations in the ram are used by the system rom, and you need
to know about some of them. What they do in more detail will be described
in the individual routine writeups.
Here are some important addresses:
C80F-11 used by switches() routine, don't touch
C812-19 state of buttons on both consoles
C81A conversion resolution for joystick
C81B-E values of the 4 joystick pots
C81F-22 enable flags for joystick pots
C823 joystick mode; <0, successive approx., >=0 l/r/u/d
C824 check0ref enable flag, 0 -> no, else yes
C825,6 waitrecal call counter
C827 current z axis value, set by zaxis routines
C828 dot dwell time
C829 blanking pattern to shift out during drawing
C82A,B used to store 1st 2 bytes of param block by printu()
C82C,D used to save ureg by printu()
C837,8 used to store cartridge address ptr during bootup
C83B if 0, don't display score on reboot, else show score
C83D,E t2 reload value, t2-l first. x3075 is 20 msec
C83F-4C 14 byte buffer, used to output to sound chip
C84D used by sound chip routines
C84F,0 saves first two bytes of a music block
C851,2 saves next two bytes of a music block
C853,4 save ptr pointing to 5th byte of music block
C856 1 when music ready, x80 when music rtn is called, 0 when done
C85E
C87D incremented by restart
CBEA sp is set here by boot
CBEB-F0 (6 bytes), holds current score in ascii form
CBF1 terminates above string, don't use
CBF2 swi 2,3 vectors here
CBF5 firq vectors here
CBF8 irq vectors here
CBFB swi, nmi vectors here
CBFE,F checked by reset for x7321, decides whether this is cold boot
reset clears C800->7A
Before getting into the routines, a little discussion of the hardware
is appropriate.
In general, all vectors are drawn relative to the last beam position.
Two values, the relative coordinates to move to, are sent to a d/a and
loaded into the y and x sample/holds (there isn't really one for x,
but pretend that there is). These values are applied to integrators,
which when enabled will cause the beam to move at a rate proportional
to the values times the duration of integration. What this means is
that for a given set of vectors, the drawn object can be zoomed just
by changing the integration time. The PIA timer 1 is used to control
integration time. One disadvantage of this method is that accumulated
integration errors can become large. It's necessary to recalibrate the
integrators every now and then, and a routine is provided to do this.
PIA timer 2 is used for determining refresh rate, but may be used by
application code with a little care.
The shift register in the PIA is used to control the beam blanking
pattern when drawing vectors; this allows patterned lines to be
drawn.
The sound chip is an unknown area. If anyone has any information
on the AY3-8192 chip from General Instruments, I'd appreciate it.
Ok, now for some stuff on using your own proms and writing your
own stuff for the Vectrix.
The rom in the game cartridges is compatible with 2532's, with
the minor exception that pin 21, which is Vpp on the 2532, is
an additional enable on the rom, enabled by logic 0. (~enable)
This is inconvenient since pin 20, ~E, is driven by the processor
E signal. This means that you need to do a little additional
decoding, or use a prom with 2 enable inputs, such as a 2716,
which only gives you 2K. In any case, all processor lines are
available at the cartridge connector, so you can do whatever you
want.
Ok, on the the good stuff.
When the Vectrex boots, it puts up its little intro and then looks
at location 0000-000A for the string 'g GCE ????' followed by 0x80,
where '?' is any character. Why 'g'? The Vectrix uses lower case
characters to select special symbols. 'g' is the copyright symbol.
If this isn't found, it runs 'Mine Storm'.
If it is found, it expects the next two bytes to be a pointer to a
music block (more later), followed by a string block.
A string block is used by several routines to display strings of text.
It consists of a 4 byte header, followed by the ascii characters,
terminated by 0x80. More string blocks may immediately follow. The last
block must have 0x00 after it.
The first byte of the header is the height of the
displayed string, the second is the width, the third is the relative
y coordinate of the start of the string, the fourth is the relative x
coordinate. I should note that the coordinate system used has (0,0) at
the center of the screen, +y is up, +x is right. Remember, however, that
all vectors are drawn relative to the last beam position.
The height and width bytes interact to some extent, and sometimes changing
the width disproportionally to the height will give slanted characters.
The two size bytes are signed, so it is possible to draw backwards and/or
upside down characters. The y,x coordinate corresponds to the upper
left-hand corner of the first character, so to get a normal string, you
need to use a negative height (draw down from upper left hand corner) and
a positive width (draw right from upper left hand corner). The vertical
size covers a much greater size range than the horizontal; play around a bit.
The sizes used in the 'MINE STORM' string are -8, 0x50 respectively.
Immediately after the string block is the first instruction to execute.
To recap, a valid rom looks like this:
.org 0;
.byte 'g GCE 9999', 0x80; /* special case, needs no 0x0 */
.word music_block_addr;
.byte 0xf8,0x50,0x20,0xe8,'MY ROM',0x80,0x0;
first executable instruction
A music block consists of a 4 byte header, followed by pairs of bytes.
The first two bytes are the address of a parameter block that configures
the sound chip to simulate various things, the next two bytes I have no
idea about. The sequence '.word 0xED8F, 0xFEB6;' works. The byte pairs
consist of a note and a duration. The note scale starts at 'C', which
is 0, and goes up. It includes chromatics; C# is 1, D is 2, etc.
The note list is terminated by 0x0, 0x80;
The following will play 'Mary had a little lamb':
music: .word 0xED8F;
.word 0xFEB6;
.byte 4,30, 2,10, 0,20, 2,20, 4,20, 4,20, 4,40;
.byte 0, 0x80; /* end of music */
After the boot code has determined that you have a valid rom, it plays
your music, displays your text string, your copyright notice, and then
jumps to the first instruction in your cartridge. There is unfortunately
no way around this nonsense without making a new exec rom.
Ok, now that you are booted, here are some routines, entry points, etc.
that let you do real stuff:
check0ref 0xF34F
If 0xC824 is non-zero, call reset0ref (see below), else return.
Dp is assumed to be D0. Many of the line drawing routines
call this guy, so if you want to be sure that your dot position
doesn't magically go to (0,0), be sure 0xC824 is 0.
dotixb 0xF2BE
Draw a dot at the rel. y, rel. x coordinates pointed to by the x
register, with an intensity proportional to the value in the b
register. The x register is incremented by 2. Dp is assumed to
be D0.
dptoC8 0xF1AF
The dp register is set to C8, direct addresses start at C800.
The a register is trashed.
dptoD0 0xF1AA
The dp register is set to D0, direct addresses start at D000.
This is used to access the PIA.
The a register is trashed.
drawl1 0xF40C
Draw a list of vectors. The x register has a pointer to a vector list
of the following form:
.byte integration time (scale factor or magnification)
followed by byte triplets of the form:
.byte mode, rel. y coord., rel. x coord
where mode:
< 0 visible vector
= 0 invisible vector
> 0 end of list, return to caller
The dp register is assumed to be set to D0.
drawl1b 0xF40E
Draw a list of vectors, just like above, except the magnification
is in the b register, not in the vector list.
drawl2 0xF46E
Draw a list of vectors, with a few added twists. This routine does not
set or change the magnification factor, it is whatever is currently
in t1-l in the PIA. It uses the same triplet structure as above, except
the mode byte values, which are:
< 0 use value in C829, 0 is invisible, 0xFF is visible
= 0 invisible
= 1 end of list, return to caller
> 1 visible
joystick 0xF1F8
Read the joystick positons of the two consoles. This routine requires
a fair amount of setup before calling. First, C81F-22 are enable flags.
EVERY ONE must be set to one of:
0 ignore
1 pot 0 (console 1 left/right)
3 pot 1 (console 1 up/down)
5 pot 2 (console 2 left/right)
7 pot 3 (console 2 up/down)
Locations C81B-1D will be set with the pot values corresponding to the
enable flags.
Location C823 determines what kind of input conversion to do.
If it is >= 0, the return value will be <0, 0, >0 depending upon
whether the joystick is left (or down) of center, centered, or
right (up) of center.
If it is < 0, a successive approximation algorithm is used to read the
actual value of the joystick pot, a signed value. In this case, C81A
must bet set to a power of 2 to control conversion resolution. 0x80 is
least accurate, 0x00 is most accurate. Any value other than a power
of 2 is treated as 0. Dp is assumed to be set to D0. The routine clears
C823 before returning!
move170u 0xF308
Move the (invisible) dot. The x register points to a byte pair, rel. y
and rel. x, to move to. The integration time is set to 170 usec. This
routine leaves t1-l set to 0xFF (170 usec). Dp is assumed to be D0.
The x register is incremented by 2 before returning.
move85u 0xF30C
Same as above, except t1-l is set to 0x7F (85 usec).
moved 0xF312
Move the (invisible) dot to the rel. y in the a register, rel. x in
the b register. The integration time is whatever was last set in t1-l,
which is left unchanged. Dp is assumed to be D0.
moveix 0xF310
Same as move170u, except the current value of t1-l is used, and is
left unchanged, like moved above. Dp is assumed to be D0.
printu 0xF385
Display the string whose string block is ptd to by the u register.
See above a bit for what a string block is. Dp is assumed to be D0.
reset0ref 0xF354
This routine zeros the integrators and the reference cap for them.
Call this every once in a while during a complex set of vector drawing
to keep everything matched up. It resets the origin to (0,0).
This routine leaves the integrators in zero mode, so you can't draw
anything until you do a move, or set 0xD00C to 0xCE. Dp is assumed
to be D0;
startt2 0xF1A2
This routine loads the refresh timer, t2, with the value in C83D,E
and recalibrates the vector generators, leaving the dot off at
location (0,0), the center of the screen. Note that C83D is loaded
into t2-l, C83E into t2-h. T2 decrements at 1.5 mhz; a value
of 0x3075 in C83E will give a refresh rate of 50 hz.
Dp is assumed to be D0.
switches 0xF1B4
Read the switch values of the two consoles. Locations C812-19 are
set, corresponding to console 1, switch 1,2,3,4, console 2 switch 1,2,3,4.
Before calling the routine, the a register must contain a bit mask,
bit 0 corresponding to cons. 1, sw 1, etc. If a bit is 0, the current
value of the switch is returned in the appropriate location, 1 for down,
0 for up. If a bit is 1, the return location is set to 1 only on the
depression transititon of the button. Additional calls will return 0 until
the switch is released, and then depressed again. Dp is assumed to be
set to D0.
waitrecal 0xF192
This routine waits for t2 to time out, reloads it from 0xC83D, and
then recalibrates the vector generators to (0,0). You MUST call
this routine once every refresh cycle, or your vectors will go
bonzo, due to accumulated integration errors in the hardware.
This routine calls reset0ref, so the integrators are left in zero
mode. Dp is assumed to be D0.
zaxto1F 0xF29D
zaxto3F 0xF2A1
zaxto5F 0xF2A5
zaxto7F 0xF2A9
These routines set the z axis (intensity) sample/hold to 0x1F, 0x3F,
etc. The z axis should be reloaded at least every refresh cycle; you
can certianly change it on the fly. The current value is saved in
0xC827. Dp is assumed to be D0.
zaxtoa 0xF2AB
Same as above, except the z axis value to use is in the a register.
There are many more entry points; if anyone is interested, let me know
and I'll prepare another article. Also, I have a 6809 assembler that
runs under Unix and produces Intel hex format output. If anyone wants
it, I'll provide it gratis. If enough people want it, I'll post it.
Bill Ezell
Software Innovations, Inc.
(decvax,ittvax)!sii!wje
(603) 883-9300
The following program draws an expanding square with its lower left hand
corner at the center of the screen:
#define dptoC8 0xF1AF
#define dptoD0 0xF1AA
#define drawl1b 0xF40E
#define waitrecal 0xF192
#define xaxto7F 0xF2A9
#define mag 0xC900
.text;
.org 0x0;
.byte 'g',' ','G','C','E',' ','2','0','0','1',0x80;
.word music;
.word 0xf850;
.word 0x30e8;
.byte 'B','O','X', 0x80,0;
start: clr mag; /* start with smallest box */
ldd #0x3075;
std 0xC83D; /* set t2 timer for 20 msec */
jsr dptoD0;
std 0x8; /* start t2 */
loop: jsr waitrecal; /* wait for end of cycle */
jsr xaxto7F; /* adjust beam intensity */
lda #0xCE;
sta 0xC; /* get out of zero mode! */
inc mag; /* grow box once each cycle */
ldb mag;
ldx #box;
jsr drawl1b; /* xreg points to dlist now */
bra loop;
boxloc: .byte 0,0;
box: .byte -1,0,50;
.byte -1,50,0;
.byte -1,0,-50;
.byte -1,-50,0;
.byte 1;
/* the startup stuff points to this as our initial music */
music:
.word 0xed8f;
.word 0xfeb6;
.byte 4,30, 2,10, 0,20, 2,20, 4,20, 4,20, 4,40;
.byte 0, 0x80; /* end of music */