Atari Graphics and Arcade Game Design
Home
Mapping the Atari
Atari Graphics and Arcade Game Design
Old Hackers Newsletter

Chapter 3
Character Set Graphics

The Atari computer is one of the few machines endowed with enormous flexibility when it comes to handling character set graphics. Most computers have a set of character shapes stored permanently in Read Only Memory (ROM). The Atari is no exception. In addition to the usual upper and lowercase letters and numbers, twenty-nine control keys represent graphics symbols. These include a heart, diamond, spade, and club useful for creating playing cards, and a number of triangles, small squares, and diagonal and edge lines suitable for creating geometric graphic displays. While special characters can be useful in some playfield designs, it is impossible to achieve widely varied playfield designs using characters on most computers without resorting to slower bit-mapped character shapes.

The Atari, fortunately, takes the concept of character graphics one step further and allows the user to redefine the character shapes to create colorful and varied backgrounds. A hardware register and its shadow register in RAM keeps track of the location of the character set currently being used. Normally the register defaults to the set stored in ROM at $E000, but you can give it any RAM address that falls on a 1K boundary. When ANTIC is given instructions to put character data on the screen it fetches character data from the character set stored at this location.

A character set consists of a maximum of 128 eight-byte shapes. Each character is represented on the screen as a group of 8 x 8 dots or pixels. When reading text on the screen it appears that there are gaps between characters, but there aren't. The gaps are provided by leaving blank space near the edge of the block alloted to the individual characters. Since there is no space otherwise defined between characters, it is possible to create larger shapes consisting of groups of adjacent characters, or background with no breaks in it.

If we take a look at a single character like the capital letter A from the ROM character set, it forms a pattern of onoff dots as illustrated below. The pixels in each of the eight rows, numbered here from 0 -7, are represented in the character set as a value from 0 -255 ($00-3FF). The positions of the individual pixels in the row determine the value.

Pixel values are calculated using a base two numbering system. If a pixel is in the rightmost column of a row, the bit is set, and it has a value of one. If the lit pixel is in the next column to the left, that bit is set, and it has a value of two. The values increase by a factor of two until we reach the left most column which has a value of 128. Thus, if you want to determine the value of the row in a character you add up the individual bit values for the lit pixels. If we look at row #1 in the letter A we find that the fourth and fifth pixels are lit. The value of the set bits is 8+16 = 24 ($18). If all the pixels were lit we would have 128+64+32+16+8+4+2+1=255 ($FF).

(figure)

Each character is stored sequentially in the character set as groups of eight values. The first character is stored in the first eight memory locations (0-7) in the character set, the second character is stored in the second eight memory locations (8-15), etc. The first character is called the 0 th internal character, the second the first, etc. Eight values times 128 characters requires 1024 bytes of memory. If you look in your BASIC book you will see that internal character values 128-255 produce inverse characters. These characters are the same as the ones in the internal character set except the seventh or leftmost bit of the character number is set or turned on. This is equivalent to adding 128 to the internal character number. If the high bit is set, ANTIC automatically interprets the character as inverse, and it plots it on the screen.

Since the computer can only store numbers from 0-255, all characters are assigned ATASCII (Atari ASCII) numbers. For example the letter A is assigned the ATASCII value 65. If you look at the individual characters stored internally from 0- 127 in the character set and then compare them to the ATASCII values you will find them out of order. In fact the order is as follows:

Type                              ATASCII   MEMORY
                                  Order     Order 
uppercase, numbers, punctuation    32- 95   64- 95
graphics characters                 0- 31   64- 95
lowercase, some graphics           96-127   96-127 

Essentially, Atari moved the graphics characters and placed them between the uppercase and lowercase letters. It may seem illogical at first since it complicates the calculation of the memory locations for any ATASCII character value, but it actually enables you to choose between upper and lowercase graphics in modes one and two. In both of these enlarged text modes only sixty-four characters are available, and the set is only 512 bytes long. They use the leftmost two bits of each byte to point to the color register for that character. The uppercase characters in internal positions 0-63 use color register #0. If you attempt to print lowercase characters to the screen, you still get uppercase characters but using a different color register. The letter "A" is internal character 33 while the letter "a" is internal character 97. This is equivalent to 33 + 64 or toggling one of the two high bits. The same is true for inverse. This can be demonstrated in the following program.

10 GRAPHICS 2+16
20 PRINT #6;"HELLO hello"
30 PRINT #6;"HELLO hello" ; REM THIS LINE IN INVERSE
100 GOTO 100

The result is the word HELLO printed in capital letters to the screen in four different colors. This occurs because the half-size, 512-byte character set doesn't contain any inverse or lowercase letters. The computer interprets only the lower six bits of the ASCII character as the character number and the upper two bits as the color register. Uppercase letters use color register 0 so they appear in orange. Lowercase characters are interpreted as color register I and appear in aqua. Inverse uppercase characters, which appear in the second line, use color register 2 and are blue, while inverse lowercase uses color register 3 and are light red. Remember that these colors are the computer's default values and the user can change them. The PRINT #6 prints to the graphics portion of the screen. Regular PRINT statements print to the text window, if there is one.

(figure)

There is a method of obtaining lowercase letters on the screen, but if you use it you can't have uppercase characters, too. Since lowercase characters are in the upper 512 bytes of the character set, you can tell the computer that this is your new character set by changing the character set address pointer to an address that is 512 bytes higher. Location 756, the character base shadow register, normally points to the character set at $E000 and has a value for the high byte of 224 ($E0). If you add the line

40 POKE 756,226
the characters will be printed from this alternate set. They will all appear as lowercase in different colors. If you want to mix upper and lowercase letters you will need to move the character set into RAM and copy the lowercase characters into the locations occupied by the numbers and symbols.

Finding Your Character Within a Set

If you are going to move or change characters you have to be able to find them in memory by either internal or ATASCII value. Finding them by internal value is simple. Each character is 8 bytes long. The formula is;

MEMORY LOCATION = START+ 8 * INTERNAL VALUE

Finding them by ATASCII value is more difficult because the graphics characters have been sandwiched between the upper and lowercase letters. The three formulas are as follows. Please remember that the starting value of the character set defined by START should be on a 1K boundary. That value is 57344 if you are using the ROM character set.

ATASCII Value (AV)     Starting Memory Location
32-95                  START+(AV-32)*8
0-31                   START+(AV+64)*8
96-127                 START+(AV*8)  

Changing the Character Set

The easiest method of customizing a character set is to copy the ROM character set to RAM and change individual characters within it. To do this from BASIC you will need to reserve 1K or four pages of memory at the top of memory so that the set will reside in a safe place and not be wiped out by either your program or the display area of memory. The top of memory for any computer is found at decimal location 106. The actual value is PEEK(106)*256. If we move this high byte pointer down four pages there will be a new top of memory. We then POKE this value back into location 106 so that BASIC won't put anything above this. The RAM character set begins at this new top of memory. Do a graphics call after changing location 106.

We must now inform ANTIC of the new location for the character set. Location 756 ($2F4) is the character base shadow register. The value in this location is copied into the character base hardware register at location 54281 ($D409) every sixtieth of a second during the vertical blank period. We can't store this value directly in the hardware address, or it will be overwritten during the next vertical blank period.

Moving the character set requires reading a byte from ROM and storing it in the appropriate position in RAM. In our case CHROM is 57344 ($E000) and CHRAM=NMEMTOP*256. To accomplish this PEEK the values in ROM and POKE them into RAM during a FOR-NEXT loop from 0 to 1023. Once you have copied the set you can then modify specific characters and use these in PRINT statements to the screen. Or if you wish, you can alternately POKE their internal character numbers directly into screen memory. This will require a calculation to determine the correct position.

In order to show the power of a redefined character set, we will create a large Halloween pumpkin that consists of four adjacent lowercase characters, the letters a, b, c and d. The diagram below shows a magnified view of the pumpkin's lit pixels along with the screen arrangement of the four characters or letters that they represent. The letter "a" when redefined shows only the upper left quarter of our pumpkin. Similarly, the letter "b" shows only the upper right portion of our pumpkin.

(figure)

Once the pixel positions in the eight rows of each character are translated into numerical data equivalent to our drawing, they are then POKEd into the appropriate position in the RAM character set as replacements to the existing characters. The four lowercase letters are internal characters 97-100. Since the four are next to each other, we can POKE in all four characters together in a FOR-NEXT loop. The starting location of the ninety-seventh character is the starting location of the RAM character set offset by 8 bytes x 97 characters. Therefore

START = NMEMTOP*256 + (8*97)

Copying the character set into RAM using a FOR-NEXT loop is quite slow and requires ten seconds. Normally, you copy the set first and then change the character set pointer at decimal location 756. 1 thought it might be more fun to watch the set being copied so I listed the program to the screen and changed the character base pointer before the copy loop. At first the listing appears fine but when the character set points to a garbage section of RAM it degenerates into random dots. As the ROM set is copied more and more of the listing becomes legible. The numbers and symbols appear first, followed by the uppercase letters. The lowercase letters form last, since they are in the last quarter of the ROM character set. If you stare at the lowercase letters in our two PRINT statements listed on lines 160 and 170, you will see them change into a pumpkin. The pumpkin will then be printed in the upper left corner of the screen. Each time that you wish to draw a pumpkin you have to use two PRINT statements on the screen. The POSITION statement can be used to PRINT the four characters that represent the pumpkin in the correct spot, but you will need a POSITION statement for each of the two lines representing the upper and lower halves.

Download PUMPKIN1.BAS (Saved BASIC)
Download / View PUMPKIN1.LST (Listed BASIC)

Many games use character set playfields as backgrounds in either BASIC mode 2 (ANTIC 7) or ANTIC mode 4. The latter allows small but detailed four-color characters four pixels wide by eight deep but requires nearly 2K of screen memory. While individual characters are limited to a single color in BASIC mode 2, they have an advantage in that these large characters don't require much memory. With twelve rows of twenty characters a screen requires only 240 bytes of memory. Each character position can be plotted in one of four colors, determined by toggling the two high bits of the internal character number.

The next example is similar to the last, except that the redefined characters are plotted as BASIC mode 2 characters. Again, space is reserved above the top of memory by moving down the top of memory pointer four pages (1024 bytes). The ROM character set is copied as before, but only half of the set, or 512 bytes, is used in this graphics mode. The shortened set doesn't contain both upper and lowercase letters. If you use the lower half of the set, you get uppercase letters only, even if you print lowercase letters. To print the four redefined lower case letters you will need to change the character set pointer to the upper half of the RAM character set. This can be accomplished in line 100 by adding two pages to NMEMTOP and POKEing it into the character set pointer at location 756.

(figure)

Download PUMPKIN2.BAS (Saved BASIC)
Download / View PUMPKIN2.LST (Listed BASIC)

The pumpkin is printed to the screen in four separate positions. By using all of the uppercase and lowercase normal and inverse combinations for the letters. a, b, c, and d, the pumpkin will appear in four different colors determined by the playfield color registers 0-3. The background is controlled by playfield 4 and can be changed by a POKE 712, COLOR VALUE. You will notice that the pumpkins are completely surrounded by yellow colored hearts. This occurs because screen memory contains zeros everywhere except where we placed pumpkins. ANTIC interpretes the Oth character in the lower half of the character set as a heart. The hearts can be removed from sight by POKING the color in playfield 0 to zero, but one of the pumpkins will also fade from sight. Therefore, it is best to create a blank character by POKEing zeros into this Oth character. To do this, add the following lines.

234 START=(NMEMTOP+2)*256
235 FOR I=0 TO 7
236 POKE START+I,0:NEXT I

Copying the ROM character set is the slowest part of the program. This operation could be speeded up a hundred or more times by substituting a Machine language subroutine that can be called by BASIC's USR function. In order to make the routine versatile, the user is given the option of either relocating the full 1024 byte ROM character set to RAM, or just the upper 512 bytes as might be needed in a Graphics 2 display. The routine is also fully relocatable and presently resides in the upper half of page six in memory. Its calling format is A= USR(1664,CHRAMH, 1 or 2). The number 1664 is the starting location of the Machine language subroutine. CHRAMH is the high byte value of the relocated RAM character set. This should be on a 1K page boundary for full sized (1024 byte) character sets and on a 1/2-K page boundary for half size (512 byte) character sets. The value one tells the routine to move all 1024 bytes of the ROM character set to the new RAM location, while a two tells the routine to skip the first 512 bytes of the ROM character set and just move the last half of the set to the new RAM location. The Graphics 0 mode pumpkin example is repeated below to show you the obvious speed advantage in using the Machine language subroutine.

Download PUMPKIN3.BAS (Saved BASIC)
Download / View PUMPKIN3.LST (Listed BASIC)

Note: Assembly language listing of subroutine in appendix.

Character Editor

Customizing a character set can be rather tedious, if you don't use a character set editor. We have furnished a simple one that you can operate by either keyboard or joystick control. The editor allows you to design single-color characters and afterwards save your custom set to a disk for later use in your own program.

The screen displays an enlarged 8 x 8 grid at the top for editing or designing your characters. The entire 128 character set is displayed below. Newly edited characters are also displayed in their proper position within the set. A character is chosen for editing by giving the internal character number. You should use the appendix in the back of this book to choose the correct character. The CTRL CLEAR key will erase the bit pattern of the present character if you wish to design rather than modify an old character. A white square above the enlarged grid indicates that you are in the Draw mode. It can be turned off and will erase by pressing the U key for Undraw. It can be toggled back by pressing the D key. You can move the cursor without affecting anything by moving the joystick or by using the arrow keys. Each time that you want to draw a pixel you can either press the joystick button or the space bar. Since pressing the space bar for each pixel is tedious, I recommend that you edit using a joystick. When you are finished editing the character, just hit return to enter it. You can avoid entering a newly edited character by pressing the ESC key.

Once you are completely satisfied with your custom set, you can save it to the disk by pressing the S key for Save. You only need give it a file name, and it will be saved to disk. If you are re-editing a previous set, you can load one from disk using the L key for Load. You don't have to worry about loading a set to edit the first time you use the utility, because the program automatically defaults to the ROM character set which it has copied into RAM.

Download CHSETED.BAS (Saved BASIC)
Download / View CHSETED.LST (Listed BASIC)

Character Set Loader

In order to use these custom character sets you will need a Machine language routine to load your custom character set into memory from disk. The subroutine can be accessed through a USR call. The format is U=USR(CALL,IOCB, SET,LENGTH,CMD). If you are placing it into page six, CALL = 1536. IOCB = I for a read. The device # that was opened is for the file (like open #1, etc.). The set is placed at SET=CB*256 where CB is the high byte location of the character set, and LENGTH =1024. The format CMD=ADR("L") is somewhat unusual. The subroutine was designed to accept either an "L" or "S" letter command. The statement physically passes the address where the letter is and the computer looks and finds the actual letter stored at that address.

The program asks for the name of the file. The "D:" doesn't need to precede the name because this string is included at the beginning of F$ . The input name string is Q$. The statement F$(3)=Q$ tacks the name of the file to the "D:". The one last thing that you have to do before you call the subroutine is to open the channel to access the drive. This is in the form OPEN #IOCB, I0,0,F$. IOCB= 1, IO=4, and the 0 is unused. I won't go into details of the actual Machine language routine. The listing, however, is provided for machine language programmers; my comments accompany it.

Download CHSETLOD.BAS (Saved BASIC)
Download / View CHSETLOD.LST (Listed BASIC)

Note: Assembly language listing in appendix.

Multi-Color Characters

There are two important character graphics modes that are not supported directly by OS. Both ANTIC modes 4 and 5 use characters that are only four pixels wide instead of the usual eight pixels. Each byte is broken up into four bit pairs. The advantage is that each pixel can be of four different colors, counting the background color. Since each pixel is addressed by two bits instead of one, the value of each bit pair can determine from which color register the pixel derives its color. This becomes a very clever use of color indirection. Note that if the high bit of the character number is set, you get a fifth playfield color. The bit pair order and its associated playfield color register are as follows:

BIT PAIR          PLAYFIELD    COLOR REGISTER
0 0               4 (Bkd)      712
0 1               0            708
1 0               1            709
1 1	/character#   2            710
1 1 \high bit set 3            711
Multi-colored characters in ANTIC mode 4 can be used to create colorful and detailed playfields. They are also useful in games in which a large number of multicolored animated objects are required. While it is always tempting to use player-missile animation because it is easier, large detailed multicolored players aren't always available unless you overlap or combine your relatively few available players.

Most multi-colored ANTIC 4 shapes require a number of adjacent redefined characters since 4 x 8 pixel sized characters are tiny and don't allow for much detail. This group of characters is called a matrix.

To give you an example of the detail possible we are going to create a multicolored boat using four ANTIC mode 4 characters. The boat is 8 pixels high by 16 pixels wide and consists of three colors; yellow, red and green. The black background is actually the fourth color in our character. Each group of 4 horizontal

(figure)

pixels makes up an individual character. Each column of colored pixels is then expanded into a double column bit pair. These eight columns make up a normal byte of character data. Each color pixel is then translated into its appropriate bit pairs so that they point to the proper color registers. Green pixels set the low bit of the pair, red pixels set the high bit of the pair, and yellow pixels set both bits. No bits are set when using the background color register. While the background is blank in our example, it can be set to a non-black color if the background register is part of the character shape. This occurs quite frequently if you are producing a map from a character set.

(figure)

The first column of the first character of our ship contains only one red pixel in the second row. Since the left column of the character uses the two high bits of each byte, only the high bit is set in the second row. If the pixel would have been yellow, both high bits would have been set in that row. You will notice that after the appropriate bits in the bit pairs have been set for each character, the resultant pattern doesn't necessarily resemble the original image. Once you have finished the translation the final character data is then obtained by adding up all of the set bits in each byte as you would ordinarily do if they were single color characters.

(figure)

It is very easy to modify a BASIC 0 (ANTIC 2) display list to produce an ANTIC 4 screen. Both text modes consist of 24 rows of 40 characters. Only the interpretation of the character data by ANTIC is different. Since both display lists are identical in length, all that has to be changed is the LMS instruction and the 23 mode line instructions that follow. The LMS instruction for an ANTIC 4 screen is 64+4 =68. This follows the three instructions that blank 8 scan lines. Therefore we need only POKE this new value into location DLIST+3 where DUST is the beginning of the display list. The following two bytes in the display list are just the address of the first byte of screen data. We ignore these two bytes. The 23 bytes following this are mode instructions. We need only change these to the value 4 in a FOR-NEXT loop in which we go from DLIST+6 to DLIST+28

The rest of the example is essentially the same as in the pumpkin example. We copy the character set into RAM, change the character set pointer, then read in the four ANTIC 4 characters. They are placed in the 97th -100th internal character positions so that they represent the letters "a" through "d". The ship is placed on the screen by printing the string "abcd" at position 5,5.

0 NMEMTOP-PEEK(106)-4 10 POKE 106,NMEMTOP
20 GRAPHICS
30 DLIST=PEEK(560)+256*PEEK(561)
40 POKE DLIST+3,68
50 FOR I=DLIST+6 TO DLIST+28:POKE I,4:NEXT I
60 POKE 708,216:POKE 709,52:POKE 710,250
70 CHROM=PEEK(756)*256
80 CHRAM=NMEMTOP*256
90 REM COPY ROM CHARACTER SET TO RAM
100	FOR I=O TO 1023
110 POKE CHRAM+I,PEEK(CHROM+I):NEXT I
120 REM MODIFY CHARACTER SET POINTER
130	POKE 756,NMEMTOP
140	START=NMEMTOP*256+(8*97)
150	FOR I=0 TO 31
160	READ A:POKE START+I,A:NEXT I
170	REM PRINT SHIP ON SCREEN
180	POSITION 5,5
190	PRINT "abcd"
200	GOTO 200
210	DATA 32,32,160,20,10,1,0,0
220	DATA 0,0,51,12,131,84,170,21
230	DATA 63,51,51,4,42,85,168,80
240	DATA 0,0,51,12,48,64,0,0
Designing an ANTIC 4 character set is quite laborious and prone to error when done by hand as in this example. It is best accomplished by using a character set generator like the one furnished in this book for single color characters. If you are planning to use multi-colored character sets frequently, we would suggest that you buy one of the commercial packages. The best one that we have found is Datasoft's "Graphic Generator" for $24.95. It will design character sets in all text modes including single color, two color and four color sets. It allows you to design a block of characters called a matrix on the screen at one time. The matrix can be set to any group of characters that you choose.

Character Graphics Animation

There are two methods for achieving character set animation. The easier, but least memory-efficient, method is to rotate through a series of different character sets. The characters that are printed to the screen change, if there are distinct differences between the character data for a particular character number in each of the sets. You don't have to reprint the individual characters to the screen since the animation occurs by substituting the character data from another set rather than by changing the character itself. The change requires only a single POKE to the character set pointer at decimal location 756. The disadvantages is that each set required 1K of memory. Since animation sequences require at least 4K of memory to store the various character sets. This is a waste of space unless you are using a large number of characters. In that event this method certainly becomes the best one.

Rotating Character Sets

We have written a very simple example to illustrate the simplicity of the technique. The example rotates through four different character sets. In order to create the first three from the ROM character set, we just offset them by varying degrees during the copy stage. The goal was to create different sets in which the four graphics characters, which have a small dot in four different corners, line up. These four characters are internal characters numbers 9,11, 12, and 15. If we offset the lowest set in memory by six characters of forty-eight bytes, internal character #9 from that set will line up with character #15 from the ROM set. The other two sets need to be offset by only two and three characters respectively for the graphics symbols to all line up.

ROM     0 1 2 3 4.5 6 7 8 9 10 11 12 13 14 15
RAM1                0 1 2 3  4  5  6  7  8  9
RAM2            0 1 2 3 4 5  6  7  8  9 10 11
RAM3          0 1 2 3 4 5 6  7  8  9 10 11 12

Now when you print CHR$(15), internal character #15, the equivalent of internal character #9, will appear when the character set pointer points to the set at RAM I, and at #I I when using the RAM2 set, and at # 12 when using the RAM3 set. This arrangement also causes internal numbers 17,19,20, and 23 to line up in the various sets when you print CHR$(23).

The various sets are copies into RAM memory in an area made safe by effectively lowering the actual top of memory by twelve pages or 3K. The starting address is set at the new top of memory and the others are offset by 1K each. Since the first character set is offset by eight characters or fortyeight bytes, it is best to zero out these missing characters or garbage may appear in the 0th character, which is a blank or null character, and clutter most of the screen in one or more of the RAM character sets. Copying the sets takes thirty seconds, so be patient.

The actual animation is remarkably simple once you have printed several characters to the screen. You just cycle through the various frames by changing the character set pointer at decimal location 756 ($2F4) to each of the four different character sets. A brief delay is placed between set changes, of the animation would be too fast to see.

0 NMEMTOP-PEEK(106)-12
10 POKE 106,NMEMTOP
20 GRAPHICS 0
30 REM CALCULATE STARTING ADDRESS FOR 4 CHARACTER SETS
40 CHROM-PEEK(756)*256
50 CHRAMl=NMEKrOP*256
60 CHRAM2=CHRAM1+1024
70 CHRAM3=CHRAM2+1024
80 REM CALCULATE HIGH BYTE OF 4 CHARACTER SETS
90 C1H=NMEMTOP
100 C2H=ClH+4
110 C3H=CIH+8
120 C4H=PEEK(756)
130 PRINT "        COPYING CHARACTER SETS"
140 REM COPY ROM CHARACTER SETS TO RAM
150 FOR I=0 TO 975
160 POKE CHRAM1+48+I,PEEK(CHROM+I):NEXT I
170 FOR I=0 TO 991
180 POKE CHRAM2+32+I,PEEK(CHROM+I):NEXT 1
190 FOR I-0 TO 999
200 POKE CHRAM3+24+I,PEEK(CHROM+I):NEXT I
210 REM CLEAR MISSING CHARACTERS TO ZERO
220 FOR I-0 TO 47:POKE CHRAMI+I,O:NEXT I
230 FOR I=0 TO 31:POKE CHRAM2+I,O:NEXT I
240 FOR 1-0 TO 23:POKE CHRAM3+I,G:NEXT I
250 REM PRINT CHARACTERS TO SCREEN
260 POSITION 8,8:PRINT CHR$(15)
270 POSITION 19,8:PRINT CHR$(23)
280 POSITION 30,8:PRINT "7"
290 REM ROTATE CHARACTER SETS
300 POKE 756,CIH
310 FOR DE=1 TO 200:NEXT DE
320 POKE 756,C2H
330 FOR DE=1 TO 200:NEXT DE
340 POKE 756,C3H
350 FOR DE=1 TO 200:NEXT DE
360 POKE 756,C4H
370 FOR DE=1 TO 200:NEXT DE
380 GOTO 300

Animation Using Different Characters

The second and more common method of achieving character set animation is to rewrite different characters to the screen in the same location. Of course this either requires a new print statement each time or a new character value POKEd directly into screen memory. The advantage of this method is that you can have as many variations as you like in the animation cycle without requiring many different character sets.

As a first example we have designed a simple ANTIC 4 character demonstration that places a random number of eggs on the screen. Each of these eggs begins to hatch into one of five different bug shapes before it eventually explodes. The complete life to death cycle requires thirteen animation frames, or fourteen, if you count the blank character needed to erase it at the end. The eggs, insects, and explosions consist of character pairs set side by side, and they are basically printed to the screen as character strings in a fixed position.

The program setup is quite similar to earlier examples. Space is reserved for the RAM character set by moving the top of memory downward, and a Graphics 0 display list is modified to ANTIC 4. Once the characters are read in through data statements and written into the RAM character set, they are transferred into strings to facilitate printing them quickly.

H$, BUG$, and BOOM$, contain the various character pairs for hatching, bug shape. and explosions respectively. Both the hatching and explosion animation is produced by printing two characters out of the string to the screen. This occurs in a loop in which the character pair shifts down the string until the sequence is completed. The egg-hatching string consists of the following characters;

H$ = "/0123456789:;<=>"

(figure)

(figure)

They are printed immediately to the screen as pairs without pause in order to have the eggs look like they are wobbling. If we examine the series of characters printed in the loop containing lines 330 and 340, they will be as follows:

1st cycle "/0"
2nd cycle "12"
3rd cycle "34"
4th cycle "56"
8th cycle "=>"

There are five random bug pairs contained in the string BUG$. BUG$ = "!"#$%&()*". Lines 370-379 print one random bug on the screen.

(figure)

(figure)

The routine that steps through the character strings BOOM$ to print each of the character pairs that make up the explosion sequence is quite similar to the egghatching one. There is a little more lag, since we are only printing one set at a time. If we examine the sequence of characters printed in the loop in line 460 they are as follows:

BOOM$ = "?@ABCDEFGH"

1st cycle "?@"
2nd cycle "AB"
3rd cycle "CD"
4th cycle "EF"
5th cycle "GH"

After the bugs disappear, a caterpillar-like bug dashes across the screen. This is the same shape as the ship from our previous ANTIC 4 example in the last section of this chapter. This shape is four characters wide. The string M$ = " +,-." contains a blank as the first character so that is erases the left character of the shape as it moves rightward across the screen. Essentially we print the string on top of the previous one but shifted slightly to the left. As we repeat this pattern in a continuous loop, the result is animation.

10 REM CHARACTER DEMO - DAN PINAL
15 POKE 106,PEEK(106)-8:SET=(PEEK(106)+4)*256:GRAPHICS 0:POKE 756,SET/256:POKE 752,?
20 DLIST=PEEK(560)+256*PEEK(561):POKE DLIST+3,68
25 FOR LI=DLIST+6 TO DLIST+28:POKE L1,4:NEXT Ll
30 FOR L1=0 TO 327:READ X:POKE SET+LI,X:NEXT Ll
40 DIM COUNT(20,I),BUG$(10),H$(16),BOOM$(10),E$(2),M$(5)
50 FOR Ll-33 TO 42:BUG$(Ll-32)--CHR$(Ll):NEXT Ll
60 FOR L1=47 TO 62:H$(Ll-46)--CHR$(Ll):NEXT Ll
70 BOOM$="?@ABCDEFGH":E$=H$
80 M$=" +,-."
90 POKE 708,216:POKE 709,52:POKE 710,250
200 COUNT=INT(RND(0)*20+1)
210 FOR L1=1 TO COUNT
220 H=INT(13*RND(0))*2+5:COUNT(Ll,O)=H
230 V=INT(17*RND(0)+l):COUNT(Ll,l)=V
240 IF L1=1 THEN 280
250 FOR L2=1 TO L1-1
260 IF COUNT(L1,0)=COUNT(L2,0) AND COUNT(L1,1)=COUNT(L2,1) THEN POP :GOTO 220
270 NEXT L2
280 POSITION H,V:? E$:NEXT Ll
290 FOR L1=1 TO COUNT
300 X=COUNT(L1,O):Y=COUNT(L1,1)
310 FOR L2=1 TO 4
320 FOR L3=1 TO 4
330 POSITION X,Y:? H$((L2-1)*4+1,(L2-1)*4+2)
340 POSITION X,Y:? H$((L2-1)*4+3,(L2-1)*4+4)
350 SOUND 0,155-L2,10,8+L2:SOUND 1,155-L2,10,8+L2:POKE 53768,192:NEXT L3:NEXT L2
360 SOUND 0,0,0,0:SOUND 1,0,0,0
370 CHAR=INT(5*RND(O)):CHAR=CHAR*2+1
380 POSITION X,Y
390 ? BUG$(CHAR,CHAR+1);
400 NEXT Ll
410 FOR L1=1 TO COUNT
420 H=COUNT(L1,O):V=COUNT(L1,1)
430 FOR L2=0 TO 4
440 SOUND 0,20,8,14-2*L2
450 SOUND 1,227,6,14-L2
460 POSITION H,V:? BOOM$(L2*2+1,L2*2+2);
470 FOR W=1 TO 5:NEXT W
480 NEXT L2
490 SOUND 0,0,0,0:SOUND 1,0,0,0
500 NEXT Ll
510 H=O:V=INT(20*RND(O))
520 FOR L1=0 TO 35:POSITION H+Ll,V:? M$
530 SOUND 0,100+LI,12,8:SOUND 1,135-LI,12,8:POKE 53768,192
540 NEXT Ll
550 SOUND O,O,O,O:SOUND 1,0,0,0:POSITION 35,V:?
560 GOTO 200
10000 DATA 0,0,0,0,0,0,0,0,4,69,20,53,245,241,192,0,64,68,80,112,124,60,12,0, 8
10010 DATA 138,38,58,250,242,192,0,128,136,96,176,188,60,12,0,128
10015 DATA 44,9,169,1,9,40,136,8,224
10020 DATA 128,168,0,128,160,136,8,11,2,6,22,86,80,64t32,224,128
10025 DATA 144,148,149,5,1,0,0,22
10030 DATA 14,62,242,24O,O,O,Otl48,176,188,143,15,0,8,8,168,21
10035 DATA 10,1,0,0,0,0,17,4
10040 DATA 129,84,170,21,63,59,59,4,21,85,170,84,0,0,17,4,16,64
10045 DATA 0,0,3,15,63,255,255
10050 DATA 63,15,3,192,240,252,255,255,252,240,192,0,63,63,63
10055 DATA 63,63,63,0,0,252,252,252,252,252
10060 DATA 252,0,3,15,63,253,253,63,15,3,192,240,252,127,127
10065 DATA 252,240,192,0,63,63,62,62,63,63
10070 DATA O,Ot252,252,188,188,252,252,0,3,15,62,250,250,62,15
10075 DATA 3,192,240,188,175,175,188,240,192
10080 DATA 0,63,53,53,53,53,63,0,0,252,92,92,92,92,252,0,3,15
10085 DATA 62,248,248,62,15,3,192
10090 DATA 240,188,47,47,188,240,192,0,63,49,52,52,49,63,O,O
10095 DATA 252,76,28,28,76,252,0,0,0
10100 DATA 12,13,1,0,1,0,0,192,192,0,16,128,128,0,0,1,4,8,12,4,2,0,0,192,48
10110 DATA 16,32,16,64,0,16,64,66,72,72,72,66,16,4,1,129,33,33
10115 DATA 33,129,4,14,32,192,0
10120 DATA 48,0,192,4,16,4,48,2,0,1,12,208,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

Animated Birds

The final example is one of animated birds. The animation sequence consists of four different wing positions, so that the birds appear to be flapping their wings in flight. To give the feeling that the birds are actually hovering rather than glued in place, they moved randomly about the screen.

Each of the shapes consists of an array of characters, six across by three high. Since each shape uses it takes an array of eighteen characters, each of the four animated figures are placed eighteen characters apart in the character set.

An animation sequence like 0-1-2-3-0-1 ... which we would call circular, would produce a discontinuity in the motion. The bird's wings are moving downward during the four cycles but if we went back to the 0th frame again, the bird's wings would suddenly be at the top. Therefore, the motion must be oscillatory so that the wings begin moving upward after reaching the bottom. The sequence is 0-1-2-3-2-1-0-1-2.... Line 40 in the program accomplishes this.

(figure)

Printing a matrix of characters is usually tedious using normal print statements. If there is more than one row of characters involved, you will need several print and position statements. Certainly, an easier method would be to develop a Machine language subroutine in which you only had to specify the first character in the matrix, the height and depth of the matrix, and the position on the screen that you wish to print it.

The subroutine is called MCYCLER and it is stored in this example as a string, Since some users may prefer to POKE it into page six or even somewhere else, it is written as fully relocatable code. It is called with a USR statement. X = USR(MCYCLER, ROW, COL, MATRIXH, MATRIXV, STARTING CHARACTER # ). MATRIXH and MATRIXV are just the horizontal and vertical size of the matrix. In the bird example they are six and three respectively. MCYCLER is the starting address of the subroutine, but since it is stored as a string, the value is ADR (MCYCLER$). The subroutine will print the matrix to the screen, except when the starting character # is zero. This feature is needed to erase the previous matrix in its last position before you draw the next frame.

(figure)

Erasing one frame and drawing the next is accomplished in lines 80 and 90. The old location of the matrix is at BX (L1), BY (L1), and the new location is at NX (L1), NY (L2). Notice that the starting character during the draw stage depends on which frame, B(L1), we are drawing, and that each matrix is eighteen characters apart in the set. The value of one is added since the Oth character is actually a blank or null character. After the subroutine draws the matrix, the old location is transferred to the new location. This is necessary since the last figure drawn must be erased from its old position BX(L1), BY(L1) before the next one is drawn at a new random position NX(L1), NY(L1).

We thought it might be instructive to see which internal characters are actually being printed to the screen. You can toggle between the ANTIC 4 bird set in RAM and the normal ROM set by pressing the SELECT key. The OPTION key resets it.

10 REM CHARACTER ANIMATION - DAN PINAL
15 ?	"HOW MANY BIRDS (1-4)";:INPUT N:N=N-1
20 GOSUB 10000:REM INITIALIZE
30 FOR L1=O TO N
40 B(Ll)=B(Ll)+F(Ll):IF B(Ll)-3 OR B(Ll)=O THEN F(Ll)-F(Ll)
50 R-INT(3*RND(0))-l:NX(Ll)-BX(Ll)+R*(BX(Ll)+R>0 AND BX(Ll)+R<35)
60 R=INT(3*RND(0))-l:NY(Ll)-BY(Ll)+R*(BY(Ll)+R>0 AND BY(Ll)+R<20)
80 X=USR(ADR(MCYCLER$),BY(Ll),BX(Ll),6,3,0)
90 X=USR(ADR(MCYCLER$),NY(LI),NX(Ll),6,3,B(Ll)*18+1):BX(Ll)=NX(Ll):BY (Ll)-NY(L1)
95 NEXT Ll
96 IF	PEEK(53279)=5 THEN POKE 756,224
97 IF	PEEK(53279)=3 THEN POKE 756,CB
100 GOTO 30
10000	DATA 104,104,104,168,166,89,104,104,24,101,88,144,1,232
10005	DATA 136,48,8,24,105,40,144,248,232,208,245
10010	DATA 133,212,134,213,104,104,133,206,104,104,133,207,104,104
10015	DATA 170,160,0,145,212,201,0,240,2,232,138
10020	DATA 200,196,206,208,243,24,169,40,101,212,133,212,144
10025	DATA 2,230,213,138,198,207,208,225,96
10030	DIM MCYCLER$(72):FOR L--1 TO 72:READ X:MCYCLER$(L,L)--CHR$(X):NEXT L
10040	POKE 106,PEEK(106)-9:GRAPHICS O:POKE 709,170:POKE 710,10:POKE 712,130
10050	POKE 752,1:CB=PEEK(106)+l:CHRSET--CB*256
10060	DLIST=PEEK(560)+256*PEEK(561)
10070	POKE DLIST+3,68:REM LMS ANTIC 4
10080	FOR Ll=DLIST+6 TO DLIST+28:POKE Ll,4:NEXT L1:REM CHANGE DUST FROM GR. 0 TO ANTIC 4
10090	POKE 756,CB:REM SET POINTER TO OUR NEW CHRSET.
10100	REM NOW POKE IN NEW CHRSET
10110	FOR L1=O TO 583:READ X:POKE CHRSET+Ll,X:NEXT Ll
10200	DIM B(3),BX(3),BY(3),NX(3),NY(3)oF(3)
10210	FOR U-0 TO 3:B(Ll)-Ll:BX(Ll)=Ll*10:BY(Ll)=5:F(Ll)=l:NEXT L1:F(3)=-l
10220 RETURN
20000	DATA 0,0,0,0,0,0,0,0
20001	DATA 0,0,0,0,0,0,0,0
20002	DATA 2,2,2,2,2,2,2,2
20003	DATA 0,0,0,0,0,128,128,128
20004	DATA 0,0,0,0,0,0,0,0
20005	DATA 32,32,32,32,32,160,160,160
20006	DATA 0,0,0,0,0,0,0,0
20007	DATA 0,0,0,0,0,0,0,0
20008	DATA 2,0,0,0,0,0,0,0
20009	DATA 128,160,160,163,160,34,42,42
20010	DATA 0,2,2,242,l94,98,106,170
20011	DATA 160,128,128,128,0,0,0,0
20012	DATA 0,0,0,0,0,0,0,0
20013	DATA 0,0,0,0,0,0,0,0
20015 DATA 42,10,9,6,4,4,4,17
20016 DATA 170,168,152,164,4,4,4,17
20017 DATA 0,0,0,0,0,0,0,0
20018 DATA 0,0,0,0,0,0,0,0
20019 DATA 0,0,0,32,8,2,2,0
20020 DATA 0,0,0,0,0,0,128,160
20021 DATA 0,0,0,0,0,0,0,3
20022 DATA 0,0,0,0,0,0,0,240
20023 DATA 0,0,0,0,0,0,0,2
20024 DATA 0,0,0,2,8,32,160,128
20025 DATA 0,0,0,0,0,0,0,0
20026 DATA 40,42,10,2,0,0,0,0
20027 DATA 0,2,130,170,170,42,9,6
20028 DATA 192,96,96,170,170,170,152,164
20029 DATA 10,42,168,160,128,0,0,0
20030 DATA 0,0,0,0,0,0,0,0
20031 DATA 0,0,0,0,0,0,0,0
20032 DATA 0,0,0,0,0,0,0,0
20033 DATA 4,4,4,17,0,0,0,0
20034 DATA 4,4,4,17,0,0,0,0
20035 DATA 0,0,0,0,0,0,0,0
20036 DATA 0,0,0,0,0,0,0,0
20037 DATA 0,0,0,0,0,0,0,0
20038 DATA 0,0,0,0,0,0,0,0
20039 DATA 0,0,0,3,0,2,2,10
20040 DATA 0,0,0,240,192,96,96,168
20041 DATA 0,0,0,0,0,0,0,0
20042 DATA 0,0,0,0,0,0,0,0
20043 DATA 0,0,0,0,0,0,0,0
20044 DATA 0,0,2,2,10,8,40,160
20045 DATA 42,170,169,134,4,4,4,17
20046 DATA 170,170,154,164,4,4,4,17
20047 DATA 0,128,160,160,40,40,10,2
20048 DATA 0,0,0,0,0,0,0,0
20049 DATA 0,2,2,2,0,0,0,0
20050 DATA 128,128,0,0,0,0,0,0
20051 DATA 0,0,0,0,0,0,0,0
20052 DATA 0,0,0,0,0,0,0,0
20053 DATA 2,0,0,0,0,0,0,0
20054 DATA 128,160,32,32,0,0,0,0
20055 DATA 0,0,0,0,0,0,0,0
20056 DATA 0,0,0,0,0,0,0,0
20057 DATA 3,0,2,2,10,42,42,169
20058 DATA 240,192,96,96,168,170,170,154
20059 DATA 0,0,0,0,0,0,0,128
20060 DATA 0,0,0,0,0,0,0,0
20061 DATA 0,0,0,0,0,0,0,0
20062 DATA 0,0,2,2,2,2,2,0
20063 DATA 166,164,164,132,145,128,128,160
20064 DATA 166,6,6,4,17,0,0,2
20065 DATA 128,128,160,160,160,160,128,128
20066 DATA 0,0,0,0,0,0,0,0
20067 DATA 0,0,0,0,0,0,0,0
20068 DATA 0,0,0,0,0,0,0,0
20069 DATA 160,32,32,40,40,8,8,2
20070 DATA 2,2,2,10,10,8,8,32
20071 DATA 128,0,0,0,0,0,0,0
20072 DATA 0,0,0,0,0,0,0,0

Return to Table of Contents | Previous Chapter | Next Chapter