|
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).
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.
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.
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.
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 EditorCustomizing 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 LoaderIn 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 CharactersThere 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
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.
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.
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 AnimationThere 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 SetsWe 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:;<=>"
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.
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 BirdsThe 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.
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.
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
|
|