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 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 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 Many thanks for corrections to Wolfgang Lorenz 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 = 40øC to +70øC PB1 =| 11 30 |= D3 | | E = 40øC to +85øC 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