The graph paper: scrolls and other creatures. (part 7) David Lubar.
The Graph Paper
Last month, we covered some methods of screen manipulation and took a preliminary look at character graphics. This time, we'll go deeper into character graphics and cover some other types of screen manipulation, such as scrolling. If you haven't already made a copy of the lookup table listed in the last issue, I'd suggest doing so now, since it will be used extensively from here on.
The question of the month comes from Avis N. Wyatt, Jr., who asks "How can you print on the high resolution screen?' Several methods are discussed below.
The Art of Text
The simple example of character graphics listed last month covered the basic concept of placing a series of bytes on the screen to form an image. The strength of this method is that each image uses little memory and each shape can easily be placed on the screen.
The disadvantage is that minimum horizontal motion is limited to steps of one byte, causing the image to jump seven pixels whenever it moves. While this may seem like a large jump, it works well in some programs. There are actually many rpograms on the market that move objects two bytes at a time.
If the object is large enough, the result is still smooth animation. Next month, we'll go into smooth, single bit movements. For now, we'll look at various applications of character techniques.
There are two main uses for characters graphics. One, as mentioned, and as demonstrated last month, is animation. The other, more crucial application is for placing text on the graphics screen. In the old days, games usually put all text on the bottom four lines, making use of the Apple text window. Nowadays, messages appear all over the place, in various fonts.
Text messages can be broken into two types: dedicated blocks and character sets. A dedicated block is a message that doesn't change, such as the phrase HIGH SCORE. A phrase such as this can be saved as a group of bytes, and then can be placed on the screen wherever it is needed. There is no reason to develop a whole character set for such a message. To do so would be analogous to primitive printing methods where whole words were carved from wood.
Listing 1 shows a general program for placing a message on the screen. To use the routine, just pass the desired coordinates and the routine will place the block of bytes on the screen. See Figure 1 for an example of how the byte pattern for the message was composed. Note that theroutine doesn't check for screen boundaries. Since this is an important consideration, I'll take a short sidetrip into the topic now.
Checking For Screen Boundaries
When an object goes past the edge of the screen, it should either disappear or wrap around. Unlike Applesoft shape tables, here the programmer has control of what happens. But if you aren't careful, you can blow your whole program away and hang the system in various nasty manners. The lookup table we use contains a hi byte and to byte for each line of the screen. But if your program tried to use the table for a location greater than #$BF (the last line of the screen), it would produce an address that might be anywhere in memory. It would grab a hi byte from the beginning of the lo byte table, and a lo byte from somewhere past the end of the lo byte table.
If the resulting address were to be used as a pointer, you might end up placing bytes in undesirable areas.
Objects that go past the screen horizontally are not as disastrous, but can be unattractive since they don't wrap smoothly but appear on the next memory location which, as you known, is not the next sceen location.
There are two ways to handle these problems. The first is to pad out the lookup table. Since each table is only 192 bytes long, and the 6502 registers can index 256 bytes, the rest of the table can be padded with harmless addresses.
As mentioned last month, there are certain unused portions of the screen, and objects that go past the bottom line can safely be placed in them. If the lo byte table is padded with values of $F8 and the hi byte table padded with $20, shapes that go past the bottom of the screen will be placed in the undisplayed portion of memory starting at $20F8. This works fine as long as the shape occupies no more than eight bytes per line.
A better method is to check coordinates within your plotting routine. If the vertical coordinate is greater than $BF, nothing is put to the screen. This cuts an object off if it leaves the screen.
To create wraparound, te vertical value is set to $00 when it passes $BF. Horizontal coordinates are treated in a similar manner. You can clip an image by stopping the display when the horizontal location exceeds $27, or create wraparound by allowing the values to cycle. If the horizontal position goes past $27, it becomes $00. If it goes below $00, it becomes $27. In essence, the line is treated in a MOD 27 fashion. We'll explore this in greater depth in later articles.
Getting back to the printing analogy, the fixed message blocks are fine for dedicated uses, but are inefficient for programs that contain many messages or have changing text. One example would be a score display. While the word SCORE can be saved as a block, the various score values cannot. It would be ridiculous to save 100 different images to display scores from 1 to 100.
Just as a linotype creates text from combinations of letters rather than stored words, we can put messages and scores on the screenusing a character set. The character set contains all the desired letters and numbers, stored as byte images.
To use such a method, you must supply the routine with the desired coordinates and, additionally, information concerning which member of the set is to be displayed. This information is the shape number.
A set might start off with the digits from 0 to 9, followed by the letters of the alphabet. So the shape number for 0 would be 0, the number for A would be 10, B would be 11 and so on.
If your characters are one byte wide by eight bytes high, you can fit 32 characters into a single page of memory, and need be concerned only with the lo byte of the address of each character. These lo bytes can be either calculated or looked up from a table. In this example, where eight bytes are used for each character, the calculation is simple. Three ASL operations suffice to multiply a number by 8, thus providing the lo byte location.
A partial character display routine is given in Listing 2. I have defined the first few entries of the set, but haven't done the entire alphabet. To show how this routine might be use, Listing 3 contains a program that makes use of Listing 2. The program puts up a six-digit number and increments it.
This sort of program could be used for score keeping and other such functions.
Note the use of the 6502 BCD (Binary Coded Decimal) mode. I have found this to be the easiest method for keeping scores. The BCD mode treats each byte as two decimal digits, limiting each nibble to values from 0-9. Addition and subtraction are handled just as if the nibbles were decimal digits. But increment and decrement instructions still operate as if the digit were normal hex, so be careful.
If you add 01 to 009 in the BCD mode, the result will be 10, but an increment will produce 0A, which is not a valid BCD value. Also, be careful of branches when the decimal mode is set. It is best to clear the mode immediately after using it.
Listing 2 can be thought of as a dedicated routine. Each shape is the same size. But there is no need to restrict characters to one byte in width, nor is there any rule that says all characters must be the same size. Each character can be preceded by two bytes telling how wide and deep it is, allowing character sets with differing sizes. Also, larger characters allow for more creative use of color, a subject we'll look at now.
Small character fonts really can't make much use of color. Since there are only seven plotting bits in a byte, and since each letter should be bordered by blank bits on one side to prevent the text from blurring together, there isn't much room to produce both color and an appealing font.
As mentioned in previous articles, color is produced by plotting every second bit. This can't be done effectively in a small font. But it works out nicely in larger displays, as evidenced by the colorful large fonts found in many games.
If you create a large font and place it on the screen, you may find that the letters alternate in color, since an image started on an odd byte will be a different color than the same pattern started on an even byte. There are several ways around this problem.
One fairly painless but limiting solution is to make each image an even number of bytes wide. Thus, if a message is started on an odd byte, each succeeding letters will also start on an odd byte. Figure 2 shows an image for a letter that is two bytes wide and a single color. Note how the hi bit is skipped and how the set bits are designed to maintain color.
Other methods of dealing with color involve color masks and shifting. A color mask is simply a byte pattern that turns off undesired bits. The first step would be to create a white image by setting sequential bits in the shape.
Now suppose you want only the odd bits to be set. You would make two masks, one with only even bits set ($2A), the other with odd bits set ($55 ignoring the hi bit).
Now, when a byte is placed on an even byte, it is ANDed with the even mask value $2A. This turns off all the bits occuring on even locations. When the byte is to be placed on an odd position, it is ANDed with the $55 mask. As a result, only bits occurring on odd positions will be displayed (see Figure 3).
Fortunately, it is very easy to determine whether a coordinate is odd or even. Just take the horizontal value and perform the LSR operation. If the carry is set, the number is odd.
Rotation can be used to change or control the color of an image. Let's say you have a character set defined with internal color by skipping every other bit. As mentioned, it will have one color when a character is started on odd bytes, and another color when started on even bytes.
But suppose you want to maintain color. The soluion is to rotate each byte before putting it on the screen. Assume the color you desire is the one produced when the byte starts at an odd location. To produce the same color on even locations, each byte is rotated before being placed on the screen.
The problem with this method is that bits that rotate out of one byte must be placed in the next byte. If all eight bits of a byte were used for plotting, this would be a simple operation, since the carry would pass on such bits, but the hi bit of the Apple gets in the way, complicating the process. In general, the masking method is quicker and simpler.
One other alternative, when space is no problem, would be to have two separate fonts--one with the odd bits set, the other with even bits set. In general, this approach isn't worth the space.
I am indebted to Ernie Brock, author of Pascal Graphics Editor, for providing me with a weath of information on character graphics and color control.
Ups and Downs
Scrolling is just a specialized form of byte manipulation. To make the screen scroll down, the bytes in the next to last line are moved to the last line, the bytes from the third from last line are moved to the next to last line, and so on. If required, new data is brought in to fill the vacated top line.
A downward scroll must start at the bottom and work up. If the procedure started at the top, the result would be duplication of the top line all the way down. First, the top line would be moved to the second line, then the second line (which contains the same values as the top line) would be moved to the third line, and so on.
To demonstrate the process, Listing 4 contains a short program that scrolls the whole screen downward. After each move, the top line is replaced with $00, so the end result is a blank screen. To scroll horizontally, the same basic technique is used, but rather than move each line up or down, each byte within a line is moved over.
Because of the odd/even color problem on the Apple, a scroll of one byte will produce an image that alternates between colors with each move.
Listing 5 shows a program that scrolls the screen horizontally. In this case, the byte that moves off the edge is replaced on the other side. Note that execution time could be improved by using separate source and destination pointers rather than manipulating the register so much.
In many cases, you won't want to scroll the whole screen. Many games have a small area scrolling across the bottom. In such cases, where the scene being scrolled is larger than the screen, an image of the area is kept elsewhere in memory. For each screen line, there is a data line containing the full landscape image for that screen line. In this way, a varying landscape can be scrolled across the screen. A variable is kept pointing to the portion of the data which is currently the start of a screen line. To scroll the image, the pointer is changed, and the bytes for each line are taken from the data and put to the screen.
You may have noticed that the fullscreen scroll is not very fast. When speed is required, there is another method available, using dedicated code. Rather than use indirect indexing, this method contains a sequence that specifies each memory location in the scrolling sequence. The advantage of such a method is high speed. The problem is that the code is rather long. For a full screen, the code would contain 192 loads and 192 stores. A segment of such a routine is shown in Listing 6. In general, this type of dedicated code should be used whenever speed is essential.
Code of Many Colors
We have already seen how the six basic colors are obtained on the Apple. Alternating dots produce color, sequential dots produce white, and an absense of dots results in black. But many other colors can be produced on the Apple by combining colors.
For instance, suppose you turn on every fourth pixel of a line. If you repeate the process on the next line, the result would be alternating colored and black vertical stripes. This, however, is not an interesting blend.
But suppose the lines were staggered. The first line starts on bit 0 and sets every fourth pixel. The next line starts with bit 2 and again sets every fourth pixel. The third line begins at 0, the fourth line at 2 and so one. Now, the colors blend to produce a darker shade of the original color.
This is the general method for obtaining those "extra' colors. Such patterns can be stored as a series of bytes, then be moved to the screen, or be ANDed with the screen, to produce colorful effects.
For instance, the pattern described above would be $11, $22, $44, $08, repeated to fill the first line. Then, for the second line, the sequence would be $44, $08, $11, $22, etc. If you want to fill an area, you just store the bytes to the screen. If you want to color an object, you AND it with the color bytes.
That's about all that will fit this month. Next time, we'll get into full animation with pre-shifted shapes. We'll cover the various methods being used in arcade games.
Table: Listing 1.
Table: Figure 1. Byte pattern for a message image.
Table: Listing 2.
Table: Listing 3.
Table: Listing 4.
Table: Figure 2. 2 byte wide colored letter. Note use of alternating (circled) bits.
Table: Figure 3. Bit masks for displaying odd bits.
Table: Listing 5.
Table: Listing 6.