by Craig
Patchett
The display
list
You probably already know at this point that the
display list is something that describes how the screen will be set up.
For example, the display list for a graphics mode zero screen tells the
computer that there are to be 24 graphics mode zero lines on the
screen. In graphics mode one, it tells the computer that there are to
be ten graphics mode one lines and four graphics mode zero lines
(remember that graphics mode one is a split-screen mode). The unique
thing about the display list, however, is that you can set it up any
way you choose. That means you can design a custom screen that is a mix
of whichever graphics modes you choose, and the ability to do this
comes in very handy when you're trying to design a game with a special
look.Let's begin by taking a look at a sample display list, in this case for graphics mode 0:
Display list for
GRAPHICS 0
(small
text)
CHR 2 LMS - GRAPHICS 0 lines with screen memory address JVB - go back to the beginning of the display list, wait until the end of VBLANK, and do it all over again! JVB LOW JVB HIGH |
FIGURE
1
If this was the display list that the computer was using, then it would leave the first 24 scan lines blank (because they're not completely visible on the screen) and then put 24 graphics mode zero lines on the screen. Once it was done with that, it would run into the JVB command, which tells it to go back to the beginning of the display list and wait for the end of vertical blank. Why? You'll recall from our previous discussion on the television screen that the screen has to be redrawn every 1/60 of a second, and that vertical blank is the time between screens. So by making the display list into a loop that gets executed after every vertical blank, we make sure that screen gets redrawn properly.
You probably noticed that I forgot to explain the LMS after the first CHR2 instruction. LMS stands for "Load Memory Scan," which is just a fancy way of saying "Here's where the screen memory is." In the last column we discussed the need for this feature, and you should make sure that you use an LMS whenever you want to change the address of screen memory. The first line on the screen must have an LMS. If the next one doesn't, then the computer will just assume that the screen memory for that line comes right after the screen memory for the previous line. In the case of graphics mode 0, each line needs 40 bytes, so the screen memory for the second line will begin 40 bytes after the screen memory begins for the first line.
So, what are the steps to creating a display list? First of all, there are a few simple rules that must be followed. If you want to make sure that the whole screen is visible, the display list should begin with three BLK 8 instructions, like in Figure 1. In some cases, like when you're doing vertical scrolling, you may not need them. Assuming you use them, then the main part of the screen should use a total of 192 scan lines if you want to make sure that the bottom of the screen is completely visible. Again, there is no reason why you can't use more. If you use too many, however, the screen will jump. If this happens, just get rid of a few until it stops jumping. The final rule, before we get going, is that no matter what you do to the display list, no matter how badly it gets screwed up, no matter how bizarre the resulting screen looks, you cannot screw up the computer or your program. If something does go wrong, just press System Reset, and you'll go back to a graphics mode 0 screen with your program intact. With these basic rules in mind, let's now take a look at the display list's instruction set.
BLK n: This instruction is used to leave blank scan lines on the screen. We've already seen one possible use-to leave the top of the screen blank so that the rest will be visible. Blank scan lines are also useful when, for some reason, you're not using part of the screen. The more BLK instructions you use, the faster (just a little) your program will run. Unfortunately, there are very few times when you don't use the entire screen. Personally, I hardly ever use them except at the beginning of the screen.
JMP: This is a jump instruction, the equivalent of BASIC's GOTO. The only reason it's needed is because the display list is not allowed to cross a 1K boundary. What's a 1K boundary? It's a memory location that is a multiple of 1024. When the GRAPHICS instruction is used, the display list is automatically positioned so that it does not cross such a boundary. When you design your own display lists, however, you may run into this problem (apparently not very often, however, since I have yet to do it). If you do, then you should put a JMP instruction right before the boundary. It's a three-byte instruction (one for the instruction and two for the address to jump to), so it should begin at the boundary address minus three, and then should jump to the boundary address. If this sounds kind of silly, it's only because it is.
JVB: This is the other jump instruction that we ran across in our example display list above. It tells the computer to go back to the beginning of the display list and wait until the end of vertical blank before drawing the screen again. Like the JMP instruction, it is also three bytes, with the second two specifying the address of the beginning of the display list. You'll see later exactly how to do this.
CHR: This specifies one of the character modes. For each character mode line on the screen you would use one of these instructions.
MAP n: This is the same as CHR, except it's used to specify a bit-mapped mode (PLOT and DRAWTO) instead of a character mode.
So much for the basic display-list instruction set. You may have noticed a few things that were missing, like the LMS instruction, for example. Well, the LMS is actually a modification, not an instruction. What this means is that the LMS, along with three other modifications, is added on to the above instructions. So you would have a CHR 2 LMS instruction, for example, or a CHR 6 LMS HSC instruction, which would be a graphics mode 1 line with LMS and horizontal fine scrolling. Here are the four modifications possible:
HSC: This is used to specify a horizontally fine-scrolling line.
VSC: This is used to specify a vertically fine-scrolling line.
LMS: We already know that this is used to specify the address of screen memory. An instruction with the LMS modification must be followed by a two-byte address.
DLI: This is used to specify a line with a display list interrupt at the end of it. We'll get into display list interrupts in next month's column.
You can add any or all of these modifications to the CHR and MAP instructions, but, for obvious reasons, the JMP, JVB and BLK instructions can only have the DLI modification. By this stage, you're probably wondering just how to get from CHR 6 LMS HSC, or whatever, to the number that will get POKEd into the display list. If you look at Figure 2 you will find a chart that gives the values for each of the possible instructions. To use the chart, first go down the left-hand side and find the row with the instruction you want to use. Then look at the top of the chart and find the column with the modifications you want to use. Go down the column until you get to the row you want, and you'll find the correct value. If charts bother you, then there is another way. Each instruction and each modification has its own value. If you take the values of the instruction and the modifications you want and add them all together, you'll get the correct value for the whole thing also. Here are the individual values:
BLK 1: 0
BLK 2: 16
BLK 3: 32
BLK 4: 48
BLK 5: 64
BLK 6: 80
BLK 7: 96
BLK 8: 112
JMP: 1
JVB: 65
CHR n: n
MAP n: n
HSC: 16
VSC: 32
LMS: 64
DLI: 128
BLK 2: 16
BLK 3: 32
BLK 4: 48
BLK 5: 64
BLK 6: 80
BLK 7: 96
BLK 8: 112
JMP: 1
JVB: 65
CHR n: n
MAP n: n
HSC: 16
VSC: 32
LMS: 64
DLI: 128
`You can check for yourself that this method will give the same values as the chart. Now the big question is, "What are all these different modes?" After all, we've already seen that CHR 2 is not graphics mode 2 but graphics mode 0. What about the other other modes? Here's an explanation of what each of the CHR and MAP modes are:
CHR 2 is GRAPHICS 0.
CHR 3 is the same as GRAPHICS 0 except the characters are ten scan lines high instead of eight. This allows you to have lowercase descenders, which means that the tails on "g," "j," "p," "q" and "y" can drop below the line, as they're supposed to. How do you use this mode? The first step is to redefine the character set. Actually, you only have to change the lowercase letters. What will happen is the computer will take the first two bytes of the character description and stick them on the end of the character. It will then make the first two scan lines of the character blank. For non-lowercase characters, it will leave the bytes in order and make the last two scan lines blank.
You should note that because each character is now ten scan lines high, you can only have 19 rows on the screen (192/10). Make sure of this when you change the display list.
CHR 4 lets you use multicolored characters. We mentioned this mode briefly in the column on character sets. CHR 4 characters are the same size as graphics mode 0 (CHR 2) characters. The difference, however, is in the size of the pixels that make up the characters. The pixels are the same height in both modes, but in CHR 4, they are twice as wide as in graphics mode 0. Why? In order to have four colors per pixel, there has to be two bits per pixel rather than one. This means that each character will be four pixels wide and eight high. While this isn't much use for designing letters (unless you put two characters side by side), it's great for graphics. Regardless of what you end up using this mode for, here's how the computer interprets a character description byte in this mode:
Although it's
true that you have
to know machine language to push
the display list to its limits, there's
more than enough that can be
handled from BASIC.
to know machine language to push
the display list to its limits, there's
more than enough that can be
handled from BASIC.
BITS | 7
6 |
5
4 |
3
2 |
1
0 |
USE |
PIXEL 1 | PIXEL 2 | PIXEL 3 | PIXEL 4 |
FIGURE
3
PIXEL VALUE |
COLOR REGISTER |
00 01 10 11 |
COLOR4
(background) COLOR0 COLOR1 COLOR2 |
Now, there is a way to add one more color to get all five colors on the screen at the same time. If you print a CHR 4 character in inverse video (i.e., the normal character value plus 128), then the pixels with a value of 11 binary will get their color from COLOR3 instead of COLOR2.
Finally, in case it isn't obvious, you should note that the procedure for designing a character set for this mode is exactly the same as that for graphics mode 0, with the exception, of course, that you will now be using two bits per pixel instead of one.
CHR 5 is the same as CHR 4 except the characters are twice as high (the same height as graphics mode two).
CHR 6 is GRAPHICS 1.
CHR 7 is GRAPHICS 2.
MAP 8 is GRAPHICS 3.
MAP 9 is GRAPHICS 4.
MAP 10 is GRAPHICS 5.
MAP 11 is GRAPHICS 6.
MAP 12 is the same as MAP 11 except the pixels are one scan line high instead of two.
MAP 13 is GRAPHICS 7.
MAP 14 is the same as MAP 13 except the pixels are one scan line high instead of two. This mode is sometimes called GRAPHICS 7.5 because it's halfway between GRAPHICS 7 and GRAPHICS 8. We'll be using it in our game, so you'll be able to see it in action.
MAP 15 is GRAPHICS 8.
Whew! Well, that's all the modes that the Atari makes available for you, with the exception of the GTIA modes or GRAPHICS 9, 10 and 11. We'll be covering those modes later in a special column just for them.
Okay, now we know almost everything that we need to know to actually design a display list, so let's add a few small details and then actually do some designing. First of all, you should know that locations 560 and 561 hold the address of the beginning of the display list. (PRINT PEEK(560)+PEEK(561)*256 will give you the decimal address.) Let's see, what else? Oh, screen memory is not allowed to cross a 4K boundary (a memory location that is a multiple of 4096). In the GRAPHICS modes, the only time this happens is in graphics modes 8 through 11, since in these modes screen memory takes up a total of 7680 bytes, well over 4096. In these modes, an extra LMS is added to the line where the offensive boundary is crossed. This is also what you should do if you need screen memory to cross a 4K boundary. Simply add LMS to the line that will cross the boundary and have it point to the first address after the boundary.
I realize that I have presented a lot of information so far without any concrete examples. This is mainly due to the fact that the display list is probably one of the most powerful graphics techniques that the Atari has to offer. Although it's true that you have to know machine language to push the display list to its limits (which is true with all the other techniques as well), there's more than enough that can be handled from BASIC. You've already seen the powers of fine scrolling in the last chapter, and we're now going to see how to go about mixing modes. Before we do, however, you may want to quickly go back to the last month's column and look again at the sections that involved the display list. Now that you have a better understanding of how the display list works, those sections may make a little more sense to you.
We're now ready to get our screen in the format that we decided on earlier in another column.
The first step is to get an initial display list and screen memory. There's no reason why we can't start off from scratch, but as long as there's a way for BASIC to help us out, we may as well take advantage of it by using the GRAPHICS command, since it automatically sets up a display list and screen memory that we can fool around with. The question is, which graphics mode do we use? In the past, most people pick the mode that has the biggest screen memory out of the ones they want to mix. That way, they make sure that there will be enough screen memory for the custom screen. It also means that some of the display list won't need to be changed. This method is fine, but it tends to waste memory. For example, in the screen that we're designing, GRAPHICS 7.5 uses the most screen memory at 7680 bytes. But we're not using that many GRAPHICS 7.5 lines.
Display list
interrupts (DLIs)
are important and powerful
features of your Atari computer:
are important and powerful
features of your Atari computer:
We must figure out the total amount of memory that we need for screen memory. We see that it only comes out to 1360 bytes (16*20 +24*40+3*20+1*20), afar cry from 7680. That gives us two options. We can either set up for GRAPHICS 8, which is the same screen memory as GRAPHICS 7.5) and waste the extra memory, which is fine if you've got it to spare, or we can set up for GRAPHICS 6, which uses 1920 bytes for screen memory, much closer to our 1360. The choice is entirely up to you, but I would recommend wasting as little memory as possible, since the less memory your game requires, the more people that will be able to use it. So this will be the first part of our display list redefinition:
4000
GRAPHICS 22:POKE 55
9,0:POKE 756,CB+2
9,0:POKE 756,CB+2
Remember, this is a change to the program that we've been developing throughout the previous columns. Our next step is to find the display list so we can go about changing it. We already did that in last month's column, but here's the line again, just to refresh your memory:
5010
DLIST=PEEK(560)+PEE
K(561)*256
K(561)*256
Now we're all set to go in and change the display list. Here are the program lines to do:
5020
POKE DLIST+3,86
5030 L=PEEK(DLIST+4)+44:
POKE DLIST+5,PEEK(DLIST+
5)+(L>255):POKE DLIST+4,
L-256*(L>255)
5040 FOR X=6 TO 20:POKE
DLIST+X,22:NEXT X:FOR X=
24 TO 50:POKE DLIST+X,14
:NEXT X
5050 MEM7=PEEK(88)+PEEK(
89)*256+600
5060 POKE DLIST+21,78:PO
KE DLIST+23,INT(MEM7/256
):POKE DLIST+22,MEM7-INT
(MEM7/256)*256
5070 POKE DLIST+31,78:PO
KE DLIST+33,INT((MEM7+32
0)/256):POKE DLIST+32,ME
M7+320-PEEK(DLIST+33)*25
6
5080 POKE DLIST+41,78:PO
KE DLIST+43,INT((MEM7+64
0)/256):POKE DLIST+42,ME
M7+640-PEEK(DLIST+43)*25
6
5090 POKE DLIST+51,22:PO
KE DLIST+52,22
5100 POKE DLI5T+53,22
5110 POKE DLIST+54,6:POK
E DLIST+55,70:POKE DLIST
+56,PEEK(88):POKE DLIST+
57,PEEK(89)
5120 POKE DLIST+58,65:PO
KE DLIST+59,PEEK(560):PO
KE DLIST+60,PEEK(561)
5030 L=PEEK(DLIST+4)+44:
POKE DLIST+5,PEEK(DLIST+
5)+(L>255):POKE DLIST+4,
L-256*(L>255)
5040 FOR X=6 TO 20:POKE
DLIST+X,22:NEXT X:FOR X=
24 TO 50:POKE DLIST+X,14
:NEXT X
5050 MEM7=PEEK(88)+PEEK(
89)*256+600
5060 POKE DLIST+21,78:PO
KE DLIST+23,INT(MEM7/256
):POKE DLIST+22,MEM7-INT
(MEM7/256)*256
5070 POKE DLIST+31,78:PO
KE DLIST+33,INT((MEM7+32
0)/256):POKE DLIST+32,ME
M7+320-PEEK(DLIST+33)*25
6
5080 POKE DLIST+41,78:PO
KE DLIST+43,INT((MEM7+64
0)/256):POKE DLIST+42,ME
M7+640-PEEK(DLIST+43)*25
6
5090 POKE DLIST+51,22:PO
KE DLIST+52,22
5100 POKE DLI5T+53,22
5110 POKE DLIST+54,6:POK
E DLIST+55,70:POKE DLIST
+56,PEEK(88):POKE DLIST+
57,PEEK(89)
5120 POKE DLIST+58,65:PO
KE DLIST+59,PEEK(560):PO
KE DLIST+60,PEEK(561)
And here is the line-by-line explanation:
5020: This makes the first line a CHR 6 HSC LMS. Remember that the first 24 scan lines are left blank, which requires three BLK 8 instructions. That's why the first CHR 6 line is at DLIST+3 not DUST.
5030: This is from a previous column, where we decided that we were going to skip over the first 44 bytes of screen memory, using them later to hold the score. If you don't remember why we did this, go back and double-check. In any case, this line sets up the LMS address for the first line on the screen.
5040: Here we set up 15 more CHR 6 HSC lines and 27 MAP 14 lines. Wait a minute, though. Don't we only want 24 MAP 14 lines? And what happened to DLIST+21 through DLIST+23? The reason that things look a little funny at this point is because we want to set up the MAP 14 area in three sections. Remember that the invaders will eventually move far enough down the screen so that they run into the barriers, which are in the MAP 14 section. When they do this, we want to switch the part of the barriers that they run into to a CHR 6 line. We can do that quite simply, but we have to be careful about screen memory, since one CHR 6 line (20 bytes of screen memory) will be replacing eight MAP 14 lines (320 bytes of screen memory). To avoid a problem, we'll put an LMS at the beginning of each group of eight MAP 14 lines (three groups altogether). You'll see this being done in the next few program lines, and you will then understand why we did things in a funny way here.
5050: To make sure that the invaders' screen memory is completely separate from the barriers' screen memory, we'll start the barriers' screen memory 600 bytes past the beginning of the invaders' screen memory. Why 600? Why not! Actually, it could have been anything greater than 444 (20 CHR 6 lines times 20 bytes apiece, plus the initial 44 bytes we skipped over). I just chose 600 to make sure there would be no conflicts.
5060: Now we put the first MAP 14 LMS into the display list. Notice that it fills in the gap we were worrying about earlier.
5070-5080: Here are the other two MAP 24 LMSes. These two go in the middle of the 27 MAP 14s we put in before. The two LMS addresses replace four of the MAP 14s, leaving 23 intact. Add these to the one we set up in Line 5060, and that gives us the 24 that we wanted initially.
Since the DLI
has to do whatever
it does in an extremely short time,
a DLI routine has to be written
in machine language.
it does in an extremely short time,
a DLI routine has to be written
in machine language.
5090-5100: Here we add the last three CHR 6 HSCs. Did you notice that we didn't use an LMS here? That means that screen memory for these three lines will come right after screen memory for the barriers. Is that what we want? No, but the invaders won't appear in these lines until after the MAP 14s have been replaced with CHR 6 HSCs, like I mentioned earlier. Once this happens, the three CHR 6 HSCs here will be attached to the invader screen memory, as they should be.
5110: We're not done yet. Here we put in the CHR 6 and the CHR 6 LMS for the score line (remember that the address for this LMS is the beginning of the original screen memory).
5120: Now we finish the display list with a JVB back to the beginning. Note that unless you're doing fancy stuff with alternating display lists, this is the way to end any display list.
Okay, now our custom display list is in place and ready to go. Unfortunately, we now run into a few problems. First of all, the computer thinks we're in graphics mode 6. This is no problem at the moment, but it will be later when we try to PRINT the score, and when we try and PLOT and DRAWTO in graphics mode 7.5. (There's no problem with the invaders, since we're moving them directly into screen memory.) How do we tell the computer which graphics mode we want to use? Luckily for us, location 87 exists just to tell the computer what graphics mode is being used. All we have to do is POKE location 87 with the graphics mode we want to use (for graphics mode 7.5, we POKE 87,7 since GRAPHICS 7 is the closest thing to it from BASIC). So much for this "problem."
Our next problem is with screen memory. For example, let's suppose that we hadn't shifted screen memory around and that the score line was at the end of screen memory. What if we wanted to PRINT the score? We could try POSITION 0,23: PRINT #6; "SCORE." Would this work? No, because the computer will print the score 460 (23*20) bytes into screen memory, which would be somewhere in our graphics mode 7.5 area. Because the computer only expects there to be 480 bytes of screen memory in graphics mode one, it will not let you get at the score line. So how do we get around this problem? Locations 88 and 89 point to where the computer thinks screen memory is (there is actually a separate chip called ANTIC that interprets the display list and draws the screen. That's why the computer needs locations 87, 88 and 89). So to print the score, we would change locations 88 and 89 to point to the beginning of the screen memory that the score line uses. Then we can POSITION 0,0 and PRINT #6; "SCORE." Add these lines to our program to see what I mean:
5180
MEM1=PEEK(DLIST+4)+
PEEK[DL15T+5)*256:SCRL=P
EEK(DLIST+56):SCRH=PEEK(
DLIST+57)
5190 POKE 87,1:POKE 88,5
CRL:POKE 89,SCRH:POSITIO
N 0,0:? #6;" SCORE:
0"
PEEK[DL15T+5)*256:SCRL=P
EEK(DLIST+56):SCRH=PEEK(
DLIST+57)
5190 POKE 87,1:POKE 88,5
CRL:POKE 89,SCRH:POSITIO
N 0,0:? #6;" SCORE:
0"
You'll see another example of this next when we draw the barriers.
Display list
interrupts
I know, I know, this section was supposed to be
about drawing the barriers. So I lied. But I did mention display list
interrupts (DLIs) in the last section, and I promised to explain them;
so this will be a relatively quick and painless explanation.Everybody says that the DLI is the most powerful feature of Atari computers. I don't know about "most," but it certainly is powerful. DLIs let you change the screen colors partway down the screen or have more than one character set on the screen at the same time. They let you scroll part of the screen in one direction and the other part in another direction. They let you reposition players partway down the screen, so that one player appears to be two, three and more. Anything else they can do is up to your creativity and imagination.
You already know that a DLI is a modification to a display list instruction. You'll also recall that ANTIC is the chip that is responsible for drawing the screen. Anyway, when ANTIC gets to a display list instruction with a DLI, it first draws that line and then interrupts the 6502 or main chip. The 6502 executes the DLI routine and then goes back to whatever it was doing before the interruption. And that's all there is to a DLI.
Unfortunately, since the DLI has to do whatever it does in an extremely short amount of time, a DLI routine has to be written in machine language. Luckily though, it's usually very simple machine language. For example, here's a DLI routine to change the color of the screen:
PHA
LSA #212
STA WSYNC
STA COLBK
PLA
RTI
LSA #212
STA WSYNC
STA COLBK
PLA
RTI
What this does is save the accumulator value, load the new color value into the accumulator, and then store it into WSYNC at location 54282. When any value is stored into WSYNC, the 6502 refrains from doing anything until the next HBLANK (the time between scan lines). Anyway, then the color value gets stored into the background color register at location 53274, the accumulator is restored to its original value, and we return from the interrupt.
"But," you may be wondering, "isn't the background color register at location 712?" Actually, only sort of. Location 712 is something called a shadow register, which more or less acts like a messenger to the real color register at location 53274. Does this sound complicated? It is a little. You see, during VBLANK, the value in location 712 is transferred to location 53274. Why? Take another look at the above DLI routine. We'll see it in action a little later on, but for now take my word that it will change the background color, so that part of the screen is one color and the other part is another. Think about this. The DLI changes the color partway down the screen, but how does it know to change it back at the beginning of the next screen? It knows from the shadow register. If it wasn't for the shadow register, you would have to have a DLI at the top of the screen as well. In any case, just remember to change the hardware registers in a DLI, not the shadow registers.
How does the computer know where the DLI routine is? Locations 512 and 513 hold the address of the DLI routine. So to get a DLI going, you would change the display list, store the routine somewhere safe in memory (somewhere that doesn't shift around, which means you can't store it in a string like other routines), set locations 512 and 513, and then POKE 54286, 192 to turn the DLI on (POKE 54286, 64 will turn it off again). That's all there is to it. Actually, this would be a good time to add our sample routine above to our invaders program and prove to you that it isn't that difficult; so why don't you try adding the following lines to our program:
3100
FOR BYTE=0 TO 10:RE
AD DAT:POKE 1536+BYTE,DA
T:NEXT BYTE
3110 DATA 72,169,212,141
,10,212,141,26,208,104,6
4
5100 POKE DLIST+53,15B:P
OKE 512,0:POKE 513,6:POK
E 54286,192
AD DAT:POKE 1536+BYTE,DA
T:NEXT BYTE
3110 DATA 72,169,212,141
,10,212,141,26,208,104,6
4
5100 POKE DLIST+53,15B:P
OKE 512,0:POKE 513,6:POK
E 54286,192
Here's an explanation of what you just did:
3100: This just reads in the data for the routine and stores it in page 6, where it will be safe.
3110: Here's the data. We'll be taking a closer look at this in a minute, showing you how you can change it for your own use.
5100: This adds a DLI modification to the display list, sets up the DLI routine address, and turns on the DLI.
That wasn't too tough now, was it? But, of course, all it does is change the color. What if we wanted more than one DLI? Can we do that? Well, yes, but it starts to get a little more complicated. The problem with having more than one DLI is that we have to update locations 512 and 513 each time we change routines. For example, the first routine would have to update the locations to point to the second routine, the second to point to the third, and so on up to the last routine, which would have to update the locations to point back to the first routine.
Not too difficult, right? (Assuming you know some machine language.) Right, but this limits what you can do during the DLIs since there is not much time available. Actually you really can't do much more than change a few locations before you run out of time. If you decide to get adventurous and design your own DLIs, and if you try to do a lot of stuff during them, and if the screen looks kind of funny once they're working, then you have probably used up too much time. In other words, don't expect a DLI to do too much. Oh, and DLIs are best suited for making changes to the screen. Save everything else for your regular program.
Okay, we've now gotten all of the annoying little details out of the way; so let's take a quick look at how you would make changes to the DLI routine I've given you, assuming you want it to do something different. First of all, here's how the numbers in the Line 3110 correspond to the assembly language:
72 PHA
169 212 LDA #212
141 10 212 STA NSYNC
141 26 208 STA COLBK
104 PLA
64 RTI
169 212 LDA #212
141 10 212 STA NSYNC
141 26 208 STA COLBK
104 PLA
64 RTI
What good does this do you? Well, if you wanted to change to a different color, you could just change the first 212 in Line 3110 to something else. Try it. Also, suppose you wanted to change character sets instead of colors. You would change the first 212 to the page address of the second character set, and then you would change COLBK (26, 208) to CHBASE (9, 212). As you can see, this simple DLI routine can be changed by you in a number of ways to get the specific result you want; so don't be afraid to play. I should warn you, however, to save the program before trying a new DLI. If you did something wrong by mistake, there is a chance that you lock up the machine. If you do, and System Reset doesn't help, just turn the computer off and back on again, and load back the program so you can figure out what went wrong.
Bit-mapping
(Part 1)
This is probably going to be one of the easiest
sections in this column, because bitmapping is a relatively simple
techniqueat least it is when you're using it for stationary objects, as
we are with the barriers. You'll recall that bit-mapping can also be
used for simple animation, in which case things can get a little more
complicated and also extremely slow. For BASIC games, you're better off
only using bitmapping for non-animated objects.The first step for bit-mapping is the same as that for character sets and for player/missile graphics, deciding what the object is going to look like. Figure 2 is our barrier shape.
|
FIGURE
2
Drawing the
barriers
The next question is: How do we draw this quickly
and easily? Actually, it should be how do we draw these, since there
are supposed to be four barriers, not just one. Well, let's look at the
methods available to us. We could PLOT each of the points in the
barriers, but that would obviously take too long in this case. We could
use PLOT and DRAWTO and draw them line by line. This is better, but
let's see what else there is. We could use PLOT, DRAWTO and X10 18
(FILL). This is faster still, but doesn't look quite as neat. Finally,
we could store the data for the barriers in a string and then use
MOVMEM to transfer this data directly into screen memory. This is by
far the quickest method, but we're talking here about 40*24=960 bytes
of screen memory, and therefore 960 bytes of string space. Keeping
memory requirements down is important, so we'll stay away from this
method. What's our final decision then? Well, it comes down to PLOT and
DRAW TO or PLOT and DRAWTO with FILL. I'd like to try for a technique
that gives the effect of building the barriers, so, as I explained
earlier, I personally want to use the visual effect that PLOT and
DRAWTO give. There's no reason, however, why FILL could not be added if
that's your own personal preference.Our next step is to figure out the easiest way to use our chosen technique. We're obviously going to want to use some FOR/NEXT loops, since there is a lot of repetition in the barrier shape (and there are also four identical barriers). Let's break the barrier up into groups of horizontal rectangles, since a PLOT and DRAWTO FOR/NEXT loop is good for drawing rectangles. Figure 3 shows our barrier shape broken up this way.
Now we have what we need to start programming. What we'll do is set up three nested FOR/NEXT loops. The outside loop will count off the ten rectangles, the next will count off the horizontal lines in each rectangle, and the inside loop will count off the four barriers. We'll store the vital information about each rectangle in a DATA statement so that the loops can get to it easily.
If I told you right now to try and write the loops I described by yourself, how could you do it? Would you jump right in and start work on the first loop? If you would, then you forgot all about what we discussed at the end of the previous section. (Don't feel too badly, I'm writing this paragraph because I forgot also!) Now do you remember? Because we're using a custom display list, we have to tell the computer where our screen memory is and what mode we're using. The following line will do that for us:
5210
POKE 87,7:POKE 89,I
NT(MEM7/256):POKE 88,MEM
7-PEEK(89)*256:COLOR 3
NT(MEM7/256):POKE 88,MEM
7-PEEK(89)*256:COLOR 3
Remember that location 87 tells the computer the BASIC mode number, and locations 88 and 89 tell it the location of screen memory. If you're using a graphics mode that doesn't have a BASIC equivalent (as we are here), then choose the BASIC mode that is closest to it.
Okay, with that out of the way, we can now go ahead and write our loops. Here are the lines to add to our program:
5210
POKE 87,7:POKE 89,I
NT(MEM7/256):POKE 88,MEM
7-PEEK(89)*256:COLOR 3
5230 RESTORE 5260
5240 FOR X=1 TO 10:READ
N,XS,Y,XE:FOR T=N-1 TO 0
STEP -1:FOR Z=0 TO 3:PL
OT Z*40+9+XS,Y+T:DRAWTO
Z*40+9+XE,Y+T
5250 NEXT Z:NEXT T:NEXT
X
5260 DATA 4,16,28,20,4,1
,20,5,2,15,18,20,2,1,18,
6,2,14,16,20,2,1,16,7,10
,1,6,20,2,2,4,19,2,3,2,1
8,2,4,8,17
NT(MEM7/256):POKE 88,MEM
7-PEEK(89)*256:COLOR 3
5230 RESTORE 5260
5240 FOR X=1 TO 10:READ
N,XS,Y,XE:FOR T=N-1 TO 0
STEP -1:FOR Z=0 TO 3:PL
OT Z*40+9+XS,Y+T:DRAWTO
Z*40+9+XE,Y+T
5250 NEXT Z:NEXT T:NEXT
X
5260 DATA 4,16,28,20,4,1
,20,5,2,15,18,20,2,1,18,
6,2,14,16,20,2,1,16,7,10
,1,6,20,2,2,4,19,2,3,2,1
8,2,4,8,17
And, of course, the explanation:
5230: Any time you have a program with more than one set of DATA statements, it's a good idea to RESTORE the first line of the DATA you want to use. Why? When BASIC encounters a READ statement, it will look for the DATA that comes after whatever it read last. It will not look for the DATA closest to the READ statement. So, since you'll be reading data out of order often, the RESTORE becomes a necessity.
5420-5250: This is our loop. X keeps track of which rectangle we're drawing. N is the number of lines in the current rectangle, XS is the starting X position for the rectangle, Y is the starting Y position, and XE is the ending X position. T keeps track of which line we're drawing within the current rectangle, and Z keeps track of which barrier we're working on. Keeping all this in mind, you should be able to figue out what's going on here.
5260: This is the data for the rectangles. There are four numbers for each rectangle (N, XS, Y and XE), the meanings for which were discussed above.
Well, that's about it. (I told you this was easy.) Incidentally, just in case you decide that you prefer to use FILL, here's a brief introduction to using X10 18:
1. PLOT a point at the lower left-hand corner of your object.
2. DRAWTO the upper right-hand corner.
3. DRAWTO the upper left-hand corner.
4. POSITION the cursor at the lower righthand corner.
5. POKE location 765 with the number of the color register you want to FILL with. (This number is the same as that you would use with the color command.)
6. Now do X10 18,#6,0,0,"S:".
There is one problem with FILL; the area that you're FILLing has to be empty. If there are any points already turned on within the area, FILL won't work correctly.