Chapter Fourteen
Advanced Atari Graphics
No matter how good a BASIC programmer you may be, there are some things that you just can’t do in a BASIC program. BASIC is simply not fat enough to handle such tasks as fine scrolling, high-speed character animation, and player-missile graphics. Assembly language can handle all three of these tasks quite easily, and in this chapter, you’ll see exactly how. Let’s start with fine scrolling.
Fine Scrolling
To demonstrate fine scrolling, I’m going to use an expanded version of the title screen program you typed in the preceding chapter. If you’ve saved that program on a disk, you can load it into your computer right now. Then, with a little editing and a few additions, you can modify it until it looks like the listing below. After you’ve modified it, you can save it on a disk, run it, and take a look at it in action. Then I’ll explain how it works, and you’ll have an eye-catching assembly language program that you can use from now on to help you create customized title screens for your own programs.
A Demonstration of Fine Scrolling
FINESCRL.SRC for the Atari Assembler Editor Cartridge
05 ; 10 ; 20 ;HELLO SCREEN (FINE) 30 ; 40 *=$3000 50 JMP INIT 60 ; 70 TCKPTR=$2000 80 FSCPTR=TCKPTR+1 90 ; 0100 SDMCTL=$022F 0110 ; 0120 SDLSTL=$0230 0130 SDLSTH=$0231 0140 ; 0150 COLOR0=$02C4;OS COLOR REGISTERS 0160 COLOR1=$02C5 0170 COLOR2=$02C6 0180 COLOR3=$02C7 0190 COLOR4=$02C8 0299 ; 0210 HSCROL=$D404 0220 ; 0230 VVBLKI=$0222; OS INTERRUPT VECTOR 0240 SYSVBV=$E45F; INTERRUPT ENABLE VECOTR 0250 ; 0260 SETVBV=$E45C; SET VERTICAL BLANK INTERRUPT (VBI) VECTOR 0270 XITVBV=$E462; EXIT VBI VECTOR 0280 ; 0290 ; DISPLAY LIST DATA 0300 ; 0310 START 0320 LINE1 .SBYTE " PRESENTING " 0330 LINE2 .SBYTE " " 0340 .SBYTE " the big program " 0350 .SBYTE " " 0360 LINE3 .SBYTE " By (You" 0370 .SBYTE "r Name) " 0380 LINE4 .SBYTE "PLEASE STAND BY " 0390 ; 0400 ; DISPLAY LIST WITH SCROLLING LINE 0410 ; 0420 HLIST ;('HELLO' LIST) 0430 .BYTE $70, $70, $70 0440 .BYTE $70,$70,$70,$70,$70 0450 .BYTE $46 0460 .WORD LINE1 0470 ;NOTE THAT THE LAST BYTE IN THE 0480 ;NEXT LINE IS $57, NOT $47 AS IT 0490 ;WAS IN THE PRECEDING CHAPTER 0500 .BYTE $70,$70,$70,$70,$57 0509 ;THIS THE LINE WE'LL SCROLL 0510 SCROLN ;(THIS IS THE LINE WE'LL SCROLL) 0520 .WORD $00; A BLANK TO BE FILLD IN LATER 0530 .BYTE $70,$42 0540 .WORD LINE3 0550 .BYTE $70,$70,$70,$70,$46 0560 .WORD LINE4 0570 .BYTE $70,$70,$70,$70,$70 0580 .BYTE $41 0590 .WORD HLIST 0600 ; 0610 ;RUN PROGRAM 0620 ; 0630 INIT ;PREPARE TO RUN PROGRAM 0640 LDA COLOR3; SET COLOR REGISTERS 0650 STA COLOR1; 0660 LDA COLOR4; 0670 STA COLOR2; 0680 ; 0690 LDA #0 0700 STA SDMCTL 0710 LDA #HLIST&255 0720 STA SDLSTL 0730 LDA #HLIST/256 0740 STA SDLSTH 0750 LDA #$22 0760 STA SDMCTL 0770 ; 0780 JSR TCKSET; INITIALIZE TICKER ADDRESS 0790 ; 0800 LDA #40; NUMBER OF CHARACTERS IN SCROLL LINE 0810 STA TCKPTR 0820 LDA #8 0830 STA FSCPTR; NUMBER OF CLOR CLOCKS TO FINE SCROLL 0840 ; 0850 ; ENABLE INTERRUPT 0860 ; 0870 LDY #TCKINT&255 0880 LDX #TCKINT/256 0890 LDA #6 0900 JSR SETVBV 0910 ; 0920 ; TICKER INTERRUPT 0930 ; 0940 TCKINT 0950 LDA #SCROLL&255 0960 STA VVBLKI 0970 LDA #SCROLL/256 0980 STA VVBLKI+1 0990 ; 1000 INFIN 1010 JMP INFIN; INFINITE LOOP 1020 ; 1030 SCROLL 1040 LDX FSCPTR; 8 TO START 1050 DEX 1060 STX HSCROL 1070 BNE CONT 1080 LDX #8 1089 ;CONINUE 1090 CONT 1100 STX FSCPTR 1110 CPX #7 1120 BEQ COARSE 1130 JMP SYSVBV 1140 COARSE 1150 LDY TCKPTR; NUMBER OF CHARACTERS TO SCROLL 1160 DEY 1170 BNE SCORSE; LOOP BACK TILL FULL LINE IS SCROLLED 1180 LDY #40 1190 JSR TCKSET; RESET TICKER LINE 1199 ;DO COARSE SCROLL 1200 SCORSE 1210 STY TCKPTR 1220 INC SCROLN; LOW BYTE OF ADDRESS 1230 BNE RETURN 1240 INC SCROLN+1; HIGH BYTE OF ADDRESS 1250 RETURN 1260 JMP SYSVBV 1270 ; 1280 TCKSET 1290 LDA #LINE2&255 1300 STA SCROLN 1310 LDA #LINE2/256 1320 STA SCROLN+1 1330 ENDIT 1340 RTS
Download / View (Assembly source code)
As soon as you’ve typed this program, be sure to save it on a disk immediately. Then you can run it, debug it if necessary, and save it again. Once you have it up and running properly, I think you’ll agree with me that it’s quite a nice display, and it certainly is an example of very smooth scrolling! Now for an explanation of how the program works.
How to Implement Fine Scrolling
The reason fine scrolling works so smoothly is that it has eight times the resolution of coarse scrolling. When coarse scrolling is employed in a program, it causes lines of text to jump across (or up or down) the screen one full character at a time. But when fine scrolling is used, text can be moved around the screen one-eighth of a character at a time. Here’s how that works: Look closely at a text character on your video screen, and you’ll see that it’s made up of a matrix of tiny dots. If you use a magnifying glass, you will be able to see that there are exactly 64 dots in each character. Every character on your screen is eight rows of dots (or scan lines) high, and eight rows of dots (or color clocks) across. And these rows of dots, scan lines and colors, are the increments used in fine scrolling.
To create and implement a fine scrolling routine, several steps are required. First, you must go to your display list and enable fine scrolling by setting certain bits in the LMS instruction that appears before every line you want to scroll. When bit 4 of an LMS instruction is set, the line that follows the LMS instruction can be scrolled horizontally. When bit 5 of an LMS instruction is set, the line that follows the instruction can be scrolled vertically. If both bit 4 and bit 5 of an LMS instruction are set, then the line that follows the instruction can be scrolled both horizontally and vertically.
Take a look at lines 500 through 520 in the program you just typed, and you’ll see that the LMS instruction preceding the line which I’ve labeled SCROLN (the line that scrolls) is $57. Look at the program in its previous incarnation, the version in Chapter 13, in which fine scrolling was not enabled. You will see that this LMS instruction has been changed from $47 to $57.
The number $47, expressed in binary notation, is 0100 0111. As any assembly language programmer can plainly see, bit 4 of that binary number (the fifth bit from the right, since the first bit of a binary number is bit 0), is zero. In other words, bit 0 is not set. When we set bit 4, the number we’re looking at becomes 0101 0111, or $57. Therefore, when horizontal fine scrolling of the line labeled SCROLN is enabled, lines 500 through 520 of our program become:
0500 .BYTE $70,$70,$70,$70,$57 0510 SCROLN; (THIS IS THE LINE WE'LL SCROLL) 0520 .WORD $00; A BLANK TO BE FILLD IN LATER
Now suppose you wanted to scroll SCROLN vertically instead of horizontally. What would you do? Well, you’d simply set bit 5 of the LMS instruction in line 500. Then the $57 that you see in that line would become $67 or, in binary notation, 0110 0111. If you wanted to enable both horizontal and vertical scrolling of the line, you’d simply change the LMS instruction $77 (0111 0111).
Fine scrolling, just like coarse scrolling, can be performed on any number of lines of text on your screen. Just set the proper bit (or bits) in the proper LMS instruction (or instructions), and the desired type of scrolling can be implemented for each selected line. But, you may ask, what if there is no LMS instruction for a line you want to scroll? Well, in that case, you could simply write one. There’s absolutely no reason that a display list can’t have an LMS instruction for every line on the screen. If you want to scroll an entire screen, you must, in fact, put an LMS instruction in front of every line. So far, all we’ve talked about is how to enable fine scrolling. But now that you know how to enable it, how do you actually do it?
Good question.
When fine scrolling of a line is enabled, control of the line is handed over to one of two scrolling registers that reside in your Atari's operating system. If you have authorized a horizontal scroll on a given line of a display, then that line becomes subject to control of a horizontal scroll register, which is abbreviated HSCROL and is situated at memory address $D404. When a vertical scroll has been enabled for a given display list line, then that line becomes subject to the control of a vertical scroll register, or VSCROL, situated at address $D405. If both horizontal and vertical scrolling of a given line are enabled, then that line becomes subject to the control of both the HSCROL and the VSCROL registers. Once control of a line has been turned over to HSCROL, VSCROL or both, then you can implement a fine scroll by simply loading a value into the appropriate scrolling register (or registers). When you load a number into the HSCROL register, every display list line that has been put under the control of that register will be shifted to the right by the number of color clocks loaded into the VSCROL register, and every line for which a vertical scroll has been enabled will be scrolled upward by the number of scan lines you have specified.
Combining Fine Scrolling and Coarse Scrolling
There is one hitch, though. The scrolling registers in your computer are 8-bit registers, and only four of these 8-bits in each register are ever used. That means that fine scrolling can be taken only so far. To work properly, fine scrolling must be combined with coarse scrolling, which can handle as much scrolling data as you can program. Generally speaking, the best way to combine fine scrolling with coarse scrolling is to fine scroll a line or column of characters by seven color clocks or scan lines, and then to reset the appropriate fine scrolling register to its initial value and implement one coarse scroll. Loop through this kind of procedure over and over, and the result will be a smooth fine scroll. You can see how this procedure works by studying and experimenting with the fine scrolling routine in the title screen program we’ve been examining.
Smoothing Out Your Scrolling Action
That’s about all there is to fine scrolling if you don’t mind putting up with a jerk, a jump or a smear every now and then on your video display. If those kinds of messy situations don’t appeal to you, and I’m sure they don’t, then we might as well go ahead and talk about how to make a fine scrolling operation perfect: smooth, smear proof and jerk free.
As you may recall from the preceding chapter, the display on your computer monitor is redrawn by an electron gun 60 times every second, and between each screen refresh there’s a split second total screen blackout that takes place too rapidly for you to see. Well, when you write a fine scrolling routine in assembly language and don’t take a few special precautions, your display may (in fact it virtually always will) smear a bit and jump around a little from time to time. That’s because some of the scrolling action you’ve programmed will sometimes take place while a display is being drawn on your screen by the electron gun inside your video tube. But there is a way to keep that from happening. The folks who designed your Atari have provided you with something called a Vertical Blank Interrupt (VBI) vector, and once you learn how to use that vector, you can perform all kinds of graphics tricks on your computer screen, in real time and without any danger whatsoever of messing up your computer’s screen display.
A vector, as you may know, is a point in your computer’s operating system that contains the address of a specific routine. The primary purpose of a vector is to give you an easy method for implementing an often used routine. When you jump to an OS vector during the course of a program, your program will automatically jump to the OS routine that the vector points to, and you can thus implement that routine without having to write from scratch all of the code that it contains. Vectors can sometimes be used in another way, too. Sometimes you can “steal” a vector; that is, you can change its value so that it will point to some routine you’ve written yourself, rather than the OS routine that it originally pointed to. That means that you can sometimes use a vector as an easy method for controlling the behavior of your computer’s operating system. And that brings us to the point at hand: vertical blank interrupt vectors.
Actually, there are two VBI vectors in your computer, and each one has a corresponding pointer in your Atari’s operating system. One of these vectors is called VVBLKI (“I” for “Immediate”), and the pointer that can be used to access it is situated at memory address $0222. The other VBI pointer is VVBLKD (“D” for Deferred) and resides at memory address $0224.
Every time your Atari starts a vertical blank interrupt, it takes a look at the contents of the VVBLKI pointer. If the program that is being processed does not make use of the VVBLKI pointer, then that pointer will contain nothing but an instruction to jump to a predetermined memory address: specifically, memory address $E45F (which Atari has labeled SYSVBV). Memory address $E45F usually doesn’t contain anything exciting, either. All that it ordinarily contains is an instruction for your computer to continue its normal processing. By stealing the VVBLKI vector, however, you can make it point to any routine you like – usually one you yourself have written. Then, 60 times every second (every time your computer begins its 60 Hertz vertical blank interrupt) it will automatically process the routine whose address you have stored in the VVBLKI pointer. When your routine is finished, your computer will resume its normal processing.
Your computer’s other VBI vector, VVBLKD, also points directly to an exit point unless it has been stolen for a software application. The VVBLKD vector’s normal exit point is memory address $E462, which Atari calls XITVBV, it works just like SYSVBV. It merely terminates your computer’s vertical blank interrupt period and allows your computer to resume normal processing. Once you understand how the VVBLKI and VVBLKD interrupt work, it isn’t difficult to steal them. Here’s all you have to do:
- Write a routine that you would like to see take place during a vertical blank interrupt.
- Make sure that your routine ends with a jump to SYSVBV or XITVBV (depending upon whether the vector you use is immediate or deferred).
- Store the address of your routine at VVBLKI for an immediate interrupt, or at VVBLKD for a deferred interrupt.
Once those steps are taken, your computer will process your new routine 60 times every second, just before it begins each VBI interrupt if you’ve used the immediate vector, or just before it returns from each VBI interrupt if you’ve used the deferred vector. That makes vector stealing a very valuable technique for writing programs involving high-speed, high-performance processing, such as the processing of routines involving graphics and sound.
At this point, you may be wondering why there are two vectors that you can steal, and what the differences between them are.
Well, the reason is simply that certain user-written routines are best performed at the start of a vertical blank period, while others should not be performed until a VBI has ended. More information on this point can be found in De Re Atari and in The Atari 400/800 Technical Reference Notes.
One More Thing . . .
There’s just one more important fact that you should remember about VBI vector stealing. After you’ve stolen a vector, there’s a small chance that an interrupt will begin after the first byte of the pointer you’re using has been updated, but before the second byte has been changed. If that happens, it could crash your program. But this possibility can be easily avoided. All you have to do is use an operating system routine that the good people who designed your computer thoughtfully provided. This routine is called SETVBV, and it begins at memory address $E45C.
Here’s how to use the SETVBV routine: First, load the 6502 Y register with the low byte of the address of the routine that instructs your computer to begin a vector changing routine. Then load the X register with the high byte of the address. Next, load the accumulator with a 6 for an immediate VBI or a 7 for a deferred VBI. Then do a JSR SETVBV, and your interrupt will be safely enabled. That’s just about the whole story on how to use vertical blank in assembly language fine scrolling programs.
Customizing A Character Set
Your Atari computer has a very fine built-in character set. If you have a late model Atari, it may even have two sets of characters built into its ROM. But how would you like to be able to create your own character sets, including not only letters, numbers and special text symbols, but graphics characters, too?
Well, you can do that quite easily if you know assembly language. You can do it at lightning speed, too – not at the snail’s pace you may have agonized over if you’ve ever tried to alter a character set using a BASIC program. It’s actually quite easy to design a character set on an Atari computer. As I pointed out earlier in this chapter, each character that your computer prints on your video monitor is made up of an 8 by 8 matrix of dots. This 8 by 8 grid is stored in your computer’s RAM as eight bytes of data. The letter A, for example, is stored in your computer’s memory as a string of binary digits that could be represented in this fashion:
Binary Notation Hexadecimal Notation Appearance 0000 0000 00 0001 1000 18 XX 0011 1100 3C XXXX 0110 0110 66 XX XX 0110 0110 66 XX XX 0111 1110 7E XXXXXX 0110 0110 66 XX XX 0000 0000 00
The primary character set in your computer is composed of 128 letters, each made up of 64 dots that could be arranged in the same 8 by 8 format as the letter “A.” In fact, that is precisely the format in which the letters are arranged when they’re displayed on your computer screen. Since there are 128 characters in a set, and since each character is made up of eight bytes of data, a full character set occupies 1,024 bytes of RAM. Put all of that data together, and you have quite a lengthy table. You also have a table which must start on a 1K boundary because of your computer’s architecture. Character sets can be stored almost anywhere in an Atari computer’s memory, but the address of the character set currently in use is always stored in a specific pointer in the computer’s operating system. That pointer, labeled CHBAS by the engineers who designed your Atari, is situated at memory address $2F4.
To locate a character in your computer, you only have to know two things: the current value of CHBAS, and the ATASCII code for the character you’re seeking. Add the character’s ATASCII code number to the value of CHBASE, and that will be the memory address of the character you’re looking for. If your computer is an Atari 400 or an Atari 800, then its character set is built into ROM and thus, cannot be modified. If you have a later model Atari, your character set has a RAM address and can therefore be accessed somewhat more easily. But, if you want to alter your computer’s built-in character set, you have to do it in a rather indirect way, no matter what kind of Atari you own.
The best way to modify a character set is to copy your computer’s built-in character to some free and easily accessed block of RAM. You can then modify the contents of CHBAS so that is points to the starting address of your own block of characters rather than your computer’s built-in character set. Then you can use either set of characters you like, either the one built into your Atari at the factory or the one you have created on your own.
Once you’ve defined a new character set and stored it in RAM, all you have to do to change your screen display from one character set to another is change the contents of CHBAS. That makes it easy to write routines calling for character animation. All you have to do to animate a character set is draw several different character sets that vary slightly, and then switch back and forth among them by simply changing the contents of the CHBAS pointer. BASIC is too slow to handle a job like that very well, but when you know assembly language, you can animate character sets in real time, at lighting fast speeds. In a moment, I’ll show a program that you can use to custom design your own character sets. The program has two distinct parts. The first part copies the entire Atari character table to a spot in memory selected by the programmer. The second part alters just one character – the character “A”. But the same technique can be used to alter any other character or, if you wish, all of them!
I’d like to make two more observations before you type the next program. First, I’d like to point out that the first half of the program, the part that moves the character set, can be used to move any block of data from any location to any other location in your computer’s RAM. That’s a useful utility, since data often has to be moved from one block of memory to another in assembly language programs. The data moving portion of the program that follows is a particularly good one, since it is designed to move blocks of memory a full page at a time, using 8-bit pointers instead of 16-bit pointers and thus saving a considerable amount of processing time. The second point I’d like to make is that there are ways to create character sets without having to go through the drudgery of drawing character sets on graph paper and then punching them into memory a byte at a time. There are several excellent programs on the market that can help you create your own character sets for Atari computer right on the screen, using a cursor, a set of menus, and keyboard commands. So if you’re interested in computer graphics, it might be to your advantage to take a look at some professionally produced character generator programs.
But that’s enough from me. Here’s your program:
MOVING AND MODIFYING A CHARACTER SET
10 ; 20 ;ALTERING GRAPHICS CHARACTERS 30 ; 50 *=$0600 60 JMP MOVDAT 70 ; 80 CHBAS=$02F4 90 NEWADR=$5000 0100 TABLEN=1024 0110 ; 0120 MVSRCE=$B0;PAGE ZERO PTR 0130 MVDEST=MVSRCE+2;DITTO 0140 CHRADR=MVDEST+2;DITTO 0150 ; 0160 LENPTR=$6000;ANOTHER POINTER 0170 RAMCHR=LENPTR+2;DITTO 0180 ; 0190 SHAPE .BYTE $18,$DB,$42,$7E,$18,$7E,$66,$E7;A MAN 0200 ; 0210 START=MOVDAT 0220 ; 0230 MOVDAT 0240 ; 0250 ; STORE VALUES IN PONTERS 0260 ; 0270 LDA #0 0280 STA MVSRCE;LOW BYTE 0290 LDA CHBAS;HIGH BYTE 0300 STA MVSRCE+1;HIGH BYTE 0310 ; 0320 LDA #NEWADR&255 0330 STA MVDEST 0340 LDA #NEWADR/256 0350 STA MVDEST+1 0360 ; 0370 LDA #TABLEN&255 0380 STA LENPTR 0390 LDA #TABLEN/256 0400 STA LENPTR+1 0410 ; 0420 ;START MOVE 0430 ; 0440 LDY #0 0450 LDX LENPTR+1 0460 BEQ MVPART 0470 MVPAGE 0480 LDA (MVSRCE),Y 0490 STA (MVDEST),Y 0500 INY 0510 BNE MVPAGE 0520 INC MVSRCE+1 0530 INC MVDEST+1 0540 DEX 0550 BNE MVPAGE 0560 MVPART 0570 LDX LENPTR 0580 BEQ MVEXIT 0590 MVLAST 0600 LDA (MVSRCE),Y 0610 STA (MVDEST),Y 0620 INY 0630 DEX 0640 BNE MVLAST 0650 MVEXIT 0660 ; 0670 ;PART II: CHANGE CHARACTER 0680 ; 0690 ;WE'LL ALTER THE CHARACTER "A" 0700 ; 0710 LDA #33;RAM CODE: "A" 0720 STA RAMCHR 0730 ; 0740 ;NOW WE CALCULATE RAMCHR'S ADR 0750 ; 0760 LDA #0 0770 STA RAMCHR+1;CLEARING IT 0780 LDA RAMCHR;#33:AN "A" 0790 CLC 0800 ASL A;MULT BY 2 0810 ROL RAMCHR+1;GET CARRY 0820 ASL A;AGAIN 0830 ROL RAMCHR+1 0840 ASL A;AND AGAIN 0850 ROL RAMCHR+1 0860 STA RAMCHR;MULT BY 8 DONE 0870 ; 0880 FNDADR 0890 CLC 0900 LDA RAMCHR 0910 ADC #NEWADR&255 0920 STA CHRADR 0930 LDA RAMCHR+1 0940 ADC #NEWADR/256 0950 STA CHRADR+1 0960 ; 0970 ;NOW WE CHANGE THE CHARACTER 0980 ; 0990 NEWCHR 1000 LDY #0; NR OF BYTES+1 1010 DOSHAPE 1020 LDA SHAPE,Y 1030 STA (CHRADR),Y 1040 INY 1050 CPY #8;WRONG IN BOOK 1060 BCC DOSHAPE;REPEAT TILL DONE 1070 ; 1080 ;STORE NEW CHR SET ADR IN CHBAS 1090 ; 1100 LDA #NEWADR/256;HIGH BYTE 1110 STA CHBAS 1120 ; 1130 FINI 1140 RTS
Download / View (Assembly source code)
Notice The "A" Has Been Changed
Notice The "A" Has Been Changed
Player-Missile Graphics
Although animation can be programmed by flipping through alternate character sets, a far easier way to animate characters in an Atari assembly language program is to take advantage of a special graphics feature of Atari computers called player-missile graphics. Player-missile graphics is a technique for programming animation using graphics characters called (not surprisingly) players and missiles. Your Atari computer is equipped with four players, numbered 0 through 3, and four missiles, one for each player. If you write a program that doesn’t call for missile, you can combine your four missiles to form a fifth player. Players and missiles can be used in any graphics mode, and can be drawn and moved around on a video display completely independently of anything else on the screen. They can pass over or under other objects on the screen, and over or under each other. Alternatively, they can be programmed to come to a halt when they run into things, or even to explode upon impact with onscreen objects or with each other!
Each of your Atari's four players is 8-bits wide, just like an ordinary graphics character. But players can be much taller than they are wide. Their maximum height is either 128 or 256 bytes high, depending on their vertical resolution. That means a player can be as tall as the full height of your video screen. Each player has its own color register. So by using players, you can add more color to a program than would ordinarily be available. Players are usually single color entities. It is possible, however, to merge two or more players into a multicolored player by placing one player on top of another.
Players can have a vertical resolution of either one or two scan lines. The maximum horizontal resolution of a player is eight pixels, but the width of each horizontal pixel is variable; each pixel can be 8 color colocks, 16 color clocks or 32 color clocks wide. This choice is up to the programmer. The missiles used in player-missile graphics are 2-bit wide spots of light that can be used as bullets, starts, or other small graphics objects. Or, as previously mentioned, they can be combined to form a fifth full-size player.
Players are made up of grids of dots, just as standard text characters are. They can therefore be disigned on graph paper, in the same way that ordingary text characters are created. Before you start blocking out a player on graph paper, however, it might be a good idea to remember what a player looks like on the screen when all of its bits are filled in. When a player is completely filled in, it looks like a ribbon extending from the top of a video screen to the bottom. You won't neeed nearly all of that height for most programming needs. before you start drawing a player, it's usally a good idea to "erase" that entire ribbion by filling it in with zeros. The whole ribbon will thus become invisible. Then you can draw your player in exactly the same way you'd draw a conventional text character, by filling in its shape with "on" bits, or binary ones. When you've finished drawing your players in this way, you can store them almost anywhere in RAM. You can tell your computer when your players and missiles are by simply storing the starting address of the RAM in which they appear in an OS pointer called PMBASE. The address of PMBASE is $D407.
Since player and missiles are graphics objects, it is considered good programming practice to store them in high RAM, just below your computer's screen display. Atari's in-house programmers, who generally know what they're talking about, recommend that you store your player-missile display RAM about 2K bytes below the top of your Atari computer's memory. Once you've figured out where your player-missile RAM is going to be stored, and consequently, what address PMBASE will point to, you can start storing the data for your players and missiles right into RAM. Here's a chart showing where the RAM for each player and missile will start, in respect to the address stored in PMBASE:
SINGLE-LINE RESOLUTION OFFSET FROM PMBASE CONTENTS ----------------------------- + 0 - 767 Unused + 768 - 1023 Missiles + 1024 - 1279 Player 0 + 1280 - 1535 Player 1 + 1536 - 1791 Player 2 + 1792 - 2047 Player 3 + 2046 End of P/M RAM Must start on a 2K address boundary.
DOUBLE-LINE RESOLUTION OFFSET FROM PMBASE CONTENTS ----------------------------- + 0 - 383 Unused + 384 - 511 Missiles + 512 - 639 Player 0 + 640 - 767 Player 1 + 768 - 895 Player 2 + 896 - 1023 Player 3 + 1023 End of P/M RAM Must start on a 1K address boundary.
When your players and missiles are drawn and stored in RAM, it's faily simple to move them around on your screen. In your computer's operating system there's a set of memory registers that are used to keep track of the horizontal positions of all players and missiles. These registers are labeled HPOSP0 through HPOSP3 (for players) and HPOSM0 through HPOSM3 (for missiles). The addresses of these registers are:
HORIZONTAL REGISTER FOR: LABEL ADDRESS Player 0 HPOSP0 D000 Player 1 HPOSP1 D001 Player 2 HPOSP2 D002 Player 3 HPOSP3 D003 Missile 0 HPOSM0 D004 Missile 1 HPOSM1 D005 Missile 2 HPOSM2 D006 Missile 3 HPOSM3 D007
When you w ant to move a player or a missile from one horizontal position to another, all you have to do is change the value of the appropriate horizontal register.
Changing the vertical position of a player or a missile is a little more difficult. To move a player up or down, you have to "erase" the player from the vertical ribbon on which it appears by filling that space in with zeros. Then you have to redraw it in a position higher or lower in the block of RAM that makes up the ribbon.
The utility program below, the final program in this book, is a demonstration of how to use player-missile graphics. As a special bonus, it will also show you how to use a joystick in assembly language programs.
The joystick reading portion of the program begins with a set of instructions setting bit 2of a register called PACTL (for "Port A ConTroL") located at memory address $D302. When bit 2 of PACTL is set, the reading of game controllers is enabled. The direction switches of a joystick plugged into port A can then be read, in much the same way as they are read in BASIC programs. When you type this program in and run it, you'll be able to move a little pink heart around on your screen using a joystick controller. When the heart disappears from the screen in any direction, keep your joystick switch pressed, and it will soon "wrap around" and reappear on the opposite edge of the screen, just where you would expect it to, moving in the same direction.
This program uses vertical blank interrupts, just like the smooth scrolling routine presented at the beginning of this chapter. As you'll see when you type the program and run it, assembly language is the best possible language to use when you're working with player-missile graphics. If you've ever worked with player-missile graphics using BASIC, you'll soon see that PM/G action is much faster and much smoother when it's programmed in assembly language.
10 ; 20 ; PLAYER-MISSLE GRAPHICS ROUTINE 30 ; 50 *=$5000 60 JMP START 70 ; 80 RAMTOP=$6A ;TOP OF RAM PTR 90 VVBLKD=$0224 ;INTERRUPT RTN 0100 SDMCTL=$022F ;DMA CNT. SHADOW 0110 SDLSTL=$0230 ;SDLST, LOW BYTE 0120 STICK0=$0278 0130 PCOLR0=$02C0 ;PLAYER COLOR 0140 COLOR2=$02C6 ;BKG COLOR 0150 ; 0160 HPOSP0=$D000 ;PLAYER HORZ PSN 0170 GRACTL=$D01D 0180 PACTL=$D302 ;JS PORT CNTRL 0190 PMBASE=$D407 ;PM BASE ADR 0200 SETVBV=$E45C ;ENABLE INTRPT 0210 XITVBV =$E462 ;EXIT INTERRUPT 0220 ; 0230 HRZPTR=$0600 ;HORIZ PSN PTR 0240 VRTPTR=HRZPTR+1 ;VRT PSN PTR 0250 OURBAS=VRTPTR+1 ;OUR PMBASE 0260 TABSIZ=OURBAS+2 ;TABLE SIZE 0270 FILVAL=TABSIZ+2 ;BLKFIL VALUE 0280 ; 0290 TABPTR=$B0 ;TABLE ADDR PTR 0300 TABADR=TABPTR+2 ;TABLE ADDRESS 0310 ; 0320 PLBOFS=512 ;PLAYER BAS OFFS 0330 PLTOFS=640 ;PLAYER TOP OFFS 0340 ; 0350 SHAPE .BYTE $00,$6C,$FE,$FE,$7C,$38,$10,$00 0360 ; 0370 START 0380 ; 0390 ;CLEAR SCREEN 0400 ; 0410 LDA #0 0420 STA FILVAL 0430 LDA SDLSTL 0440 STA TABPTR 0450 LDA SDLSTL+1 0460 STA TABPTR+1 0470 LDA #960&255 ;BYTES PER SCRN 0480 STA TABSIZ 0490 LDA #960/256 0500 STA TABSIZ+1 0510 JSR BLKFIL 0520 ; 0530 ;DEFINE PMG VARIABLES 0540 ; 0550 LDA #0 0560 STA COLOR2 ;BLACK BKG 0570 LDA #$58 0580 STA PCOLR0 ;PINK PLAYER 0590 ; 0600 LDA #100 ;SET HORIZ PSN 0610 STA HRZPTR 0620 STA HPOSP0 0630 ; 0640 LDA #48 ;SET VERT PSN 0650 STA VRTPTR 0660 ; 0670 LDA #0 ;CLEAR OURBASE 0680 STA OURBAS 0690 STA OURBAS+1 0700 ; 0710 SEC 0720 LDA RAMTOP 0730 SBC #8 0740 STA PMBASE ;BASE=RAMTOP-2K 0750 STA OURBAS+1 ;SAVE BASE ADR 0760 ; 0770 LDA #46 0780 STA SDMCTL ;ENABLE PM DMA 0790 ; 0800 LDA #3 0810 STA GRACTL ;ENABLE PM DSPLY 0820 ; 0830 ;FILL PM RAM W/ZEROS TO CLEAR 0840 ; 0850 CLC 0860 LDA OURBAS 0870 ADC #PLBOFS&255 0880 STA TABADR 0890 STA TABPTR 0900 LDA OURBAS+1 0910 ADC #PLBOFS/256 0920 STA TABADR+1 0930 STA TABPTR+1 0940 ; 0950 SEC 0960 LDA #PLTOFS&255 0970 SBC #PLBOFS&255 0980 STA TABSIZ 0990 LDA #PLTOFS/256 1000 SBC #PLBOFS/256 1010 STA TABSIZ+1 1020 ; 1030 LDA #0 1040 STA FILVAL 1050 JSR BLKFIL 1060 ; 1070 ;DEFINE PLAYER 1080 ; 1090 PLAYER 1100 ; 1110 ;DRAW PLAYER 1120 ; 1130 JSR DRAWPL 1140 ; 1150 ;ENABLE INTERRUPT 1160 ; 1170 LDY #INTRPT&255 1180 LDX #INTRPT/256 1190 LDA #7 1200 JSR SETVBV 1210 ; 1220 INTRPT 1230 LDA #RDSTIK&255 1240 STA VVBLKD 1250 LDA #RDSTIK/256 1260 STA VVBLKD+1 1270 ; 1280 ;INFINITE LOOP 1290 ; 1300 INFIN 1310 JMP INFIN 1320 ; 1330 ;READ JOYSTICK 1340 ; 1350 RDSTIK 1360 LDA #4 1370 ORA PACTL ;SET BIT #5 1380 ; 1390 LDA STICK0 1400 CMP #$F ;JS STRAIGHT UP? 1410 BEQ RETURN ;YES, NO ACTION 1420 ; 1430 TRYAGN 1440 CMP #$07 ;RIGHT MOVE 1450 BNE TRYAG2 1454 STA EYEOFS 1460 LDX HRZPTR 1470 INX 1480 STX HRZPTR 1490 STX HPOSP0 1500 JMP RETURN 1510 ; 1520 TRYAG2 1530 CMP #$0B ;LEFT MOVE 1540 BNE TRYAG3 1550 ; 1560 LDX HRZPTR 1570 DEX 1580 STX HRZPTR 1590 STX HPOSP0 1600 JMP RETURN 1610 ; 1620 TRYAG3 1630 CMP #$0D ;DOWN MOVE 1640 BNE TRYAG4 1650 ; 1660 INC VRTPTR 1670 JSR DRAWPL 1680 JMP RETURN 1690 ; 1700 TRYAG4 1710 CMP #$0E ;UP MOVE 1720 BNE RETURN 1730 ; 1740 DEC VRTPTR 1750 JSR DRAWPL 1760 JMP RETURN 1770 ; 1780 RETURN 1790 JMP XITVBV 1800 ; 1810 ;BLOCK FILL ROUTINE 1820 ; 1830 BLKFIL 1840 ; 1850 ;DO FULL PAGES FIRST 1860 ; 1870 LDA FILVAL 1880 LDX TABSIZ+1 1890 BEQ PARTPG 1900 LDY #0 1910 FULLPG 1920 STA (TABPTR), Y 1930 INY 1940 BNE FULLPG 1950 INC TABPTR+1 1960 DEX 1970 BNE FULLPG 1980 ; 1990 ;DO REMAINING PARTIAL PAGE 2000 ; 2010 PARTPG 2020 LDX TABSIZ 2030 BEQ FINI 2040 LDY #0 2050 ; 2060 PARTLP 2070 STA (TABPTR),Y 2080 INY 2090 DEX 2100 BNE PARTLP 2110 ; 2120 FINI 2130 RTS 2140 ; 2150 DRAWPL 2160 PHA ;SAVE ACC VALUE 2170 CLC 2180 LDA TABADR 2190 ADC VRTPTR 2200 STA TABPTR 2210 LDA TABADR+1 2220 ADC #0 2230 STA TABPTR+1 2240 ; 2250 LDA #0 2260 FILLPL 2270 LDA SHAPE,Y 2280 STA (TABPTR), Y 2290 INY 2300 CPY #8 2310 BCC FILLPL ;REPEAT TILL DONE 2320 PLA ;RESTORE ACC VAL 2330 RTS
Download / View (Assembly source code)
Not The End
This concludes this traveler’s guide to the fascinating world of Atari assembly language. If you have typed and saved all of the programs in this book, you now have a fairly extensive library of assembly language routines that you can (no doubt) improve upon, and use in your own programs. If you have absorbed the material that surrounds the routines in this volume, you now know just about all you need to know to start writing some pretty sophisticated programs in assembly language.
I have just one more suggestion. If you’re interested in doing more programming in Atari assembly language - and I certainly hope you are - then there are two other books which you should definitely own. They are (in case you haven’t already guessed) De Re Atari and The Atari 400/800 Technical Reference Notes both published by Atari. With those two books, and the one you have just finished, you should be able to do just about anything you want to do from now on in Atari assembly language.
Return to Table of Contents | Previous Chapter | Next Chapter