Apple Graphics & Arcade Game Design Text
Apple Graphics & Arcade Game Design Text
Jeffrey
5tanton
APPLE GRAPHICS &
ARCADE GAME DESIGN
BY JEFFREY STANTON
1
ACKNOWLEGEMENTS
A book like this was a long and difficult undertaking. I would like to thank
my publisher, James Sadlier for having faith in the book despite its long
development time, Don Worth and LouRivas for reading the book for
technical accuracy, and John Dickey and Gary Kevorkian who edited this
book. I would also like to thank Dale Washlake, Phil Wasson, Jim Nitchals,
and others who answered many of my graphics questions, and Shannon Hogan
who did the cover art from one of my far fetched ideas.
Copyright ©
1982 by Jeffrey Stanton and The Book Company. All rights
reserved. Printed in the United States of America. No part of this publication
may be reproduced or distributed in any form or by any means, or stored in a
data base or retrieval system, without the prior written permission of the
publisher, with the exception that the program listings may be entered, stored,
and executed in a computer system, but they may not be reproduced for
publication.
TABLE OF CONTENTS
INTRODUCTION —6
CHAPTER 1 APPLESOFT HI-RES —9
1. Description and Screen Layout
2. Screen Switches and Control
3. Memory Considerations
4. Colors and Background Fill
5. Page Flipping
6. Apple Shape Tables
A: Designing Shapes
B: Assembling a Directory
7. Graphic Animation Using Shape Tables
8. Character Generators
3
B: Forming Bit Mapped Shape Tables
C: Shifted Tables for Precise Positioning
D: Color Problems
1. Introduction
2. Paddle Routines
3. Dropping Bombs and Shooting Bullets
4. The Invaders Type Game
5. Steerable Space Games
6. Steerable and Free Floating Space Ships
7. Debug Package
8. Laser Fire & Paddle Button Triggers
9. Collisions
10. Explosions
1 1 . Scorekeeping
12. Page Flipping
6
I will attempt to explain the ideas in this book through a combination of text,
drawings, and flow charts. The concepts in this book may seem easy at times,
and somewhat difficult at other times. The Apple with its many idiosyncrasies
isa strange beast to master. My advice is to read the book in stages and try the
examples. Learn how they work.
While my goal for presenting this material was to educate a new generation
of arcade game designers, I dread the proliferation of copy cat games. The
world doesn’t need an eighth Asteroids game, or a tenth PacMan game. They
have been done. I do hope that programmers both young and old will use their
imaginations to create something novel and exciting.
JEFFREY STANTON
VENICE, CALIFORNIA
APRIL 16, 1982
orders please add $5.00 for air mail). California residents add 6% state sales
tax (Los Angeles County residents add 6 A X
%
sales tax). Available from The
Book Co., 11223 S. Hindry Avenue, Suite 6, Los Angeles, CA
90045. (See
order card at back of this book.)
A bit-mapping utility program, which was mentioned briefly in Chapter 4, is
available to readers who purchase the above disk for an additional $10.00 plus
tax. It enables the user to design any multi-colored bit-mapped shape on a grid
49 pixels wide by 32 lines deep. The program calculates the subsequent shape
table in hexadecimal for both even and odd starting offsets, plus six additional
shifted tables if that option is selected. Shapes can be displayed in their actual
size and color as well as saved to disk. The program supports a line printer but
it not required.
is
The
Applesoft and machine language object files provided will run on any
standard Apple II Computer, but the assembly language source code requires
one of three assemblers to interpret them. Big Mac and TED II + assemblers
are available from Call A.P.P.L.E. Additionally, Merlin is available from
Southwestern Data Systems. These binary source files can also be reformated
for use in other assemblers like Lisa 2.5 or Tool Kit by using a text editor such
as Apple Pie.
8
CHAPTER 1
APPLESOFT HI RES
The Apple II computer has the ability to display color graphic images on a
video monitor or television screen. It displays these images through a process
known as Memory Mapped Output. Various circuits scan specific areas of
Random Access Memory (RAM) to determine what should be displayed on
the screen. These circuits convert memory
information into images containing
pixels or dots that are either turned on or off at particular screen positions.
Each memory location contains a coded series of instructions for a particular
segment of the Hi-Res screen. Thus the hardware maps the image coded in
memory to the video screen.
The Apple II computer has two distinct graphics modes. Lo-Res graphics,
which occupies the memory space reserved for the text page ($400 - $800), has
a resolution of 40 dots horizontally by 48 dots vertically. Each dot is very coarse
(7 X 8) pixels. Any one of sixteen colors can fill each of the 1920 positions on
the screen. Hi-Res graphics, on the other hand, is much more detailed or
dense. The resolution is 280 horizontal dots by 192 vertical dots. This gives
53,760 points on the screen. However, only six different distinct colors are
available in this graphics mode. (There are actually eight colors including two
whites and two blacks.)
Both graphics modes can either be full screen or they can be a mix of
graphics and four lines of text at the bottom of the screen. This format reduces
the Lo-Res screen to 40 lines and the Hi-Res screen to 160 lines.
Each of the graphics modes has two distinct pages or screens. They reside in
specific areas of memory which are hardware set. Each screen can be viewed
separately by setting a series of software switches that are located in Read Only
Memory (ROM). These are not real physical switches but switches that can be
toggled by POKEing values to their ROM reserved memory locations. These
switches tell the video hardware to display either text or graphics, Lo-Res or
Hi-Res, full screen graphics or mixed text and graphics, and either page 1 or
page 2.
When you execute the GR statement in BASIC the computer turns on the
,
Lo-Res graphics mode, clears display memory so that the screen is black, and
defaults to four lines of text at the bottom of the screen. The text window can be
eliminated by typing the statement POKE - 16302,0, thus giving full screen
Lo-Res graphics. Similarly, the HGR statement turns on page one Hi-Res
graphics, clears Hi-Res memory so that the screen is black, and defaults to the
mixed text and graphics mode. Full screen graphics can be achieved by the
statement, POKE - 16302,0. And if you wish to view page 2 of Hi-Res
9
.
"S'
TEXT MIXED TEXT PAGE2 HI-RES
& GRAPHICS
-16303 -16301 -16299 -16297
$C051 SC053 $C055 $C057
memory, the command HGR2 turns it on. The statement POKE - 16301,0
sets fullscreen graphics for page 2
The principal disadvantage of using HGR or HGR2 is that executing either
of these commands clears the Hi-Res page selected, regardless of your wishes.
There are times when you have produced a display and want to switch to a full
page of text. If you return from text mode through the above commands, your
display will be erased.
It is possible to enter the Hi-Res graphics mode without erasing
the display
screen. If you set the following soft switches which reside in reserved memory
locations - 16304 through - 16297 ($C050 through $C057), you can display
Hi-Res graphics page 1 without erasing its previous contents.
-(HIMEM:
PROGRAM LINES LOCATIONS (202,203)
BUILD DOWN
4
VARIABLE STORAGE
BUILDS UP LOCATIONS (204,205)
U-LOMEM:
Applesoft, on the other hand, stores its program just above the text page at
2048 ($800). Program lines build upwards towards the top of memory. As the
program gets longer, LOMEM:, which is the end of the Applesoft program, is
pushed upwards. Simple variables and array variables begin just above
LOMEM:, and string storage beginning at HIMEM:, builds downward.
Thus, setting LOMEM: to a value above the Hi-Res screen would not relocate
the Applesoft program nor prevent a long program from occupying the same
memory space as the Hi-Res screens.
11
K-HIMEM:
STRING STORAGE
BUILDS DOWN
t
-K-LOMEM:
PROGRAM LINES
PUSH LOMEM: UP
12
"
There are eight color choices (0-7) on the Hi-Res screen. These are selected
by the HCOLOR statement. Since the screen is arranged in alternating col-
umns of either violet-green or blue-orange colors, depending on whether the hi
bit is set in a screen memory byte, the absence of color produces two different
blacks, and the presence of two adjacent lit pixels produces two different
whites. (See chapter 5 for a more detailed explanation.) Thus, only six distinct
colors are available. These are listed in the following chart.
13
COLOR NUMBER
BLACK 0
GREEN 1
VIOLET 2
WHITE 3
BLACK 4
ORANGE 5
BLUE 6
WHITE 7
PAGE FLIPPING
Using both Hi-Res screens is an effective way of smoothing
animation, or
creating an image on one screen while viewing the alternate screen. When a
group of objects or lines are drawn successively to the screen
during an anima-
tion frame, the last object drawn is on screen only
a fraction of the time that the
first object is on the screen. And if there
are many large objects, the continuous
drawing becomes noticeable.
Page flipping is an effective method to reduce flicker between
animation
frames. However, one assumes a reasonable animation
frame rate of at least 10
frames per second, or the animation appears slow and jerky.
The trick to this
method is controlling the screen that is drawn to, regardless of
the screen
switch positions. There is a pointer in zero page, decimal
location 230 ($E6)
that sets which screen 'is plotted to. A POKE
230,32 indicates screen #7, and
POKE 230,64 indicates screen #2.
The following example demonstrates the technique. The program
HPLOTs
thirty random line segments on one screen while
the other screen is viewed. It
then changes viewing screens to the screen where the
image had just been
drawn, and erases the opposite screen before randomly drawing
thirty new line
segments. The result is a series of completed line drawings
that change from
one image to the next without anyone being aware that they
are being drawn
elsewhere.
.
When screen #1 isviewed by toggling the switch with POKE - 16299,0 the ,
statement, POKE 230,64 tells the computer to draw to screen #2. Since $E6
,
points to screen #2 when the clear screen is called at line 52, it clears screen #2
before plotting our thirty random line segments. When we switch viewing
screens to the completed picture with a POKE - 16300,0 ,we reset $E6 to the
opposite screen with a POKE 230,32. Now we are viewing screen #2, and
drawing on screen #1
5 XI = 0:Y1 = 0
10 REM CLEAR BOTH SCREENS
20 HOME : HGR HGR2 : HC0L0R= 3 :
15
As you view the different supposedly random screens,
you will notice that
the screens appear to repeat every few frames.
The repetition, although not
perfect, is due to a faulty random number
generator in Applesoft. This pro-
gram graphically illustrates the fault.
A demonstration of the same program without page flipping
can be shown
If you take theprevious listing and make the following changes,
the images can
be seen as they are drawn.
16
DESIGNING AND FORMING SHAPES
The first step in this procedureis to define your shape or shapes on a piece of
graph paper. Direction vectors are drawn to indicate the sequence of coded in-
structions that will become our shape table. You can start your vectors around
your shape in either a clockwise or counterclockwise direction; it doesn’t mat-
ter. Next, we unwrap these vectors, starting with vector one at the left. This se-
quence forms a graphic list of our plotting vectors. Solid vectors indicate moves
while plotting, and dotted vectors indicate moves without plotting. These vec-
tor codes range in value from 0-7 and are summarized in the table below.
1
MOVE DOWN WITHOUT PLOTTING 010 2
*
< MOVE LEFT WITHOUT PLOTTING Oil 3
17
Each shape table byte (8 bits) is divided into three
sections. Sections one and
two are three bits each and contain any
plotting vector. But section three
which contains only two bits, can only hold
certain plotting vectors. The three
vectors allowed are down, left and right
without plotting. Most of the time this
section remams unused. This is acceptable,
because if section three of the
shape definition byte is zero, Applesoft
ignores the section and advances to the
next byte of the shape.
BIT 7 6 5 4 3
M = MOVEMENT BIT M M P M M
2
P
1
M M
0
P = PLOT /NO PLOT BIT
~
There is some ambiguity with plotting
^«
vectors that are equal to zero In
tions one or two, a zero specifies that you can sec-
“move up without plotting” but
n section three it means ‘no movement
- ™ £*£
and no plotting” This also means
a
rr„r
Whe
P ;'tr
'
£>
presIm m
be present in a mw
°f
“T U P. without Pitting” vectors that
row If for example, sections one and
move up without plotting” vectors and the next
two both contained
can
encoding
CaSier ^ ^ in groups of two or three within
im ° tW ° nibbles
a byte it
bits each f°r easier
)
18
-< SHAPE #1
V
*ta*t
*
*
9fA/cr
SHAPE #2
19
DISPLACEMENT
0 NUMBER OF SHAPES
IN TABLE ($0 -FF)
1 UNUSED
2 OFFSET TO SHAPE 1
LO ORDER BYTE
3 OFFSET TO SHAPE 1
HI ORDER BYTE
LENGTH DEPENDS
ON NUMBER OF
2N+2 OFFSET TO SHAPE N SHAPES IN TABLE
LO ORDER BYTE (2 BYTES/SHAPE)
PLOTTING VECTORS
SHAPE N
If we construct a directory for our previous two shape examples, it takes the
following form.
BYTE
0 02 NUMBER OF SHAPES
1 00 UNUSED
2 06 LO BYTE OF OFFSET TO SHAPE #1
3 00 HI BYTE
4 09 LO BYTE OF OFFSET TO SHAPE #2
5 00 HI BYTE
6 2C
7 3E C SHAPE #1
8 00 J
9 2C
A 2E /
B 3E f
C 3E y SHAPE #2
D 3C \
E 2C )
F 00 J
This procedure very time-consuming and, if the shape is complex, prone
is
21
The current cursor position can be moved by the
IJ,K,M keys. If you want
to plot a point press the P key after a move. If
you makea mistake, the E key
will erase the last plotted point;
however, this must be done before the cursor is
moved again Sorry but it doesn’t step back through
your keystrokes. When
you are finished with the shape, you simply
(Q)uit.
When you are returned to the main menu, you have
a choice of (V)iewing
the shape or (A)ddmg the shape to the table. Look at the shape first, because if
it is incorrect you can try again with the (C)reate command rather than add it
to the table You can also save the table or load a new table
at any time
lhis Applesoft program must be relocated
above Hi-Res screen page 1 Use
the program discussed earlier to
createan EXEC file which will reset the
pointers. Set the loading address at
16385 decimal. The Shape Creator stores
its shape tables at
$800, or 2048 decimal. If you choose to put
your tables
m “
IISXd ?HAPE ) aK
h ' Pr° Sram a SP iflC Sta " ing l0Cati0n address
Applesoft
3t ^ first
h ° ld '
location contains the maximum
numbe^of
ThiS n ° tati0n iS entirdy com atible
P
22
N
42 IF =
PK 67 THEN 100
43 IF =
PK 65 THEN 500
44 IF =
PK 86 THEN 600
45 IF =
PK 76 THEN 65
46 IF =
PK 83 THEN 700
47 IF =
PK 81 THEN 2000
48 GOTO 39
49 REM INITILIZE TABLE
50 HOME PRINT: INPUT " NO. OF SHAPES IN TABLE? ";MAX
:
120 HOME
: VTAB 22: PRINT "MOVE PLOT CURSOR WITH KEYS"
122 PRINT "J -LEFT, K -RIGHT I -UP, M - DOWN" ,
124 PRINT "P -PLOT ,E -ERASE LAST PLT Q -QUIT": POKE 36,
,
41
126 KY$ = "":KSVE$ = "": GOTO 145
128 IF FLAG = 1 THEN 132
130 XDRAW 1 AT XI, Y1
132 XI = X:Y1 = Y : FLAG = 0
23
135 XDRAW 1 AT X,Y
140 KI$ = KSVE$:KSVE$ = KY$
145 GET KY$
150 IF KY$ < > "I" THEN 160
155 SYMBOL = 0:Y = Y - 10: IF Y = > 0 THEN
225
157 Y = Y + 10: CALL - 1052: GOTO
145
160 IF KY$ < > "K" THEN 170
165 SYMBOL = 1:X = X + 10: IF X < =250
THEN 225
167 X = X - 10: CALL - 1052: GOTO 145
170 IF KY$ < > "M" THEN 180
175 SYMBOL = 2:Y = Y + 10: IFY< = 150 THEN
225
177 Y = Y - 10: CALL - 1052: GOTO 145
180 IF KY$ < > "J" THEN 190
185 SYMBOL = 3:X = X - 10: IFY= > 0 THEN 225
187 X = X + 10: CALL - 1052: GOTO 145
190 IF KY$ < > "P" THEN 200
195 FLAG = 1: GOSUB 300: GOTO 135
200 IF KY$ = "Q" THEN 400
205 IF KY$ < > "E" THEN 145
210 HCOLOR= 0:FLAG = 0: GOSUB 300
220 KSVE$ = KI$: HCOLOR= 3: GOTO 130
225 IF KSVE$ = "P" THEN SYMBOL = SYMBOL
+ 4
230 CYCLE = CYCLE +1
235 IF CYCLE < > 1 THEN 245
240 BYTE = SYMBOL: GOTO 128
245 IF CYCLE < > 2 THEN 270
250 BYTE = BYTE + 8 * SYMBOL
255 IF BYTE > 7 THEN 128
260 BYTE = BYTE + 8: POKE ADDR,BYTE:ADDR =
ADDR +
265 BYTE = 24:CYCLE = 2: GOTO 128
270 IF SYMBOL > 3 THEN 280
275 BYTE = BYTE + 64 * SYMBOL
280 POKE ADDR, BYTE: ADDR = ADDR + 1
285 IF SYMBOL = 0 OR SYMBOL > 3 THEN 295
290 CYCLE = 0: GOTO 128
295 CYCLE = 1 : BYTE = SYMBOL: GOTO 128
300 FOR Y2 = Y - 3 TO Y + 3 STEP 6 HPLOT
X - 1,Y2 TO X + 1
,Y2: NEXT Y2
305 FOR Y2=Y-2TOY+2 STEP 4; HPLOT X - 2,Y2 TO X + 2
,Y2: NEXT Y2
F°R Y2 = Y “ 1 T0 Y + 1: Hp LOT X - 3,Y2
TO X + 3,Y2: NE
XT°Y2
315 IF X = XS AND Y = YS THEN RETURN
320 XDRAW 1 AT X,Y: RETURN
400 IF KSVE$ < > "P" THEN 430
24
405 IF CYCLE < > 2 THEN 415
410 POKE ADDR, BYTE: ADDR = ADDR + 1
415 IF CYCLE < > 1 THEN 425
420 BYTE = BYTE + 32: GOTO 430
425 BYTE = 4
430 POKE ADDR, BYTE: ADDR = ADDR + 1
435 POKE ADDR, 0: ADDR = ADDR + 1
440 POKE - 16303,0: HOME : VTAB 22: PRINT " (A)DD SHAPE TO
TABLE IF CORRECT" :AFLAG = 0: GOTO 39
450 HOM : VTAB 22: PRINT " SHAPE TABLE FULL!!!": GOTO 39
499 REM ADD SHAPE TO TABLE
500 HOME : IF AFLAG = 1 THEN 540
502 OFF = ADDR - 2048: AFLAG = 1
505 IF N < > MAX THEN 515
510 HOME : VTAB 22: PRINT "TABLE FULL WITH THIS SHAPE!!!"
515 IF N > MAX THEN 550
520 POKE 2050 + 2 * N.OFF - 256 * INT (OFF / 256)
525 POKE 2050 + 2 * N + 1, INT (OFF / 256)
530 GOTO 39
540 VTAB 22: PRINT "NO SHAPE TO ADD!": GOTO 39
550 VTAB 22: PRINT "TABLE FULL CAN'T ADD SHAPE!!!": GOTO 39
640 SCALE= 1: POKE 233,3: VTAB 23: PRINT " PRESS ANY
KEY!": POKE 36,41
645 GET Q$: POKE - 16368,0: POKE - 16303,0
650 HOME : VTAB 22: IF AFLAG = 0 THEN PRINT " (A)DD SHAPE
TO TABLE IF CORRECT"
655 GOTO 39
699 REM SAVE
700 HOME : PRINT : INPUT "SHAPE TABLE NAME? ";NAME$
705 PRINT D$ ; "BSAVE" NAME$ " A2048 L" ADDR
;
;
, , ;
25
SIMPLE GRAPHIC ANIMATION USING APPLE SHAPE TABLES
vi^D^
XDRAWn to the screen, the pixels are reversed, so that
is
0
62 A
*
32
ROTATION ANGLES
26
When a shape is plotted to the screen, Applesoft needs to know the location
of the stored shape table. Locations 232 and 233 decimal contain the starting
address of the table, lo byte first. Thus, if the table were stored in memory at
$300 or 768 decimal, Applesoft would be informed with 232,0 POKE : POKE
233,3 (00 being the lo order byte and 03 being the hi order byte).
It is important to find a safe spot in memory for your table, a place where it
HI = INT ( HIMEM/256 )
LO = HIMEM - 256 He HI
Then use the statements POKE 116, HI POKE 115,LO to reset HIMEM:.
:
The shape table is then BLOADed at this address and locations 232 and 233
are set to point to the table.
Sometimes it is best to illustrate a concept with an example. Many animated
shapes like gun crosshairs are moved around the screen by paddle or joystick
control. We can take shape #2, which is shaped like a cross, from our previous
shape table example, and XDRAW it to the screen at a position determined by
28
1 POKE 232,0: POKE 233,3
5 FOR I = 0 TO 15: READ V: POKE 768 + I,V: NEXT I
10 HGR POKE - 16302,0: HCOLOR= 3
:
15 SCALE= 4: ROT= 0
20 BUT = PEEK ( - 16287): IF BUT < 128 THEN 60
30 SALE= INT ( PDL (0) / 8 + 1)
32 XDRAW 2 AT X,Y
34 FOR DE = 1 TO 50: NEXT DE
36 XDRAW 2 AT X,Y
40 BUT = PEEK ( - 16287): IF BUT > 127 THEN 30
50 GOTO 90
60 BUT = PEEK ( - 16286): IF BUT < 128 THEN 90
70 ROT= INT ( PDL (1) / 4)
72 XDRAW 2 AT X,Y
74 FOR DE = 1 TO 50: NEXT DE
76 XDRAW 2 AT X,Y
80 BUT = PEEK ( - 16286) IF BUT > 127 THEN 70
:
Drawing shapes to the screen with XDRAW commands isn’t the only
method of drawing erasing background is not a concern. The
if DRAW com-
mand works just as well for putting an object on the screen. The XDRAW
command is still used for erasing the object. However, the DRAW command
doesn’t work properly at certain combined rotation angles and scale factors.
This can be demonstrated in the last program by changing the XDRAWs in
lines 32, 72 and 110 to DRAWcommands. Now if the program is run, pixels
from the shape sometimes aren’t erased at some rotation angles with large scale
factors. Thus, it is safer to always use the XDRAW
command.
29
CHARACTER GENERATORS
Character generators are designed to assist the programmer in
placing text
on the Hi-Res screen. Their ability to mirror the print functions
on the text
screen makes them extremely easy to use from BASIC
programs Once the
character generator is engaged (usually by a CALL to its
starting address) any
print statements within the BASIC program are
printed on the Hi-Res screen
instead of the text page. The HTAB and VTAB
functions are fully supported,
so that Hi-Res text can be accurately positioned.
Since the character set is in memory rather than in
keyboard, character sets can be changed at will. An
a ROM
chip on the
Old English or Gothic
character set could easily be substituted for the standard
ASCII character set
used in the ROM.
This versatility in character set design has led to users
creating character sets
consisting of playing cards, alien monsters for
games, or electrical symbols
used in schematics. While each character is only
characters can be arranged in a block to form larger
7X8 pixels, groups of
shapes. A playing card
could easily consist of nine different characters, forming
a three by three block.
If the QWEASDZXC letters were used to define the queen of hearts,
printing them to the screen in the following form would produce the playing
QWE
ASD
ZXC
With 96 different characters available in one character set, you could easily
represent the 13 card values, if two of the diagonal character elements
defined
the suit.
Many programmers have taken advantage of the high speed drawing ability
of these machine language character generators to do animated graphics.
Since
sequences of characters representing shapes can be rapidly “printed” on the
Hi-Res screen, each animated frame consists of characters “printed” at a new
position.
Animating with character generators is relatively easy; however, it does have
several disadvantages. First, the speed advantage gained
by the machine
language routine is badly offset by interfacing it with Applesoft. BASIC
pro-
grams need to be compiled into machine code in order to produce marginal
frame rates. Second, animation appears to be jerky due to the nature
of the
character position boundaries. There are only 40 horizontal positions
and 24
vertical positions for placing a character on the Hi-Res screen.
Since characters
can’t be drawn in-between positions, they tend to jump 8 pixel positions ver-
ticallyand 7 pixel positions horizontally. Lastly, as a rule, character generator
animation lacks color. Most limit color because of the peculiarities of
the Hi-
Res screen. If, for example, a green character were “printed” in column
one,
it would appear violet in column
two. This would require two character sets to
30
compensate for this annoying effect between even and odd columns. It is easier
to buffer the color to white.
The need to design new character sets has spawned a number of commercial
character set editors and character set generators. One versatile package is in-
cluded in the DOS TOOL KIT that is available from Apple Computer Incor-
porated. has a program called “Animatrix” that enables you to construct
It
ANIMATRIX DRAWING
31
HOW CHARACTER GENERATORS WORK
Character generators incorporate high speed machine language routines that
calculate the character’s position, then draws it on the
screen one byte at a
time. Characters consist of eight bytes in memory,
where each byte represents
the on/off positions of seven adjacent pixels. Each
character is 7 pixels wide by
8 pixels deep. There are 96 characters in a set, each eight bytes in
length for a ’
total of 768 bytes of memory.
The program has an index to the character set. Each character
fits in a par-
ticular position within the set depending on
its ASCII assigned value. The
character numeric values range from decimal 160 to
255, including both upper
case characters. When the character generator
begins processing the
P tat ment Wlth,n the BASIC program, it
reads a character, determines
J
a c TT ^
its ASCII value, then indexes to the
proper eight bytes in its table to obtain the
character shape bytes to be drawn to the screen. For
example, the program
says to print an H, which is interpreted as the
ASCII character 200. That
character is 40 characters past the tables first character
shape begins 40 X
value. Therefore, the H
8 bytes into the character set storage table. Now
those eight
bytes which will be plotted on the screen don’t have
to represent an H. They
may have been redefined with a character editor to be a section of
a much
larger shape.
9 ••••••,
•
32
Most character generators use control characters to set various modes. The
Apple a true lower/upper case shift key; control characters are used for
II lacks
this function. Sometimes, control characters are used to put the
user in “Block
Mode”. This saves inserting numerous VTABs and HTABs when printing a
multi-character shape such as playing cards. Other control characters are
often
used to clear to the end of a line or even an entire page. This facilitates
erasing
the old characters before drawing new ones on the screen.
Screen animation isobtained by drawing the characters at one position, then
moving them to the next position. Unlike Apple shape tables, you
don’t’ need
to XDRAW to erase characters. Instead, leading or trailing blanks
are added
to help erase characters from the old string that may
not be erased when draw-
ing the new string. It is equivalent to using a DRAW command, with spaces
inserted on either side of the shape. The other alternative
is to erase the
character shape entirely using blanks. This method is more
likely to increase
screen flicker since an extra step is involved.
The TOOL KIT character generator has one feature not found in other
packages. It has the ability to preserve background while drawing
characters
A good example of this is the demo game, RIB * BIT. The character generator
stores the background picture on Hi-Res page two,
and ORs the characters
against it while drawing on Hi-Res page one. This technique
also facilitates
erasing the characters in their previous position. One is
relieved of the task of
printing blanks to the Hi-Res screen before repositioning the
character shape.
In summation, although a character generator is capable
of animating sim-
ple games from BASIC for beginners, it doesn’t offer
the speed, flexibility,
color, and smoothness that is required for quality
arcade games. Although
character generators have their place, there are better methods
presented later
in this book.
33
CHAPTER 2
LO RES GRAPHICS
35
reading for a more comprehensive introduction to assembly language program-
ming. However, it does not cover graphics.
36
Hexadecimal numbers are very much like decimal numbers. They can be
added and subtracted in like manner. The only difference is that instead of
having units, tens and hundreds, etc, the hexadecimal numbers have units,
sixteens and 256’s, and so forth. Each successive digit is 16 times the position
to
the right instead of ten times as in our decimal system.
DECIMAL HEXADECIMAL
1 6 5 $ 1 3 A
1 HUNDRED 1- 256
6 TENS 3 SIXTEENS
5 ONES A - ONES
Hexadecimal numbers are used to address the Apple II’s 48000 + memory
locations. Each group of 256 bytes ($00 - $FF) is called a page, starting with
page zero. In 48K Apples, memory is directly addressable from locations
$0000
to $BFFF (0 - 49050). Locations above $BFFF are also
addressable, but these
locations don’t contain RAM. These locations, from $C000 - $FFFF, either
address physical connections like the speaker and game switches at
locations
$C000 — $CFFF, or address the ROM (Read Only Memory) beginning at
$D000 and extending to $FFFF. The latter area contains machine language
monitor routines and either Integer or Applesoft BASIC, depending on
whether you have an Apple II or Apple II Plus.
37
MEMORY MAP
192
191
150
149
96
95
HI-RES PAGE #2
$4000 - $5FFF OR
FREE RAM
64
63
12
11
$800 - $BFF FREE MEMORY OR
PAGE #2 TEXT & L0 RES
8
7
$400 - $7FF PAGE #1 TEXT & L0 RES
4
3 $300 - $3FF MONITOR VECTOR LOCATIONS
2 $200 - $2FF GETLN INPUT BUFFER
1 $100 - $1FF SYSTEM STACK
0 $00 - $FF ZERO PAGE - SYSTEM VARIABLES
1
the program counter. The program counter contains the current address of the
instruction that is being processed. When the computer finishes with an in-
struction, it sets a flag or condition in a seven bit, Program Status Word, which
is a register. For example, if you want to test if a
value in the Accumulator is
equal to zero, you can compare the Accumulator to zero. If true, the zero flag
will be set and the instruction Branch Equal to Zero (BEQ) will be
executed.
Other flags that can be set are the carry flag, overflow flag, and the negative
flag. A diagram of the Program Status Word is shown below.
7 6 5 4 3 2 1 0
N V B D I Z c
SIGN OVERFLOW BREAK DECIMAL INTERRUPT ZERO CARRY
PROGRAM STATUS WORD
39
-
>CALL-151
*800 :A9 05 8D 00 09 CE 00 09 AD 00
09 C9 00 DO F6 60 < CR >
If you enter a 800L from the monitor you will see the following:
40
again. The code will perform this small loop until the value in $900 becomes
zero. At that time, the test for a zero becomes true and the program returns to
whatever called it. In our case, we called the code from the monitor - thus it
returns to the monitor. If we had called it from within a program, it would have
returned to the appropriate place in the code to continue the program.
Does it work? First, type 900 :AA <CR>
to place something in that
memory location, then type 800G <CR>
from the monitor. The code will
return you back to the monitor when it finishes. Type 900 <CR>
and a 00 is
returned. This is the value in memory location $900. If you have an Integer
machine that has STEP and TRACE, you can do a 800S <CR>
instead,
followed by a S <CR > each time and watch the code single step. The value in
the Accumulator is the first value displayed. When it finally reaches zero the
program will reach the RTS and finish.
30 IF X <> 0 THEN 20
40 RETURN
The major differences between the two programs is that in assembly
language there are no line numbers, and you have to take care of every detail.
BASIC automatically assigns the storage locations of all variables and the loca-
tion of each instruction in memory. In assembly language programming, we
have X variable to memory location $900 and have to calculate
to assign the
the relative branch or GOTO so that references the memory location $805.
it
This is done by branching back $F6 bytes, or -8 bytes, to the proper address.
Yet, many of these details can be greatly simplified if we use an assembler to do
our programming.
The same program using an assembler looks like the following:
41
The assembler generates identical machine code, but many of the tedious
details are simplified. Once X
equated to the memory location in line 3,
is
references to that variable in lines 5 through 7 are handled
were assigned to a different memory location because our
automatically. If X
program was
lengthened, you would only have to change line 3. Also, labels
are allowed.
They act like line numbers
in BASIC. Since the assembler assigns the line
of
code labeled LOOP to
a particular memory location, it can calculate the cor-
rect relative branch automatically when it encounters
line 9 during assembly.
The ORG and OBJ in lines one and two are pseudo-opcodes, understood only
by the assembler. These do not generate machine code, but tell
the assembler
where the code is to be run and stored, respectively.
Although the ORG
can be specified anywhere in memory, the
OBJ is
peculiar to older assemblers. The OBJ, or the place in
memory where the code
that is built is stored, must not overwrite either the
assembler or the text file
containing your source program.
Older assemblers, like TED II + need to be told where the
, location is.
Default values are recommended. Newer assemblers
like BIG MAC
MERLIN, and TOOL KIT don’t use OBJ pseudo-opcodes since they default
to those values automatically.
When an assembler builds its code for an ORG
different from its OBJ (as in
the above example), the code has addresses and relative
branches that will only
execute at the proper ORG
runtime address. The assembler, however, saves
the code that is physically stored, beginning at address
$6000. It will not ex-
ecute if run at that address, so that you need to load or run
it at $800 using a
“,A$800” after the name of the program.
Now that you have had a taste of assembly language programming and have
seen that it isn t as bad as you thought, there are a number
of fundamental
operations that must be learned. The most important operation
is to move
numbers from one memory location to another. This can be accomplished
by
loading a value into any one of the three internal 6502
registers, the Ac-
cumulator, X or Y registers, and storing that number somewhere in
memory.
A LDA (Load Accumulator) instruction can be carried out in several different
ways depending on its addressing mode. First, we can load the
Accumulator
with a real hexadecimal value(LDA #$05). This is called Immediate Mode Ad-
dressing. Sometimes, we need to be able to load the
Accumulator with a
variable stored in a memory location (LDA $900). This is called Absolute Ad-
dressing. The only other addressing mode which we will discuss for the time
being the indexed addressing mode. It takes the form of LDA
LDA
is
$900, orX
Y
$900, depending on whether the X
or Y register is used as an index. If,
for example, the X
register contains #$05, then the instruction above
loads the
value from location $900 + $5 or $905. This addressing mode
is used primarily
for indexing into tables stored at particular memory
locations.
Store operations are similar to load operations. You can
(<
store a value into an
absolute” memory location, or you can
store indirectly into a memory loca-
tion, offset by the value contained in either the X
or Y register.
42
XX
YY YY XX
In summary, the table below shows the various load and store operations.
43
LDA $900 ;L0AD ACCUMULATOR WITH VALUE AT $900
CMP #$05 ;COMPARE $5 WITH ACCUMULATOR
Most assemblers offer alternative mnenomics for BCC and BCS. Since, dur-
ing comparisons, the carry flag is set when the value is equal or
greater than the
value compared, BCS might be called BGE Branch Greater
( or Equal ).
Likewise, BCC is equivalent to BLT Branch Less Than
( ). Why use these alter-
natives? Because they are easier to remember and visualize,
and they make it
clear that you are doing logical comparisons rather than testing
the results of an
addition or subtraction.
44
There isone other important concept that should be understood when doing
comparisions. I implied that the subsequent branch was like a GOTO in
BASIC or like a JMP instruction in machine language. This is not entirely
true, since the range of the branch can not exceed - 126 to +129 bytes. This is
because the branch instruction is only two bytes long. The first byte is the in-
struction code and the second the relative address. It takes a two byte address
to branch to any place in memory (Except Page Zero). The JMP instruction
has the advantage that it is three bytes long. In most cases, this limitation will
not cause problems. But if a branch out of range error occurs, you must reverse
the test so that it will reach the required destination via a JMP instruction.
This change causes the program to drop through to the JMP instruction if
the zero flag was set, and then jump to location SKIP. However, if the zero flag
is not set, it will advance ahead five bytes to the instruction following the
JMP.
All of the other branch instructions work in a similar manner. This gives the
equivalent of a Long Branch.
Simple addition and subtraction of unsigned numbers is easily accomplished
in machine language. All addition and subtraction must be performed one byte
at a time. Thus, large numbers or multi-byte numbers (those that exceed $FF),
must be added or subtracted one byte at a time, and the carry flag must be
accounted for. It’s actually not much different than addition of two multi-digit
long decimal numbers. Those numbers have a digit in the one's column,
another in the ten's, etc. If you add 65 to 78, you add the one's column first.
Five plus eight equals 13. The value in the one's column is 3; you then carry
the one into the tens digit before you add the two numbers in the ten's column.
Hexadecimal addition is similar. You clear the carry before you add. If the sum
of the two values exceeds $FF, the carry is set. Since you don't clear the carry
when adding the next higher byte, the resultant answer will be the sum plus the
previously computed carry, as in the following example:
EXAMPLE : +CARRY
63 F4
+ 02 + 16
66 0A ;
SETS CARRY
45
The code for additions and subtractions is as follows:
ADDITIONS
SUBTRACTIONS
You should be aware that the rules for subtraction are different than for
ad-
The carry must be set first. This is equivalent to a borrow in subtrac-
dition.
tion. After the subtraction operation, the carry will
be clear if an underflow
(borrow) occurred. The carry will be set otherwise. Setting the carry
is very im-
portant, a step that many beginners forget. The results are
invariably incorrect
if this step is skipped - and possibly even
“random”, since the status of the
carry flag can be on or off when the subtraction operation
is performed. This
can make debugging difficult.
46
LO-RES SCREEN
The Lo-Res screen occupies the same memory locations as the text page:
$400 to $7FF for page one and $800 to $BFF for page two. When the Lo-Res
graphics mode is toggled, the 1024 memory locations are presented as colored
blocks rather than ASCII characters. Each ASCII character becomes two col-
ored blocks, stacked one upon the other. Since the text page contains 24 lines of
forty characters, the Lo-Res screen shows 48 rows of blocks, 40 blocks wide.
Each block can be any one of 16 colors.
MAGENTA VALUE
LOCATION $400 $D1
YELLOW
I would like to point out that the map of the text screen is not
sequential in
memory. Like its big brother, the Hi-Res screen, the first 40 bytes map across
the first row but the second 40 bytes represent a row which is a third of the way
,
down the screen. The third 40 bytes consitute a row in the bottom third of the
screen. The exact order is not important at this time, because monitor
subroutines calculate the base address for any Lo-Res color plotting
automatically. To plot any Lo-Res point you need only give the monitor
subroutine located at $F800 the row and column to plot and the proper color.
The column is loaded into the Y register, the color into memory location $30,
and the row into the Accumulator. A call to $F800 will plot a Lo-Res dot to the
47
screen, and
be seen if the Lo-Res graphics display is activated first.
will
The
dot s value
always placed into Lo-Res memory by this subroutine,
is
even if
you are viewing Hi-Res screen memory.
Iwould like to interject a word of caution when inputting
color values for Lo-
Res plotting subroutines. Because setting the proper
color nibble depends on
whether you are plotting on an odd or even row, it
is safer to put the color
desired in both low and high nibbles. To illustrate
the point, let’s assume we
placed a $01 in the color register and we wanted to
plot the point on row
umn 0. The plotting subroutine would use the lower order nibble to 0, col-
$1 plot the
magenta dot, then it would ignore the higher order nibble.
However, if we
choose instead to plot at row 1 column 0 the subroutine
, , will use $0 for the col-
or and ignore the lo order nibble. Thus, the screen
would remain black. The
solution is to put the color in both nibbles. Placing
$1 1 in the color register will
always plot the proper color in the above example
anywhere on the Lo-Res
screen.
48
ORG $6000 ; ASSEMBLE CODE AT $6000
OBJ $6000
JSR $FB40 ;SET LO-RES GRAPHICS MODE
JSR $FC58 ; CLEAR SCREEN
LDA #$66 ;SET COLOR BLUE
STA $30 ; STORE IN COLOR LOCATION
LDY #$05 ; COLUMN
LDA #$03 ;ROW
JSR $F800 ;PLOT POINT
LDA #$99 ;SET COLOR ORANGE
STA $30 ; STORE IN COLOR LOCATION
LDA #$08 ;END COLUMN
STA $2C ; STORE END COLUMN
LDY #$02 ; START COLUMN
LDA #$06 ;ROW
JSR $F819 ;PLOT HORIZ ROW
RTS ;RETURN TO MONITOR
run by typing a 6000G <CR> from the monitor. If the ORG assembled
is
elsewhere with another assembler type, the appropriate start. For example, if
LISA assembles your code at $800, then type 800G <CR>.
As you can see, plotting with Lo-Res graphics is relatively easy but involves
tedious details. The same code in BASIC, as listed below, would have taken a
mere five statements. Yet the machine language program will run at least
twenty times faster.
49
!
LDA #$00
STA COUNTER
LDA #$05 ; START FIFTH ROW
STA ROW
LDA #$11 ;RED COLOR FIRST 4 ROWS
STA $30 ; COLOR STORAGE
LDA #$22 ;END COLUMN
STA $2C
LOOPA LDA ROW
LDY #$05 ; START COLUMN
JSR $F819 ;PLOT HORIZ LINE
INC ROW ;NEXT ROW
INC COUNTER ; COUNTER = COUNTER + 1
LDA COUNTER
CMP #$04 ;HAVE WE DONE ALL FOUR ROWS
BNE LOOPA ;NO GOTO LOOPA
!
RTS ;DONE
.
with the paddle determines the ball’s direction. Balls striking the left end travel
upwards and to the left at a 45 degree angle, while balls striking the inside left
travel in the same direction but at a 60 degree angle. Balls striking the paddle’s
right side travel at similar angles but to the right.
Determining where the ball struck the paddle is easy. The four block- wide
paddle always drawn at row 35 decimal or $23, and the first block begins at
is
tion to PADX first, and then PADX + 1, etc, when a collision is detected, the
ball’s velocity components VX
and VY are reset. VY is always reset to -1 so
that the ball travels upwards. However, VX
varies with which block was hit.
As we mentioned earlier, the two outside blocks would cause the ball to travel
at 45 degree angles. This would mean a VX
of + 1 or - 1. The inside blocks
would cause the ball to bounce at 60 degree angles or VX
at + 1/2 or - 1/2.
Incrementing the ball’s position by 1/2 is not possible in machine code. But
if the incremented value was first doubled before calculating the ball’s new
position, and the result divided by two, the same result would be obtained with
the loss of the fractional part. This doesn’t matter since the ball can only be
placed at whole number positions.
BX = BX + VY = 6 + 1/2 = 6 (ROUNDED).
If the numbers were doubled and the result divided by two, then
BX = 12 + 1 = 13/2 = 6 (ROUNDED).
If the doubled position is kept rather than discarded and we wished to move
the ball another 1/2 position, then
BX = 13 + 1 = 14/2 = 7.
right
51
PADDLE DEFLECTOR
52
ASL 7 6 5 4 3 2 1 0
In order to update the ball's position, we take the ball’s old BX,BY position
in each direction and add the change in position or its directional velocity.
Negative values are converted to their two’s complement equivalent so that all
operations are simple additions. A negative one becomes a $FF, so that $FF
plus $02 = $01.
BX = BX + VX X DIRECTION
BY = BY + VY Y DIRECTION
The ball’s X position is calculated using doubled position values DBX and
doubled velocities values VX to avoid 1/2 values
Thus, DBX = DBX + VX and BX - DBX/2.
53
LDA DBX ;OLD DOUBLED X POSITION
CLC
ADC VX ;X DIRECTION VALUE
STA DBX ;THIS DOUBLED VALUE WILL RETAIN FRACTION
LSR ;DIVIDE BY 2 WILL LOSE FRACTION
,
As the ball bounces around the screen, it will soon collide with one
of the col-
ored 2 by 2 bricks at the top of the screen. Since these are colored
blocks, colli-
sions can be detected between the ball and these blocks
with the SCRN func-
tion. This monitor subroutine will return the value
of the color at any position.
This test is performed before the ball is drawn to the screen,
or the test becomes
meaningless at the ball s position since the ball will plot over the
background
color blocks.
We will want to delete the block if a non-black ( background ) color is return-
ed during the test. The brick
four times larger than our ball, so we must
is
delete all four blocks at once. This is a troublesome
operation, since we might
have collided with any of the four color blocks that comprise the
brick The
block that we hit is BX,BY. If we hit the top left block of the
brick we will want
to delete block BX,BY ,BX + 1,BY BX
+ 1,BY + 1 , and BX,BY + 1. The
,
LDA BX
LSR ; DIVIDE BY TWO
BCC EVEN ;BX IS EVEN IF CARRY IS CLEAR
ODD JMP SKIP
EVEN NOP ; CONTINUE WIH EVEN CODE
54
Once removed, the score must be incremented by the point value
the block is
for each block. In game, yellow is worth one point, blue two points, and
this
red three points. The score is kept in a memory location called SUM. There
has been no attempt in this example to convert the hexadecimal value of SUM
to a decimal value. That type of scorekeeping routine is outlined in Chapter 6.
The scorekeeping routine first checks the color of the block hit for yellow. If it
isequal to #$0D (Yellow) it will add #$01 to SUM. Otherwise, it will branch to
the label NEXT. There it encounters a test for the color blue. If the block isn’t
blue it branches to the label NEXT1. If it is blue, #$02 is added to SUM,
otherwise #$03 is added to SUMbecause it must be red.
55
SCORE LDA COLOR
CMP #$0D ; HIT YELLOW?
BNE NEXT
LDA SUM
CLC
ADC #$01
STA SUM
JMP SC0RE1
NEXT LDA COLOR
CMP #$06 ;HIT BLUE?
BNE NEXT1
LDA SUM
CLC
ADC #$02
STA SUM
JMP SC0RE1
NEXT1 LDA COLOR
CMP #$01 ;HIT RED?
BNE SC0RE1
LDA SUM
CLC
ADC #$03
STA SUM
SC0RE1 JSR PRINT
CMP #$F0 ; SUM=240 FOR ALL BLOCKS
BGE END
A test is needed to detect the end of the string. Since a general purpose
print
output routine is desired for any length string up
to 255 characters it is best
,
not to restrict the test to detecting the length
of the string, but to detect a
character that is never sent to the screen. The
sign) is rarely used and is a good
hexadecimal 00 (the reverse
choice for a test byte. When the code detects
@
56
X
this byte, it knows it has completed the string and exits the print loop. The
The “Breakout” game needs paddle control. The paddle is used both
. . i
to
the game by a button press, and to move the deflector
itially start
back and
forth at thebottom of the screen. Button presses are the easiest to detect. There
are three paddle switches that are located at $C061 -$C063.
The lowest hard-
ware location is for paddle #0. If the button is pushed, the value loaded
into the
Accumulator is negative. The program can be put into an endless loop
waiting
for a button press with the following code:
57
DRAW COLOR TARGET BLOCKS & FI ELI
BALL=5
|
INITILIZE START POSITION OF BALI
Q- -
H
k 1
BUTTON PRESSED?
1
yes
y
XDRAW OLD POSITION OF PADDLE
58
59
23 27
1 ** B R E A K OUT
2 ORG $6000
3 JMP PROG
4 ROW DS 1
5 COUNTER DS 1
6 BX DS 1
7 BY DS 1
8 BBX DS 1
9 BBY DS 1
10 vx DS 1
11 VY DS 1
12 DBX DS 1
13 PDX DS 1
60
E
14 PADX DS 1
15 PRT DS 1
16 PLEFT DS 1
17 SUM DS 1
18 BALL DS 1
19 COLOR DS 1
20 CBALL DS 1
21 CPDL DS 1
22 PITCH DS 1
23 TIME DS 1
24 PREAD EQU $FB1E
25 COUT EQU $FDFO
26 TABV EQU $FB5B
27 PR BYTE EQU $FDDA
6017: : 20 40 FB 28 PROG JSR $FB40 ;SET LORES GRAPHICS MODE
601A: : 20 58 FC 29 JSR $FC58 ; CLEAR SCREEN
30 *DRAW SCREEN & BLOCKS
601D A9 88 31 LDA #$88 ;SET COLOR BROWN
601F 85 30 32 STA $30
6021 A9 23 33 LDA #$23 ;END COLUMN
6023 85 2C 34 STA $2C
6025 A9 00 35 LDA #$00 ;TOP ROW
6027 AO 04 36 LDY #$04 ;START COLUMN
6029 20 19 F8 37 JSR $F819 ;PLOT HORIZ LINE
602C A9 27 38 LDA #$27 ;END ROW
602E 85 2D 39 STA $2D
6030 A9 01 40 LDA #$01 ; START ROW
6032 AO 04 41 LDY #$04 ; COLUMN
6034 20 28 F8 42 JSR $F828 ;PLOT VERT LINE
6037 A9 01 43 LDA #$01 ; START ROW
6039 AO 23 44 LDY #$23 ; COLUMN
61
6082 AD 03 60 74 LOOPC LDA ROW
6085 AO 05 75 LDY #$05 START COLUMN
;
60A3 8D 05 60 88 STA BX
60A6 8D 06 60 89 STA BY
60A9 A9 28 90 LDA #$28
60 A B 8D OB 60 91 STA DBX
60 A E A9 00 92 LDA #$00 {INITIAL VELOCITY BALL
50B0 8D 09 60 93 STA vx
60B3 A9 01 94 LDA #$01
60B5 8D OA 60 95 STA VY
60B8 A9 11 96 LDA #$11 INITIAL PADDLE POSITION
;
62
6109: A9 23 134 LDA #$23
610B: 20 19 F8 135 JSR $F819 XPLOT PADDLE
;
SR $F819
™
*iiDn*'rf
161 * UPDATE POSITION BALL
; plot horiz paddle
162 NOTE ALL VX VALUES DOUBLED
6140: AD OB 60 163 TO AVOID 1/2 VALUES
;OLD DOUBLED X POS VALUE
6143: 18 164
6144: 6D 09 60 165
;X DIRECTION VELOCITY
6147: 8D OB 60 166
.-THIS DOUBLED VALUE WILL
167 KEEP FRACT-
;TIONAL PART OF NEW POSITION
614A: 4A 168
.‘HALF VALUE WILL LOSE FRACTION
614B: 8D 05 60 169
JNEW BALL X POS
614E: AD 06 60 170
;OLD Y POS
6151: 18 171
6152: 6D OA 60 172
;ADD Y DIRECTION VELOCITY
6155: 8D 06 60 173
fnun unu
;NEW BALL Y POSITION
174 *TEST IF BALL HIT SIDES OR PADDLE
6158: AD 06 60 175 BY
615B: C9 23 176 #$23 ;AT PADDLE ROW?
615D: FO 03 177 PAD1 YES!
615F: 4C B7 61 178 ;
LEFT
6162: AD OD 60 179
PADX
6165: 8D OF 60 180
PLEFT
6168: AD 05 60 181 BX
616B: CD OF 60 182
PLEFT
616E: DO OA 183 SECOND
6170: A9 FF 184 #$FF
6172: 8D OA 60 185 VY ;VY=-1
6175: A9 FE 186 #$FE
6177: 8D 09 60 187 VX ;VX=-2
617A: EE OF 60 188 PLEFT
61 7D: AD 05 60 189
BX
6180: CD OF 60 190 PLEFT
6183: DO 08 191 THIRD
6185: A9 FF 192 #$FF
6187: 8D OA 60 193 VY
63
618A: 8D 09 60 194 STA VX VX=-1
618D: EE OF 60 195 THIRD INC PLEFT
6190: AD 05 60 196 LDA BX
6193: CD OF 60 197 CMP PLEFT
6196: DO OA 198 BNE FOURTH
6198: A9 FF 199 LDA #$FF
619A: 8D OA 60 200 STA VY ;VY=-1
619D: A9 01 201 LDA #$01
619F: 8D 09 60 202 STA VX ;VX=1
61A2: EE OF 60 203 FOURTH INC PLEFT
61A5: AD 05 60 204 LDA BX
61A8: CD OF 60 205 CMP PLEFT
61AB: DO OA 206 BNE LEFT
61AD: A9 FF 207 LDA #$FF
61AF: 8D OA 60 208 STA VY ;VY=~1
61B2: A9 02 209 LDA #$02
61B4: 8D 09 60 210 STA VX VX=2
61B7: AD 05 60 211 LEFT LDA BX
61BA: C9 06 212 CMP #$06 ;HIT LEFT SIDE?
61BC: BO OB 213 BGE RIGHT ;N0!
61BE: AD 09 60 214 LDA VX REVERSE VX
61C1: 49 FF 215 EOR #$FF COMPLEMENT
61C3: 8D 09 60 216 STA VX
61C6: EE 09 60 217 INC VX VALUE CORRECTED
61C9: AD 05 60 218 RIGHT LDA BX
61CC: C9 22 219 CMP #$22 HIT RIGHT SIDE?
6 ICE: 90 OB 220 BLT TOP NO!
61D0: AD 09 60 221 LDA VX REVERSE VX
61D3: 49 FF 222 EOR #$FF COMPLEMENT
61 D5: 8D 09 60 223 STA VX
618: EE 09 60 224 INC VX ;
VALUE CORRECTED
61DB: AD 06 60 225 TOP LDA BY
61DE: C9 01 226 CMP #$01 HIT TOP?
61E0: DO OB 227 BNE BOTTOM NO!
61E2: AD OA 60 228 LDA VY REVERSE VY
61E5: 49 FF 229 EOR #$FF COMPLEMENT
61E7: 8D OA 60 230 STA VY
61EA: EE OA 60 231 INC VY VALUE CORRECTED
61ED: AD 06 60 232 BOTTOM LDA BY
61F0: C9 27 233 CMP #$27
61F2: DO 3A 234 BNE BLOCKS
61F4 CE 11 60 235
: DEC BALL
61F7: A9 FF 236 LDA #$FF BAD SOUND FOR MISSING
61F9: 8D 15 60 237 STA PITCH
61FC: 8D 16 60 238 STA TIME
61FF: 20 E9 63 239 JSR SOUND
6202: A9 FF 240 LDA #$FF SHORT DELAY
6204: 20 A8 FC 241 JSR $FCA8
6207: AD 11 60 242 LDA BALL
620A: C9 00 243 CMP #$00 : ALL BALLS GONE?
620C: DO 03 244 BNE CONT
620E: 4C DD 62 245 JMP END
246 *ERASE BALL & PADDLE
6211: A9 00 247 CONT LDA #$00
6213: 85 30 248 STA $30
6215: AC 05 60 249 LDY BX
6218: AD 06 60 250 LDA BY
62 IB: 20 00 F8 251 JSR $F800 ; XPLOT BALL
621E: AD OE 60 252 LDA PRT
1 !! )
66
Y
67
— X
The Applesoft ROMcontains a full set of Hi-Res graphics routines. But Ap-
plesoft, being an interpretive language rather than a compiled language, ac-
cesses these routines rather inefficiently as far as speed is concerned. This
is
because the interpreter has to determine where to go and what to do with each
tokenized BASIC instruction as it encounters it. The speed penalty for this ad-
ded overhead is considerable. The interpreter runs these routines from four to
six times slower than if they were called directly from machine
language.
At first glance, it appears to be rather simple to call to graphics subroutines
located in the ROM. In retrospect, it is, provided that you understand how the
interpreter handles the data structure both internally and externally as it ex-
ecutes these graphics subroutines. Since the information has never been fully
documented, it is some help if you have the Programmer’s Aid Manual, where
a source listing of that ROM chip is quite similar to the ROMApplesoft Hi-
Res subroutines.
I’m quite reluctant at this stage to attempt an explanation of how these
routines actually work. A
solid grounding both in machine language and in the
Hi-res screen’s peculiarities won’t come until much later in the book. I will,
however, discuss the data structure in regards to what you need to input, and
how you input these parameters when calling the subroutines.
There are a series of memory locations stored in zero page that specify a
point on the Hi-Res screen. Some people call these locations External Cursor
Data. They are as follows:
$E7 Scale
: factor for drawing shapes
$E8: Lo byte pointer to beginning of shape table
$E9: Hi byte pointer to beginning of shape table.
69
Y
There are also a number of zero page page locations that the Hi-Res
subroutines use internally when doing the actual screen plotting of points,
or
strings of points called lines. Some of these contain the memory
address of the
byte to plot on the screen, while others contain the color and masking
informa-
tion, so that only the correct pixel within that seven-pixel
byte is turned on or
off.
$1C: The color masking byte, which is shifted for odd addresses but other
wise remains unchanged.
$26:Lo address for the leftmost byte in a particular vertical row.
$27:Hi address for the leftmost byte in a particular vertical row.
$E5: The integer part of the horizontal screen coordinate divided by
7, or the horizontal offset into row.
$30: The bit position taken from the Bit Position table.
What I should point out is that after a series of other subroutines set up the
position to plot on the screen, the actual plotting of the point is done with a five
line subroutine called PLOT located at $F45A, as in the following:
LDA $1C
EOR ($26),
AND $30
EOR ($26),Y
STA ($26),Y
RTS
The internal cursor data is more important than the external cursor data if
speed is the consideration. There are internal subroutines within the
that set the external cursor data to correspond with
ROM
the internal data, and
several more that can manipulate the screen cursor directly.
However, for plot-
ting points and drawing shapes from Apple shape tables,
you
need not concern
yourself with any internal workings of these subroutines.
Instead, I’ve sum-
marized all of the necessary subroutines in the table below,
and will
demonstrate examples using them.
70
1 A
HGR2 $F3D8
71
10 HGR
20 HCOLOR = 3
30 HPLOT 100,50 TO 150,100 TO 50,100 TO 100 50
40 END
The program sets the mode to Hi-Res graphics page one, mixed text
and
graphics, by calling HGR at $F3E2. The plotting color is set to white
(3) by
a call to HCOLOR at $F6F0. Then, by loading the Accumulator
and the X
& Y registers with the correct screen coordinates,
the point at 100 50 is
plotted to the screen with a call to HPLOT at $F457. Each of the triangle’s
lines are drawn by calling HLINE at $F53A.
This subroutine draws a line
from the internal cursor position (last point) to the point
defined by the in-
put to HLINE. Since the last point was at 100,50
and we are inputting the
coordinates 150,100 the line is drawn between these two points.
,
After
drawing the next two lines, the triangle is completed
and the program
°
ends. The complete code follows.
100,50
72
1 *PLOT TRIANGLE
2 0RG $6000
6000: 20 E2 F3 3 JSR $F3E2 HGR
6003: A2 03 4 LDX #$03 C0L0R=WHITE
6005: 20 F0 F6 5 JSR $F6F0 HC0L0R
6 *PL0T FIRST PT
6008: A0 00 7 LDY #$00 H0RIZ P0S HI BYTE
600A: A2 64 8 LDX #$64 H0RIZ P0S L0 BYTE
600C: A9 32 9 LDA #$32 VERT P0S
600E: 20 57 F4 10 JSR $F457 HPLOT
11 *DRAW TO SECOND POINT
6011: A2 0012 LDX #$00 H0RIZ P0S HI BYTE
6013: A9 9613 LDA #$96 H0RIZ P0S L0 BYTE
6015: 64
A0 14 LDY #$64 VERT POS
6017: 3A F5 15
20 JSR $F53A HLINE
16 *DRAW TO THIRD POINT
601A: A2 00 17 LDX #$00 H0RIZ POS HI BYTE
601C: A9 32 18 LDA #$32 H0RIZ POS LO BYTE
601E: A0 64 19 LDY #$64 VERT POS
6020: 20 3A F5 20 JSR $F53A HLINE
21 *DRAW TO FIRST POINT
6023: A2 00 22 LDX #$00 H0RIZ POS HI BYTE
6025: A9 64 23 LDA #$64 H0RIZ POS L0 BYTE
6027: A0 32 24 LDY #$32 VERT POS
6029: 20 3A F5 25 JSR $F53A HLINE
602C: 60 26 RTS
— END ASSEMBLY-
73
The technique for accessing elements of a shape table involves
loading the
first of a pair of bytes into the Accumulator, and the second
byte into the X
register before calling HLINE to draw the segment. Each element of the
line
table is stored at a particular two-byte address.
In our example, the very first
element is called the Oth element of the table and is located
at $6044. Elements
of a table can be accessed by using a zero page indexing
system called Indexed
Indirect Addressing. It takes the form LDA (SHPL.X).
If the X-register were
zero.it would load a byte from an address
indicated by a pair of bytes, SHPL
and SHPH stored in zero page. For example, if location
$FC and $FD, which
are equivalent to SHPL and SHPH respectively, contain a #$44 and #$60 in
that order, then LDA (SHPL.X) will load a #$61 from location $6044 into the
Accumulator.
SHPL
SHPH
BASE ADDRESS
As you will soon discover, there are never enough registers in the 6502. Cer-
tainly, the Accumulator and X and Y registers are not enough when all three
need to be loaded to call a subroutine, and you also need to use
two of them
simultaneously for retrieving data from a table. The solution is
to temporarily
store your data in a memory location. When you’re
done with the table and
your registers are free, the data can be moved to the proper registers
just before
calling the subroutine. The important thing is to
be careful that you do not
clobber your working registers.
In the example below, the X-register must be set to zero
each time the index-
ed indirect load is used to retrieve a value from the table. This
is no problem
the first time through the loop, but this value for
the horizontal position lo byte
eventually needs to reside in the X-register before calling
HLINE. Since we
74
need to do another indirect indexed load using both the Accumulator and
X-register for the next byte, we temporarily store our data in XLOW. If we in-
crement SHPL, the lo byte pointer to our shape data, it will point to the next
byte in our shape table. At this point, since we haven't disturbed the
X-register, we don't need to put zero into it to perform our next indirect index-
ed load. This second value retrieved — the vertical coordinate is transferred to
the Y-register. The horizontal hi byte is placed into the X-register and the
horizontal lo byte, which was temporarily stored at XLOW, is moved into the
Accumulator before calling the subroutine HLINE.
75
decimal hex
PT X Y x Y
1 69 65 45 41
2 80 52 50 34
3 106 57 6A 39
4 87 57 57 39
5 76 71 4C 47
6 88 77 58 4D
7 81 85 51 55
8 72 77 48 40
9 59 88 38 58
10 64 108 40 6C
11 50 84 32 54
12 63 72 3F 48
13 59 67 3B 43
14 58 64 3A 40
15 60 62 3C 3E
16 64 62 40 3E
17 69 65 44 41
FF FF
76
6026: A2 00 29 LOOP LDX #$00
6028: A1 FC 30 LDA (SHPL, X) ;H0RIZ P0S L0 BYTE
602A: 8D 00 60 31 STA XL0W
602 D: E6 FC 32 INC SHPL ;NEXT BYTE IN TABLE
602F: A1 FC 33 LDA (SHPL, X) ;THIS IS VERT VALUE FOR PT
6031: C9 FF 34 CMP #$FF
6033: F0 0E 35 BEQ DONE ;IF BYTE CONTAINS 255, DONE
6035: A8 36 TAY ;VERT IN Y REG
6036: A2 00 37 LDX #$00 ;H0RIZ P0S IN HI BYTE
6038: AD 00 60 38 LDA XL0W ;H0RIZ P0S IN LO BYTE
603B: 20 3A F5 39 JSR HLINE
603E: E6 FC 40 INC SHPL ;NEXT BYTE
6040: 4C 26 60 41 JMP LOOP
6043: 60 42 DONE RTS
43
6044: 45 41 50
6047: 34 6A 39
604A: 57 39 44 SHAPE HEX 454150346A395739
604C: 4C 47 58
604 F: 4D 51 55
6052: 48 4D 45 HEX 4C47584D5155484D
6054: 3B 58 40
6057: 6C 32 54
605A: 3F 48 46 HEX 3B58406C32543F48
605C: 3B 43 3A
605F: 40 3C 3E
6062: 40 3E 47 HEX 3B433A403C3E403E
6064: 44 41 FF
6067: FF 48 HEX 4441FFFF
Shape tables that cross page boundaries (256 byte sections of memory where
the hi byte is constant) can cause problems. If, for example, our table began at
$60FC instead of $6044, after incrementing four times, the lo byte would be
#$00. The program would attempt to load the byte at location $6000 instead of
the byte at location $6100. This can be prevented if a test is performed after
you increment SHPL. If SHPL were equal to zero, it would increment SHPH;
otherwise, it would skip this step.
The code to animate our HPLOTed bird Try it, then try
in Applesoft follows.
the same algorithm written in machine language.
should point out that the
I
speed differences can not be directly correlated, since to
keep the object on the
screen longer than off, a delay loop of 7 milliseconds
per frame was used. If you
remove the delay or set the value in the Accumulator to
#$01 before calling the
delay subroutine at $FCA8, the speed increases to 8
times that of the Applesoft
version. However, screen flicker becomes more
noticeable.
78
The code for the moving bird is quite similar to the stationary bird, except
that oncewe plot the bird, it must be erased before replotting it at a different
position. becomes rather convenient to place the entire plotting program in a
It
When alternating between drawing and erasing, the color shifts between
white and black, respectively. The pointers to the shape table must also be reset
for each plot/erase cycle because these pointers are incremented when retriev-
ing bytes within the table. The flow chart and machine code for the moving
bird follows.
79
1)
The advantage of accessing Apple shape tables ( vector shape tables) directly
from machine language results in a sixfold increase in animation speed. For
many applications and simple games, this speed increase may be sufficient. If it
isn’t, you should use raster or block shape animation.
I think that beginning machine language programmers, whose prior
experience is with Apple shapes in BASIC, should attempt the techniques in
this section before learning more complicated methods shown later in this
book.
Ifyou were to DRAW
or XDRAW
a shape in BASIC, you would set the
and rotation before doing a
color, scale, DRAW
1 at 10,10. The location of the
shape table would have been indicated by poking the address to locations
decimal 232 and 233. These two locations are $E8 and $E9, respectively.
However, before calling the DRAW
subroutine at $F601 or at XDRAW
$F65D, the pointers to the correct shape number must be set through a
subroutine that I call SHPTR (short for shape pointer). This subroutine
located at $F730 takes the shape number, which is inputted via the X-register,
and shape in locations $1A (lo byte) and $1B (hi byte).
sets the pointers to the
This subroutine is deeply linked into the Applesoft interpreter. It calls
4
subroutines that increment the Applesoft ‘Get Next Character” Routine.
Although I don’t believe that this subroutine located at $B7 will cause any pro-
81
blems, before you clobber anything, I would pay
attention to the chart of
available zero page locations in the Apple
Reference Manual. Don’t touch the
l S d APPles°ft- You can 3180 disconnect that routine by placing a
i«<fn ?D^rc
#$60 (RTS)f in location $B7 (its first location), but be sure to put the original
value, #$AD, back when you’re done, or you
will hang the computer when it
returns the Applesoft prompt, and doesn’t
understand anything that you type.
In short, don’t make the change unless you
think it is causing you grief.
The second thing that must be set before calling the DRAW subroutine is
the internal cursor position, or where you want
to plot your shape. This is easi-
ly accomplished with the HPOSN subroutine at $F411. Once the horizontal
and vertical locations are inputted, the subroutine sets
locations $26, $27, $30
and $E5 to begin plotting. When you finally call the DRAW or XDRAW
subroutine, the only inputs that are required are the
rotation value in the Ac-
cumulator and the pointers to the correct shape that are stored
at $1A and $1B
in the Xand Yregisters. It may sound complicated but if
you examine the
following code, you will see that it is relatively
straight-forward. The following
routine XDRAWs two shapes. The first, a square, is plotted at X = 64, Y
= 64,
and the second shape, a cross, is plotted at X
= 128, Y =50. The scale is 4.
’
82
6036: 20 F4 33
11 JSR HPOSN
6039: A6 1A 34 LDX $1A ;L0 BYTE SHAPE ADDRESS
603B: A4 IB 35 LDY $1B ;HI BYTE SHAPE ADDRESS
603D: A9 00 36 LDA #$00 ;R0T
603F: 20 5D F6 37 JSR XDRAW
6042: 60 38 RTS
Animating a shape is simple. You plot it once, erase it, move it to a new posi-
tion, and then replot it at its new position. The procedure is accomplished via a
loop. There is very little to say about the method. It is the same in Applesoft. I
think the only thing you should be aware of is that HPOSN doesn’t need to be
called twice, since the erase is done at the same screen position as the
XDRAW. In the example, shape #2 moves horizontally to the right, while
shape #1 is stationary. The move routine checks for wrap-a-round at X * #$FF
as it moves the shape across the screen. The flow chart and code follows.
SHAPE #1 SHAPE #2
6050: 30 F7 44
20 JSR SHPTR
45 DON'T HAVE TO DO HPOSN BEFORE ERASE
46 BECAUSE POSITION HASN'T CHANGED
6053: A6 1A 47 LDX $1A ;L0 BYTE SHAPE ADDRESS
6055: A4 IB 48 LDY $1B ;HI BYTE SHAPE ADDRESS
84
6057: A9 00 49 LDA #$00 ;ROT
6059: 20 5D F6 50 JSR XDRAW ;ERASE SHAPE #2
51 *M0VE SHAPE TO NEW POSITION
605C: AD 00 60 52 LDA XLOW
605F: 18 53 CLC
6060: 69 05 54 ADC #$05
6062: C9 FF 55 CMP #$FF
6064: DO 02 56 BNE SKIP
6066: A9 OA 57 LDA #$0A
6068: 8D 00 60 58 SKIP STA XLOW
606B: 4C 31 60 59 JMP LOOP
85
CHAPTER 4
The Apple II has two Hi-Res graphics screens, a primary and a secondary,
each with a resolution of 280 dots horizontally (columns) and 192 dots or lines
vertically. This gives an effective screen resolution of 53,760 picture elements
or pixels per screen.
The large number of pixels presented a dilemma to the Apple II designers.
Using one memory location for each dot would far outstrip the Apple’s 48K
memory; besides, they wanted to have two screens. Their solution was to
divide the screen horizontally into 40 groups of 7 pixels. Each memory location
would represent information for seven adjacent pixels. This lowered the
memory requirement to 7680 bytes per screen. Since it was easier to work in
8K blocks of memory, this left an unused 512 bytes of memory per page.
In 1977, when memory chips were expensive, most Apple II computers were
sold with only 16K of memory. With various monitor areas, zero page, the
stack, and the text page using the first 2K (2048) bytes of memory, it seemed
logical to place Hi-Res graphics screen # one at the upper end of memory, loca-
tions 8192 to 16383 ($2000- $3FFF). Screen # two of Hi-Res graphics was plac-
ed in the 8K block of memory just beyond locations 16384 to 24575 ($4000
-$5FFF). It was usable by owners who purchased extra memory. Both of these
screen’s locations are hardwired into the machine and, unfortunately, are not
relocatable. In those days, before DOS and Applesoft made their debut, In-
teger BASIC programmers whose machines contained 48K of memory could
start their program at the top of memory and write 32K of code.
Today, Applesoft programmers face the dilemma of where to place their pro-
grams without overwriting the information stored in the Hi-Res screen areas.
Since Applesoft loads a program immediately above the text screen which
begins at $800 or 2048 decimal, only small programs fit, if they are using Hi-
Res graphics commands. The solution is to set the Applesoft pointers so that
the program loads above the Hi-Res screen. Unfortunately, you waste the 6K
of usable memory between the operating system and the beginning of Hi-Res
screen one. In retrospect, what seemed to be a logical choice in 1977 is cumber-
some today.
The Apple’s Hi-Res screen is considered memory-mapped. If you were to
change the values of the first 40 bytes of screen memory so that each turned on
all 7 pixels, then the screen would display a solid white line at the top. Chang-
ing any particular byte in Hi-Res memory directly affects the resultant picture.
Any
byte in screen memory consists of a sequence of
eight individual bits If
a bit is on,
it has a value of 1 if it is off, it has a value of 0. This
;
on-off system of
numbers is called “Binary”. Binary numbers, represented
by strings of 0’s
and 1 s, have their least significant numbers starting at the right, as shown:
128 64 32 16 8 4 2 1
° 0 0 00001 =$01
Each successive move of a bit to the left results in the value of the byte beine
multiplied by two. 5
128 64 32 16 8 4 2 1
0 0 0 00010 = $02
Eventually, the on bit would be shifted to the far left with a value of $80 or
128 decimal.
H
Th
to right.
f ^ eS screen
’
s convention is in reverse. Pixel
This can be verified by poking values into the
values increase from left
primary screen’s first
Ration, $2000. To do this it, is best to enter the monitor with a
<JA LL -151 from BASIC. Hi-Res graphics with
mixed text can be invoked
with the following commands:
*2001<2000.3FFFM <CR>
If you enter 2000:01 <CR>, a single dot appears
at the top left. If you
enter 2000:02 <CR>, the dot moves one
position to the right. 2000:04 A
<CR> moves it right once again. Since seven dots are controlled by one
byte,
you can do this seven times. The value $40 shifts it to the seventh
position. If
you shift the dot one extra time with the value $80, nothing
happens. This
eighth bit position doesn’t activate any pixels.
88
PIXEL POSITIONS BINARY
128 64 32 16 8 4 2 1
You can see from the diagram that 2000:07 turns on the first three pixels and
either 2000:7F (127) or 2000:FF (255) turns on all seven dots. As you shall see
shortly, the eight bit, the high bit or most significant bit, is used for color
con-
trol. While it is not important to use the hi bit in black and
white graphics, it
does explain why there is a WHITE1 and WHITE2, as well as a BLACK 1 and
BLACK2. The difference between WHITE 1 and WHITE2 is whether or not
the hi bit is set.
89
alternated the colors every other column. The leftmost column in any row
always starts with violet if the high bit is off, followed by green in the next col-
umn. Thus, there are 140 violet-green pairs in any row. Since the leftmost col-
umn is column 0, violet pixels are always in even columns, (i.e., 0,2,4 ... 278).
Conversely, green pixels are always in odd columns (i.e. 1,3,5 ... 279).
There is a logical reason for alternating the Apple’s colors from column to
column. The pairs of colors are related to the square wave pulses in respect to
the colorburst reference signal in television receivers. If the Apple sends a
pulse
that corresponds with the peak of the color signal, you get one color; if the
pulse
corresponds to the low point of the color signal, you get the complementary col-
or. The Apple can send a pulse shifted 1/4 cycle (in between). That
generates
two other complementary colors, also in adjacent pairs. I should note that this
arrangement is completely independent of the physical locations of the colored
phosphors on the television picture tube.
V G EGD0[00 00
$2000
G V
$2001
G HI
0TH BYTE (EVEN) 1ST BYTE (ODD)
When the hi-bit is set in any byte, the pixel colors shift to blue (cyan) and
orange.
HI- BIT ON (1)
Ll 0 000 0 0 0
$2000
B B B
$2001
B
90
V/B G/O V/B G/O V/B G/O V/B HI-BIT
• • • $2A GREEN
• • • • • $D5 BLUE
• • • • $AA ORANGE
• • • • • • • OPT $7F or $FF WHITE
1 2 4 8 16 32 64 128 VALUE (DECIMAL)
EVEN BYTE
One of the first things you notice, is that although violet and green pixels can
be mixed in the same byte, violet and orange pixels can't. The hi-bit is either
on or off. You must settle for combinations of violet and green, or blue and
orange.
Applesoft users might recall some of the color problems they have en-
countered in the past. If you were plotting an orange horizontal line starting at
column 0 that extended some 20 pixels across the screen and then attempted to
plot a white line vertically in column 0 that crossed that orange line, the first
few pixels would suddenly turn green. This is because the white color chosen,
WHITE1, turned the hi bit off.
The unfortunate result in choosing seven pixels per byte is that the starting
color of every other byte alternates. The even bytes start with violet, while the
odd bytes start with green. If you were to poke a $55 into location $2000, you
would get a violet line. But if you poked $55 into location $2001 you would get
,
91
In order to correct this effect, the pixels in the second byte would
have to be
shifted over A
one position so that the value of $2 would produce violet, as
shown below. We will continue this discussion later, when we discuss shape
tables.
V G V G V G V G V G V G V G
• —• • — t
T
$55 @ location $2000 $2 A @ $2001
The following table lists the values needed to display solid colored lines:
93
LINE ADDRESS
A formula can be derived from the preceding such that, given any line
number, the starting memory address for that line can be found. If Y is the line
number from 0 to 191, then the section of the screen that the line is in is A =
INT(Y/64). To find which subsection the line is in, use B = INT(D/8), where
D = Y — 64 j|eA. And to find which line Y is on within the subsection, use C
-D-8*B.
94
L
If SN =1 then
memory Location = 8192 + 1024*5 + 128*3 + 40*5 = 13796.
An assembly language implementation of this algorithm is shown below.
4 A DS 1
5 D DS 1
6 B DS 1
7 C DS 1
8 TEMP DS 1
9 SN DS 1
10 WORKL DS 1
11 WORKH DS 1
95
603C: A9 00 43 LDA #$00 ; CLEAR WORKING REGISTER
603E: 8D 07 60 44 STA WORKL
6041: 8D 08 60 45 STA WORKH
6044: AD 06 60 46 LDA SN ;L0AD SCREEN #
6047: 0A 47 ASL ;MULT BY 32
6048: 0A 48 ASL
6049: 0A 49 ASL
604A: 0A 50 ASL
604B: 0A 51 ASL
604C: 8D 08 60 52 STA WORKH ; STORE IN HIGH ORDER
604 F: AD 04 60 53 LDA C ; LOAD C
6052: 0A 54 ASL MULTIPLY BY 4
6053: 0A 55 ASL
6054: 6D 08 60 56 ADC WORKH ADD TO PREVIOUS HI ORDER
6057: 8D 08 60 57 STA WORKH STORE BACK IN HI ORDER
605A: AE 03 60 58 LDX B RECALL B
605D: E8 59 CONT INX
605E: CA 60 DEX
605F: F0 14 61 BEQ SKIPO CHECK FOR B=0
6061: CA 62 DEX
6062: F0 OC 63 BEQ SKIP1 CHECK FOR B=1
6064: CA 64 DEX
6065: A9 01 65 LDA #$01 ; ADD 1 TO HIGH ORDER
6067: 6D 08 60 66 ADC WORKH
606A: 8D 08 60 67 STA WORKH
606D: 4C 5D 60 68 JMP CONT CONTINUE COUNTING
6070: A9 80 69 SKIP1 LDA #$80 LOAD ACC WITH 128
6072: 8D 07 60 70 STA WORKL ADD TO LOW ORDER
6075: AD 01 60 71 SKIPO LDA A RECALL A
6078: OA 72 ASL MULTIPLY BY 32
6079: OA 73 ASL
607A: OA 74 ASL
607 B: OA 75 ASL
607C: OA 76 ASL
607D: 6D 07 60 77 ADC WORKL ADD TO LOW ORDER
6080: 8D 07 60 78 STA WORKL ;
STORE BACK IN LOW ORDER
6083: AD 01 60 79 LDA A RECALL A
6086: OA 80 ASL MULTIPLY BY 8
6087; OA 81 ASL
6088: OA 82 ASL
6089: 6D 07 60 83 ADC WORKL ;
ADD TO LO ORDER
608C: 8D 07 60 84 STA WORKL
608F: AD 08 60 85 LDA WORKH ;
MOVE RESULTS TO ZERO PAGE
6092: 8D OA 60 86 STA HIRESH
6095: AD 07 60 87 LDA WORKL
6098: 85 01 88 STA HIRESL
609A: 60 89 RTS
— END ASSEMBLY—
are doing fast screen animation. Consider the problem of simply plotting a
moving background for your space game. Twenty stars are scattered about
star
the screen. takes 480 instructions just to locate the starting memory locations
It
for each line where the star is to be plotted. This doesn’t even consider the
algorithm needed to decide which pixel in which of 40 bytes on the line needs to
be activated. Clearly, a much faster method must be devised. That method is
called Table Lookup, and it will be thoroughly discussed in the next chapter.
X
The coordinate calculation is much clearer, since the 40 bytes in each line
are stored sequentially in memory. Recalling that there are 7 bits per byte
times 40 bytes per line gives us 280 bits per line.
E = INT (X/7).
F = X - 7*E
For example, if the X coordinate is 152
While the formulas for finding the proper byte and bit positions for the X
direction are rather simple; dividing by seven normally requires a complicated
divide subroutine. Again, speed is a problem. Although I’ll present a complex
subroutine below to accomplish the job, it is much faster and simpler to resort
to Table Lookup algorithms. Still, it is a matter of trade-offs, using speed ver-
sus memory. The tables require 384 bytes plus some code; the subroutine re-
quires only the code.
X
The subroutine below accepts the coordinate as a hexadecimal value in the
X X
A and registers. The register contains the hi byte value. It returns the
horizontal byte offset in the Y register and the bit position within that byte in
the Accumulator. The theory behind the algorithm is rather simple, but the im-
plementation is complicated because to divide the X
position (0-279) by 7 to
obtain the horizontal offset is tedious in machine language, in addition to being
97
complicated by the use of a double precision X value (X values >255 require
two bytes).
BNZ XC0R4
XC0R3 ADC #$8C
XC0R4 SEC
SBC #$46 WHICH QUARTER OF SCREEN
;
BCS XC0R5
ADC #$46
JMP XC0R6 ;SKIP TO 8THS STAGE
XC0R5 PHA ;SAVE ACC
TYA ;GET QUOTIENT
CLC
ADC #$0A ; INCREMENT FOR QUARTER
TAY
PLA
XC0R6 SEC
SBC #$23 ; WHICH 8TH OF SCREEN?
BCS XC0R7
CLC
ADC #$23 ; RESTORE DIVIDEND
JMP XC0R8
XC0R7 PHA
TYA
CLC
ADC #$05 ; INCREMENT FOR EIGHTS
TAY ; RESTORE QUOTIENT
98
PLA
XC0R8 SEC
SBC #$07 ;NOW KEEP SUBTRACTING 7
BCC. XC0R9 UNTIL ZERO IS CROSSED
;
INY
BNZ XC0R8
XC0R9 CLC
ADC #$07 ; RESTORE TO GET REMAINDER
TAX
LDA BITS, X; GET BIT FROM TABLE
RTS
BITS HEX 01 02 04 08 10 20 40 ;BIT POSITION TABLE
99
RASTER GRAPHICS
Programmers talk about Raster Graphics and Vector Graphics on the Apple
II.In reality, due to the nature of the hardware, vector graphics is a misnomer.
Television sets and monitors are raster scanners. Starting at the top of the
screen, they scan one line at a time and turn pixels on or off as needed. True
vector graphics generators have an electron gun that can move in any direc-
tion, so that the beam draws directly between end points.
What ismeant by Vector Graphics on the Apple is that a line consisting of a
string of pixels is drawn by the television’s raster scan. However, raster
graphics differs in that entire bytes representing parts of the shape or line are
placed into Hi-Res memory locations to obtain a Hi-Res picture. You don’t
deal in individual pixels per se, but in manipulating Hi-Res shapes a byte at a
time. The entire shape is plotted as a block. In some literature, it is referred to
as the block shape method.
toss up, but the real advantage in using bit-mapped shape tables is the speed of
implementation. Placing a bit-mapped shape table on the screen involves only
moving bytes of that table stored in memory to the specific screen memory
locations where you want that shape to be drawn. Apple shape tables, on the
other hand, require time-consuming machine language routines to translate
these plotting vectors into a shape on the screen.
100
1st Byte 2nd Byte 3rd Byte
WHITE SHIP
As a example, we shall plot this shape in white, thus ignoring color pro-
first
blems time being. Recall that the color white is produced when adjacent
for the
violet and green pixels, or blue and orange pixels, are activated simultaneous-
ly. To produce a white ship, all of the pixels will be used to form the table.
Some of the readers will question whether the ship is entirely white where bytes
have an odd number of pixels, such as in the first and third lines. If you took a
magnifying glass to the ship’s shape on the TV screen, you would see fringes of
violet or green at the edges of an otherwise white ship. This, of course, would
not matter on a black and white monitor.
For those that have difficulty converting pixel patterns into hexadecimal
values, it is easier if you split the byte’s seven bits into a 4-3 pattern.
Remember that the right most three dots plus its hi bit is the first part of the
byte, or “hi nibble’’, as four bit halves of a byte are called.
101
Encoding the rocket’s first byte, the first row is as follows:
01 00 00
03 00 00
07 00 00
OF 00 00
7F 7F 00
7F IF 07
7F 7F IF
78 7F 7F
Producing a shape table for the same ship in a particular color presents a
more difficult problem. To produce a violet color, all of the green pixels (or
those dots in odd columns) must be suppressed. The revised drawing of the
ship’s shape table is shown below.
102
VIOLET SHIP
(EVEN OFFSET)
1 2 3 4 |
5 6 7 PIXEL POSITION
The complete shape table for the violet colored space ship is:
01 00 00
01 00 00
05 00 00
05 00 00
55 2A 01
55 0A 05
55 2A 15
50 2A 55
103
At this time it would be instructive to actually plot both white and violet
space ships on the Hi-Res screen. This can be done by poking the appropriate
bytes into Hi-Res memory.
When we talked about how the screen was mapped, we showed the starting
addresses for the first eight lines of the screen. The starting addresses of each
line are 1024 bytes or $0400 apart. Enter the monitor with a CALL - 151,
then turn on the Hi-Res graphics page 1 and clear the screen as follows:
* 2000:010000
*2400:03 0000
*2800:07 0000
*2C00:0F 0000
*3000:7F 7F00
*3400:7F 1F07
*3800:7F 7F1F
*3C00:78 7F7F
A white ship appears. Now clear the screen and poke in the shape table of the
violet ship.The violet ship’s table starts at the screen’s far left, which is the 0th
byte or offset into a particular 40 byte row. Since 0,2,4 are considered even
numbers, this is an even offset. As an experiment, poke the violet ship’s values
into an odd offset, one byte over. First, clear the screen, then type the follow-
ing:
*2001:01 0000
*2400:01 0000
*2800:05 0000
*2C00:
etc.
Instead of a violet ship, you get a green space ship. This is because the even
with violet as the first pixel, and the odd offsets start with green.
offsets start
Turning the first pixel on in the odd byte no longer turns on a violet dot, but a
green dot. The solution is to use two sets of shape tables; one for even offsets
and one for odd offsets. Another solution would be to shift the shape’s bit
pattern one bit when going from even to odd offsets; however, this is too time
consuming for fast animation.
104
VGVGVGVGVGVGVGVGVGVGVG VGVGVG
If the original (white) ship's shape is placed so that it begins in an odd offset
(above diagram), and the green -columned pixels (the odd columns) are sup-
pressed, the shape becomes:
00 00 00
02 00 00
02 00 00
0A 00 00
2A 55 00
2A 15 02
2A 55 0A
28 55 2A
The first thing that you notice is that the two plotted shapes (even and odd)
aren't identical. This can be observed by plotting the even offset table beginn-
ing at $2000, and the odd offset table beginning at $2005. You will see that the
odd and the peak of the tail lacks a pixel in row
offset ship is slightly shorter
one. This caused by a lack of symmetry.
is
This problem can be partially remedied by planning the shape so that the
violet column and its adjacent green column are identical in form. For exam-
ple, if an extra pixel were placed in row 1, column 2 of the orginal white shape
of the ship, the peak of the tail would look identical for both the even and odd
offsets.
To reinforce the concept of keeping a shape symmetrical and identical while
moving a byte at a time to the right or
it left, we will consider the following
shape, a green alien:
105
VGVGVGVGVGVGVG HEX
28 01
28 01
EVEN
08 01
OFFSET 22 04
(GREEN) 22 04
22 04
22 04
22 04
GVGVGVGVGVGVGV HEX
54 00
54 00
44 00
11 02
11 02
11 02
11 02
11 02
The even and odd offset shapes have been plotted directly below each other
to show that the shapes are indeed identical, but the lower shape has been
shifted one dot to the left. This effect is inherent in the hardware, because the
colors alternate from column to column. Black and white shapes, however,
don’t require any shifts and, therefore, do not need both odd and even shape
tables.
important to design your shape with pixels of double width. Otherwise,
It is
when you block out the columns of the non-needed color, part of the shape may
be absent in the designated color. While this isn’t likely to happen if you form
shape tables by hand, those ambitious programmers who write a utility to do
this automatically might be surprised when plotting their utility generated
shape tables.
106
What we have discussed so far is fine for simply plotting a shape on the
screen, or even moving a shape left or right one byte or seven pixels at a time.
But what would happen if you wanted to move a shape only one pixel or one
horizontal position to the right? If the shape is moved to the right, it no longer
has the same bit patterns in each byte.
Consider the alien shape plotted entirely in white. Each time it is shifted
right it forms a new bit pattern. By the sixth rightward shift, only the first col-
umn of the shape remains in the first byte. Shift it right once more, and we are
back to the beginning pattern, but one entire byte to the right.
Since the width of a byte is seven pixels, there are seven shifted tables (0-6)
for each of the seven positions. When the shape is shifted the fifth time, the pix-
els extend into a third byte. This requires each of the seven shifted tables to be
three bytes wide.
107
White - 6th Shift
Color shape tables, as you might have guessed, have a similar logic for odd
and even offsets. But, as we shall demonstrate, only seven offset tables are
needed rather than the expected fourteen.
If you take a simple horizontal line, six pixels wide, as a shape and form a
shape table for its green color, you would always have three green pixels lit. As
you shift this line over the seven positions, starting first with the even offset,
then continuing over the odd offset, you will notice a pattern. Every other time
that you shift, the pixel pattern remains the same.
If you were to shift this shape to the right one column for each screen cycle
using 14 shape tables, the shape would remain static for two cycles, then move,
then stay put for two, then move once again. This produces a very jerky mo-
tion. Since the shape tables duplicate themselves in pairs, it would be easier to
use the Oth even, 2nd even, 4th even, 6th even, 1st odd, 3rd odd, and 5th odd
for a total of 7 shifted tables. The 6th odd shape in the above figure, which ap-
pears to be the eighth shape, isn’t. It is actually a duplicate of the Oth even
shape, but beginning at the next even-odd pair.
In summary you have learned how bit-mapped shape tables are formed. In
the next chapter, we shall learn how to draw and animate these shape tables.
EVEN VGVGVGVGVGVGVGVGVGV GVGV
0th 1 D D
1st Si n a i1B
2nd D aB i BB
3rd m a n an B BB
4th u D B BB BB
5th mBf Si B B B L_ j
6th u B B BB BB
ODD —
Oth 9 I B B B B
1st I 1 B B DB B _
2nd S? i B B B
I BB
3rd Bm B BB
4th s Si Bid B B
5th i ID BO OB
Efl
6th L L B B BOB
109
CHAPTER 5
ple procedure once the basic concept understood. The shape table is stored
is
c D E F
±1II
t
Shape Table
in memory
Ill
Y
If our YVERTH table were stored at $6800 and we wanted to find the start-
ing address of line 1 (remember lines are numbered 0-191), we would index
in-
to the table one position and load that value into the Accumulator,
LDA YVERTL, Y will take the value $00 stored in memory location
$68C0 + $01 = $68C 1 then place it in the Accumulator.
,
Eventually, we will want to store the first byte from the shape table into
memory location $2400. This can be done efficiently if the two byte address is
stored sequentially in zero page. Let’s store the lo byte half of the address,
HIRESL, at location $26, and the hi byte half, HIRESH, at location $27 in
zero page:
We can change a particular Hi-Res screen memory location using zero page
by indirect indexed addressing in the form:
112
INDIRECT INDEXED ADDRESSING
STA (HIRESL) ,Y
The final addressing mode that we must consider is Indexed Indirect Ad-
dressing. It is of the form:
LDA (SHPL,X)
It isvery similar to the the Indirect Indexed addressing mode except the in-
dex is added
to the zero page base address before it retrieves the effective ad-
dress. It is primarily used for indexing a table of effective addresses stored in
zero page. But in the form we are going to use it, the X
register is set to 0; thus,
it simply finds a base address:
113
INDEXED INDIRECT ADDRESSING
LDA (SHPL.X)
We can index into the shape table by incrementing the low byte SHPL by
one each time, then store that byte into the next screen position
on a particular
line by incrementing the Y register. This zero
page method is faster than doing
the equivalent code with absolute index addressing,
because two byte addresses
can be handled with fewer instructions, less memory space,
and with fewer
machine cycles.
Obviously, a generalized subroutine must be developed to find
the screen
memory address ( HIRESL & HIRESH
), given a line number and a horizon-
tal displacement. We will call this subroutine
GETADR, short for Get Ad-
dress:
114
HORIZ. OFFSET
0 1 2 3 4 5 6 7 38 39
If you are designing an arcade game, you will probably have several dif-
ferent shapes on the screen at the same time. Perhaps your defending space
ship is paddle-controlled to move vertically but always remains at one par-
ticular horizontal offset; while the aliens, attacking in zig-zag fashion, always
move horizontally from one side of the screen to the other. Keeping track of
each shape’s variables, which are inputted into a generalized drawing routine,
is more easily done if a setup subroutine is incorporated into your program.
This assures that you haven’t forgotten to initialize anything before entering
the drawing subroutine.
115
Only a few variables need to be defined in the setup routine:
the location of
the shape table, the horizontal displacement
on the screen, and the width and
depth of the shape.
The following example is for the space ship that we designed a shape table
A word on the notation used for determining the lo and
for in the last chapter.
^‘addresses for the shape called SHIP is suitable
here. In the TED II + and
BIG MAC assemblers from CALL APPLE, MERLIN
from Southwestern
Data Systems, and TOOL KIT from Apple, LDA
#<SHIP obtains the lower
order address of the table called SHIP. LDA #>
SHIP returns the higher order
addr ss In the LISA assembler from ON-LINE
Systems, LDA
'
Mcurn
ffSHIP loads the ^lower order byte and LDA /SHIP
,
*SHIP SETUP
S SETUP LDA #<SHIP I LOAD LOWER ORDER BYTE OF SHAPE TABLE
STA SHPL
LDA #>SHIP ;L0AD HIGHER ORDER BYTE OF SHAPE TABLE
STA SHPH
LDA #$08
STA DEPTH ; SHAPE IS 8 LINES DEEP
LDA #$09
STA HORIZ ; SHAPE STARTS IN 10TH COLUMN
LDA #$03
STA SLNGH ;SHAPE IS 3 BYTES WIDE
STA TEMP ;STORED HERE ALSO BECAUSE DRAWING
;ROUTINE DECREMENTS SLNGH ON EACH
;LINE AND VARIABLE MUST BE RESTORED
;AT START OF NEXT ROW
RTS
116
INPUT:
SHAPE ADDR: SHPL.SHPH
X POSITION: HORIZ
DEPTH OF SHAPE: DEPTH
WIDTH OF SHAPE: SLNGH
& TEMP
Map of elements in
shape table as they
appear on the screen
0 1 2
3 4 5
6 7 8
9 • • •
J
2
Although the row of the shape can be plotted at any TVERT (0-191)
first
position, if TVERT began at190, the computer would attempt to plot the third
line at TVERT, which would equal 192.
Indexing into the table that far would
most likely produce garbage, as you would index beyond the
end of the table.
You should be always careful that:
TVERT < = 192 - DEPTH
118
A simple test somewhere before the draw subroutine would suffice. Normal-
should be incorporated into a paddle read-routine. This
ly, this will be discuss-
ed further in the next chapter.
XD RAWING SHAPES
Objects that move on
the screen are shifted in position by erasing the object’s
first drawing it at its new position. The simplest method to ac-
position before
complish this is to draw the shape by exclusive-oring it before shifting it.
The exclusive-or instruction (EOR) is primarily used to determine which
bits differ between two operands, but it can also be used to complement
selected Accumulator bits. The way it works is elementary. If neither a par-
ticular memory bit or Accumulator bit is set or their values are zero, the result
is zero. If either one is set, then the result is on. But if both are set, they cancel
1 0 1
1 1 0
If we take a byte on the screen and EOR it with the same byte
0 0 0 0 0 0 0 BLANK SCREEN
EOR 0 110
0 11 WITH SHAPE
0 110 0 11 RESULT IS SHAPE ON SCREEN
EOR 0 110 0 11
119
0 0 0 0 0 0 1 BACKGROUND
EOR 0 10 110
0 WITH SHAPE
0 10 110 1 RESULT ON SCREEN (SHAPE
DISTORTED LAST BIT)
EOR 0 10 110 0 WITH SHAPE
0 0 0 0 0 0 1 GET BACKGROUND BACK
In the above example, an extra pixel in the shape’s last
bit position distorts
the shape drawn on the screen. In the example below, the fourth bit position
becomes a hole in the shape.
0001000 BACKGROUND
EOR 0 10 110 0 WITH SHAPE
0 1 0 0 1 0 0 RESULT ON SCREEN
^ hole here
EOR 0 10 110 0 WITH SHAPE
0 0 0 1 0 0 0 GET BACKGROUND BACK
There are techniques to avoid distorting the shape wherein the background
is likely to interfere during the drawing process. This involves a combination of
EORing and ORing the Hi-Res screen, with the background stored on a sec-
ond Hi-Res screen. An alternate method is to store the screen memory bytes
in
a temporary table equal in size to your shape, while
you draw your shape.
When erasing, you replace theshape with the background stored in your temp-
orary table. This is a little complicated, but it works. An example using this
method is presented at the end of this chapter.
120
.
If the background were as follows, and you ORed it with the shape, the
shape is correct.
0 10 10 10 BACKGROUND PAGE 1
121
2
from the screen, its new position is calculated, then it is redrawn at this new
position. The procedure is outlined below:
A delay has been inserted between the DRAW and the XDRAW to allow
the object to be on the screen longer than it is off. Without the delay, the object
is erased immediately after it is drawn. This does not give the shaped image
sufficienttime to remain on screen during one animation frame. The result is a
badly flickering image. The necessary delay can be a accomplished by a call to
the monitor WAIT subroutine. A hundredth of a second delay is sufficient, but
it could be doubled by changing the value in the Accumulator to $56.
LDA #$3C
JSR $FCA8 ;CALL TO WAIT SUBROUTINE
122
COLOR PROBLEMS WITH HORIZONTAL MOVEMENT
When moved vertically, as with our paddle driven space
colored shapes are
ship, they remain in either the same even or odd offset in which they started.
However, when an object moves horizontally a byte at a time, colors shift, or
alternate, as the shape moves from an even to an odd offset. As we saw in the
last chapter, two different shape tables are needed, one for the even offsets and
another for the odd offsets.
An algorithm must be devised to determine whether the HORIZ offset is
odd or even. You can ascertain if a value is odd or even by right-shifting the
value in the Accumulator so that the low bit enters the carry bit. Since only odd
128 64
—END ASSEMBLY-
123
.
You can easily see in the above example that the pointers to the proper shape
table will be used correctly by our drawing subroutine. You can put a HORIZ
value in location $6000 and single step the code in the monitor. If you don’t
have the single step and trace feature because you have an APPLE II PLUS,
type a 6001G, then check locations $50 and $51 for the values of SHPL, and
SHPH, respectively. Thus, if both the even and odd offset tables are generated
for a violet colored object, the object will always remain violet at any horizontal
screen position 0 -39 if the correct table is used.
Color shifting problems become more intricate if you intend to do very fine
movement or single pixel moves to the left or right, versus coarse movements of
a byte or seven pixels at a time. As we discovered in the last chapter, single
pixel movements in color aren’t effective due to the alternating columns of
complementary colors. The shape tends to lag a cycle, then jumps two pixels.
SCREEN
0 1 2 3 4 5 6 7 8 9 A B C D
V G V G V G V G V G V G V G
SHAPE 0 4 5 6
TABLE
You can
see from the above illustration that our shape stays in the same posi-
tion for two
cycles, then moves. It would be easier to move a shape two pixels
horizontally at a time and use only seven shape tables for a shape instead of
fourteen
The simplest method for keeping track of which offset table is to be used at a
particular horizontal position is through tables. One table (XBASE) is needed
for the horizontal byte for any horizontal screen position, and another (XOFF)
is needed to determine which of the seven offsetted shape table is to be plotted.
The tables take the following form:
124
XBASE HEX 00000000000000
HEX 00010101010101
HEX 02020202020202
HEX 02030303030303
HEX 26262626262626
HEX 26272727272727
XOFF 00 00 01 01 02 02 03 03 04 04 05 05 06 06 00 00
XBASE 00 00 00 00 00 00 00 00 01 01 01 01 0! 01 02 02
1 6
,0
^ 2_^3 B^C^J) E F
X COORDINATE VALUE
125
Y
or XBASE, 2 = $00
or XOFF, 2 = $01
or XBASE, 7 = $00
or XOFF, 7 = $03
SHPLO is a table seven bytes long that contains the lo order byte address of
our shapes. Assuming that there are seven shapes, each containing 24 bytes,
which are stored at $800 in a table called SHAPES, then the table takes the
following form. The HEX pseudo-op in most assemblers informs the assembler
to place hexadecimal data bytes beginning at the location SHPLO. It is
equivalent to directly assigning storage space and filling in the values, as
follows:
126
SHPLO HEX 00 18 30 48 60 78 90
OTH 1ST
SHAPE SHAPE ETC.
The obvious intent of the previous method was to save shape table space. If a
shape were three bytes wide by eight rows deep, seven tables would
require 168
bytes of storage. Requiring the use of all fourteen shapes
would double that.
While 336 bytes isn’t much memory, ten shapes use nearly 3.5K and
if any of
these were to be rotating shapes, much of memory would
be wasted with shape
v
tables.
For those readers who would feel more comfortable calculating
and using all
fourteen shapes in their table, the code is the same but the
tables differ slightly.
The tables are more straight-forward because there are no boundary problems.
HEX 26262626262626
HEX 27272727272727
Replace the last two instructions for the hi byte in our setup routine with the
iollowing:
127
LDA SHPHI X , ; INDEX TO GET HI BYTE OF SHAPE TABLE
STA SHPH ; STORE HI BYTE IN ZERO PAGE
There an alternate way to avoid modifying the XBASE table. You could
is
test for the combination of drawing the third shape while at an odd offset.
At first it seemed plausible that using fourteen shape tables might be the
better method if, say, the gun were in color and its bullets were in B&W. But
since the gun shifted two dots per move, the bullet should do likewise. Besides,
the same drawing routines could be accessed.
background color. The Accumulator is loaded with a value (#$00 for black) and
stored successively in all 8192 screen memory locations. If we had a sixteen-bit
machine and could index all 8192 locations in one gigantic loop, things would
be easy. But it has to be done in 256 byte blocks, or in what is called pages of
memory. The flow chart is shown below.
Remember that the instruction STA (HIRESL),Y uses a two byte address in
zero page
128
BNE CLR2 ;D0 ALL 256 BYTES; AT 256TH BYTE WRAPS
;BACK TO 0 IN Y REGISTER .FALLS THROUGH
INC HIRESH ;D0 NEXT PAGE
LDA HIRESH
CMP #$40 ; FINISHED WITH SCREEN?
BLT CLR1 ;N0, START NEXT 256 BYTE PAGE
RTS ;YES, ALL DONE
129
The screen can be cleared somewhat faster if inline code is used. This is
sometimes desirable if part of a screen must be cleared quickly, but becomes a
very long and tedious routine if every line is to be cleared. A zero is stored in
each screen memory location indicated for a particular column or offset. When
it is finished with that column, it increments to the
next and clears that, also.
Since the code contains the addresses for each line sequentially, precise control
can be achieved over what portion of the screen is to be cleared. Of course,
other colors can be used too. For instance:
. . . ; Other lines
INY
CPY #$28 ; RIGHT SIDE SCREEN?
BEQ END
JMP LOOP ;NEXT COLUMN
END RTS
blue, a #$D5 would be stored in all even offset memory locations, while a #$AA
would be required in all odd offset memory locations. Therefore, we have to
load and store in pairs as we completely fill the screen memory with bytes that
cause only the blue pixels to be activated.
Fortunately, this routine only changes our clear screen routine slightly. You
load a #$D5 for the even offset in the Accumulator, store it at the appropriate
screen location referenced by HIRESL & HIRESH, then increment the index
or pointer in the Y register. Then #$AA is loaded and stored for the odd offset
in the next screen location. The Y register pointer is then incremented again.
Because the BNE test only falls through when the Y register reaches 0 (or
actually 256), this can only happen on an even increment. Therefore, the test
isn’t needed after the first INY, as it can’t happen when Y is an odd value.
130
6008: AO 00 9 CLRl LDY #$00
600A: A9 D5 10 CLR2 LDA #$D5 ;BLUE (EVEN)
600C: 91 26 11 STA (HIRESL).Y
600 E: C8 12 INY
600F: A9 AA 13 LDA #$AA ;BLUE (ODD)
6011: 91 26 14 STA (HIRESL).Y
6013: C8 15 INY
6014: DO F4 16 BNE CLR2
6016: E6 27 17 INC HIRESH ;D0 NEXT PAGE
6018: A5 27 18 LDA HIRESH
601 A: C9 40 19 CMP #$40 ; FINISHED WITH SCREEN’
601C: 90 EA 20 BCC CLRl ;NO, START NEXT 256 BYTE PAGE
601E: 60 21 RTS ;YES! DONE
—END ASSEMBLY-
Hi bit
10 10 10 11 LDA #$D5
0 0 0 0 1 1 1 1 AND #$F0
0 0 0 0 1 0 1 1 RESULT #$D0
above exam ple effectively stripped off the first four pixels
. of the byte
While it is difficult to design a simple case for using the AND
instruction in
selectivedrawing, it is used for “making a hole” in a
background before
URing a colored shape into the hole. It is a tricky procedure for
beginners,
because the complement of an equivalent white shape
operation.
is used during the AND
131
We have the following background and colored shape:
1 1 1 1 0 0 0 1 1 0 1 1 1 1 BACKGROUND
1 0 1 0 1 0 1 0 1 0 0 0 0 0 SHAPE
0 0 0 0 0 0 0 0 0 1 1 1 1 1
0 0 0 0 0 0 ~o 0 0 0 1 1 1 1 RESULTANT HOLE
0 0 0 0 0 0 0 0 0 0 1 1 1 1 BACKGROUND HOLE
1 0 1 0 1 0 1 0 1 0 0 0 0 0 ORA COLORED SHAPE INTO
HOLE
Notice that the background doesn’t interfere with the colored shape but
surrounds it.
The AND instruction is also quite useful in detecting collisions. The pro-
cedure will be discussed in detail in the next chapter.
Thegoal of any programmer is to write fast and efficient code. You can do
thisby taking advantage of the way the screen is mapped and manipulated in
memory. Because it is faster to change a byte, or group of seven pixels rather
than each of the pixels separately, it is easier to have separate shapes for each
movement to the right or left within a byte. It is also easier to move a shape or
object one byte, or seven pixels at a time, horizontally.
Likewise, it is easier during horizontal movement to keep a shape within one
of the 24 - eight row subgroups on the Hi-Res screen. If you adhere to that
restriction, only the memory address of the first line of the shape need be
accessed by tables. Each succeeding line is +$400 in memory at any given
horizontal offset. This method saves many machine cycles by not accessing the
GETADR routine for each and every horizontal line in the shape. If your
shape is three bytes wide by eight lines deep, the drawing algorithm only has to
call the GETADR
routine once. Each successive byte in that offset or column is
plotted at a location incremented by + $400 bytes in screen memory. After all
132
eight bytes have been plotted in that column, screen memory is
decremented
by $2000 bytes to return to the top of the subgroup in order to plot in the next
column. It is a very fast method, one that many games, like Apple Invaders,
uses. If you examine that game, the aliens move slowly
across the screen, each
character being eight lines deep. When they advance closer to
landing, they
jump a full eight lines, to be plotted within the next lower eight
line subgroup.
Although moving 40 aliens may appear slow in the game, there is a
very long
delay loop. Perhaps some readers have seen the modified
version with the
hyperspeed option. The game is quite capable of running ten
times faster.
The subroutine shown below has the following inputs which can be set in
another subroutine called SETUP.
X POSITION IN Y REGISTER
BASE ADDR: HIRESL HIRESH
,
Another way of keeping the code simple is to use only the first 256
horizontal
screen positions. This simplifies horizontal paddle routines
and eliminates the
problem of multi-byte additions to reach screen positions between X =256 and
X = 279. A large number of games like GAMMA GOBLINS and ASTEROID
FIELD have resorted to this technique. The 256 position field need not
be left
justified,but could be centered using a fixed left margin displacement.
133
INTERFACING THE DRAWING ROUTINES TO AN APPLESOFT
PROGRAM
Bit-mapped shape tables, as we have
seen, are much more detailed and more
colorful than APPLE shape tables. There are many programmers not writing a
high speed animated game who would like to use these shape drawing
routines
in an Applesoft program.
If you wanted to control the vertical movement of our
space ship by paddle
control from an Applesoft program, it can be accomplished in
the following°
manner:
The machine language drawing routine and the setup routine require only
the inputs of where to start drawing the ship on the screen. The
ship’s horizon-
tal location is called HORIZ in the machine language
subroutine. The ship can
be positioned horizontally from the far left
(0) to nearly the right hand side of
the screen (37). At 37, the ship’s nose touches the right screen
boundary.
Larger values would produce a very strange wrap-a-round, especially
at 38 and
39. HORIZ is located at $6001 or 24577 decimal. A value has
only to be poked
in at this location to change the ship’s horizontal location.
The ship’s vertical
position is set by TVERT. Its value is trimmed to 0-183
to prevent vertical
wrap-a-round. It is located at $6000 or 24576 decimal. TVERT can be
directly
driven by a paddle routine in the Applesoft program.
HORIZ
TVERT—
The machine language subroutine with code, lookup and shape tables is only
502 bytes long. It starts a$6006 or 24582 decimal. It sets up the drawing
routine before calling it. The drawing routine EOR’s the ship’s shape to the
screen, one byte at a time.
This routine is quite versatile and could handle multiple shapes
from
Applesoft with little modification to the code. The variables for each
shape in
the setup routine; lo and hi bytes of the shape, as well as its
depth and length,
would have to be poked in from Applesoft. The JSR to SSETUP would
be
removed and the new shapes would be added to the end or in a table elsewhere
in memory, in a location where it wouldn’t be overwritten
by your Applesoft
program.
135
H
You must be careful with zero page pointers when interfacing BASIC pro-
grams to machine language programs. Although I’ve been lax in choosing loca-
tions$52 through $58, these conflict with both BASICS. There is a chart in the
Apple II Reference manual which shows which zero page locations are free.
Safe locations for either BASIC are $6 to $9 $1A to $1F $EB to $EF, and
, ,
$F9 to $FF. There are others, but I would consult the manual.
Our small Applesoft interface routine is listed below and the machine
language code follows.
136
A :
33 *GETADR SUBROUTINE
602C B9 5E 60 34 GETADR LDA YVERTL, Y ;LOOK UP LO BYTE OF LINE
602F 18 35 CLC
6030 6D 01 60 36 ADC HORIZ ;ADD DISPLACEMENT INTO LINE
6033 85 1A 37 STA HIRESL
6035 B9 IE 61 38 LDA YVERTH, Y ;LOOK UP HI BYTE OF LINE
6038 85 IB 39 STA HIRESH
603A AD 05 60 40 LDA TEMP
603D 8D 04 60 41 STA SLNGH ; RESTORE VARIABLE
6040 AO 00 42 LDY #$00
6042 60 43 RTS
44 *SHIP SET UP SUBROUTINE
6043 A9 DE 45 SSETUP LDA #<SHIP ;OCATION OF SHIP SHAPE TABLE
6045 85 1C 46 STA SSHPL
6047 A9 61 47 LDA #>SHIP
6049 85 ID 48 STA SSHPH
604B A9 08 49 LDA #$08 ; DEPTH 8 LINES
604D 8D 02 60 50 STA DEPTH
6050 A9 09 51 LDA #$09 ; STARTING HORIZ POSITION
6052 8D 01 60 52 STA HORIZ
6055 A9 03 53 LDA #$03 ; SHIP 3 BYTES WIDE
6057 8D 04 60 54 STA SLNGH
60 5 8D 05 60 55 STA TEMP
605D 60 56 RTS
605E 00 00 00
6061 00 00 00
6064 00 00 57 YVERTL HEX 0000000000000000
6066 80 80 80
6069 80 80 80
606C 80 80 58 HEX 8080808080808080
606E 00 00 00
6071 00 00 00
6074 00 00 59 HEX 0000000000000000
6076 80 80 80
6079 80 80 80
607C 80 80 60 HEX 8080808080808080
607E 00 00 00
6081 00 00 00
6084 00 00 61 HEX 0000000000000000
6086 80 80 80
6089 80 80 80
608C 80 80 62 HEX 8080808080808080
608E 00 00 00
6091 00 00 00
6094 00 00 63 HEX 0000000000000000
6096 80 80 80
6099 80 80 80
609C 80 80 64 HEX 8080808080808080
609E 28 28 28
60A1 28 28 28
60A4 28 28 65 HEX 2828282828282828
60A6 A8 A8 A8
60A9 A8 A8 A8
60AC A8 A8 66 HEX A8A8A8A8A8A8A8A8
60AE 28 28 28
60B1 28 28 28
60B4 28 28 67 HEX 2828282828282828
60B6 A8 A8 A8
60B9 A8 A8 A8
60BC A8 A8 68 HEX A8A8A8A8A8A8A8A8
:
60BE: 28 28 28
60C1: 28 28 28
60C4: 28 28 69 HEX 2828282828282828
60C6: A8 A8 A8
60C9: A8 A8 A8
60CC: A8 A8 70 HEX A8A8A8A8A8A8A8A8
60CE: 28 28 28
60D1: 28 28 28
60D4: 28 28 71 HEX 2828282828282828
60D6: A8 A8 A8
60D9: A8 A8 A8
60DC: A8 A8 72 HEX A8A8A8A8A8A8A8A8
60DE: 50 50 50
60E1: 50 50 50
60E4: 50 50 73 HEX 5050505050505050
60E6: DO DO DO
60E9: DO DO DO
60C: DO DO 74 HEX D0D0D0D0D0D0D0D0
60EE: 50 50 50
60F1 50 50 50
60F4: 50 50 75 HEX 5050505050505050
60F6: DO DO DO
60F9: DO DO DO
60FC: DO DO 76 HEX DODODODODODODODO
60FE: 50 50 50
6101: 50 50 50
6104: 50 50 77 HEX 5050505050505050
6106: DO DO DO
6109: DO DO DO
610C: DO DO 78 HEX DODODODODODODODO
61 OE: 50 50 50
6111: 50 50 50
6114: 50 50 79 HEX 5050505050505050
6116: DO DO DO
6119: DO DO DO
611C: DO DO 80 HEX DODODODODODODODO
81 *
61 IE: 20 24 28
6121: 2C 30 34
6124: 38 X 82 YVERTH HEX 2024282C3034383C
6126: 20 24 28
6129: 2C 30 34
612C: 38 3C 83 HEX 2024282C3034383C
612E: 21 25 29
6131: 2D 31 35
6134: 39 3D 84 HEX 2125292D3135393D
6136: 21 25 29
6139: 2D 31 35
613C: 39 3D 85 HEX 2125292D3135393D
613E: 22 26 2A
6141: 2E 32 36
6144: 3A 3E 86 HEX 22262A2E32363A3E
6146: 22 26 2A
6149: 2E 32 36
614C: 3A 3E 87 HEX 22262 A2E32363A3E
614E: 23 27 2B
6151: 2F 33 37
6154: 3B 3F 88 HEX 23272B2F33373B3F
6156: 23 27 2B
6159: 2F 33 37
138
615C: 3B 3F 89 HEX 23272B2F33373B3F
615E: 20 24 28
6161: 2C 30 34
6164: 38 X 90 HEX 2024282C3034383C
6166: 20 24 28
6169: 2C 30 34
616C: 38 X 91 HEX 2024282C3034383C
616E: 21 25 29
6171: 2D 31 35
6174: 39 3D 92 HEX 2125292D3135393D
6176: 21 25 29
6179: 2D 31 35
617C: 39 3D 93 HEX 2125292D3135393D
617E: 22 26 2A
6181: 2E 32 36
6184: 3A 3E 94 HEX 22262A2E32363A3E
6186: 22 26 2A
6189: 2E 32 36
618C: 3A 3E 95 HEX 22262A2E32363A3E
618E: 23 27 2B
6191: 2F 33 37
6194: 3B 3F 96 HEX 23272B2F33373B3F
6196: 23 27 2B
6199: 2F 33 37
619C: 3B 3F 97 HEX 23272B2F33373B3F
619E: 20 24 28
61A1: 2C 30 34
61A4: 38 X 98 HEX 2024282C3034383
61A6: 20 24 28
61A9: 2C 30 34
61AC: 38 X 99 HEX 2024282C3034383C
61AE: 21 25 29
61B1: 2D 31 35
61B4: 39 3D 100 HEX 2125292D3135393D
61B6: 21 25 29
61B9: 2D 31 35
61BC: 39 3D 101 HEX 2125292D3135393D
61BE: 22 26 2A
61C1: 2E 32 36
61C4: 3A 3E 102 HEX 22262A2E32363A3E
61C6: 22 26 2A
61C9: 2E 32 36
6 ICC: 3A 3E 103 HEX 22262A2E32363A3E
61CE: 23 27 2B
61D1 : 2F 33 37
61D4: 3B 3F 104 HEX 23272B2F33373B3F
61D6: -23 27 2B
61D9: 2F 33 37
61DC: 3B 3F 105 HEX 23272B2F33373B3F
61DE: 80 00 00
61E1 : 82 00 00
61E4: 82 00 106 SHIP HEX 8000008200008200
61E6: 00 8A 00
61E9: 00 AA D5
61EC: 80 AA 107 HEX 008A0000AAD580AA
61EE: 95 82 AA
61F1: D5 8A A8
61F4: D5 AA 108 HEX 9582AAD58AA8D5AA
140
IS DEPTH = 0?
H 1 Y
142
Y
144
6163: 2D 31 35
6166: 39 3D 108 HEX 2125292D3135393D
6168: 21 25 29
616B: 2D 31 35
616E: 39 3D 109 HEX 2125292D3135393D
6170: 22 26 2A
6173: 2E 32 36
6176: 3A 3E 110 HEX 22262 A2E32363A3E
6178: 22 26 2A
617B: 2E 32 36
617E: 3A 3E 111 HEX 22262A2E32363A3E
6180: 23 27 2B
6183: 2F 33 37
6186: 3B 3F 112 HEX 23272B2F33373B3F
6188: 23 27 2B
618B: 2F 33 37
618E: 3B 3F 113 HEX 23272B2F33373B3F
6190: 20 24 28
6193: 2C 30 34
6196: 38 X 114 HEX 2024282C3034383C
6198: 20 24 28
619B: 2C 30 34
619E: 38 X 115 HEX 2024282C3034383C
61A0: 21 25 29
61A3: 2D 31 35
61A6: 39 3D 116 HEX 2125292D3135393D
61A8: 21 25 29
61AB: 2D 31 35
61AE: 39 3D 117 HEX 21 25292 D3135393D
61B0: 22 26 2A
61B3: 2E 32 36
61B6: 3A 3E 118 HEX 22262A2E32363A3E
61B8: 22 26 2A
6 IBB: 2E 32 36
61BE: 3A 3E 119 HEX 22262A2E32363A3E
61C0: 23 27 2B
61C3: 2F 33 37
61C6: 3B 3F 120 HEX 23272B2F33373B3F
61C8: 23 27 2B
61CB: 2F 33 37
61CE: 3B 3F 121 HEX 23272B2F33373B3F
61D0: 20 24 28
61D3: 2C 30 34
61D6: 38 X 122 HEX 2024282C3034383C
61 D8: 20 24 28
61DB: 2C 30 34
6 IDE: 38 X 123 HEX 2024282C3034383C
61E0: 21 25 29
61E3: 2D 31 35
61E6: 39 3D 124 HEX 2125292D3135393D
61E8: 21 25 29
61EB: 2D 31 35
61EE: 39 3D 125 HEX 2125292D3135393D
61F0: 22 26 2A
61F3: 2E 32 36
61F6: 3A 3E 126 HEX 22262A2E32363A3E
61F8: 22 26 2A
61FB: 2E 32 36
61FE: 3A 3E 127 HEX 22262A2E32363A3E
6200: 23 27 2B
6203; 2F 33 37
6206; 3B 3F 128 HEX 23272B2F33373B3F
6208; 23 27 2B
620B; 2F 33 37
620E; 3B 3F 129 HEX 23272B2F33373B3F
6210: 80 00 00
6213: 82 00 00
6216: 82 00 130 SHIP HEX 8000008200008200
6218: 00 8A 00
621B: 00 A D5
621E: 80 AA 131 HEX 008A0000AAD580AA
6220: 95 82 AA
6223: D5 8A A8
6226: D5 AA 132 HEX 9582AAD58AA8D5A/
133 BACK DS 24
—END ASSEMBLY-
ERRORS: 0
576 BYTES
CHAPTER 6
ARCADE GRAPHICS
INTRODUCTION
Arcade game animation uses many of the graphics techniques introduced in
the previous chapter. Their requirement for high frame rates, coupled with
smooth yet detailed animation, necessitates raster shape tables using their
inherent high speed drawing routines. Yet, to produce quality games requires
game designers to pay particular attention to the smallest programming
details.
The fundamentals of any arcade game, in the broad sense, are easy to grasp.
It isthe details that elude the average programmer. While it is obvious that any
object that can be moved must also be controlled, it isn’t obvious how that
motion is programmed in machine language.
This chapter and the next will discuss the three major types of arcade games
and the algorithms that make them work. First, there is the Invaders-type
game, wherein a movable gun in the horizontal plane defends against attackers
from above. Second, there is the fully maneuverable spaceship from the Space
War and Asteroid-type games. These ships fly or float freely in both the and X
Y axis. Finally, there are the games that simulate horizontal or vertical motion
by scrolling the background. These games have ships that are usually
maneuverable in the non -scrolling axis only. Apple games like Pegasus II and
Phantoms Five fall into this category.
There are numerous details to consider in game design, such as paddle con-
trol, bullets Firing and bombs dropping. A game must also include a scorekeep-
ing device for determining a winner, and an explosion subroutine for ridding
the screen of losers. And, sometimes, page-flipping techniques are needed to
smooth the flickering effects of complex animation. It is hoped that by my first
flow charting these routines, then presenting and explaining commented
machine language subroutines, you will be able to use these techniques in your
own games. And for those who need an example of a working game, many of
these routines are combined in a functioning yet unfinished arcade game.
PADDLE ROUTINE
We previously controlled our moveable plane through an Applesoft inter-
face. Whileit is easy to access the paddle routine directly from machine
147
“ The Hi-Res screen’s vertical axis ranges only from 0-191. Paddle
the other hand, range from 0-255. An attempt to
The
following paddle subroutine prevents instantaneous
jumps of the
plane s by rapid paddle movement. It accomplishes this by adjusting
position
VERT, the ship s vertical position, rather than storing the paddle position
(PDL) directly as VERT . This adjustment is based on the relationship of PDL
to VERT.
There is a certain maximum paddle-driven movement that is
desirable in
any game. If the movement, in this case, is set to ten units per
frame and the
animation was twenty frames per second, then the plane will
require approx-
imately one second to move from top to bottom. Slower
movement factors will
take more The speed constant is subjective, and is determined by what
time.
you think a suitable and a controllable speed.
is
148
VERT = PDL so that the resulting VERT position is exactly that of the
paddle value. The same type of test performed if PDL is greater than
is VERT,
and VERT is homing in on the paddle value from a higher value.
Rather than proceed with the development of what is to become a very com-
plex game using our ship, I would like to digress to another paddle routine.
This one controls a moveable gun turret in the horizontal plane. It is used quite
frequently in most Invaders-type games.
The screen range on the horizontal axis is 0-279. Our paddle range is, as
usual, limited to 0-255. In Applesoft, it was easy to multiply by 1.1 to obtain
149
the proper range. However, in machine language the multiplication
and divi-
sion routines are too complex, and require numerous
machine cycles to
execute. Besides, they return the result as two byte values, which
means that
all of our adding and subtracting would require
two byte operations.
It is much easier to accept the fact that the right 10% of the screen is
unusable or can’t be reached by paddles, unless we center
the screen by
adjusting the horizontal offsets. Actually, if our gun is large,
we can use part of
this space without adjustment. Take the gun turret
illustrated below. It is 14
pixels, or two bytes wide.
PIXEL I
GAP -H
—
|-6 14 PIXELS -*\
When the paddle value is at zero, the gun plots between 0-13 on the horizon-
tal axis, and when the paddle is at 255, the gun plots between 255 and 269.
That leaves only a ten pixel gap, which is hardly noticeable.
In order to use the paddle routine already developed for the vertical
axis, it
must be modified. The paddle s full range is needed, so clipping is removed
just after the paddle is read. Instead, we must place a test in the code
to prevent
it from incrementing past $FF
( 255 decimal as it homes in on the actual
)
paddle value. In this case, we have slowed the turret’s movement to five units
per animation cycle. Again, the value of five is based on the frame rate, and
what appears to be a reasonable movement rate on the screen.
After testing the various possibilites of whether the paddle is set to a value
greater than PHORIZ (the horizontal position) you must prevent it from
adding five to PHORIZ if PHORIZ > 250. In this case, the PADDLE value is
251 to 255, and PHORIZ is set equal to the PADDLE.
39 *READ PADDLE #1
6028: A2 01 40 RPDL LDX #$01
602A: 20 IE FB 41 JSR PREAD
602D: 8C 07 60 42 SKIPP STY PDL
6030: 98 43 TYA
6031: CD OB 60 44 CMP PHORIZ ;PADDLE<HORIZ POS THEN SUBTRACT 5
6034: BO IE 45 BGE PADDLE3
6036: AD OB 60 46 LDA PHORIZ
6039: 38 47 SEC
603A: E9 05 48 SBC #$05
603C: BO 08 49 BGE PADDLE1 jMAKE SURE =>0
603E: A9 00 50 LDA #$00
6040: 8D OB 60 51 STA PHORIZ
6043: 8D OC 60 52 STA TPHORIZ
6046: CD 07 60 53 PADDLE CMP PDL ; DON'T WANT TO GO PAST PADDLE POS
6049: BO 03 54 BGE PADDLE2
604 B: AD 07 60 55 LDA PDL
604E: 8D OB 60 56 PADDLE2 STA PHORIZ
6051: 4C 71 60 57 JMP PADDLE6
6054: CD OB 60 58 PADDLE3 CMP PHORIZ ; PADDLE>PHORIZ POS THEN ADD 5
6057: FO 12 59 BEQ PADDLE4
6059: AD OB 60 60 LDA PHORIZ
605C: C9 FA 61 CMP #$FA j IS PH0RIZ>250
605E: BO OB 62 BGE PADDLE4
6060: AD OB 60 63 LDA PHORIZ
6063: 18 64 CLC
151
)
PADDLE CROSSTALK
Many readers will attempt at some future time to combine two paddle read
routines together to control a ship, or a gun crosshair with a joystick. They will
be dismayed to learn that the paddle values don’t read properly. This is called
paddle crosstalk.
When a paddle trigger is strobed, all the timers start. If the first paddle that
you read has a low value, it will return quickly from PREAD with a paddle
value. But the timers are still counting. If you immediately call PREAD again,
the timers aren’t restarted at zero, so that you may see a value from the first
paddle trigger instead of the second. The solution is to wait a sufficient time
before reading the second paddle. How long is sufficient? Not more than 255
machine cycles is needed. It is best to space your paddle reads with other code
in between.
Analternate solution is to read two paddles simultaneously by triggering
both strobes (or timers) together. Since the code takes longer to execute while
the paddle timers count down, the full paddle range can not be expected. The
code shown below is suitable for joystick control, but only has a range of 40 to
127. Clever programmers will either adjust these values or offset them to suit
their needs.
7 * ! i
8 * ! i
9 * ! i
10 * ! i
11 * ! i
12 *126,47 44,47
13 *
14 0RG $300
15 ZERO DS 1
16 ONE DS 1
0302: A2 00 17 LDX #$00
0304: 8E 01 03 18 STX ONE
0307: 8E 00 03 19 STX ZERO
030A: A2 7F 20 LDX #$7F
030C: AD 70 CO 21 LDA $C070 ; STARTS BOTH TIMERS
152
030F: AD 64 CO 22 LOOP LDA $C064 ; PADDLE 0 TIMER
0312: 29 80 23 AND #$80
0314: 0A 24 ASL
0315: 2A 25 R0L
0316: 6D 00 03 26 ADC ZERO
0319: 8D 00 03 27 STA ZERO
031C: AD 65 CO 28 LDA $C065 ; PADDLE 1 TIMER
031F: 29 80 29 AND #$80
0321: 0A 30 ASL
0322: 2A 31 R0L
0323: 6D 01 03 32 ADC ONE
0326: 8D 01 03 33 STA ONE
0329: CA 34 DEX
03 2A: DO E3 35 BNE LOOP
032C: A9 7F 36 LDA #$7F
03 2E: 38 37 SEC
032F: ED 00 03 38 SBC ZERO
0332: 8D 00 03 39 STA ZERO
0335: A9 7F 40 LDA #$7F
0337: 38 41 SEC
0338: ED 01 03 42 SBC ONE
033B: 8D 01 03 43 STA ONE
033E: 60 44 RTS
— END ASSEMBLY-
153
DROPPING BOMBS AND SHOOTING BULLETS
Simulating a bomb drop realistically involves some knowledge of how a body
in motion reacts to a constant force; in this case, gravity. The physics of a body
in motion requires advanced mathematics, mainly calculus. But calculus
actually involves the summation of many bits and pieces of a body’s velocity
and acceleration to determine the actual distance an object travels. The com-
puter, fortunately, automatically divides our time frame into small units,
or
animation frames, wherein the force vectors can be displayed as direction vec-
tors.
Let s examine an object
in simple linear motion. The object is initially at
rest. It isthen given a horizontal velocity of one unit to the left. Thus, the
velocity is +1 unit/time frame. During each animation frame, the object
moves + 1 units to the right.
An and its magnitude is represented by a line seg-
object’s direction of travel
ment called a vector. An
object’s velocity vector always points in the direction
of travel. Our object shown below has a velocity of + 1 units/ time frame, so
that the velocity is pointing to the right. Since the velocity vector is to the right,
the object moves to the right.
2 3 4 5 6
Frame #4 FRAMES
154
1
2 2 3
3 3 6 VX = VX +
4 4 10 X = X + VX
5 5 15
6 6 21
VELOCITY
5 10 15 20
X POSITION
155
This driving force that speeds up our object is called acceleration ( V = V +
A ) . The acceleration in the previous example was + 1 units/frame. The
acceleration in space games is a rocket’s thrust and, for falling bombs, it is
gravity. To simplify things, when working with a falling bomb, we will neglect
variables like wind resistance, and assume that the bomb has a small forward
velocity equal to that of the plane. The plot of the trajectory of a falling bomb
is
shown below. The trajectory forms a curve that is often called “parabolic”.
You should note that although the velocity in the X
direction remains constant,
the velocity in the Y direction (VY) grows larger with time. It grows larger
because gravity accelerates the object constantly in the downward direction.
This same effect can be observed by dropping a ball from the second or third
story of a building. At first, the ball falls slowly, but then it begins falling faster.
Observers at ground level will note an accelerated moving ball just before it
bounces.
The velocity of the falling bomb has two components represented by velocity
vectors - one in the X
direction and the other in the Y direction. These two
velocity vectors can be graphically added together to form a total velocity
vector. The summation of the two vectors determines the resultant direction of
an object s motion for each animation frame. Since the VY vector grows larger
with each frame, the total velocity vector begins to point downward. Eventual-
ly, the bomb will be falling almost straight down. Thus:
VX = CONST
VY = VY + GRAVITY
156
If you are programming the motion of a falling bomb, the equations or
algorithm are as follows.
VX = CONST X - X + VX
VY = VY + GRAVITY Y = Y + VY
For all practical purposes, a gravity constant of 3 to 5 will produce realistic
curves on the Apple’s Hi-Res screen, but this, again, like our choice of a con-
stant for paddle movement, is dependent on factors like the animation frame
rate and the scale of other objects on the screen.
The trajectories of bullets and artillery shells are another useful feature in
games. Bullets in games like Apple Invaders and Galaxian travel straight up-
wards on the screen.
X = 0
VY = NEGATIVE CONSTANT
so that
X = CONST
Y = Y +( -VY)
Bullets that travel diagonally, but at a constant velocity in
the direction shown, have a VY
that is negative and a VX
that is positive. The velocity vector determines the direc-
tion of travel.
VX = POSITIVE CONSTANT
VY = NEGATIVE CONSTANT
so that
X = X + VX
Y = Y +( - VY)
Our bullet is fired from a movable gun base at the bottom of the screen. Its
location, in relation to the gun barrel, is shown in the design at the right. The
bullet’s shape is eight units tall by four units
wide and, like the gun base, uses seven
BVERT different offset shape tables. Although the
bullet is white, it is easier to use the same draw-
8 Lines ing routine to move it in conjunction with the
Bullet gun base.
PHORIZ,
157
The bullet’s horizontal velocity VX
= 0 and its vertical velocity is VY =
is
-8. Thus, X = X + or X
= const and Y = Y - VY. The bullet’s
VX, ,
screen line while it plots the bullet. The value is also used to index into the
XOFF table, which in turn acts as an index to the proper shape table when the
bullet is plotted on the screen.
The bullet travels further toward the top of the screen during each screen
frame. Notice that it travels exactly eight lines upwards per cycle. This allows
us to begin drawing at the start of one of the 24 eight line subgroups.
The code also prevents you from firing more than one bullet at a time. When
a bullet is on the screen, a flag called BON
(short for “bullet on”) is set to pre-
vent you from firing again. There is more than a casual reason for doing this. If
more than one bullet were fired at one time, you would need to keep track of
each bullet’s position separately. While two bullets might be manageable, a
large number would involve storing the position values into tables, then access-
ing them in sequence during the bullet setup routine.
A flow chart of the algorithm and the code is shown below.
158
; YY X
The gravity vector tends to bend our velocity vector so that it no longer
travels at 45 degree angle. By the time our bullet reaches the peak of
its initial
its flight, the gravity vector has incrementally subtracted our vertical velocity
vector to zero. At that point, there is only the horizontal velocity component.
Since gravity affects our bullet at every time increment, it soon causes our
velocity vector to have a negative vertical component. The bullet then begins to
fall.
VY = VY + -G) (
Y = Y + VY
VX = CONST X = X + VX
Once you understand the vector concept of how an object falls, the bomb
drop routine becomes elementary. The bomb must fall from the center of our
plane because, by design, bomb bays are located at the plane’s center of gravi-
ty. Since the tail of our plane is the vertical paddle position (VERT) and the
plane is eight lines deep, the first available plotting position beneath the plane
is at (VERT + 9).
160
The bomb can be defined by the following shape table.
07
?E
07
OFFSET
9 A B C D E
ACCEL = 5
VY = VY + ACCEL
YB = YB + VY
1
CYCLE VY YB OFFSET
1 0 0 A
2 5 5 B
3 10 15 C
4 15 30 D
5 20 50 E
6 25 75 F
To simplify the graphics, it is easier to move the bomb horizontally one byte
(or seven pixels) at a time. Consequently, with the bomb plotted
in white, the
even - odd offset color problems vanish. The flowchart and code follow.
161
bomb
162
11 )
163
)Y
164
occupied is now
off screen. Arcade games like Pegasus II involve constant
terrain scrollingfrom right to left as your spaceship moves further into the
enemy’s territory. This type of animation will be discussed in the following
chapter.
The sequence of events in an Invaders game is diagrammed above. It is
typical of most games. While we aren’t going to develop the entire game, we
will integrate the paddle and bullet firing routines previously outlined in this
chapter with the color drawing routines discussed in Chapter 5.
Using a good assembler makes the job of writing a program relatively easy.
All the tediousmechanical problems like relative addressing for branch instruc-
tions, references to variable storage, and memory storage assignments are
handled automatically. In fact, the assembler is so adept at calculating
addresses that I often use it for generating internal reference tables to the loca-
tions of my shapes.
166
Normally, itgood programming practice to put shape tables in some
is
DB SHAPES + $90
The assembler looks up the lo byte address for each of our shapes according
to the address that we give to it. Each shape is 24 (or $18) bytes long. This
accounts for the reason each succeeding shape address increases by $18. Notice
on the left of the above listing that the actual byte value is placed into our table
for each shape. ( SHPLO 16 2E 46 5E ...). This corresponds exactly to the
lo
byte values in our floating shape table. I’ll extend a word of caution about
using this method. Shape tables must not cross page boundaries, because the hi
byte, which is stored at SHPH in our drawing routine, must be kept constant.
Sometimes, extra space needs to be allocated in the code just before the shape
table for correcting this problem. The DS pseudo-op code to Define Storage
can be used.
The lo and hi bytes for a particular shape are determined by the following
code:
If you were to choose, instead, to put the shape table at $7000 in memory,
you would use a table called SHPADR to index to the proper shape. Each posi-
tion in the table would reference the lo byte of a shape in the shape table.
SHPADR HEX 00 18 30 48 60 78 90
167
The setup routine is modified as follows:
168
1 *CODE FOR PART OF INVADERS GAME
2 ORG $6000
6000: 4C 17 60 3 JMP PROG ; JUMP TO START OF CODE
4 COUNT DS 1
5 INDEX DS 1
6 PADDLEL DS 1
7 PADDLEH DS 1
8 PDL DS 1
9 TEMP DS 1
10 VERT DS 1
11 TVERT DS 1
12 PHORIZ DS 1
13 TPHORIZ DS 1
14 BHORIZ DS 1
15 BPHORIZ DS 1
16 HORIZ DS 1
17 OBJ DS 1
18 LNGH DS 1
19 DEPTH DS 1
20 SLNGH DS 1
21 SHOT DS 1
22 BVERT DS 1
23 BON DS 1
24 HIRESL EQU $26
25 HIRESH EQU HIRESL+$1
26 SHPL EQU $50
27 SHPH EQU SHPL+$1
28 SSHPL EQU $52
29 SSHPH EQU $53
30 STESTL EQU $54
31 STESTH EQU STESTL+$1
32 PREAD EQU $FB1E
6017: AD 50 CO 33 PROG LDA $C050
601A: AD 52 CO 34 LDA $C052
601D: AD 57 CO 35 LDA $C057
6020: 20 8E 60 36 JSR CLRSCR
6023: A9 00 37 LDA #$00
6025: 8D 16 60 38 STA BON
39 *READ PADDLE #1
6028: A2 01 40 RPDL LDX #$01
602A: 20 IE FB 41 JSR PREAD
602D: 8C 07 60 42 SKIPP STY PDL
6030: 98 43 TYA
6031: CD OB 60 44 CMP PHORIZ ;PADDLE<HORIZ POS THEN SUBTRACT 5
6034: BO IE 45 BGE PADDLE3
6036: AD OB 60 46 LDA PHORIZ
6039: 38 47 SEC
603A: E9 05 48 SBC #$05
603C: BO 08 49 BGE PADDLE1 ;MAKE SURE =>0
603E: A9 00 50 LDA #$00
6040: 8D OB 60 51 STA PHORIZ
6043: 8D OC 60 52 STA TPHORIZ
6046: CD 07 60 53 PADDLE1 CMP PDL ; DON'T WANT TO GO PAST PADDLE POS
6049: BO 03 54 BGE PADDLE2
604B: AD 07 60 55 LDA PDL
604E: 8D OB 60 56 PADDLE2 STA PHORIZ
6051: 4C 71 60 57 JMP PADDLE6
6054: CD OB 60 58 PADDLE3 CMP PHORIZ ; PADDLE>PHORIZ POS THEN ADD 5
6057: FO 12 59 BEQ PADDLE4
6059: AD OB 60 60 LDA PHORIZ
:: YY
170
: YY Y
171
Y YY XX
614B: BC
94 65 181 LDY SHPADR , ;X IS 0-6
614E: B9
9B 65 182 LDA SHPLO , ; INDEX TO GET LO BYTE SHAPE TABLE
6151: 85
50 183 STA SHPL
6153: A9
66 184 LDA #>SHAPES ;GET HI BYTE OF SHAPE
6155: 85
51 185 STA SHPH
6157: A9
03 186 LDA #$03
6159: 8D
13 60 187 STA SLNGH
615C: 8D
08 60 188 STA TEMP
61 5F: 08
A9 189 LDA #$08
6161: 8D
12 60 190 STA DEPTH
6164: BO
A9 191 Lfe #$B0
6166: 09 60 192
8D STA VERT
6169: 8D OA 60 193 STA TVERT
616C: 60 194 RTS
195 * BULLET SETUP
616D: AD OD 60 196 BSETUP LDA BHORIZ
6170: 8D OF 60 197 STA HORIZ
6173: AC OE 60 198 LDY BPHORIZ
6176: BE 7C 64 199 LDX XOFF, ;INDEX TO WHICH SHAPE TABLE
6179: BD A2 65 200 LDA BSHPLO, ;INDEX TO GET LO BYTE OF BOMB -
201 ; SHAPE TABLE
617C: 85 50 202 STA SHPL
617E: A9 67 203 LDA #>BSHAPES ;GET HI BYTE OF SHAPE
6180: 85 51 204 STA SHPH
6182: A9 02 205 LDA #$02
6184: 8D 13 60 206 STA SLNGH
6187: 8D 08 60 207 STA TEMP
618A: A9 07 208 LDA #$07 SHAPE 7 LINES DEEP
;
172
::
173
6274: 50 50 50
6277: 50 50 50
627A: 50 50 266 HEX 5050505050505050
627C: DO DO DO
627F: DO DO DO
6282: DO DO 267 HEX DODODODODODODODO
6284: 50 50 50
6287: 50 50 50
628A: 50 50 268 HEX 5050505050505050
628C: DO DO DO
628F: DO DO DO
6292: DO DO 269 HEX DODODODODODODODO
6294: 50 50 50
6297: 50 50 50
629A: 50 50 270 HEX 5050505050505050
629C: DO DO DO
629F: DO DO DO
62A2: DO DO 271 HEX DODODODODODODODO
272 *
62A4: 20 24 28
62A7: 2C 30 34
62AA: 38 3C 273 YVERTH HEX 2024282C3034383C
62AC: 20 24 28
62AF: 2C 30 34
62B2: 38 3C 274 HEX 2024282C3034383C
62B4: 21 25 29
62B7: 2D 31 35
62BA: 39 3D 275 HEX 2125292D3135393D
62BC: 21 25 29
62BF: 2D 31 35
62C2: 39 3D 276 HEX 2125292D3135393D
62C4: 22 26 2A
62C7: 2E 32 36
62CA: 3A 3E 277 HEX 22262A2E32363A3E
62CC: 22 26 2A
62CF: 2E 32 36
62D2: 3A 3E 278 HEX 22262A2E32363A3E
62D4: 23 27 2B
62D7: 2F 33 37
62DA: 3B 3F 279 HEX 23272B2F33373B3F
62DC: 23 27 2B
62DF: 2F 33 37
62E2: 3B 3F 280 HEX 23272B2F33373B3F
62E4: 20 24 28
62E7 : 2C 30 34
62EA: 38 3C 281 HEX 2024282C3034383C
62EC: 20 24 28
62EF: 2C 30 34
62F2: 38 X 282 HEX 2024282C3034383C
62F4: 21 25 29
62F7: 2D 31 35
62FA: 39 3D 283 HEX 2125292D3135393D
62FC: 21 25 29
62FF: 2D 31 35
6302: 39 3D 284 HEX 2125292D3135393D
6304: 22 26 2A
6307: 2E 32 36
630A: 3A 3E 285 HEX 22262A2E32363A3E
630C: 22 26 2A
630F : 2E 32 36
174
6312: 3A 3E 286 HEX 22262A2E32363A3E
6314: 23 27 2B
6317: 2F 33 37
631A: 3B 3F 287 HEX 23272B2F33373B3F
631C: 23 27 2B
631F: 2F 33 37
6322: 3B 3F 288 HEX 23272B2F33373B3F
6324: 20 24 28
6327: 2C 30 34
632A: 38 3C 289 HEX 2024282C3034383C
632C: 20 24 28
632F: 2C 30 34
.6332: 38 3C 290 HEX 2024282C3034383C
6334: 21 25 29
6337: 2D 31 35
633A: 39 3D 291 HEX 2125292D3135393D
633C: 21 25 29
633F: 2D 31 35
6342: 39 3D 292 HEX 2125292D3135393D
6344: 22 26 2A
6347; 2E 32 36
6 34 A: 3A 3E 293 HEX 22262A2E32363A3E
634C: 22 26 2A
634F: 2E 32 36
6352: 3A 3E 294 HEX 22262A2E32363A3E
6354: 23 27 2B
6357: 2F 33 37
635A: 3B 3F 295 HEX 23272B2F33373B3F
635C: 23 27 2B
635F: 2F 33 37
6362: 3B 3F 296 HEX 23272B2F33373B3F
6364: 00 00 00
6367: 00 00 00
6 36 A: 00 297 XBASE HEX 00000000000000
636B: 00 01 01
636E: 01 01 01
6371: 01 298 HEX 00010101010101
6372: 02 02 02
6375: 02 02 02
6378: 02 299 HEX 02020202020202
6379: 02 03 03
637C: 03 03 03
637F: 03 300 HEX 02030303030303
6380: 04 04 04
6383: 04 04 04
6386: 04 301 HEX 04040404040404
6387: 04 05 05
638A: 05 05 05
638D: 05 302 HEX 04050505050505
638E: 06 06 06
6391: 06 06 06
6394: 06 303 HEX 06060606060606
6395: 06 07 07
6398: 07 07 07
639B: 07 304 HEX 06070707070707
639C: 08 08 08
639F: 08 08 08
63A2 : 08 305 HEX 08080808080808
63A3: 08 09 09
63A6: 09 09 09
175
63A9: 09 306 HEX 08090909090909
63AA: 0A OA OA
63AD: OA OA OA
63B0: OA 307 HEX OAOAOAOAOAOAOA
63B1: OA OB OB
63B4: OB OB OB
63B7: OB 308 HEX OAOBOBOBOBOBOB
63B8: OC OC OC
63BB: OC OC OC
63BE: OC 309 HEX OCOCOCOCOCOCOC
63BF: OC OD OD
63C2: OD OD OD
63C5: OD 310 HEX OCODODODODODOD
63C6: OE OE OE
63C9: OE OE OE
63CC: OE 311 HEX OEOEOEOEOEOEOE
63CD: OE OF OF
63D0: OF OF OF
63D3: OF 312 HEX OEOFOFOFOFOFOF
63D4: 10 10 10
63D7: 10 10 10
63DA: 10 313 HEX 10101010101010
63DB: 10 11 11
63DE: 11 11 11
63E1 : 11 314 HEX 10111111111111
63E2: 12 12 12
63E5: 12 12 12
63E8: 12 315 HEX 12121212121212
63F,9: 12 13 13
63EC: 13 13 13
63EF: 13 316 HEX 12131313131313
63F0: 14 14 14
63F3: 14 14 14
63F6: 14 317 HEX 14141414141414
63F7 : 14 15 15
63FA: 15 15 15
63FD: 15 318 HEX 14151515151515
63FE: 16 16 16
6401: 16 16 16
6404: 16 319 HEX 16161616161616
6405: 16 17 17
6408: 17 17 17
640B: 17 320 HEX 16171717171717
64 OC: 18 18 18
640F: 18 18 18
6412: 18 321 HEX 18181818181818
6413: 18 19 19
6416: 19 19 19
6419: 19 322 HEX 18191919191919
641A: 1A 1A 1A
641D: 1A 1A 1A
6420: 1A 323 HEX 1A1A1A1A1A1A1A
6421: 1A IB IB
6424: IB IB IB
6427: IB 324 HEX 1A1B1B1B1B1B1B
6428: 1C 1C 1C
642B: 1C 1C 1C
642E: 1C 325 HEX 1C1C1C1C1C1C1C
642F: 1C ID ID
6432: ID ID ID
176
6435: ID 326 HEX 1C1D1D1D1D1D1D
6436: IE IE IE
6439: IE IE IE
643C: IE 327 HEX 1E1E1E1E1E1E1E
64 3D: IE IF IF
6440: IF IF IF
6443: IF 328 HEX 1E1F1F1F1F1F1F
6444: 20 20 20
6447: 20 20 20
644A: 20 329 HEX 20202020202020
644B: 20 21 21
644E: 21 21 21
6451: 21 330 HEX 20212121212121
6452: 22 22 22
6455: 22 22 22
6458: 22 331 HEX 22222222222222
6459: 22 23 23
645C: 23 23 23
645F: 23 332 HEX 22232323232323
6460: 24 24 24
6463: 24 24 24
6466: 24 333 HEX 24242424242424
6467: 24 25 25
646A: 25 25 25
646D: 25 334 HEX 24252525252525
646E: 26 26 26
6471: 26 26 26
6474: 26 335 HEX 26262626262626
6475: 26 27 27
6478: 27 27 27
647B: 27 336 HEX 26272727272727
64 7C: 00 00 01
647F: 01 02 02
6482: 03 337 XOFF HEX 00000101020203
6483: 03 04 04
6486: 05 05 06
6489: 06 338 HEX 03040405050606
648A: 00 00 01
648D: 01 02 02
6490: 03 339 HEX 00000101020203
6491: 03 04 04
6494: 05 05 06
6497: 06 340 HEX 03040405050606
6498: 00 00 01
649B: 01 02 02
649E: 03 341 HEX 00000101020203
649F: 03 04 04
64A2: 05 05 06
64A5: 06 342 HEX 03040405050606
64A6: 00 00 01
64A9: 01 02 02
64AC: 03 343 HEX 00000101020203
64AD: 03 04 04
64B0: 05 05 06
64B3: 06 344 HEX 03040405050606
64 B4: 00 00 01
64B7: 01 02 02
64BA: 03 345 HEX 00000101020203
64BB: 03 04 04
64BE: 05 05 06
64C1 : 06 346 HEX 03040405050606
64C2: 00 00 01
64C5: 01 02 02
64C8: 03 347 HEX 00000101020203
64C9: 03 04 04
64CC: 05 05 06
64CF: 06 348 HEX 03040405050606
64 DO: 00 00 01
64D3: 01 02 02
64D6: 03 349 HEX 00000101020203
64D7: 03 04 04
64DA: 05 05 06
64DD: 06 350 HEX 03040405050606
64 DE: 00 00 01
64E1 : 01 02 02
64E4: 03 351 HEX 00000101020203
64E5: 03 04 04
64E8: 05 05 06
64EB: 06 352 HEX 03040405050606
64 EC: 00 00 01
64EF: 01 02 02
64F2: 03 353 HEX 00000101020203
64 F3: 03 04 04
64F6: 05 05 06
64F9: 06 354 HEX 03040405050606
64FA: 00 00 01
64FD: 01 02 02
6500: 03 355 HEX 00000101020203
6501: 03 04 04
6504: 05 05 06
6507: 06 356 HEX 03040405050606
6508: 00 00 01
650B: 01 02 02
650E: 03 357 HEX 00000101020203
650F: 03 04 04
6512: 05 05 06
6515: 06 358 HEX 03040405050606
6516: 00 00 01
6519: 01 02 02
651C: 03 359 HEX 00000101020203
651D: 03 04 04
6520: 05 05 06
6523: 06 360 HEX 03040405050606
6524: 00 00 01
6527: 01 02 02
652A: 03 361 HEX 00000101020203
652B: 03 04 04
652E: 05 05 06
6531: 06 362 HEX 03040405050606
6532: 00 00 01
6535: 01 02 02
6538: 03 363 HEX 00000101020203
6539: 03 04 04
653C: 05 05 06
653F : 06 364 HEX 03040405050606
6540: 00 00 01
6543: 01 02 02
6546: 03 365 HEX 00000101020203
6547: 03 04 04
6 54 A: 05 05 06
178
654D: 06 366 HEX 03040405050606
654E: 00 00 01
6551: 01 02 02
6554: 03 367 HEX 00000101020203
6555: 03 04 04
6558: 05 05 06
655B: 06 368 HEX 03040405050606
655C: 00 00 01
655F: 01 02 02
6562: 03 369 HEX 00000101020203
6563: 03 04 04
6566: 05 05 06
6569: 06 370 HEX 03040405050606
656A: 00 00 01
656D: 01 02 02
6570: 03 371 HEX 00000101020203
6571: 03 04 04
6574: 05 05 06
6577: 06 372 HEX 03040405050606
6578: 00 00 01
657B: 01 02 02
657E: 03 373 HEX 00000101020203
657F: 03 04 04
6582: 05 05 06
6585: 06 374 HEX 03040405050606
6586: 00 00 01
6589: 01 02 02
658C: 03 375 HEX 00000101020203
658D: 03 04 04
6590: 05 05 06
6593: 06 376 HEX 03040405050606
377 TABLES
6594: 00 01 02
6597: 03 04 05
659A: 06 378 SHPADR HEX 00010203040506
379 #
659B: 16 380 SHPLO DFB SHAPES
659C: 2E 381 DFB SHAPES+$ 18
659D: 46 382 DFB SHAPES-^ 30
659E: 5E 383 DFB SHAPES+$48
659F: 76 384 DFB SHAPES+$60
65AO: 8E 385 DFB SHAPES+$ 78
65A1 : A6 386 DFB SHAPES+$90
387
65A2: 3E 388 BSHPLO DFB BSHAPES
65A3: 4C 389 DFB BSHAPES+$OE
65A4: 5A 390 DFB BSHAPES+$1C
65A5: 68 391 DFB BSHAPES+$2A
65A6: 76 392 DFB BSHAPES+$38
65A7 : 84 393 DFB BSHAPES+$46
65A8: 92 394 DFB BSHAPES+$ 54
65A9: AO 395 DFB BSHAPES+$62
396 DS $6C
397 *SHAPE TABLE GUN
6616: AO 81 00
6619: AO 81 00
661C: AO 81 398 SHAPES HEX A08100A08100A081
661E: 00 AO 81
6621: 00 A8 85
6624: 00 A8 399 HEX 00A08100A88500A8
:
6626: 85 00 8A
6629: 94 00 8A
662C: 94 00 400 HEX 85008A94008A9400
401 *2ND
662E: 00 85 00
6631: 00 85 00
6634: 00 85 402 HEX 0085000085000085
6636: 00 00 85
6639: 00 AO 95
663C: 00 AO 403 HEX 00008500A09500AO
663E: 95 00 A8
6641: DO 80 A8
6644: DO 80 404 HEX 9500A8D080A8D080
405 *3RD
6646: 00 94 00
6649: 00 94 00
664C: 00 94 406 HEX 0094000094000094
664E: 00 00 94
6651: 00 00 D5
6654: 80 00 407 HEX 0000940000D58000
6656: D5 80 AO
6659: Cl 82 AO
665C: Cl 82 408 HEX D580A0C182A0C182
409 *4TH
665E: 00 DO 80
6661: 00 DO 80
6664: 00 DO 410 HEX OODO 8 OOODO 8 OOODO
6666: 80 00 DO
6669: 80 00 D4
666C: 82 00 411 HEX 8000D08000D48200
666E: D4 82 00
6671: 85 8A 00
6674: 85 8A 412 HEX D48200858A00858A
413 *5TH
6676: CO 82 00
6679: CO 82 00
667C: CO 82 414 HEX C08200C08200C082
667E: 00 CO 82
6681: 00 DO 8A
6684: 00 DO 415 HEX 00C08200D08A00D0
6686: 8A 00 94
6689: A8 00 94
668C: A8 00 416 HEX 8A0094A80094A800
417 *6TH
668E: 00 8A 00
6691: 00 8A 00
6694: 00 8A 418 HEX 008A00008A00008A
6696: 00 00 8A
6699: 00 CO AA
669C: 00 CO 419 HEX 00008A00C0AA00C0
669E: AA 00 DO
66A1 AO 81 DO
66A4: AO 81 420 HEX AAOODOAO 8 IDOAO 8 I
421 *7TH
66A6: 00 A8 00
66A9: 00 A8 00
66AC: 00 A8 422 HEX OOA 8 OOOOA 8 OOOOA 8
66AE: 00 00 A8
66B1: 00 00 AA
66B4: 81 00 423 HEX OOOOA8QOOOA A8 100
180
66B6 AA 81 CO
:
66B9: 82 85 CO
66BC: 82 85 424 HEX AA81C08285C08285
425 *
426 DS $80
427 *BULLET SHAPE TABLE
673E: 40 01 40
6741: 01 40 01
6744: 40 428 BSHAPES HEX 40014001400140
6745: 01 40 01
6748: 40 01 40
674B: 01 429 HEX 01400140014001
430 *2ND
674C: 00 06 00
674F: 06 00 06
6752: 00 431 HEX 00060006000600
6753: 06 00 06
6756: 00 06 00
6759: 06 432 HEX 06000600060006
433 *3RD
675A: 00 18 00
675D: 18 00 18
6760: 00 434 HEX 00180018001800
6761: 18 00 18
6764: 00 18 00
6767: 18 435 HEX 18001800180018
436 *4TH
6768: 00 60 00
676B: 60 00 60
676E: 00 437 HEX 00600060006000
676F: 60 00 60
6772: 00 60 00
6775: 60 438 HEX 60006000600060
439 *5TH
6776: 00 03 00
6779: 03 00 03
677C: 00 440 HEX 00030003000300
67 7D: 03 00 03
6780: 00 03 00
6783: 03 441 HEX 03000300030003
442 *6TH
6784: 00 OC 00
6787: OC 00 OC
678A: 00 443 HEX OOOCOOOCOOOCOO
678B: OC 00 OC
678E: 00 OC 00
6791: OC 444 HEX OCOOOCOOOCOOOC
445 *7TH
6792: 00 30 00
6795: 30 00 30
6798: 00 446 HEX 00300030003000
6799: 30 00 30
679C: 00 30 00
679F: 30 447 HEX 30003000300030
— END ASSEMBLY-
ERRORS: 0
1952 BYTES
181
I d like to emphasize that careful
attention to detail is very important when
programming. Machine language is very unforgiving. Failure to
initialize a
single variable could cause your graphics to go haywire.
One of the most com-
mon mistakes is to clobber a register in your program or subroutine when call-
ing another subroutine. Some programmers automatically save the
Accumulator and X & Y registers by pushing them onto the stack before
calling
a subroutine, and restore them afterwards. It requires
six instructions in each
direction. Yet it makes more sense to have the called
subroutine save the
registers that it knows will be clobbered, and restore
them before returning.
The setup routine for the drawing program is often a source for error.
Although the setup is basically standard for a particular drawing
subroutine,
accidentally omitting one variable or failure to place a variable,
in say, the Y
register, can be disastrous. To give you an example
of unexpected results
remove the STA TVERT in line 190 by NOPing the code in memory.
6169: EA EA EA
Run program and watch the results. Imagine how long it might take to
the
find this mistake.Debugging machine language graphics is difficult because
events happen too quickly for the eye to detect. An Integer
machine or an
Integer ROM card with step and trace is almost a neccessity. There have been
times when cleared the screen manually, set the graphics mode and
I
put the
machine mode, so that I could watch the graphics being drawn in slow
in trace
motion. Always remember to enter just after your CLRSCR or you will waste
four or five minutes while the computer clears all 8K of
Hi-Res memory. The
commands for clearing screen #1 manually are as follows.
* 2000 : 00
*2001 <2000. 3FFFM
182
STEERABLE SPACE SHIPS
The first game with
a fully steerable space ship was developed at MIT. It
was called SpaceWar. While most of the newer computer owners won’t recall
this game, practically everyone is familiar with Asteroids. Most versions of this
game have a steerable spaceship that can be thrusted in the direction that it is
headed. Although some versions invoke an automatic deceleration mode, some
Asteroid games require the player to turn his ship around so that it thrusts in
the opposite direction to slow down.
We previously demonstrated, with the topic of dropping bombs and shooting
bullets, that objects move in the direction of their velocity vector.
An object’s new position is its old position plus its change in position due to
velocity, as shown:
x = x + vx
Y = Y + VY.
Using the Apple screen coordinate system for the example above, VY is
negative and VX
is positive. Therefore,
X = X + (VX)
Y - Y + (
- VY)
While the velocity vector may remain constant for many animation cycles,
resulting in a shipmoving in the same direction, sooner or later a new velocity
vector will be inputted to change the object’s course. This new velocity is the
vector sum of the old velocity vector and the new velocity vector.
Those readers who have taken Physics will recall that a body’s velocity
changes due to external forces on it while it is in motion. In space ships, that
183
force is thrust. Thrust causes an acceleration of the object’s mass as shown in
the equation
When thrust is applied to a space ship, it accelerates. If a ship is light and has
a big engine with considerable thrust, it will accelerate quickly.
But if it is
heavy, it will accelerate much slower. This acceleration is essentially brought
about by a change in the object’s velocity if the object’s mass is ignored.
Unless you are doing an actual simulation, in which values of thrust or force
and an object s mass is important, only acceleration values need to be con-
sidered. Suitable values for arcade games are small and scaled, so that objects
don’t move too fast relative to their size, or fly off the screen in a blink of the
eye.
If we consider a space ship that is in motion for two frames, then apply
thrust
during the third frame, it will change direction depending on the vector sum of
its old and new velocity vectors. This is illustrated below. The
applied thrust is
straight upwards, so that VX
= 0 and VY = -2. The ship’s new velocity
vector is calculated as follows:
VX = VX + VX =2+0=2
VY = VY + VY = -1 + (-2) = -3
184
FRAME X Y VX VY
0 10 100 2 -1 X = X + VX
1 12 99 2 -1 Y « Y + VY
2 14 98 2 -1
A paddle will control the ship’s direction in our simulation. The paddle’s
range (0-255) will be divided into eight directions (0-7). Dividing by
32 is sim-
ple in machine language. An arithmetic shift right
( LSR, four times ) will
accomplish the task. After the division, paddle values 0-31 are equal
to direc-
tion one, 32-63 to direction two, etc.
Now that we can control our ship in eight directions, we need
shape tables
for each of these directions. That means eight separate shapes. Rather
than
complicate matters unnecessarily, we will use a white ship and move it
horizon-
tally in one byte (7 pixel) increments, and vertically in
eight line jumps. This
way, we won’t need extra sets of tables for the various offsets. Also, by conven-
iently keeping the shape within one of the 24 screen subsections,
we can use an
abbreviated set of YVERT tables.
TROTATE
(0-7)
PADDLE DIRECTION
* 0 1 CM
<>
3
4 5 6 7
185
.
THRUST VECTOR
0 1 2 3 4 5 6 7
XT 00 01 01 01 00 FF FF FF
YT FF FF 00 01 01 01 00 FF
pressed, the ship moves; stops. The ship drives like a car rather than
if off, it
XS = XS + XT and YS = YS + YT
where XS & YS is the ship’s current position and XT & YT are the ship’s
velocity vector components.
186
LDX TROTATE
CLC
LDA XT,X ;GET X THRUST VECTOR FOR TROTATE VALUE
ADC XS ;ADD TO X POSITION
STA XS ; STORE NEW VALUE
Now that the ship can bemoved around the screen by both steering and
thrusting, several testsmust be implemented at the screen boundaries. Our
Apple screen is 40 bytes wide by 24 subgroups deep. To index beyond the end
of our tables would create unforeseen graphics, especially at the bottom
of the
screen.
XS can be tested for values greater than 39 and less than 0. In our case, with
a ship moving only one position per frame, the test for less than 0 would
be
equal to the value FF or — 1 If wrap -a - round is needed for an object leaving
.
the right side of the screen, just set XS = 0 and it will reenter on
the left.
Likewise, setting XS = 39 works for objects leaving the left side of the
screen.
If the wrap-a-round effect is not desired, it requires setting
XS = 39 for any at-
tempt to leave the right side of the screen, and XS = 0 for any attempt to leave
the left hand side of the screen. Essentially, the ship gets stuck at the edge. The
boundary conditions at the top and bottom are similar.
Our drawing setup routine takes the paddle value into consideration to
obtain the correctly rotated shape from the shape table for plotting. We
can
find the correct lo byte of the shape by the following formula:
187
Y
If the ship were turned so that it was pointing right, then TROTATE - 2
and SHPLO (2) = $84. This lo byte of the shape table is stored as SHPL. The
drawing routine will now plot the second shape from our shape table.
As we mentioned earlier, the ship is being moved eight lines at a time
vertically to takeadvantage of plotting the ship within one of the 24 subsections
on the Hi-Res screen. We can use the eight-line deep plotting routine, which
was developed in the last chapter, if we don’t cross any screen boundaries. This
also simplifies and shortens our 192 element YVERT tables to two, 24 byte-
long tables. Each table, one for the hi byte and one for the lo byte, stores the
line address for the beginning of each of these blocks. The correct starting block
for plotting our shape is a function of the ship’s vertical position, YS (0-23). We
index into the tables as before, using the Y register.
STA HIRESL
LDA YBLOCKH Y ;L00K UP HI BYTE ADDRESS OF LINE
,
STA HIRESH
Moving a space ship about the screen by paddle control is actually a simple
case in the overall design of a game. One XDRAWs (erases) the ship at the old
position, reads the paddle controller, calculates the ship’s new position, and
plots it at its new position. This is performed
each animation frame in an
for
endless loop. Because the code is rather short, a considerable delay is needed to
slow down the animation frame rate. With very short delays in the monitor
delay subroutine, the frame rate exceeds the 30 frame-per-second scan rate of
the television. The ship appears to blink at random during its movement. The
television hasn’t finished drawing the first animation cycle while you moved
your ship two or three times in between. A longer delay, wherein the WAIT
subroutine has a value of $C0 to $FF in the Accumulator, works fine. The flow
chart of this steerable rocket program is shown below.
188
0
TROTATE
(0-7)
PADDLE DIRECTION
THRUST VECTOR
0 1 2 3 4 5 6 7
XT 00 01 01 01 00 FF FF FF
YT FF FF 00 01 01 01 00 FF
DRAWING SETUP
190
1
6 PDL DS I
7 LNGH DS 1
8 ROTATE DS 1
9 TROTATE DS 1
10 HIRESL EQU $FB
11 HIRESH EQU HIRESL+$1
12 SHPL EQU $FD
13 SHPH EQU SHPL+$
14 PREAD EQU $FB1E
15 * ENTER HERE FIRST TIME ACCESS
6009: AD 50 CO 16 PROG LDA $C050
600C: AD 52 CO 17 LDA $C052
600F: AD 57 CO 18 LDA $C057
6012: 20 13 61 19 JSR CLRSCR
20 *INITI LIZE ROCKET'S STARTING POSITION
6015: A9 14 21 LDA #$14
6017: 8D 03 60 22 STA XS
601 A: A9 OA 23 LDA #$0A
601C: 8D 04 60 24 STA YS
601F: A9 00 25 LDA #$00
6021: 8D 07 60 26 STA ROTATE
6024: 20 F6 60 27 JSR DSETUP
6027: 20 CF 60 28 JSR DRAW ;DRAW INITIAL POSITION ROCKET
29 * PADDLE READ
602A: 20 F6 60 30 START JSR DSETUP
602D: 20 CF 60 31 JSR DRAW ERASE ROCKET
;
191
Y
192
H Y
194
STEERABLE & FREE FLOATING
Objects in the real world, once started in motion, tend to remain in motion.
Isaac Newton stated it more formally in his first law of motion. Objects remain
at rest or in motion along a straight line unless a force is
applied on them to
change that motion. The force in most games is thrust.
In the last section, we dealt with a spaceship that
had a velocity only when
thrust was applied to We
avoided any sustained velocity by zeroing our
it.
CYCLE VX X CYCLE VY Y
0 0 0 0 0 0
1 1 1 1 2 2
VX = 1 2 2 3 similarly VY - 2 2 4 6
3 3 6 3 6 12
4 4 10 4 8 20
195
the fourth frame, our velocity were 4 units/frame, we would actually be moving
28 pixels horizontally per frame. With a slow program, framing at 10 frames/
second, the ship would move entirely across the screen in 1 second. More
likely, with faster animation, it would take less than half a second. This may be
too fast.
A speed brake can be incorporated into the algorithm to prevent the velocity
from exceeding a preset value. This would be analogous to wind resistance on a
fast moving automobile. It prevents a vehicle from reaching ever-increasing
speeds. I chose a maximum velocity of 2 units/ frame. It was an arbitrary
choice based on keeping the animation smooth. Discontinuous jumps at higher
velocities produced degraded animation. The brake is placed just after the
velocity equations. If the value of VX or VY exceeds 2 units/frame, it is
196
0&8
7&15
TROTATE
2&12 (0-15)
THRUST VECTOR
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
XT 01 01 01 01 00 FF FF FF 00 01 01 01 00 FF FF FF
YT FF FF 00 01 01 01 00 FF FF FF 00 01 01 01 00 FF
Since the proper shape isdrawn from the correct section of the shape table by
setting the appropriate lo and hi byte pointers for that shape, the index to these
pointers must be corrected for the extra number of rotation angles. With
TROTATE doubled to 16 values, the SHPLO table, which contains the 16
pointers to each shape, must also contain 16 values. Since TROTATE values
are duplicated after 8 values, the SHPLO
table, as well as the and XT YT
tables, are duplicated after eight values.
Except for the changes discussed above, the steerable and free-floating ship
routine is much like the former routine, in which the ship drives around like a
car The flow chart and code are shown below. It might be instructive to
*
change the delay in line #129 to a small value like $05 to see what happens
when the animation frame rate exceeds the television’s scan rate.
197
1 ROCKET (FREE FLOATING)
2 ORG $6000
6000: 4C OB 60 3 JMP PROG
4 xs DS 1
5 YS DS 1
6 VX DS 1
7 VY DS 1
8 PDL DS 1
9 LNGH DS 1
10 ROTATE DS 1
11 TROTATE DS 1
12 HIRESL EQU $FB
13 HIRESH EQU HIRESL+$1
14 SHPL EQU $FD
15 SHPH EQU SHPL+$1
16 PREAD EQU $FB1E
17 * ENTER HERE FIRST TIME ACCESS
600B: AD 50 CO 18 PROG LDA $C050
600E: AD 52 CO 19 LDA $C052
6011: AD 57 CO 20 LDA $C057
6014: 20 49 61 21 JSR CLRSCR
22 *INITILIZE ROCKET’S STARTING POSITION
6017: A9 14 23 LDA #$14
6019: 8D 03 60 24 STA XS
601C: A9 OA 25 LDA #$0A
199
: 5
1
601E: 8D 04 60 26 STA YS
6021: A9 00 27 LDA #$00
6023: 8D 05 60 28 STA VX
6026: 8D 06 60 29 STA VY
6029: 8D 09 60 30 STA ROTATE
602C: 20 2C 61 31 JSR DSETUP
602F: 20 05 61 32 JSR DRAW
33 * PADDLE READ
;
200
:: 1 Y
202
619B: OO 01 01
619E: 01 00 FF
61A1: FF FF 190 HEX 0001010100FFFFFF
61A3: FF FF 00
61A6: 01 01 01
61A9: 00 FF 191 YT HEX FFFF0001010100FF
61AB: FF FF 00
61AE: 01 01 01
61B1: 00 FF 192 HEX FFFF0001010100FF
193 *
61B3: 13 194 SHPLO DFB SHAPES
61B4: IB 195 DFB SHAPES+$08
61B5: 23 196 DFB SHAPES+$10
61B6: 2B 197 DFB SHAPES+$18
61B7: 33 198 DFB SHAPES+$20
61B8: 3B 199 DFB SHAPES+$28
61B9: 43 200 DFB SHAPES+130
61BA: 4B 201 DFB SHAPES+$38
202 *NEXT GROUP BECAUSE PADDLE (0-15) INDEXES
203 *INTO SHAPE TABLE TWICE
6 IBB: 13 204 DFB SHAPES
61BC: IB 205 DFB SHAPES+$08
61BD: 23 206 DFB SHAPES+$10
61BE: 2B 207 DFB SHAPES+$18
61BF: 33 208 DFB SHAPES+$20
61C0: 3B 209 DFB SHAPES+$28
61C1: 43 210 DFB SHAPES+$30
61C2: 4B 211 DFB SHAPES+$38
212 *
213 SPACE DS 80
214 ROCKET SHAPES
6213: 00 08 08
6216: 08 1C 1C
6219: 36 00 215 SHAPES HEX 000808081C1C3600
216 2ND
621B: 00 00 20
621E: 14 OF 1C
6221: 08 08 217 HEX 0000201 40F1C0808
218 3RD
6223: 00 00 02
6226: OE 7C OE
6229: 02 00 219 HEX 0000020E7C0E0200
220 4TH
622B: 00 08 08
622E: 1C OF 14
6231: 20 00 221 HEX 000808 1C0F1 42000
222 5TH
6233: 00 00 36
6236: 1C 1C 08
6239: 08 08 223 HEX 0000361C1C080808
224 6TH
623B: 00 08 08
623E: 1C 78 14
6241: 02 00 225 HEX 000808 1C781 40200
226 7TH
6243: 00 00 20
6246: 38 IF 38
6249: 20 00 227 HEX 00002038 1F382000
228 8TH
203
624B: 00 00 02
624E: 14 78 1C
6251: 08 08 229 HEX 000002 14781 C0808
DEBUG PACKAGE
The debug package that was mentioned earlier is a very useful tool for pro-
grammers. allows you to single step animation by stopping the animation
It
with the ESC key. Once the ESC key is pressed, the program
goes into a tight
loop while waiting for another key press. Any key except the ESC
key will
release But since every key, with the exception of the space bar, fails to clear
it.
ignore
204
The debug package is designed so that you can’t activate any other debug
testwithout first hitting the ESC key. This way, no matter what uses your keys
have during a game, they can’t activate debug functions inadvertently.
Game designers often want to limit the amount of ammunition that can be
fired at one time. A flag can be set on when a bullet is fired, and to off when
to
the bullet either reaches the opposite end of the screen or if it hits something.
The player can’t fire again until the flag is in the off position.
Laser fire presents another problem. The beam travels from the gun or
205
spaceship to the opposite end of the screen in one frame. If the player
held the
button, the laser would fire for each frame. Essentially; it would always
be on.
The test for a pressed button must include code that would inhibit the button
being held down continuously. You can accomplish this by setting
a flag to 1
when the laser is fired. If the button is pressed and the laser was just fired
without the player releasing it first, the test for the flag prevents it from firing
again. The flag is reset to 0 only
the button isn’t pressed.
if
We set another flag called SHOT
to one if the laser is fired. This is because
we want to XDRAW
the laser much later in the animation cycle. If we
XDRAW it immediately, it would be barely seen.
Yet, if it were automatically
XDRAWn later without some sort of test, it would always appear, regardless
of whether it was previously fired or not. The XDRAW laser subroutine tests
todetermine if the SHOT is set before XDRAWs the laser shot;
it it will conse-
quently skip this routine if the laser hasn’t been fired.
Red more impressive than white lasers. They also require more
lasers look
work As usual, our nemesis, the even/ odd color offset pro-
to plot properly.
blem comes into play. The first position that our laser can be plotted is at
,
OFFSET
206
207
: 1 1 YY
208
COLLISIONS
One of the most important aspects in any arcade game, especially shoot- ’em-
up type games, is whether an object collides with another object or the
background. As a particular object is drawn to the screen, (one byte at a time,
or even by single pixels, as some programmers prefer), you can simultaneously
test to determine if any other pixels are within that byte’s (or pixel’s) screen
location. The test is performed using the AND instruction.
The truth table for the AND instruction is as follows:
Both Accumulator and memory must be on (set) for the result to be on (set).
If we take a Hi-Res screen memory location that has an object in it and AND
it with a byte from our shape table, any duplication in any bit location because
BACKGROUND
SHAPE
AND BACKGROUND WITH SHAPE
RESULT $18 > ZERO
The hi bit, (the color control bit), which isn’t used to activate any of the
seven pixel positions within the byte, could cause a problem. It is possible that
if the hi bit were set in an empty or black background ($80), and a blue or
orange shape were ANDed against the screen, the result would be non-zero.
Obviously, this is an invalid result, because you can’t collide with a black
background. The problem can be avoided if the background is first ANDed
with #$7F to mask the hi bit.
BOBOBOBHI
0 0 0 0 0 0 0 BACKGROUND
11111110 1
AND #$7F
00000000
0 10 10 11
0
RESULT ZERO
AND BLUE SHAPE
00000000 RESULT ZERO
209
Usually, in any game, if a collision
is detected, the object is to be removed.
The first drawing the object since it is to be removed,
instinct is to stop
anyway. But if you are Exclusive-ORing (EORing) the screen and
you stop in
the middle of your shape, you are going to leave a mess. It
is much better to set
a collision flag, finish drawing the shape, then remove
the object later by com-
pletely EORing the shape off the screen.
Any two objects of byte size or larger will usually have no problem with colli-
sion detection, especially graphics are in B & W. But I can think of a very
if the
specific case involving color in which a collision
would not be detected in a
game. Take our space ship or plane from Chapter Five. Let us
assume it is
violet. Let s assume a green alien collides with it.
The question is: Will it be
detected, and if not, how can we detect a collision?
Let’s map the pixel positions of the bottom row of bytes for both the violet
ship and green alien.
VGVGVGVGVGVGVGVGVGVGV
X X X X X X X X X SHIP
X X X X ALIEN
There is always some order with which objects must be drawn to the screen
to allow our program to detect collisions properly. In a game
with a laser-
armed ship pitted against several unarmed aliens (our example), something
must be drawn last. It is that final test that can sometimes get tricky. In many
games, the user s ship is often the last to be placed on the screen. If a collision is
detected, you end up wondering which alien hit it. Very often the screen
coor-
dinates of each alien must be compared to that of the ship to determine which
object was killed. This is sometimes harder to do than it looks. That
is why,
when you collide with an enemy in many games, the enemy is not wiped out
when the screen refreshes and you receive your next ship. What obviously hap-
pened is: they skipped the test.
210
The order that each object is drawn is shown in the flow chart below.
There isn’t any satisfactory way to avoid the problem of the last test without
elaborate testing. Even if we drew the ship first and the aliens last, we wouldn’t
know an alien collided with a laser or a ship. It is important that these colli-
if
sion tests be performed before any background, like stars, are drawn to the
screen. Also, any permanent background such as ground terrain will always
cause a collision.
Single pixel background stars, in some games, are often set in motion to
achieve an illusion of speed where stationary ships are involved. Of course,
they are drawn and Xdrawn before being moved. Programmers usually keep
the star field from intersecting with the ship’s range of operation, which usually
takes place at the bottom of the screen. However, sometimes it is desirable not
to worry about background stars in a program and only draw them at the start
of a game. You could adjust the collision counter to ignore single collisions
while drawing a complex shape. It is likely that a ship’s 24 byte shape would
collide with a 16 byte alien shape in more than one place. Small one byte
bullets, however, might pose a problem if the collision detector’s value were
upped to two instead of the usual one.
211
SET ERROR FLAG = 0
2
*DRAW SHIP SUBROUTINE
*DRAW SHAPE ONE LINE AT A TIME-LNGH BYTES ACROSS
STA (HIRESL), Y ;
PLOT
INC STESTL : NEXT BYTE OF MASK
INC SSHPL ;
NEXT BYTE OF TABLE
INY NEXT SCREEN POSITION
DEC SLNGH
BNE SDRAW2 !
;
IF LINE NOT FINISHED BRANCH
INC TVERT : [OTHERWISE NEXT LINE DOWN
DEC DEPTH
BNE SDRAW1 : [DONE DRAWING?
LDA ESET ! [IS EXPLOSION FLAG SET?
CMP #$00
BEQ SDRAW4 ; [NO!, EXIT
JMP EXPLODE ;
[YES!, EXPLODE SHIP
SDRAW4 RTS
213
EXPLOSIONS
A game wouldn’t be complete without the enemy blowing apart when killed.
The more dramatic the explosion, the better the effect. Although every pro-
grammer has tried it, most have done it the easy way.
Explosions are divided into two types: shape explosions and particle explo-
sions.Shape explosions are simple, because once an object is targeted for
removal, it is replaced first by a garbage-looking shape and then by a white
blob, which is larger and resembles a debris-filled fireball.
o SHAPE
nice sound routine, which can also act as a delay between plots, is often incor-
porated. These explosion shapes are stored in a table and are drawn to the
screen with drawing subroutines.
Particle explosions are much more complex. They either involve
mathematical and random number routines to keep particles streaming out-
wards from the exploded shape, or they resort to a series of tables to position
the particles on the screen. I’ve chosen the latter case for the following exam-
ple.
I envisioned a particle fireball that sometimes appears in
arcade games like
Defender. When the object begins to blow apart, there is a bright flash, then
the white hot debris begins expanding in a roughly circular fireball. These
fireballs in the arcade grow to be nearly a third the area of the screen and
then
fade to dull red before blanking out. While fading the particles to red can be
included, coding it would be rather difficult. Actually, anything can be done on
the Apple if you put your mind to it, but one should weigh the benefits against
the time involved. I achieved the basic effect of the explosion in the following
manner:
214
.
P
a a
a a
a
a n o °
a a o
a
FLASH FRAME 2 FRAME3
o
a a
a
a
Q
a
a P
a o a EXPLOSION SEQUENCE
Q
a a
FRAME 4
The explosion fills almost l/9th of the screen. The ship is XDRAWn off the
screen and replaced by a bright white block at the ship’s center. Then, white
particles, each three pixels by four pixels, are drawn in successive expanding
but randomized rings. Each frame has a ring of particles, two layers deep.
Each successively larger ring requires more particles. The closest ring has only
8 particles, whose positions are stored in two tables, EOFFX and EOFFY. The
largest rings have 18 particles.
The two position tables contain the locations of each particle. EOFFX con-
tains the true horizontal offset. EOFFY contains the relative position in rela-
tion to the ship’s vertical position. For example, the center of the fireball is at
VERT + 12. If EOFFY =8, then the particle is plotted at VERT + 12. And if
EOFFY is negative or above the center at -4 ,it is stored as $FC (the two’s
complement), so that it can be added to VERT + 4 directly without testing to
215
1 YY
218
6599: 8D OA 60 726 STA SBLOCK
659C: A9 08 727 LDA #$08
659E: 8D OB 60 728 STA EBLOCK
65A1 : 20 1A 66 729 JSR EPLOT
730 *XDRAW SEQ3-15
65A4: A9 13 731 LDA #$13
65A6: 8D OA 60 732 STA SBLOCK
65A9: A9 22 733 LDA #$22
65AB: 8D OB 60 734 STA EBLOCK
65AE: 20 1A 66 735 JSR EPLOT
736 *XDRAW SEQ2-11 OFF
65B1 : A9 08 737 LDA #$08
65B3: 8D OA 60 738 STA SBLOCK
65B6: A9 13 739 LDA #$13
65B8: 8D OB 60 740 STA EBLOCK
65BB: 20 1A 66 741 JSR EPLOT
742 *XDRAW SEQ4-16
65BE: A9 22 743 LDA #$22
65C0: 8D OA 60 744 STA SBLOCK
65C3: A9 32 745 LDA #$32
65C5: 8D OB 60 746 STA EBLOCK
65C8: 20 1A 66 747 JSR EPLOT
748 *XDRAW SEQ3-15 OFF
65CB: A9 13 749 LDA #$13
65CD: 8D OA 60 750 STA SBLOCK
65DO: A9 22 751 LDA #$22
65D2: 8D OB 60 752 STA EBLOCK
65D5: 20 1A 66 753 JSR EPLOT
754 *XDRAW SEQ5- 18
65D8: A9 32 755 LDA #$32
65DA: 8D OA 60 756 STA SBLOCK
65DD: A9 44 757 LDA #$44
65DF: 8D OB 60 758 STA EBLOCK
65E2: 20 1A 66 759 JSR EPLOT
760 *XDRAW SEQ4-16 OFF
65E5: A9 22 761 LDA #$22
65E7: 8D OA 60 762 STA SBLOCK
65EA: A9 32 763 LDA #$32
65EC: 8D OB 60 764 STA EBLOCK
65EF: 20 1A 66 765 JSR EPLOT
766 *XDRAW SEQ6-18
65F2: A9 44 767 LDA #$44
65F4: 8D OA 60 768 STA SBLOCK
65F7: A9 56 769 LDA #$56
65F9: 8D OB 60 770 STA EBLOCK
65FC: 20 1A 66 771 JSR EPLOT
772 *XDRAW SEQ5-18 OFF
65FF: A9 32 773 LDA #$32
6601: 8D OA 60 774 STA SBLOCK
6604: A9 44 775 LDA #$44
6606: 8D OB 60 776 STA EBLOCK
6609: 20 1A 66 777 JSR EPLOT
778 XDRAW SEQ6-18 OFF
660C: A9 44 779 LDA #$44
660E: 8D OA 60 780 STA SBLOCK
6611: A9 56 781 LDA #$56
6613: 8D OB 60 782 STA EBLOCK
6616: 20 1A 66 783 JSR EPLOT
6619: 60 784 RTS
219
_ X
TO START DRAWING
661D: A9 03 790 EPL0T1 LDA #$03 EACH BLOCK 3 LINES DEEP
66 IF: 8D 11 60 791 STA DEPTH
6622: 18 792 EL00P1 CLC
6623: AD OC 60 793 LDA VERT TOP OF SHIP
6626: 69 04 794 ADC #$04 NOW CENTER OF SHIP
6628: 18 795 CLC
6629: 7D 9A 69 796 ADC E0FFY , ADD RELATIVE Y POS OF PARTICLE.
662C: C9 00 797 CMP #$00 TEST NOT OFF TOP SCREEN
662E: 90 21 798 BLT N0PL0T IF OFF, DON'T LOT
;
SCOREKEEPING
It is a rare exception for machine language games to include a
Hi-Res
character generator with a complete character set. It is basically a waste of
space, because only one or two words are written to the Hi-Res screen along
with the numbers 0 through 9 for the numerical score.
For example, in our game, only the word SCORE is written at the top of the
screen. This is done once at the start of the game. The numbers, however,
change with each alien killed. It would appear that the scoring subroutine
would need to convert hexadecimal numbers to decimal numbers, since the
computer stores the numerical score as hexadecimal numbers in memory.
There is a simple method to avoid this messy approach.
The scoring registers can be broken down into three separate digits, one each
for the hundred’s digit, ten’s digit and one’s digit. This is just like the decimal
system. Each time an enemy is killed, the one’s digit storage location is
incremented. This value is tested to see if it becomes greater than 9. If so, the
one’s digit memory location is reset to zero, and the ten’s digit memory loca-
tion is incremented by one.
220
.
*°™
J
SCORE
{ objects were worth two points instead of one
point, we could JSR to
twice^Ifa target was worth ten points, one could
JSR to the middle of
the longer SCORE subroutine at a point
called SCORE 10. This is the place in
the subroutine where the ten’s digit is
incremented. Returning to the main pro-
gram would be through the usual RTS.
the following routine, SCOREA
represents the one’s digit, SCOREB
the
ten s digit, and SCOREC
the hundred’s digit. The three variables
are drawn
on the screen just after the words SCORE, which
is on the very first line at the
top of the Hi-Res screen.
VERTICAL
OFFSET
$2000+ $1D $1E $1F $20 $21 $22 $23 $24 $25
s c 0 R E 0 1 5
ABC
SCORE SCORE SCORE
cmoucu
8 ” S et aPP ro P riatel Y- The ten number shapes which are
stored
at sOURtaH .
SCOREP 00 08 10 18 .
:
::
222
223
A
225
SCREEN #1 PG 1 SCREEN #2 PG 2
(3,3) (4,3)
> t>
/
XS1 XS2
YS1 YS2
CYCLE #1
The
solution to keeping track of the objects is to store the previous location of
allobjects for both screens. In the above case, XS1 ,YS1 is always the previous
location for the object on screen #1, while XS2.YS2 is the previous screen posi-
tion for the object on screen #2. While this isn’t awkward for one or two
objects, a multitude of objects may prove difficult for most programmers. If
you are determined to pursue this, I would suggest storing the previous object
locations for each screen in tables, which can then be indexed by object
number.
To demonstrate a working example of page flipping, the free-floating rocket
ship program has been converted to dual screen. Actually, you won’t see any
226
difference in flicker, because only one small object is being drawn. It would
require at least a dozen or more objects before you might begin to see the effects
A
of flicker. small minus sign was added to the bottom left corner of screen #1
as a page reference to determine which screen was being viewed. A
single step
debug package was also incorporated to allow you to step from screen to
screen.
Screen $1 is considered the odd screen and screen # 2 the even screen. A
counter is incremented for each screen cycle. It is tested for its odd/even
character by dividing by two (LSR)and testing the carry bit. Depending
on
whether COUNTER is odd or even, you might store coordinate values and
draw on one screen while displaying the other; then, when COUNTER
changes, switch to the opposite screen. For example, if you look at the
flow
odd- pgl
ODD/EVEN ?| —
Ieven pg2
*
LOOKUP HI BYTE OF SHAPE TABLE
SHPH = HI BYTE OF SHAPES
227
chart below - when COUNTER is even,
you store screen #2’s values, XS2,
YS2, and TROT2 after calculating the ship’s new position, and draw the ship
on screen #2 while displaying screen #1. When you are finished, you shift the
view to screen #2.
Likewise, the drawing setup subroutine must set the pointers to the proper
line on the proper screen. An even-valued COUNTER needs to locate the
screen line for YS2 and the offset for XS2. In addition, #$20 must be added to
the hi byte line pointer HIRESH for screen #2. Also, the test to determine if all
have been plotted - a comparison with BOTTOM
eight lines - becomes > =
#$60, which is the end of the second Hi-Res screen.
The flow chart and code is shown below.
) PG1
) PG2
228
1 FREE FLOATING ROCKET (PAGE FLIPPING)
2 ORG $6000
6000 4C 14 60 3 JMP PROG JUMP TO START OF PROGRAM
;
4 xs DS 1
5 YS DS 1
6 XS1 DS 1
7 XS2 DS 1
8 YS1 DS 1
9 YS2 DS 1
10 VX DS 1
11 VY DS 1
12 PDL DS 1
13 LNGH DS 1
14 COUNTER DS 1
15 BOTTOM DS 1
16 ROTATE DS 1
17 TROTATE DS 1
18 TR0T1 DS 1
19 TR0T2 DS 2
20 HIRESL EQU $FB
21 HIRESH EQU HIRESL+$1
22 SHPL EQU $FD
23 SHPH EQU SHPL+$1
24 PREAD EQU $FB1E
25 ENTER HERE FIRST TIME ACCESS
6014: AD 50 CO 26 PROG LDA $C050
6017: AD 52 CO 27 LDA $C052
601A: AD 57 CO 28 LDA $C057
601D: 20 OB 62 29 JSR CLRSCR
6020: 20 25 62 30 JSR CLRSCR2
31 INITILIZE ROCKET'S STARTING POSITION
6023: A9 14 32 LDA #$14
6025: 8D 03 60 33 STA XS
6028: 8D 05 60 34 STA XS1
602B: 8D 06 60 35 STA XS2
602E: A9 OA 36 LDA #$0A
6030: 8D 04 60 37 STA YS
6033: 8D 07 60 38 STA YS1
6036: 8D 08 60 39 STA YS2
6039: A9 00 40 LDA #$00
603B: 8D 09 60 41 STA VX
603E: 8D OA 60 42 STA VY
6041: 8D OF 60 43 STA ROTATE
6044: 8D 11 60 44 STA TR0T1
6047: 8D 12 60 45 STA TROT 2
604A: A9 00 46 LDA #$00
604C: 8D OD 60 47 STA COUNTER
604F: 20 BF 61 48 JSR DSETUP ;DRAW EVEN OR PAGE 2 START POS
6052: 20 97 61 49 JSR DRAW
6055: A9 01 50 LDA #$01
6057: 8D OD 60 51 STA COUNTER
605A: 20 BF 61 52 JSR DSETUP ;DRAW ODD OR PAGE 1 START POS
605D: 20 97 61 53 JSR DRAW
6060: AD 55 CO 54 LDA $C055 DISPLAY PG 2 WHILE DRAWING ON PG
!
1
55 PUT MINUS SIGN AT BOTTOM LEFT PAGE 2 FOR REFERENCE
6063: A9 FF 56 LDA #$FF
6065: 8D DO 5F 57 STA $5FD0
58 *
230
5 1
59 ** M A I N P R 0 G R AM LOOP**
60 *
61 * PADDLE READ
6068: 20 BF 61 62 START JSR DSETUP ;WILL SETUP NON DISPLAYED SCREEN
63 *F0R SHIP XDRAW
606B: 20 97 61 64 JSR DRAW ; XDRAW SHIP ON NON DISPLAY SCREEN
606E: A2 01 65 LDX #$01
6070: 20 IE FB 66 JSR PREAD
6073: CO F9 67 CPY #$F9 ;CLIP VALUE (0-250)
6075: 90 02 68 BLT SKIPP
6077: AO F8 69 LDY #$F8
6079: 8C OB 60 70 SKIPP STY PDL
607C: 98 71 TYA
607D: CD OF 60 72 CMP ROTATE ; PADDLE<ROTATE POS THEN SUBTRACT 5
6080: BO IB 73 BGE PADDLE3
6082: AD OF 60 74 LDA ROTATE
6085: 38 75 SEC
6086: E9 05 76 SBC #$05
6088: BO 05 77 BGE PADDLE ;MAKE SURE =>0
608A: A9 00 78 LDA #$00
608C: 8D OF 60 79 STA ROTATE
608F: CD OB 60 80 PADDLE1 CMP PDL ; DON'T WANT TO GO PAST PADDLE POS
6092: BO 03 81 BGE PADDLE2
6094: AD OB 60 82 LDA PDL
6097: 8D OF 60 83 PADDLE2 STA ROTATE
609A: 4C BO 60 84 JMP PADDLE5
609D: CD OF 60 85 PADDLE3 CMP ROTATE ;PADDLE>ROTATE POS THEN ADD 5
60A0: FO OB 86 BEQ PADDLE4
60A2: AD OF 60 87 LDA ROTATE
60A5: 18 88 CLC
60A6: 69 05 89 ADC #$05
60A8: CD OB 60 90 CMP PDL ; DON'T WANT TO GO PAST PADDLE POS
60AB: 90 03 91 BLT PADDLE5
60AD: AD OB 60 92 PADDLE4 LDA PDL
60B0: 8D OF 60 93 PADDLE STA ROTATE
60B3: 4A 94 LSR ;DIVIDE BY 16 TO GET ROTATION(O-IS)
60B4 4A
: 95 LSR ;0R WO ROTATIONS
60B5: 4A 96 LSR
60B6: 4A 97 LSR
60B7: 8D 10 60 98 STA TROTATE
99 *
60BA: AD 62 CO 100 LDA$C062 ;NEG BUTTON PRESSED
60BD: 30 03 101 BMITHRUST
60BF: 4C F7 60 102 JMPNOTHRUST
60C2 AE 10 60 103
: THRUST LDXTROTATE
104 UPDATE VELOCITY VX AND VY
60C5: 18 105 CLC
60C6: BD 6F 62 106 LDA XT, X ;GET X THRUST VECTOR
60C9: 6D 09 60 107 ADC VX
60CC: C9 FD 108 CMP #$FD
60CE: DO 05 109 BNE NOCLIP
60D0: A9 FE 110 LDA #$FE
60D2: 4C DB 60 111 JMP N0CLIP1
60D5: C9 03 112 NOCLIP CMP #$03 ;CLIP MAX VELOCITY AT 2
60D7 : DO 02 113 BNE N0CLIP1
60D9: A9 02 114 LDA #$02
60DB: 8D 09 60 115 N0CLIP1 STA VX ; STORE X VELOCITY
60DE: 18 116 CLC
60DF : BD 7F 62 117 LDA YT,X
231
r
6D OA 60 118 ADC VY
C9 FD 119 CMP #$FD
DO 05 120 BNE N0CLIP2
A9 FE 121 LDA #$FE
4C F4 60 122 JMP N0CLIP3
C9 03 123 N0CLIP2 CMP #$03 ;CLIP MAX VELOCITY AT 2
DO 02 124 BNE N0CLIP3
A9 02 125 LDA #$02
8D OA 60 126 N0CLIP3 STA VY STORE Y VELOCITY;
ADC XS
60FE: C9 EO 131 CMP #$E0 CHECK FOR WRAPAROUND LEFT
;
232
: 3 Y
233
Y Y
240 *- ; TABLE
61D9: B9 8F 62 241 LDA shplo.y
61DC: 85 FD 242 STA SHPL
61DE: A9 60 243 LDA #$60 ;THIS WILL CORRECT DRAWING TEST
244 *FOR END OF 8 LINES - PG 2
61E0: 8D OE 60 245 STA BOTTOM
61E3: AC 06 60 246 LDY XS2
61E6: 4C 06 62 247 JMP SKIPPY
61E9: AC 07 60 248 PAGE1 LDY YS1
61EC: B9 3F 62 249 LDA YBLOCKH.Y ;LOOK UP HI BYTE OF LINE
61EF: 85 FC 250 STA HIRESH
61F1: B9 57 62 251 LDA Y BLOCK L,Y
61F4: 85 FB 252 STA HIRESL
61F6: AC 11 60 253 LDY TR0T1
61F9: B9 8F 62 254 LDA SHPLO ,
6242: 21 22 22
;
234
:
6245: 23 23 20
6248: 20 293 YBLOCKH HEX 20202121222223232020
6249: 21 21 22
624C: 22 23 23
624F: 20 20 21
6252: 21 294 HEX 21212222232320202121
6253: 22 22 23
6256: 23 295 HEX 22222323
6257: 00 80 00
625A: 80 00 80
625D: 00 80 28
6260: A8 296 YBLOCKL HEX 008000800080008028A8
6261: 28 A8 28
6264: A8 28 A8
6267: 50 DO 50
626A: DO 297 HEX 28A828A828A850D050D0
626B: 50 DO 50
626E: DO 298 HEX 50D050D0
299
626F: 00 01 01
6272: 01 00 FF
6275: FF FF 300 XT HEX 0001010100FFFFFF
6277: 00 01 01
627A: 01 00 FF
627D: FF FF 301 HEX 000 101 0 100FFFFFF
627F FF FF
00
6282: 01 01 01
6285: 00 FF 302 YT HEX FFFF0001010100FF
6287: FF FF 00
628A: 01 01 01
628D: 00 FF 303 HEX FFFF0001010100FF
304 *
628F: 03 305 SHPLO DFB SHAPES
6290: OB 306 DFB SHAPES+$08
6291: 13 307 DFB SHAPES+$10
6292: IB 308 DFB SHAPES+$18
6293: 23 309 DFB SHAPES+120
6294: 2B 310 DFB SHAPES+$28
6295: 33 311 DFB SHAPES+$30
6296: 3B 312 DFB SHAPES+$38
313 *NEXT GROUP BECAUSE PADDLE (0-15) INDEXES INTO
314 *SHAPE TABLE TWICE
6297: 03 315 DFB SHAPES
6298: OB 316 DFB SHAPES+$08
6299: 13 317 DFB SHAPES+$10
629A: IB 318 DFB SHAPES+$18
629B: 23 319 DFB SHAPES+$20
629C: 2B 320 DFB SHAPES+$28
629D: 33 321 DFB SHAPES+$30
629E: 3B 322 DFB SHAPES+$38
323 *
324 SPACE DS 100
325 BROCKET SHAPES
6303: 00 08 08
6306: 08 1C 1C
6309: 36 00 326 SHAPES HEX 00080808 1 C 1 C 3600
327 *2ND
630B: 00 00 20
630E: 14 OF 1C
6311: 08 08 328 HEX 0000201 40F1C0808
329 *3RD
6313: 00 00 02
6316: OE 7C OE
6319: 02 00 330 HEX 0000020E7C0E0200
331 *4TH
631B: 00 08 08
631E: 1C OF 14
6321: 20 00 332 HEX 000808 1C0F 1 42000
333 *5TH
6323: 00 00 36
6326: 1C 1C 08
6329: 08 08 334 HEX 0000361C1C080808
335 *6TH
632B: 00 08 08
632E: 1C 78 14
6331: 02 00 336 HEX 0008081C781 40200
337 *7TH
6333: 00 00 20
6336: 38 IF 38
6339: 20 00 338 HEX 00002038 1F382000
339 *8TH
633B: 00 00 02
633E: 14 78 1C
6341: 08 08 340 HEX 000002 14781C0808
—END ASSEMBLY-
ERRORS: 0
835 BYTES
236
CHAPTER 7
Scrolling games are dynamic in nature, in that the entire background moves
game’s terrain. True scrolling arcade games, such as
as the player traverses the
Pegasus II on the Apple, or Scramble and Rally X in the arcades, have multi-
screen worlds which scroll on or off the screen as the player’s plane or car
moves. These games show only a window or part of the entire background
world at one time. They differ from games that have background stars and
aliens that appear to be traveling towards you from top to bottom. Scrolling
games have objects or terrain in relatively stable positions within the game’s
world. They can be reached by traveling to that particular section of the world.
And this technique isn’t just limited to arcade games. Ultima, an adventure
game, uses a large map that scrolls as the player moves around. Your screen
view is only a small window on the game’s world.
*
XS.YS BLOCK OR UNIT
\h- 20 -H
64 T
UNITS 12 TV
SCREEN
il
*| 14 PIXELS \+
k 64 UNITS
ULTIMA MAP
The data that generates these maps is stored in large arrays. A game like
Ultima has a map 64 units square, with each block 14 pixels wide by 16 lines
deep. If one byte is used to store which shape is used for each block, 4K of
memory is needed. There is a reason why 64 units was chosen for a side. When
referencing the location of your viewing window, which is located at position
XS, YS on the large map, you retrieve data from a table or array, in which
each row of blocks is stored $40 below the previous row. Sixty-four units per
side is not etched in concrete, but some multiple of 16 is convenient. A map 128
units by 32 units would also work well.
237
Games Pegasus II on the Apple allow as many as ten
like
screen lengths to
scroll past the viewer before repeating. The horizontal scrolling
is done a byte
at a time and the data is stored in tables. Pegasus
II, which uses page flipping
to smooth the animation, gains added speed by scrolling
only sections of the
In this section
we are going to develop a scrolling game much like
Pegasus
II. be defined in much more detail than my
It will
previous examples, yet it
won t be complete. Aliens will appear, but they won’t
shoot back. You’ll be
able to kdl the aliens with your lasers and
accumulate points as you do so, but
you 11 find that there is no finish, nor even a
goal. Consider the unfinished
game a test bench to develop your graphics skills.
The first step is to define and develop a fast scrolling
subroutine. Since it is
7 le
uuT, e ° bjec s horizontall one byte per animation
should be linked with !
y
that speed if objects are to remain
frame, our scrolling
synchronzied with the
terrain. A counter can be used to
determine the screen’s location within our
much larger world. With the counter limited to 256 and
screen scrolling set at 7
pixels per frame, the most logical length
for a world would be 1 792 pixels
r or
seven screen lengths.
COUNTER
V^en the counter reaches 256, it wraps back to zero for a repeat of screen
fl.You have to be careful when approaching the upper end
of the database
Once the counter indexes beyond 215, it begins accessing
data beyond the
1791st position. This can be remedied by enlarging
the table to 2048 data
points, with the last 279 points a duplicate
of the first 279 points. The terrain
level at the end of the seventh screen
should match the terrain level at the
beginning of the first frame, as shown above.
The data points are Y axis screen coordinates
(0-191) for each of the 1792
positions along the X
axis. The data was placed into the table
by an Applesoft
program called Mountain Maker. It takes a series of X,Y
points corresponding
to each change in direction of our
terrain and, by simple slope equations
generates the data points in between. The program
is listed below.
238
5 DIM NAME$(20)
10 TEXT : HOME : PRINT : PRINT " MOUNTAIN BACKGROUND GENE
RATOR"
20 PRINT : HTAB 15: PRINT "WORKING"
25 SH = 4000
30 START = 16384
35 J = START
40 READ A,B
50 X2 - A:Y2 = B
60 READ C,D
70 IF C = - 1 THEN 1000
80 XI = X2:Y1 - Y2:X2 = C:Y2 = D
90 SLOPE = (Y2 - Yl) / (X2 - XI)
100 FOR I « XI TO X2 - 1
105 Y = INT (Yl + (SLOPE * (I - XI)))
110 POKE J,Y
120 J = J + 1
130 NEXT I: GOTO 60
150 END
1000 POKE J Y2
,
239
r° hng subroutine works
™\f
INDEX,
T
is incremented, it
as follows.
adds seven
Each time the position counter
to the lo byte of a pair of zero page
pointers, GROUNDL
and GROUNDH, through a multi-byte addition
lhese pointers index mto a table called NEW
MOUNTAINS, stored at
$4000. Starting with the first data point located at
GROUNDH, GROUNDL
the routine plots that point at X
= 0. It increments the lo byte of the
data
point, then plots the second point at = 1 It does X . that until all 280 points are
° tted; PI °“ ln S 1S accomplished by EORing the proper pixel to the screen.
f
When it finished plotting, it reloads
is
and GROUNDH GROUNDL
then
EORs the points off the screen. Note that
all
GROUNDH
are not changed during the plotting phase
and GROUNDL
because zero page locations $4 and
$5 were used to store the pointers. When these
are incremented, it doesn’t
affect our original pointers, which are
stored elsewhere.
t
GROUNDL, GROUNDH
B8
B9
BA Position on screen
BB
BC
BD
BE
BF
0 1 2 3 4 5 6 7
SCREEN X
240
Our viewing window is controlled by the counter, INDEX (0-255). We see
the terrain in that window from INDEX 5|c 7 to (INDEX+ 39) Jfc 7. While our
terrain is stored as individual data points for each pixel, our shapes are stored
and plotted as data bytes at a particular horizontal position (0-39).
Fortunately, the choice of moving the terrain seven pixels (or one screen byte
to the left with each frame) synchronizes with the easiest method of moving a
raster shape in the same direction. Single byte moves require no offset shape
tables.
Objects can be assigned reference positions corresponding to their horizontal
byte location (0-255) in our seven screen long world. A table of these values is
stored in ONPOS. Each object’s vertical position is correspondingly stored in a
table TABLEY. TABLEX
contains the object’s current screen position (0-39).
This value changes during each frame, regardless of whether the object
remains stationary with respect to the terrain.
An object first appears on the scrolling screen at the far right when INDEX
- > ONPOS( OBJ # ). The ONPOS value for an object is not actually its true
horizontal position, but one that is offset by 39 bytes.
The object moves left one byte exactly in step with the ground movement
with each successive animation frame. The value of TABLEX ( OBJ # ) is set
originally to X
=38 or $26. X
is set to 38 rather than 39 because our alien
shape is two bytes wide, and we would like to plot its full shape on the screen’s
right side rather than half of its shape. During each successive cycle, we decre-
ment the X
position in TABLEX table and test each time for a value less than
zero. If so, we are now off the screen, and we set the ONFLAG
( OBJ #)
= 0
241
*“ TAKE OBJECT
}L
TEST IF OBJECT
yes
>
yes=l update
ON FLAG ON?
no XDRAW OBJECT
\ f
I no DECREMENT X POSITION
SET ONFLAG ON 4
1 .yes
SET USED ALREADY FLAG ON | IS X POS. >0?
1
(AT FAR LEFT?)
XDRAW COLLISION ?
OBJECT |
no
NEXT OBJECT
— r
REST OF PROGRAM
242
There are several flags that are required tokeep track of certain aspects of
the game. The ONFLAG OBJ
(
used
# )
to
is determine if the object is to be
actively plotted on the screen. Assuming our object is actually alive, ALIVE (
OBJ # ) = 1 and not dead ( value = 0 ), then the ONFLAG ( OBJ # ) is tested.
If this flag was turned on because the object meets the INDEX - > ONPOS (
OBJ#) test, it will appear for the next 38 cycles unless it is destroyed by your
ship's laser. In either case, when the object reaches the end of its time on the
screen, the ONFLAG OBJ ( # )
flag is set to off, or zero.
There is one additional flag. That is the USFLAG, or used-already flag. It is
necessary because if, for example, an object were to appear on the screen when
INDEX = 50 and vanish at INDEX = 88, without this flag being set equal to
one (off), the object would again meet the requirements of INDEX = >
ONPOS ( OBJ # ) as soon as the ONFLAG (OBJ #) was zero. The object
would appear every 38 screen cycles after it first appeared until INDEX wrap-
ped around to become zero again. The object should appear only once over the
(0-255) INDEX cycle. Incidentally, once all objects have been tested and plot-
ted and INDEX = 0 again, the program resets all USFLAG ( OBJ #) =0 so
that they will reappear over the same terrain if they are still alive.
Collisions are tested during the draw routine. The collision flag, KILL, is set
if any lit pixel occupies the screen positions, where an alien or saucer shape is
drawn. The test is made by logically ANDing the shape with the screen. A non-
zero value will set the flag. If a collision is detected, the alien is immediately
XDRAWn off the screen, and both the ALIVE flag and the ONFLAG are set
to zero (off) for that object. Of course, in a real game, you wouldn't have an
alien simply disappear, but would either plot the shape of an explosion or blow
it up dramatically; a fitting end that any alien who travels so far and fights so
valiantly deserves.
I'll admit that the routine is quite complex and did require considerable
planning and thought, but I hope that the accompanying flow chart will make
it clear. Remember that this code is looped for each object successively until all
objects are tested. Only then does it increment INDEX before proceeding on
with the rest of the program.
Flexibility for displaying a varietyand a large number of shapes, plus the
change the placement of these shapes, was designed into the program.
ability to
This becomes extremely helpful during the play test when the quantity of
targets and types are liable to change frequently. Ground based laser, radar
and rocket bases, plus a dozen city buildings were envisioned as targets spread
out over seven screens. While only eight different shapes were contemplated,
ten of one type might be needed, while only three of another type might be
used.
Because of this special need, a table called SHPADR was conceived. It
would hold the shape type for each, and as many as 256 targets. The shapes
would be stored in a shape table called SHAPES. Since each shape was two
bytes wide by eight lines deep, and we need both even and odd offset shape
tables for color, thirty two bytes would be required for each shape. To keep the
243
YXY
table within one page boundary 256 bytes the scheme was limited
( ), to eight
&
shapes.
244
X
#0 #1 #2 #3 #4 #5 #6 #7
For example, if you were to look for object #2 (X reg = 2), which is an even
number, the even code would reference $01 for the SHPADR table. This in
turn would point to the #1 element in SHPLO. Thus, the code would be stored
$10 in SHPL. The high byte $69 would be stored in SHPH.
In the event that you chose to place these tables into a permanent location,
skip the construction of the SHPLO table. Instead, the SHPADR table con-
tains the lo byte for each shape. The SHPADR table’s length is doubled, for
it
now contains the locations of both the even and odd shapes.
#0 #1 #2 #3 #4 #5 #6 #7
SHPADR 00 00 08 00 00 00 00 08
10 10 18 10 10 10 18
STA SHPL
245
You can see that this is actually simpler code. If you wish to keep separate
shape tables independent oft he main program’s code, then this is the
preferred
method. However, it does involve loading your shape table into memory
when
testing a program.
4 COUNT DS 1
5 INDEX DS 1
6 PADDLEL DS 1
7 PADDLEH DS 1
8 PDL DS 1
9 TEMP DS 1
10 TEMPI DS 1
11 S BLOCK DS 1
12 EBLOCK DS 1
13 VERT DS 1
14 TVERT DS 1
15 HORIZ DS 1
16 OBJ DS 1
17 LNGH DS 1
18 DEPTH DS 1
19 SLNGH DS 1
20 SHOT DS 1
21 LFLAG DS 1
22 ESET DS 1
23 BVERT DS 1
24 TBVERT DS 1
25 BVELY DS 1
26 BHORIZ DS 1
27 BMLOCK DS 1
28 TBMLOCK DS 1
29 KILL DS 1
30 KILLNUM DS 1
31 SCOREA DS 1
32 SCOREB DS 1
33 SCOREC DS 1
249
: 1 3
6041 85 26 61 HIRESL
STA
6043 A9 05 62 LDA
#$05
6045 8D 10 60 63 STA
LNGH
6048 A9 6A 64 LDA
#>SC0REWD
604A 85 51 65 SHPH
STA
604C A9 08 66 LDA
#<SCOREWD
604E 85 50 67 STA
SHPL
6050 20 E8 66 68 JSR
SCOREDR ;PUT WORDS ON SCREEN
6053 A9 00 69 LDA
#$00
6055 8D IF 60 70 STA
SCOREB
6058 8D 20 60 71 STA
SCOREC
605B A9 FF 72 LDA
#$FF
605D 8D IE 60 73 STA
SCOREA FIRST TIME SCORE USED
; WILL—
6060 8D ID 60 74 STA
KILLNUM INCREMENT TO 0
;
6063 20 5D 66 75 JSR
SCORE
76 "INITIALIZE SHIP POSITION
6066: A9 03 77 LDA #$03
6068: 8D 12 60 78 STA SLNGH
606B: A9 D7 79 LDA #<SHIP
606D: 85 52 80 STA SSHPL
606F: A9 68 81 LDA #>SHIP
6071: 85 53 82 STA SSHPH
6073: A9 BF 83 LDA #<MSHIP
6075: 85 54 84 STA STESTL
6077: A9 68 85 LDA #>MSHIP
6079: 85 55 86 STA STESTH
607B: A9 50 87 LDA #$50
607D: 8D 0C 60 88 STA VERT
89 "INITIALIZE START OF SCROLL
6080: A9 00 90 LDA #$00
6082: 8D 04 60 91 STA INDEX
6085: 8D 05 60 92 STA PADDLEL
6088: 8D 06 60 93 STA PADDLEH
94
95 *M A I N P R 0 G R A M LOOP
96
97 "READ PADDLE #1
608B: A2 01 98 START LDX #$01
608D: 20 IE FB 99 JSR PREAD
6090: CO B8 100 CPY #$B8 ;CLIP VALUE (0-183)
6092: 90 02 101 BLT SKIPP
6094: AO B7 102 LDY #$B7
6096: 8C 07 60 103 SKIPP STY PDL
6099: 98 104 TYA
609A: CD OC 60 105 CMP VERT ;PADDLE<VERT POS THEN SUBTRACT 5
609D: BO IE 106 BGE PADDLE
609F: AD OC 60 107 LDA VERT
60A2: 38 108 SEC
60A3: E9 05 109 SBC #$05
60A5: BO 08 110 BGE PADDLE1 ;MAKE SURE =>0
60A7: A9 00 111 LDA #$00
60A9: 8D OC 60 112 STA VERT
60AC: 8D OD 60 113 STA TVERT
60AF: CD 07 60 114 PADDLE CMP PDL ; DON'T WANT TO GO PAST PADDLE POS
60B2: BO 03 115 BGE PADDLE2
60B4: AD 07 60 116 LDA PDL
60B7 8D OC 60 117 PADDLE2 STA VERT
60BA: 4C D3 60 118 JMP PADDLE6
60BD: CD OC 60 119 PADDLE3 CMP VERT ;
PADDLE>VERT POS THEN ADD 5
60C0: FO OB 120 BEQ PADDLE4
250
YX
Y
X
252
: YXY
253
Y
6271: 06 08 314 SKIP ASL $08 SHIFT LEFT INTO BYTE FOR NEXT
;
315 »_
;DOT TO PLOT
6273: 10 CB 316 BPL LOOP IF INTO BIT 7 THEN TOO FAR SO
I
317 iRESTORE TO 1
6275: A9 01 318 LDA #$01 RESTORE BIT COUNTER TO 1
;
254
Y Y
255
A: Y
256
1 YX
XYY
X
Y
258
A 1
260
658F: A9 13 721 LDA #$13
6591: 8D OB 60 722 STA EBLOCK
6594: 20 1A 66 723 JSR EPLOT
724 *XDRAW SEQ1- 8 OFF
6597: A9 00 725 LDA #$00
6599: 8D OA 60 726 STA SBLOCK
659C: A9 08 727 LDA #$08
659E: 8D OB 60 728 STA EBLOCK
65A1 : 20 1A 66 729 JSR EPLOT
730 *XDRAW SEQ3-15
65A4: A9 13 731 LDA #$13
65A6: 8D OA 60 732 STA SBLOCK
65A9: A9 22 733 LDA #$22
65AB: 8D OB 60 734 STA EBLOCK
65AE: 20 1A 66 735 JSR EPLOT
736 *XDRAW SEQ2-11 OFF
65B1 : A9 08 737 LDA #$08
65B3: 8D OA 60 738 STA SBLOCK
65B6: A9 13 739 LDA #$13
65B8: 8D OB 60 740 STA EBLOCK
65BB: 20 1A 66 741 JSR EPLOT
742 *XDRAW SEQ4-16
65BE: A9 22 743 LDA #$22
65C0: 8D OA 60 744 STA SBLOCK
65C3: A9 32 745 LDA #$32
65C5: 8D OB 60 746 STA EBLOCK
65C8: 20 1A 66 747 JSR EPLOT
748 *XDRAW SEQ3-15 OFF
65CB: A9 13 749 LDA #$13
65CD: 8D OA 60 750 STA SBLOCK
65D0: A9 22 751 LDA #$22
65D2: 8D OB 60 752 STA EBLOCK
65D5: 20 1A 66 753 JSR EPLOT
754 *XDRAW SEQ5- 18
65D8: A9 32 755 LDA #$32
65DA: 8D OA 60 756 STA SBLOCK
65DD: A9 44 757 LDA #$44
65DF: 8D OB 60 758 STA EBLOCK
65E2: 20 1A 66 759 JSR EPLOT
760 *XDRAW SEQ4-16 OFF
65E5: A9 22 761 LDA #$22
65E7: 8D OA 60 762 STA SBLOCK
65EA: A9 32 763 LDA #$32
65EC: 8D OB 60 764 STA EBLOCK
65EF: 20 1A 66 765 JSR EPLOT
766 *XDRAW SEQ6-18
65F2: A9 44 767 LDA #$44
65F4: 8D OA 60 768 STA SBLOCK
65F7: A9 56 769 LDA #$56
65F9: 8D OB 60 770 STA EBLOCK
65FC: 20 1A 66 771 JSR EPLOT
772 •*XDRAW SEQ5-18 OFF
65FF: A9 32 773 LDA #$32
6601: 8D OA 60 774 STA SBLOCK
6604: A9 44 775 LDA #$44
6606: 8D OB 60 776 STA EBLOCK
6609: 20 1A 66 777 JSR EPLOT
778 *XDRAW SEQ6-18 OFF
660C: A9 44 779 LDA #$44
660E: 8D OA 60 780 STA SBLOCK
261
1 A1 XX Y
JSR EPLOT
6619: 60 784 RTS
785
786 ^EXPLOSION PLOTTING SUBROUTINE
787 *
661A: AE OA 60 788 EPLOT LDX S BLOCK ;LOCATION IN PARTICLE POSITION
789 ;T0 START DRAWING
661D: A9 03 790 EPLOT LDA #$03 ;EACH BLOCK 3 LINES DEEP
661F: 8D 11 60 791 STA DEPTH
6622: 18 792 EL00P1 CLC
6623: AD OC 60 793 LDA VERT ;TOP OF SHIP
6626: 69 04 794 ADC #$04 ;NOW CENTER OF SHIP
6628: 18 795 CLC
6629: 7D 9A 69 796 ADC EOFFY , ;ADD RELATIVE Y POS OF PARTICLE.
662C: C9 00 797 CMP #$00 ;TEST NOT OFF TOP SCREEN
662E: 90 21 798 BLT NOPLOT J IF OFF, DON’T LOT
6630: C9 CO 799 CMP #$C0 ;TEST NOT OFF BOTTOM SCREEN
6632: BO ID 800 BGE NOPLOT ;IF OFF, DON’T PLOT
6634: 8D 09 60 801 STA TEMPI STORE VALUE IN TEMPI ;
262
::: 2 Y Y
264
A
679A 50 50 50
679D 50 50 50
67A0 50 50 925 HEX 5050505050505050
67A2 DO DO DO
67A5 DO DO DO
67A8 DO DO 926 HEX DODODODODODODODO
67AA 50 50 50
67AD 50 50 50
67B0 50 50 927 HEX 5050505050505050
67B2 DO DO DO
67B5 DO DO DO
67B8 DO DO 928 HEX DODODODODODODODO
67BA 50 50 50
67BD 50 50 50
67C0 50 50 929 HEX 5050505050505050
67C2 DO DO DO
67C5 DO DO DO
67C8 DO DO 930 HEX DODODODODODODODO
931 *
67CA 20 24 28
67CD 2C 30 34
67D0 38 X 932 YVERTH HEX 2024282C3034383C
67D2 20 24 28
67D5 2C 30 34
67D8 38 X 933 HEX 2024282C3034383C
67DA 21 25 29
67DD 2D 31 35
67E0 39 3D 934 HEX 2125292D3135393D
67E2 21 25 29
67E5 2D 31 35
67E8 39 3D 935 HEX 2125292D3135393D
67EA 22 26 2A
67ED 2E 32 36
67F0 3A 3E 936 HEX 22262A2E32363A3E
67F2 22 26 2A
67F5 2E 32 36
67F8 3A 3E 937 HEX 22262A2E32363A3E
67FA 23 27 2B
67FD 2F 33 37
6800 3B 3F 938 HEX 23272B2F33373B3F
6802 23 27 2B
6805 2F 33 37
6808 3B 3F 939 HEX 23272B2F33373B3F
680A 20 24 28
680D 2C 30 34
6810 38 X 940 HEX 2024282C3O34383C
6812 20 24 28
6815 2C 30 34
6818 38 X 941 HEX 2024282C3034383C
681A 21 25 29
681D 2D 31 35
6820 39 3D 942 HEX 2125292D3135393D
6822 21 25 29
6825 2D 31 35
6828 39 3D 943 HEX 2125292D3135393D
68 2 22 26 2A
682D 2E 32 36
6830 3A 3E 944 HEX 22262A2E32363A3E
6832 22 26 2A
6835 2E 32 36
265
6838: jA 3E 945 HEX 22262A2E32363A3E
683A: 23 27 2B
683D: 2F 33 37
6840: 3B 3F 946 HEX 23272B2F33373B3F
6842: 23 27 2B
6845: 2F 33 37
6848: 3B 3F 947 HEX 23272B2F33373B3F
684A: 20 24 28
684D: 2C 30 34
6850: 38 3C 948 HEX 2024282C3034383C
6852: 20 24 28
6855: 2C 30 34
6858: 38 3C 949 HEX 2024282C3034383C
685A: 21 25 29
685D: 2D 31 35
6860: 39 3D 950 HEX 2125292D3135393D
6862: 21 25 29
6865: 2D 31 35
6868: 39 3D 951 HEX 2125292D3135393D
686A: 22 26 2A
686D: 2E 32 36
6870: 3A 3E 952 HEX 22262A2E32363A3E
6872: 22 26 2A
6875: 2E 32 36
6878: 3A 3E 953 HEX 22262A2E32363A3E
687A: 23 27 2B
687D: 2F 33 37
6880: 3B 3F 954 HEX 23272B2F33373B3F
6882: 23 27 2B
6885: 2F 33 37
6888: 3B 3F 955 HEX 23272B2F33373B3F
956 *
957 -TABLES TO KEEP TRACK OF OBJECTS
958 *
688A : 00 00 00
688D: 00 00 00
6890: 00 959 TABLEX HEX OOOOOOOOOOOOOO
6891: 28 38 48
6894: 58 68 28
6897: 38 960 TABLEY HEX 28384858682838
6898: 01 01 01
689B: 01 01 01
689E: 01 961 ALIVE HEX 01010101010101
689F: 00 00 00
68A2: 00 00 00
68A5: 00 962 USFLAG HEX OOOOOOOOOOOOOO
68 A6: 00 00 00
68A9: 00 00 00
68AC: 00 963 ONFLAG HEX OOOOOOOOOOOOOO
68AD: 2D 40 70
68B0: 90 CO DO
68B3: FO 964 ONPOS HEX 2D407 090C0D0F0
68B4: 00 00 01
68B7: 00 00 00
68BA: 01 965 SHPADR HEX 00000100000001
966 *
68BB: 04 967 SHPLO DFB SHAPES
68BC: 14 968 DFB SHAPES+$10
68BD: 24 969 DFB SHAPES+120
68BE: 34 970 DFB SHAPES+$30
266
::: F
971 *
972 *MASK SHIP TABLE
68BF: 01 00 00
68C2: 03 00 00
68C5: 07 00 973 MSHIP HEX 0100000300000700
68C7: 00 OF 00
68CA: 00 7F 7F
68CD: 00 7F 974 HEX 000F00007 F 7 F007
68CF: IF 07 7F
68D2: 7F IF 78
68D5: 7F 7F 975 HEX 1F077F7F1F787F7F
976 *SHAPE TABLE SHIP
68D7 80 00 00
68DA: 82 00 00
68DD: 82 00 977 SHIP HEX 8000008200008200
68DF: 00 8A 00
68E2: 00 AA D5
68E5: 80 AA 978 HEX 008A0000AAD580AA
68E7: 95 82 AA
68EA: D5 8A A8
68ED: D5 AA 979 HEX 9582AAD58AA8D5AA
*•
980
981 *SHAPE BOMB
68EF: 07 7E 07 982 SHBOMB HEX 077E07
983 DS 18
984 a*
268
A
1021 DS 24
1022 *
1023 *SHAPES FOR SCOREKEEPING
6A08: 3F 01 01
6A0B: 3F 20 20
6A0E: 3F 00 1024 SCOREWD HEX 3F01013F20203F00
6A10: 3C 02 01
6A13: 01 01 02
6A16: 3C 00 1025 HEX 3C02010101023C00
6A18: IE 21 21
6A1B: 21 21 21
6A1E: IE 00 1026 HEX 1E2121212121 1E00
6A20: 3F 21 21
6A23: 3F 09 11
6A26: 21 00 1027 HEX 3F21213F091 12100
6A28: 3F 01 01
6A2B: IF 01 01
6A2E: 3F 00 1028 HEX 3F0101 1F01013F00
1029 * INDEX TO LO BYTE SCORE NUMBER SHAPES
6A30: 3A 1030 SCOREP DFB SCORESH
6A31 : 42 1031 DFB SC0RESH+$08
6A32: 4A 1032 DFB SC0RESH+$10
6A33; 52 1033 DFB SC0RESH+$18
6A34: 5A 1034 DFB SC0RESH+$20
6A35: 62 1035 DFB SC0RESH+$28
6A36: 6A 1036 DFB SC0RESH+$30
6A37 : 72 1037 DFB SC0RESH+$38
6A38: 7 1038 DFB SC0RESH+$40
6A39: 82 1039 DFB SC0RESH+$48
1040 *
1041 * NUMBER SHAPES
6A3A: 1C 22 22
6A3D: 22 22 22
6A40: 1C 00 1042 SCORESH HEX 1C2 222222222 1C00
6A42: 08 OC 08
6A45: 08 08 08
6A48: 1C 00 1043 HEX 080C08080808 1C00
6A4A: 1C 22 20
6A4D: 18 04 02
6A50: 3E 00 1044 HEX 1C22201804023E00
6A52: 3E 20 10
6A55: 08 10 22
6A58: 1C 00 1045 HEX 3E20100810221C00
6A5A: 18 14 12
6A5D: 11 3F 10
6A60: 10 00 1046 HEX 1814121 13F101000
6A62: 3E 02 02
6A65: 3E 20 22
6A68: 1C 00 1047 HEX 3E02023E202 2 1 COO
6A6A: 38 04 02
6A6D: IE 22 22
6A70: 1C 00 1048 HEX 3804021E22221C00
6A72: 3E 20 10
6A75: 08 04 04
6A78: 04 00 1049 HEX 3E20100804040400
6A7A: 1C 22 22
6A7D: 1C 22 22
6A80: 1C 00 1050 HEX 1C22221C22221COO
6A82: 1C 22 22
6A85: IE 20 10
269
6A88: OE 00 1051 HEX 1C22221E2010OE00
6A8A: 1C 22 22
6A8D: 22 22 22
6A90: 1C 00 1052 HEX 1C22222222221COO
EVEN ODD
———— A A a • — — _
40 01 40 01
——— 9 • 9 • # — —— 70 07 70 07
• • 30 06 30 06
• • • • • • AA D5 D5 AA
— — _ A a A
•—• •
j
AA D5 D5 AA
— w 9 9 • •- # — —— 70 07 70 07
00 00 00 00
00 00 00 00
brbrbrbrbrb R B R
EVEN OFFSET SHAPE
270
Y
that direction, because only two adjacent lines are involved, and the screen
addresses for those two lines are easily referenced from lookup tables.
The algorithm for scrolling down the screen involves taking the bytes from
one line and storing them in the line directly below. This is done across a row
for each column. The most important thing is that you start from the bottom of
the screen or you will overwrite lines. Also, the bottom line must be transferred
to the top of the screen if a wrap-a-round effect is desired. A cute trick which
minimizes the code considerably is to extend the YVERT table one extra byte.
That byte is the address of the Oth line. Therefore, line #191 can be moved to
line #192, which is actually line #0.
Moving an entire screen upwards a single line by this method is not that fast,
but usually, as in racing games, only narrow background strips need to be
scrolled. This produces more reasonable scrolling rates. Other techniques
involve using a background that occupies every other screen line, then scrolling
it two lines at a time. The Phantom’s Five game appears to use this method.
Another approach is to utilize straight in-line code, where scrolling for all the
lines is done a column at a time. Bytes are moved upwards with the following
code
LDA $3CD0,Y
STA $3FD0,Y
LDA $2800,Y
STA $2C00,Y
LDA $2400,
STA $2800,
LDA $2000,Y
STA $2400,
where Y is looped from $0 to $27 across the screen. This code is at least three
times faster than the first method.
Scrolling the screen upwards is quite similar to scrolling the screen
downwards. It requires moving the screen memory from the lower line to the
upper line, across all 40 columns. The bytes in the Oth line must be moved to
the 191st line if a wrap-a-round effect is desired. This requires extra code, since
we can’t do any fancy tricks as we did before.
The two one up and one down, have been put together in
scrolling routines,
the following program. The windows have been set so that part of the
scrolling
screen scrolls up and part of the screen scrolls down, while the remainder
remains stationary. The variables that control the windows are LEFT and
RIGHT for scrolling down, and LEFTU and RIGHTU for scrolling up.
These values can be modified in lines 16, 18, 20 and 22.
The flow charts and code are presented below:
271
Y r egister = $BF
|
START AT TOP
Y register = 0
INCREMENT Y re gister
LINE = Y register
-H Y register = LEFTU I
no
— j
DONE WITH LINE?
274
YY
—
607D B9 AA 60 66 STARTU LDA YVERTL, Y ;FIND SCREEN ADDRESS
6080 85 06 67 STA TOPL ;0F TOP LINE
6082 B9 6B 61 68 LDA YVERTH,
6085 85 07 69 STA TOPH
6087 C8 70 INY ;NEXT ROW
6088 B9 AA 60 71 LDA YVERTL ,Y ;FIND SCREEN ADDRESS —
608B 85 08 72 STA BOTTOML ;0F BOTTOM LINE
608D B9 6B 61 73 LDA YVERTH,
6090 85 09 74 STA BOTTOMH
6092 8C 05 60 75 STY LINE ;TEMP STORE Y REGISTER
6095 AC 06 60 76 LDY LEFTU ; START SHIFTING LINE
6098 B1 08 77 LOOP 2 LDA (BOTTOML), Y;L0AD BYTE ON SCREEN
609A 91 06 78 STA (TOPL) Y, STORE BYTE ON LINE ABOVE
;
60 A 7 DO D4 84 BNE STARTU
60A9 60 85 RTS
60AA X 00
00
60AD 00 0000
60B0 XX 86 YVERTL HEX oxxxooxoxoo
60B2 X 80 80
60B5 80 80 80
60B8 80 80 87 HEX 8080808080808080
60BA XXX
60BD 00 X X
60C0 X X 88 HEX 0X00X0X000X0
60C2 : X 80 80
60C5 80 X 80
6X8 80 X 89 HEX 8X0808080808080
6XA X X 00
6XD X X 00
60D0 X X 90 HEX OXOOXXXOOXO
60D2 X 80 X
60D5 80 X 80
60D8 80 X 91 HEX 8080808080808080
60DA : X 00 X
60DD : XXX
60E0 : X X 92 HEX 0X0X0000000X0
60E2 : X 80 80
60E5 80 X 80
60E8 80 X 93 HEX 8080808080808080
60EA 28 28 28
60ED 28 28 28
60F0 28 28 94 HEX 2828282828282828
60F2 A8 A8 A8
60F5 A8 A8 A8
60F8 A8 A8 95 HEX A8A8A8A8A8A8A8A8
60FA 28 28 28
60FD 28 28 28
61X 28 28 96 HEX 2828282828282828
6102 A8 A8 A8
6105 A8 A8 A8
6108: A8 A8 97 HEX A8A8A8A8A8A8A8A8
610A: 28 28 28
610D: 28 28 28
6110: 28 28 98 HEX 2828282828282828
6112: A8 A8 A8
6115: A8 A8 A8
6118: A8 A8 99 HEX A8A8A8A8A8A8A8A8
61 1A: 28 28 28
61 ID: 28 28 28
6120: 28 28 100 HEX 2828282828282828
6122: A8 A8 A8
6125: A8 A8 A8
6128: A8 A8 101 HEX A8A8A8A8A8A8A8A8
612A: 50 50 50
612D: 50 50 50
6130: 50 50 102 HEX 5050505050505050
6132: DO DO DO
6135: DO DO DO
6138: DO DO 103 HEX DODODODODODODODO
613A: 50 50 50
613D: 50 50 50
6140: 50 50 104 HEX 5050505050505050
6142: DO DO DO
6145: DO DO DO
6148: DO DO 105 HEX DODODODODODODODO
61 4A: 50 50 50
614D: 50 50 50
6150: 50 50 106 HEX 5050505050505050
6152: DO DO DO
6155: DO DO DO
6158: DO DO 107 HEX DODODODODODODODO
61 5A: 50 50 50
615D: 50 50 50
6160: 50 50 108 HEX 5050505050505050
6162: DO DO DO
6165: DO DO DO
6168: DO DO 00 109 HEX DODODODODODODODOOO
110 *
616B: 20 24 28
616E: 2C 30 34
6171: 38 X 111 YVERTH HEX 2024282C3034383C
6173: 20 24 28
6176: 2C 30 34
6179: 38 3C 112 HEX 2024282C3034383C
617B: 21 25 29
617E: 2D 31 35
6181: 39 3D 113 HEX 2125292D3135393D
6183: 21 25 29
6186: 2D 31 35
6189: 39 3D 114 HEX 2125292D3135393D
618B: 22 26 2A
618E: 2E 32 36
6191: 3A 3E 115 HEX 22262A2E32363A3E
6193: 22 26 2A
6196: 2E 32 36
6199: 3A 3E 116 HEX 22262A2E32363A3E
619B: 23 27 2B
619E: 2F 33 37
61A1: 3B 3F 117 HEX 23272B2F33373B3F
61A3: 23 27 2B
::
61A6: 2F 33 37
61A9: 3B 3F 118 HEX 23272B2F33373B3F
61AB: 20 24 28
61AE: 2C 30 34
61B1 38 3C 119 HEX 2024282C3034383C
61B3: 20 24 28
61B6: 2C 30 34
61B9: 38 3C 120 HEX 2024282C3034383C
61BB: 21 25 29
61BE: 2D 31 35
61C1 39 3D 121 HEX 2 1 25292D3135393D
61C3: 21 25 29
61C6: 2D 31 35
61C9: 39 3D 122 HEX 2125292D3135393D
61CB: 22 26 2A
6 ICE: 2E 32 36
61D1 3A 3E 123 HEX 22262A2E32363A3E
61D3: 22 26 2A
61D6: 2E 32 36
61D9: 3A 3E 124 HEX 22262A2E32363A3E
61DB: 23 27 2B
6 IDE: 2F 33 37
61E1: 3B 3F 125 HEX 23272B2F33373B3F
61E3: 23 27 2B
61E6: 2F 33 37
61E9: 3B 3F 126 HEX 23272B2F33373B3F
61EB: 20 24 28
61EE: 2C 30 34
61F1 38 3C 127 HEX 2024282C3034383C
61F3: 20 24 28
61F6: 2C 30 34
61F9: 38 3C 128 HEX 2024282C3034383C
61FB: 21 25 29
61FE: 2D 31 35
6201: 39 3D 129 HEX 2125292D3135393D
6203: 21 25 29
6206: 2D 31 35
6209: 39 3D 130 HEX 2125292D3135393D
620B: 22 26 2A
620E: 2E 32 36
6211: 3A 3E 131 HEX 22262A2E32363A3E
6213: 22 26 2A
6216: 2E 32 36
6219: 3A 3E 132 HEX 22262A2E32363A3E
621B: 23 27 2B
621E: 2F 33 37
6221: 3B 3F 133 HEX 23272B2F33373B3F
6223: 23 27 2B
6226: 2F 33 37
6229: 3B 3F 20 134 HEX 23272B2F33373B3F20
— END ASSEMBLY-
ERRORS: 0
556 BYTES
277
XY
6022: 90
EC 21 BLT NX BLOCK ;N0, CONTINUE
6024: 4C
OE 60 22 JMP START SCROLL ENTIRE SCREEN AGAIN
;
278
6044: FO 03 40 BEQ DRAW3 FINISHED?
;
—END ASSEMBLY-
ERRORS: 0
122 BYTES
280
CHAPTER 8
just go on and on forever. Limits should be set. Sometimes these take the form
of time limits or the amount of ammunition, balls or ships left.
For a game to be considered challenging, it should have a goal where the out-
come is uncertain. If the player is certain to reach the goal or certain not to
reach it, the game is unlikely to be a challenge and the player will lose interest.
It is very easy to introduce randomness into a game by either hiding important
These levels, oftenwith ego satisfying names like Star Commander or Pilot,
can be set by the player. Many games are designed to become harder the fur-
ther you get into them. This increasing skill level requirement presents an add-
ed challenge, while preventing the player from growing complacent. Often, the
technique is to speed up the game or place additional enemy craft into the bat-
tle. The player is required to play faster and better, honing his reflexes during
the process.
Any good game should offer a reward for reaching increasingly difficult
levels of play. Often, bonus points, extra balls, ships, or more ammunition are
rewarded for exceeding score thresholds. It is important that there be greater
rewards for winning than losing. A person’s ego is involved. A player wants to
beat a challenging game, not to be humiliated each time he loses.
Games either need to fulfill a player’s fantasy or stimulate their curiousity.
Computer game fantasies derive some of their appeal from the emotional needs
that they satisfy. Different fantasies appeal to different people.
Appealing to a player’s curiosity is often effective in keeping a game in-
teresting. While novelty is sometimes a crucial factor in the original purchase,
if the game has little depth, it becomes repetitious and boring. One method
that appeals to many game designers is to have the game progress to slightly
different scenarios. Some games change the opposition, while others vary the
scenery; some do both. The player has to excel if he is to satisfy his curiosity.
Games like Threshold, which progresses through 24 sets of alien spacecraft, or
Pegasus II, in which the scenery changes and the attacking aliens vary, offer
strong curiosity incentives.
A game’s controllability is one of the more important considerations in a
game’s design. It is sometimes referred to as human engineering. Designer’s
usually choose between keyboard and paddle/joystick control. While eye/hand
coordination more effective using paddles or joysticks, programmers attemp-
is
ting to creategames with too many control functions will opt for a keyboard
control system. At times, they produce a game that requires nine or ten
keyboard controls which, unfortunately, only a pianist can operate. Some
prefer keyboard controls because they offer a faster response time than paddle
inputs, or they are easier to program, or this approach doesn’t limit the market
to an audience with expensive joysticks. I don’t think the latter should in-
fluence your choice, but thought should be given to which method would make
the game more enjoyable. Games that require considerable time to master the
controls, often prove too frustrating to play.
Apparently, Apple owners like games which pit them against a competitive
computer opponent. There are several multi-player games in which groups of
two or more will simultaneously compete against each other. Most of these
contests are sports or card games involving two or more players. The
cooperative game is rarely seen, except in games where the computer com-
282
petitor is much too skillful. The arcade game “Ripoff 9
involves a computer
opponent that is more than a match
two players playing simultaneously. It
for
is the lone exception to the one-player-against-the-machine game.
283
a game of consumption rather than one of destruction, it appeals to players of
both sexes.
The game becomes a learning experience to the more advanced player, since
the ghosts follow a discernible pattern rather than move randomly. A
player is
able to eventually predict their movements and consequently develop a tech-
nique to clearall the dots on a particular level. The long term goal is survival
and the highest score. The game is designed so that you gain more pleasure as
you get better. Thus, players are willing to devote the time and money to
master the game.
Scrolling games, such as Scramble and Vanguard as played in the arcades,
and Pegasus II on the Apple, wherein your ship travels over a multi-screen
world, benefit strongly from player curiousity and visual variety. Vanguard, a
shoot- ’em-up game in which your ship is attacked by a variety of enemy vessels
and creatures, has an extremely long sinuous tunnel with various types of
chambers. The game has so many sections, combined with scrolling directions
which change from horizontal to diagonal to vertical, that it is like playing
many different arcade games at once. The player is given the option several
times during the game to enter battle with a time-limited energized spacecraft
which isequipped for ramming the enemy, or merely four plain old directional
lasers. A map displayed at the lower corner informs the player of his progress.
The curiousity factor is so enticing in this game, thirty seconds are provided to
lure you into inserting another quarter in order to allow you to continue from
where you left off with this unique form of arcade addiction.
The popularity of Pacman can be attributed to the game’s design. First, it
satisfies the fantasy concept of a person’s childhood dreams. As children, they
dreamt that they were being chased by evil monsters or ghosts, and felt
powerless to stop them. They wished that there was some way to turn the
tables, if only for a few moments. Pacman’s four energy dots fulfill that fan-
tasy. The game also offers the visual feedback of the number of remaining dots
to be eaten at each level. And since clearing each individual level is an im-
mediate goal, even beginners believe a level can be cleared.
The game becomes a learning experience to the more advanced player, since
the ghosts follow a discernible pattern rather than move randomly. A player is
able to eventually predict their movements and consequently develop a tech-
nique to clear all the dots on a particular level. The long term goail is survival
and the highest score. The game is designed so that you gain more pleasure as
you get better. Thus, players are willing to devote the time and money to
master the game.
Scrolling games, such as Scramble and Vanguard as played in the arcades,
and Pegasus II on the Apple, wherein your ship travels over a multi-screen
world, benefit strongly from player curiousity and visual variety. Vanguard, a
shoot- ’em-up game in which your ship is attacked by a variety of enemy vessels
and creatures, has an extremely long sinuous tunnel with various types of
chambers. The game has so many sections, combined with scrolling directions
which change from horizontal to diagonal to vertical, that it is like playing
284
many different arcade games at once. The player is given the option several
times during the game to enter battle with a time-limited energized spacecraft
which is for ramming the enemy, or merely four plain old directional
equipped
lasers. A mapdisplayed at the lower corner informs the player of his progress.
The curiousity factor is so enticing in this game, thirty seconds are provided to
lure you into inserting another quarter in order to allow you to continue from
where you left off with this unique form of arcade addiction.
Pegasus II, as implemented on the Apple, offers variety in terrain, targets
and types of enemy. Besides trying to survive ground-launched rockets, a
meteor field, attacking birds, and flying saucers, you must defeat a horde of
laser-armed dragons that separate you from your refueling base. Your im-
mediate goal is to reach the base before running out of fuel. This means ac-
curate shooting, for enemies like dragons can delay your rendezvous with the
base. Long term goals consist of reaching the tunnel and scoring the highest
number of points.
In closing, I hope I have provided you with some acquired skills for
creating
your own visual masterpieces. The arcade versions described above are, as of
this writing, being surpassed in quality by the dazzling array
of games current-
ly arriving on the personal computer market from talented
graphics program-
mers.
My hope is that this book has provided some techniques and insights into
graphics game design and programming; possibly even enough to allow
you to
join the ranks of successful Apple game designers.
285
INDEX
286
1
Venice, California.
288
• Learn Apple Hi-Res Graphics from BASIC and machine
language.
$19.95