APPENDIX TWO

PLAYER/MISSILE GRAPHICS

When I first started programming microcomputers there was no such thing as player/missile graphics. You had to do animation using PLOTS and DRAWTOs, or by pretending that " > ! < " was the Starship Enterprise. Even when I first started programming the Atari, some four years ago, player/missile graphics were just a rumor, even through the Atari was capable of doing them. That's right, Atari didn't tell anyone how to use player/missile graphics until months after the Atari was released, and even then you still had to do a lot of guessing. Anyway, so much for the old days. This appendix should contain all the information you need to be able to have all sorts of things flying around the screen with the greatest of ease.

I'll assume here that you've already read up on the various player/missile locations in the G/CTIA chip, starting at location 53248, and on DMACTL and PMBASE in the ANTIC chip at 54272 and 54279. If you haven't then do so now.

Let's start by looking at a player.



WHAT IS A PLAYER?

What is a player? Well, that is easy to answer if you are talking baseball, but a little harder to explain for a computer. We draw shapes on the screen by turning on and off bits in memory. The area of memory with these bits is called screen memory. Now the bad part of using this method to draw pictures on the screen comes when you want to animate little shapes ON TOP OF THE SCREEN PICTURE. Say you want to draw a little man and make him walk across a screen that you have carefully drawn a landscape on. As he walks, his pixels have to replace the ones of the landscape ONLY WHERE THE TWO SHAPES OVERLAP. Then, when he moves a little further across the screen, you have to replace the landscape because you want the man shape somewhere else. WOW. That is a lot of pixels to turn on and off.

Players provide a better method. You simply store a shape (the little man) somewhere other than in screen memory and tell the Atari's special chips to put the shape on top or underneath the screen data. It will figure out things like overlapping pixels automatically. All you have to do is tell the computer the options you want. Here is an example.

A normal player is as tall as the screen and as wide as a graphics mode one character (eight bits = one memory location). It can be either 256 bytes high, in which case each pixel in it is the height of a graphics mode eight row (one scan line), or it can be 128 bytes high, in which case each pixel is as high as a graphics mode seven row (two scan lines). Since the shape of a player is described in the same fashion as that of a character, you may wish to think of a player as an abnormally high character. There are four players altogether and a fifth player that can be divided into four missiles (Figure 53).

A missile is the exact same thing as a player, except it is only two bits wide. Since there are four missiles, and eight bits in a byte, all four missiles are stored in the same byte. Each byte is used as in Figure 54.

So, for example, if you wanted both pixels of missile one to be on in a particular byte, and weren't using any of the other missiles, you would set that byte to 00001100, which corresponds to a value of 12. You can also turn on only one bit of a missile to have thin missiles.

player missile positioning



            FIGURE 53.  Player missile positioning


BIT
7  6
5  4
3  2
1  0
USE
M3
M2
M1
M0

            FIGURE 54. Storing missiles


programing



PLAYER RESOLUTION AND WHERE TO STICK THEM

So we have a total of four players, each 1 byte wide and either 128 or 256 high, and four missiles, each 2 bits wide and either 128 or 256 bytes high. That means we'll need either 5*128 = 640 or 5*256 = 1280 bytes to store their descriptions. Before we look at where to find these bytes, let's look at how to choose whether we want the players and missiles to be 128 or 256 bytes high. The location that'll do the trick here is called SDMCTL (559). Setting bit four of SDMCTL chooses 256 byte player/missiles, while leaving it clear chooses 128 byte ones. So much for that, let's get back to our memory problem. Before I tell you where to get the memory from, there are a few more things you need to know. First of all, ANTIC relies on PMBASE at location 54279 to tell it where the memory is. Now ANTIC and PMBASE have some quirks that limit where the memory can be. First of all, PMBASE gives the high byte of the address only, which means that it will represent a multiple of 256. Furthermore, ANTIC says that it has to be on a 1K boundary for 128 byte player/missiles (PMBASE is a multiple of four), and on a 2K boundary for 256 byte player/missiles (PMBASE is a multiple of eight). And if that's not enough, ANTIC insists on there being extra bytes between PMBASE and the beginning of the player/missile data. Here's how ANTIC expects the data to be laid out (Figure 55a and 55b).

single line resolution



            FIGURE 55a. Single-line resolution


Double line reolution



            FIGURE 55b. Double-line resolution

Now I'm finally going to tell you where to get the free memory from. You might even have guessed already since we encountered the same need in Appendix One. That's right, we'll change RAMTOP at location 106. 1 realize that I went through a lot of information just to tell you that, but I wanted to make sure you knew what we were going to have to do to RAMTOP, and why. At least we can start our example now. We'll use 256 byte player/missiles, which means that we want to reserve eight pages using RAMTOP. You'll recall from the description of RAMTOP that we have to reserve an extra four pages for safety's sake, so we'll change RAMTOP by 12 altogether:

100 PMBAS=PEEK(740)-8
110 POKE 106,PMBAS-4
120 GRAPHICS 19

There are two things to watch out for here. If you're using graphics mode seven or eight, you should move RAMTOP by 16. The reason for this is given at RAMTOP. If you're using a redefined character set as well as player/missile graphics, you should do the following:

PMBAS=PEEK(740)-8
CHBAS=PMBAS-4
POKE 106,CHBAS-4
GRAPHICS whatever

This will make sure that PMBASE sits on a 2K boundary, and at the same time will take care of the graphics seven and eight problem.

Before we tell ANTIC where we've decided to put the data, we should make sure that any old data is removed. If you don't, you will have pixels on the screen you didn't expect to see:

130 PM=PMBAS*256
140 FOR BYTE=PM+768 TO PM+2047
150 POKE BYTE,0
160 NEXT BYTE

Too slow for you? Try this:

10 DIM CM$(36)
20 FOR CHAR=1 TO 36
30 READ CODE
40 CM$(CHAR,CHAR)=CHR$(CODE)
50 NEXT CHAR
60 DATA 104,104,133,204,104,133,203,104,170,169,0,160
70 DATA 255,224,0,208,4,104,168,169,0,145,203,136,192
80 DATA 255,208,249,230,204,202,224,255,208,234,96
140 X=USR(ADR(CM$),PM+768,1280)
150 REM
160 REM

If you want to use CM$ for other things, it needs the following USR call:

X = USR(ADR(CM$), START,NUMBER)

START is the starting address of the memory you want to clear and NUMBER is the number of bytes to clear.

For the experts in the crowd, here's the assembly code for the machine language routine in CM$:

68           PLA
68           PLA
85CC         STA STARTHI
68           PLA
85CB         STA STARTLO
68           PLA
AA           TAX
A900         LDA #0
A0FF         LDY #255
E000  LOOP1  CPX #0
D004         BNE LOOP2
68           PLA
A8           TAY
A900         LDA #0
91CB  LOOP2  STA (STARTLO),X
88           DEY
C0FF         CPY #255
D0F9         BNE LOOP2
E6CC         INC STARTHI
CA           DEX
E0FF         CPX #255
D0EA         BNE LOOP1
60           RTS

puzzle



Let's complete the example we have started. We now tell ANTIC where the data will be:

170 POKE 54279,PMBAS

We also want to tell it to send the data to G/CTIA, and that we will be using 256 byte player/missiles. This is done with SDMCTL:

180 POKE 559,62

We should also let G/CTIA know that the data will be coming. GRACTL does this:

190 POKE 53277,3

All of this essentially serves to initialize player/missile graphics. Now we're ready to go ahead and use them. Let's start by choosing the shape of a player. We'll make it look like a spaceship (Figure 56a).

Notice that we don't have to use all 256 bytes. As a matter of fact, hardly anybody ever does, and we'll see why in a little bit. First, let's translate our picture to bytes (Figure 56b).

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

FIGURE 56a. Choosing your PM shape


#  ##  # = 10011001 = 153
# #### # = 10111101 = 189
######## = 11111111 = 255
# #### # = 10111101 = 189
#  ##  # = 10011001 = 153

FIGURE 56b. Mapping your PM

We'll store these bytes in the middle of player zero:

200 FOR BYTE=126 TO 130
210 READ DAT
220 POKE PM+1024+BYTE,DAT
230 NEXT BYTE

1000 DATA 153,189,255,189,153

Can you see the player on the screen now? I can't either. Why not? We forgot to tell G/CTIA where we want it to appear on the screen. HPOSP0 at location 53248 will take care of that:

240 POKE 53248,128

Now it should be in the middle of the screen. Where is it? It is there, actually, but it has the same color as the background, so we can't see it. Let's take care of that by giving it a different color using PCOLR0 at location 704:

250 POKE 704,8

There now, that's much better. Now that we have a spaceship on the screen, though, what can we do with it? Well, the simplest thing to do would be to change its size using SIZEPO at location 53256. Try the following to see what I mean:

POKE 53256,1
POKE 53256,3
POKE 53256,0

Well, that's nice, but not too exciting. Let's try moving it. We already know how to move it horizontally, so add the following to the program:

260 HP0=129
270 S=STICK(0):IF S=9 OR S=10 OR S=11 THEN HP0=HP0-1
280 IF S=5 OR S=6 OR S=7 THEN HP0=HP0+1
290 HP0=HP0-256*(HP0=256)+256*(HP0=-1)
300 POKE 53248,HP0

What is going on here? First of all, we use HP0 to remember what the horizontal position of the player is, since HPOSP0 is POKE only. Then we look at joystick zero and move the player to the left or right if the joystick is moved to the left or right. Finally, we check HP0 to make sure that it hasn't gone below 0 or above 255. When you run this, you'll notice that you can move the player off the screen. A nice way to get rid of a player when you don't want it to be on the screen is to set its horizontal position to 0.

depot



That was pretty easy, and it looks pretty good. What about vertical movement though? You may have noticed that there are no vertical position registers. This means that you have to move each of the bytes in the player forward or backward in memory to move the player down and up, respectively. This can be a real pain and is also very slow. So slow, in fact, that I'm not even going to bother giving an example in BASIC. Instead, I'll give you two machine language routines, one for moving the player up, and one for moving it down. Here they are along with an example of how to use them (add the BASIC program lines to the preceding program):

10 DIM CM$(36),PU$(43),PD$(26)
90 GOSUB 500
260 HP0=128:VP0=128
310 IF S=6 OR S=10 OR S=14 THEN VP0=VP0-1:X=USR(ADR(PU$),PM+1024,255)
320 IF S=5 OR S=9 OR S=13 THEN VP0=VP0+1:X=USR(ADR(PD$),PM+1024,255)
330 VP0=VP0-256*(VP0=256)+256*(VP0=-1)
500 FOR CHAR=1 TO 43
510 READ CODE
520 PU$(CHAR,CHAR)=CHR$(CODE)
530 NEXT CHAR
540 FOR CHAR=1 TO 26
550 READ CODE
560 PD$(CHAR,CHAR)=CHR$(CODE)
570 NEXT CHAR
580 RETURN
800 DATA 104,104,133,204,104,133,203,104,168,104
810 DATA 170,177,203,72,138,72,160,1,177,203,136
820 DATA 145,203,200,200,240,10,192,128,208,243
830 DATA 104,72,201,127,208,237,104,168,104,145,203,96
900 DATA 104,104,133,204,104,133,203,104,104,168
910 DATA 177,203,72,136,177,203,200,145,203,136
920 DATA 208,247,104,145,203,96

Both PU$ (Player Up) and PD$ (Player Down) have the same USR format:

X=USR(ADR(PU$ or PD$),ADDRESS,HEIGHT - 1)

ADDRESS is the starting address of the player you want to move and HEIGHT-1 is 127 for 128 byte players, 255 for 256 byte players.

Here's the assembly code:

68    PU    PLA
68          PLA
85CC        STA PLAYERHI
68          PLA
85CB        STA PLAYERLO
68          PLA
A8          TAY
68          PLA
AA          TAX
B1CB        LDA (PLAYERLO),Y
48          PHA
8A          TYA
48          PHA
A001        LDY #1
B1CB  LOOP  LDA (PLAYERLO),Y
88          DEY
91CB        STA (PLAYERLO),Y
C8          INY
C8          INY
F00A        BEQ RETURN
C080        CPY #128
D0F3        BNE LOOP
68          PLA
48          PHA
C97F        CMP #127
D0ED        BNE LOOP
68   RETURN PLA
A8          TAY
68          PLA
91CB        STA (PLAYERLO),Y
60          RTS

68    PD    PLA
68          PLA
85CC        STA PLAYERHI
68          PLA
85CB        STA PLAYERLO
68          PLA
68          PLA
A8          TAY
B1CB        LDA (PLAYERLO),Y
48          PHA
88    LOOP  DEY
B1CB        LDA (PLAYERLO),Y
C8          INY
91CB        STA (PLAYERLO),Y
88          DEY
D0F7        BNE LOOP
68          PLA
91CB        STA    (PLAYERLO),Y
60          RTS

Now we can move our player in any direction. By the way, if you're using 128 byte player/missiles and you want to move them vertically by one scan line instead of two, use VDELAY at location 53276.

Is there still more? You bet. Let's put some backgound on the screen:

121 COLOR 1:PLOT 19,0:DRAWTO 19,23
122 COLOR 2:PLOT 21,0:DRAWTO 21,23
123 COLOR 3:PLOT 20,0:DRAWTO 20,23

Nothing great so far, but wait until we fool around with GPRIOR at location 623. Try each of the following and see what happens (see GPRIOR for an explanation of why):

195 POKE 623,4
195 POKE 623,8

You can also use GPRIOR to mix colors when two players overlap, and to make the four missiles into an extra player. See GPRIOR for more details.

As long as we have background on the screen, let's take a look at the collision registers. P0PF at location 53252 is used to tell whether player zero has collided with any part of the background, or playfield. See P0PF and HITCLR (53278) and try the following:

400 POKE 53278,0
410 C=PEEK(20)
420 IF C+1=256 THEN C=-1
430 IF PEEK(20)<C+1 THEN 430
440 SOUND 0,PEEK(53252)*16+15,4,8
450 GOTO 270

Lines 410 and 420 are necessary to make sure that G/CTIA has enough time to check for collisions. Note that the collision registers can be used to detect a collision between any two objects on the screen.

Well, we've just about covered everything now with one big exception: the missiles. Missiles are handled in much the same way as players, with several notable exceptions. First of all, they have the same color as the corresponding player, so you would use PCOLR0 to set the color for both player zero and missile zero. Secondly, there is only one size register for all four missiles, although you can set the size of each missile separately (see SIZEM at location 53260). But the most important difference, obviously, is in the way the missiles are stored in memory. Because all four are stored in the same bytes, it can be very difficult to move them vertically, since you must only move two bits for each, without disturbing the others. I'll come to your rescue again, however, and provide you with the machine language routines necessary to do just that:



68     MU    PLA
68           PLA
85CC         STA PLAYERHI
68           PLA
85CB         STA PLAYERLO
68           PLA
68           PLA
85CD         STA MASK
68           PLA
A8           TAY
68           PLA
AA           TAX
A5CD         LDA MASK
49FF         EOR #255
31CB         AND (PLAYERLO),Y
48           PHA
8A           TXA
48           PHA
B1CB  LOOP   LDA (PLAYERLO),Y
25CD         AND MASK
91CB         STA (PLAYERLO),Y
C8           INY
A5CD         LDA MASK
49FF         EOR #255
31CB         AND (PLAYERLO),Y
88           DEY
11CB         ORA (PLAYERLO),Y
91CB         STA (PLAYERLO),Y
C8           INY
F00A         BEQ RETURN
C080         CPY #128
D0E7         BNE LOOP
68           PLA
48           PHA
C97E         CMP #127
D0E1         BNE LOOP
68    RETURN PLA
A8           TAY
A5CD         LDA MASK
31CB         AND (PLAYERLO),Y
91CB         STA (PLAYERLO),Y
68           PLA
11CB         ORA (PLAYERLO),Y
91CB         STA (PLAYERLO),Y
60           RTS
68    MD     PLA
68           PLA
85CC         STA PLAYERHI
68           PLA
85CB         STA PLAYERLO
68           PLA
68           PLA
85CD         STA MASK
68           PLA
68           PLA
A8           TAY
A5CD         LDA MASK
49FF         EOR #255
31CB         AND (PLAYERLO),Y
48           PHA
B1CB  LOOP   LDA (PLAYERLO),Y
25CD         AND MASK
91CD         STA (PLAYERLO),Y
88           DEY
A5CD         LDA MASK
49FF         FOR #255
31CB         AND (PLAYERLO),Y
C8           INY
11CB         ORA (PLAYERLO),Y
91CB         STA (PLAYERLO),Y
88           DEY
D0EB         BNE LOOP
A5CD         LDA MASK
31CB         AND (PLAYERLO),Y
91CB         STA (PLAYERLO),Y
68           PLA
11CB         ORA (PLAYERLO),Y
91CB         STA (PLAYERLO),Y
60           RTS

These routines need the following USR call:

    X=USR(ADR(MU$ or MD$),ADDRESS,MASK,HEIGHT -1)

ADDRESS and HEIGHT-1 are the same as they were for PU$ and PD$, MASK has one of the following values:

252 if you're moving missile zero.
243 if you're moving missile one.
207 if you're moving missile two.
63   if you're moving missile three.

"Wait a minute, aren't you forgetting something? Where's the BASIC routine to set up MU$ and MD$?" Don't worry, it's on the way.

If you make the following changes to the preceding player program, it will move missile zero instead of player zero. Note that even though I'm only putting one thing on the screen at a time here, there is no reason why all four players and missiles can't be used together. I'm just lazy:

moving memory



10 DIM CM$(36),MU$(69),MD$(54)
200 FOR BYTE=127 TO 128
220 POKE PM+768+BYTE,DAT
240 POKE 53252,128
300 POKE 53252,HP0
310 IF S=6 OR S=10 OR S=14 THEN VP0=VP0-1:X-USR(ADR(MU$), PM+768,252,255)
320 IF S=5 OR S=9 OR S=13 THEN VP0=VP0+1:X=USR(ADR(MD$), PM+768,252,255)
440 SOUND 0,PEEK(53248)*16+15,4,8
500 FOR CHAR=1 TO 69
520 MU$(CHAR,CHAR)=CHRS(CODE)
540 FOR CHAR=1 TO 54
560 MD$(CHAR,CHAR)=CHR$(CODE)
800 DATA 104,104,133,204,104,133,203,104,104,133
810 DATA 205,104,168,104,170,165,205,73,255,49,203
820 DATA 72,138,72,177,203,37,205,145,203,200,165
830 DATA 205,73,255,49,203,136,17,203,145,203,200
840 DATA 240,10,192,128,208,231,104,72,201,127
850 DATA 208,225,104,168,165,205,49,203,145,203
860 DATA 104,17,203,145,203,96
900 DATA 104,104,133,204,104,133,203,104,104,133
910 DATA 205,104,104,168,165,205,73,255,49,203,72
920 DATA 177,203,37,205,145,203,136,165,205,73,255
930 DATA 49,203,200,17,203,145,203,136,208,235,165
940 DATA 205,49,203,145,203,104,17,203,145,203,96
1000 DATA 255,255

These are only the fundamentals of using players and missiles. You will learn best by experimenting with what we have given you. See what happens if you change things. If you want a complete lesson on the subject, Educational Software offers a fun to use program called "Tricky Tutorial™ #5." Write us for a list of all the tutorials.

lab


Return to Table of Contents | Previous Chapter | Next Chapter