Pet Machine Language Graphics
David Malmberg, Fremont, CA
The PET has great graphics for almost any application, especially games. The only drawbacks I have found are that sometimes the graphics are not fast enough, or certain special effects (such as reversing only a section of the screen) cannot be done easily or quickly in a BASIC program without resorting to writing special subroutines in machine language. After many frustrating attempts to get the graphics to do exactly what I wanted in various machine language routines, I decided to write a general machine language subroutine that could be easily called from a BASIC program and would give the PET a wide-ranging repertoire of graphics "tricks."
Listing 1 is a BASIC program that POKEs this general machine language subroutine into the second cassette buffer and into the top three pages (a page is 256 bytes) of memory. This program then resets the memoy boundaries to protect the machine code from any BASIC programs. This is done automatically and is independent of the memory size. The program will also determine which of the various ROMs are in the PET and modify the machine code accordingly. It will work with "old", "new" of "4.0" ROMs. However, it will not work with the new 80-column PETs.
Once the subroutine has been loaded, it will give your BASIC programs significantly enhanced graphics capabilities. Specifically, you will be able to define a rectangular area on the screen and manipulate that area at machine language speed. The rectangle may be as small as a single space or as large as the entire screen. The area may be manipulated in the following ways:
Filled with any character
Flashed on and off (i.e., fast multiple reversing)
Repositioned elsewhere on the screen
Moved (animation) in any direction at any speed with or without screen wraparound
Made to grow or shrink in size
Using The Subroutine
Your BASIC program would use the subroutine by POKEing various values into the subroutine's parameter list and then issuing a SYS(826) command. The parameter list and the corresponding POKE locations are given below:
700 Starting row (SR) (0 to 24) 701 Last row (LR) (0 to 24) 702 Starting column (SC) (0 to 39) 703 Last column (LC) (0 to 39) 704 Fill character (FC) 705 Row move direction (RD) 0 = Up 1 = Down or to side 706 # of rows to move (RM) 707 Column move direction (CD) 0 = Left 1 = Right or even 708 # of column to move (CM) 709 # of jiffies delay between iterations (JD) 710 Wraparound factor (WF) 0 = Wraparound is OK 1 = Disappear off screen edge 2 = Move to edge only 711 # of iterations before returning to BASIC (IT)
The letters inside the parentheses are short-hand variable names to which I have found it useful to assign the values of the POKE locations at the beginning of the BASIC program using the subroutine. In this way I don't have to remember that Jiffy Delay is location 709, rather I can just POKE JD, 6 if I want a 6 jiffy (i.e., 1/10 second) delay between iterations. Using these parameter names also reduces the chance of errors, and is faster since BASIC handles variables faster than constants. I recommend you adopt the use of these parameter variables when using this subroutine.
Listing 2 is a BASIC program that demonstrates the full range of capabilities of the graphics subroutine. You are urged to key it in, run it and then study it to see just how each of the graphic effects was obtained. You will find it very informative.
At this point it is appropriate to describe in more detail just how the parameters can be used to generate various graphic effects. NOTE: In the discussions that follow all of the parameters are assumed to be zero unless specifically stated otherwise. In fact you will find it convenient to GOSUB to a routine to zero all of the parameters before beginning any new graphics, e.g., GOSUB 7000 in Listing 2.
Defining The Rectangle
The rectangular area is defined by the values of the parameters in locations 700 to 703. The area is the intersection of the defined rows and columns. The routine assumes that the "first" row or column on the screen is number zero, not number one. If the value of the starting row (starting column) is greater than the last row (last column) the routine will assume that the rectangle "wraparound" the edge of the screen. The rectangle may be the entire screen or a single space.
Filling The Area
If you wish to fill the rectangular area with a character, location 704 (short-hand FC) is POKEd with the ASCII value of the desired character. For example, the following lines of code will build a border around the screen "W" wide composed of character "C":
1 POKE FC,C : POKE SR,0 : POKE LR,24 2 POKE SC,40-W : POKE LC, W-l : SYS(826) 3 POKE SC,0 : POKE LC, 39 4 POKE SR,25-W : POKE LR, W-l : SYS(826)
Lines 1 and 2 generate the sides of the border, and 3 and 4 the top and bottom. Notice that the routine uses the wraparound (start > last) feature to generate two sides of the border with the same subroutine call.
Reversing And Flashing
When you wish to reverse the area, the Fill Character, location 704, is POKEd with zero. A special case of reversing is to flash the rectangle on and off with fast multiple reversing. This effect is obtained by POKEing location 711 (IT) with the number of times the area is to be reversed, and location 709 (JD) with the number of jiffies to delay between each reverse cycle. For example, the following code will flash the entire screen on and off by reversing it "N" times at a speed of "D" jiffies:
1 POKE FC,0 : POKE JD,D : POKE IT,N 2 POKE SR,0 : POKE LR,24 3 POKE SC,0 : POKE LC,39 : SYS(826)
Repositioning The Area
The rectangle can be repositioned in a different location on the screen by setting the parameters in locations 705 to 708. Location 705 (RD) is POKEd with a zero if the relative displacement of the new position is up and with 1 if it is down or even. Location 707 (CD) is POKEd with 0 if the displacement is left and with 1 otherwise. Locations 706 (RM) and 708 (CM) are the number of rows and columns, respectively, the area is to be displaced. For example,
1 POKE RD,0 : POKE RM,10 2 POKE CD,1 : POKE CM,5
will reposition the area five columns to the right and ten rows up.
If the "old" area is to be blanked out after the repositioning, the Fill Character (FC = 704) should have been previously POKEd with 32, i.e., an ASCII blank. If FC is zero rather than 32, then both the "old" and "new" areas will be visible on the screen after returning from the graphics subroutine.
Since this repositioning is done by relative displacement rather than absolute positioning on the screen, there will be instances when the new position will be "off the edge." Just how the routine handles these situations is determined by the value of the Wraparound Factor (WF =710). If this value has been POKEd with a zero, the routine will automatically wraparound to the other edge(s) of the screen. If WF is 1, the portion of the rectangle that goes over the edge will not be shown. If WF is 2, the routine will automatically recalculate the reposition parameters so that the rectangle stops just at the edge of the screen.
Motion Or Animation
Motion, or animation, is handled very much like repositioning, except that the increments of displacement are smaller (typically only one row and/or column) and the number of iterations (IT = 711) and jiffies delay (JD = 709) are used to control the distance and speed of the movement. For example to show the rectangle moving up and to the right at a 30 degree angle at a relatively fast pace these instructions could be used:
1 POKE RD,0 : POKE RM,l : POKE CD,l : POKE CM,2 2 POKE IT, 10 : POKE JD,2 : POKE FC,32 : POKE WF,0
Setting WF to zero and FC to 32 assures the "old" area is erased and that wraparound is allowed if appropriate. Even though JD was set to 2, the actual "speed" of the movement will depend on the size of the rectangle — obviously larger areas take longer to move than smaller ones — even at machine language speed! You should experiment with various values of JD to get the speed you want for your specific areas to be moved.
After returning from the subroutine, the parameters defining the rectangle will be automatically updated to correspond to the new location, so it is unnecessary to keep track of these locations in your BASIC program or to rePOKE these locations before making another move. However if you are moving several different areas "simultaneously", you should save locations 700 to 703 after exiting the routine and then rePOKE these same values before moving again (if there are intervening moves of other areas).
Listing 2 has a number of examples of movement that should be helpful to you in understanding how to use this routine effectively. The code at lines 800 to 870 should be particularly useful because it shows an easy and fast way to control motion with the numeric key pad.
Shrink And Growing
Some very interesting graphic effects are possible if you use the routine for repositioning or motion but do not POKE the Fill Character with a ASCII blank, i.e., a 32. If FC is zero, the "old" area is not changed as the "new" area is created. This allows the total graphic area to give the appearance of growing in size. Once the area has grown, FC can be set to 32 and the direction of the movement switched by 180 degrees and the area will appear to shrink. If FC is POKEd with something other than zero or 32, movement can be handled against a non-blank background, or some other characters can be left behind as the "wake" of the movement.
Lines 880 to 980 in Listing 2 give a good example of using the routine to grow and shrink objects.
I hope you have as much fun using this routine as I did in writing it. If you develop any new or unusual uses for this routine drop me a note — or better yet, tape copy of the program.
If you don't want to spend the effort keying in the code in the Listings, send me $5.00 and I will send you a tape containing both the graphics loader program (Listing 1) and the demo program (Listing 2).