Compute S Programming The Commodore 64 The Definitive Guide
Compute S Programming The Commodore 64 The Definitive Guide
Foreword vii
Appendices 563
A. A Beginner's Guide to Typing In Programs 565
B. How to Type In Programs 567
C. The Automatic Proofreader
Charles Brannon 569
D. Screen Location Table 572
E. Screen Color Memory Table 573
F. Screen Color Codes 574
G. ASCII Codes 575
H. Commodore ASCII Codes 576
I. Screen Character Codes 580
J. The VIC-II Chip 582
K. The SID Chip 585
L. Device Numbers 586
M. Decimal-Hexadecimal Conversion Table 587
N, Opcodes in Detail 588
O. Table of 6502/6510 Opcodes 590
P, 6502/6510 Quasi-Opcodes 591
Q. Converting Commodore 64, VIC-20, and CBM Programs 593
R. Supermon 64 596
Index 603
Disk Coupon 61T
Foreword
vn
Chapter 1
Introduction
The two main objectives of this book are to teach competent programming on the
Commodore 64, and to provide a comprehensive reference book for people wanting
quick, accurate answers to questions about the 64. These two goals are difficult
enough to achieve. For example, while virtually everyone begins with BASIC and
progresses to machine language (ML), it is often desirable to use both ML and
BASIC in examples, which means comparative newcomers to the 64 find themselves
skipping sections of temporarily difficult text. It is practically impossible to arrange
the material so that everything falls into a natural sequence for all readers, because
many of the chapter headings themselves can't be understood properly without
some knowledge of the machine's structure.
This book explains BASIC and ML in sequence from simple to complex, ending
with a chapter on mixing ML with BASIC, an efficient 64 programming method.
These chapters are interspersed with machine organization details and are followed
by in-depth examinations of major topics—sound, graphics, tape, and so on.
For these reasons, the text contains two kinds of programs. First, there are very
short routines, intended to be typed in quickly (and therefore with little chance of er
ror). Second, there are longer, more practical programs, which use graphics, sound,
tape, disks, and all the other features of the 64. The shorter, example programs cover
how BASIC commands are used, how special features work (notably the VIC and
SID chips), and how to use ML. Many useful routines are included, and these can be
used successfully without any knowledge of their internal operation. Many readers will
thus be able to acquire BASIC and ML experience painlessly as they use this book.
Programming the Commodore 64 is one of a set of three books, which also in
cludes Programming the VIC and the earlier Programming the PET/CBM. The books
have been written entirely independently of Commodore; they contain criticisms,
comments, hints, and a great deal of otherwise unpublished information.
Programming Your 64
The 64 is one of the world's most popular microcomputers; like all big-selling
computers it is both rather inexpensive and rather easy to use. But many owners
have found that however easy using other people's programs may be, writing their
own programs for the 64 is not so simple. Reliable information has been difficult to
find—until now.
Programming the Commodore 64 shows you how to plan and write BASIC pro
grams, how to move programs from tape to disk to save time, and how to play mu
sic while a program runs. It also explains how to program the function keys, round
numbers to two places, use high-resolution graphics, design and store your own
graphics characters, display a screenful of sprites, make the screen scroll smoothly,
and save sections of memory to tape. This is only a small sample of problems which
have puzzled many 64 users. All these and more are comprehensively discussed
here.
About This Book
The 64 is at times a difficult machine to program, in spite of what you may have
heard. But programming is easier if you have a good overall understanding of the
machine, and Programming the Commodore 64 attempts to generalize results rather
than give isolated facts. For example, the way the VIC chip determines what kind of
graphics to display is crucially important to understanding the system, and there is a
handy table to illustrate this.
This book is just above the introductory level. There's not enough room to cover
the elementary topics (which are often better learned directly at the keyboard or
from a friend who knows the machine) and still provide the information you need
on advanced topics. But prior knowledge of Commodore systems is not essential, so
anyone with intelligence and some microcomputer experience ought to be able to
grasp the fundamentals of the 64 fairly easily.
Several versions of the 64 exist: the repackaged SX-64, the PET 64 (which has
no sound chip and only monochrome graphics), and 64s with slightly different
ROMs. As Chapter 8 explains, these computers run software similarly, but not ex
actly alike. Most of the book is applicable to all these machines, but emphasis is on
the more common models.
The programs have been tested and will almost always work without difficulty.
If there are problems, a program may have been entered incorrectly, or the soft
nature of the 64 may be to blame—there are many special memory locations in the
64, any one of which can cause odd results if altered. The first thing to do when a
program will not run properly is to check the program carefully. If that doesn't work,
it's usually easiest to save the program, turn the 64 off, then back on, reload the pro
gram, and try again. Some of the programs in this book use the "Automatic Proof
reader," Appendix C, which allows you to quickly and easily check each line you
have entered for accuracy.
Information with the widest appeal—BASIC, graphics, music, and full use of the
64's RAM capacity—is documented fully. However, minority interests are not ex
cluded. Programming the Commodore 64 doesn't gloss over difficulties and evade im
portant topics. Many people have gone to great lengths to check the information in
this book, for it is intended to be reliable. Nevertheless, there are certain to be errors,
and for any resulting inconvenience or bafflement, we apologize in advance.
Conventional Terms
The special Commodore logo key (located at the bottom left of the 64's keyboard)
will be called the Commodore key. Program listings and short examples, like SYS
64738, will usually be in capitals to mimic their appearance on the screen and print
ers of the 64 in its uppercase mode. This is the mode the 64 is in when you turn the
power on. Text can, of course, appear in lowercase mode, usually after pressing
SHIFT-Commodore key. (Don't type the hyphen; just hold down the SHIFT key and
press the Commodore key.) In either case, programs are mostly entered using
unSHIFTed keys. Named keys (CLR/HOME, SHIFT, CTRL, fl, and so on) are gen
erally referred to as they appear on the keyboard of the 64.
Some BASIC listings have been made with a program which prints the screen
editing commands, colors, and other special characters, such as {HOME}, {CYN},
{Fl}, and so on, in place of the less readable 64 characters. With apologies to people
^ \
who can spell, the book uses the spelling Kernal for the ROM routines which handle
the screen, keyboard, and input/output devices (this is the spelling Commodore
uses). This book also uses ML as a handy abbreviation for machine language.
Hexadecimal numbers are represented here with a leading dollar sign ($). If you
don't understand hexadecimal notation, read Chapter 6. In accordance with 6502
convention, the number symbol (#) indicates a value, not an address, so that LDA
#$00 denotes the command to load the accumulator with the number 0, while LDA
$00 loads the accumulator with the value in location 0.
Many 64 BASIC programs begin with a few commands to change color from the
usual light blue characters on a dark blue background. The following line sets a
green background, white border, and black characters:
POKE 53281,5: POKE 53280,1: POKE 646,0
Some of the demonstration programs include this line; others assume that CTRL-
WHITE or some similar command has already been used to improve clarity.
Acknowledgments
Several chapters, notably those on sound and graphics, are partially the work of
Marcus West. Additional hardware information has been provided by Rod Eva of Y2
Computers, Watford, U.K. COMPUTE! Publications, Inc. in the U.S., TPUG (Toronto
PET Users Group) of Canada, and ICPUG (Independent Commodore Products Users
Group) of the U.K. have provided information.
Chapter 2
Getting to Know
the 64
The 64's Connectors
The Keyboard
Editing BASIC on the 64
Chapter 2
Commodore has been making computers for a decade or so, and became a house
hold word with the introduction of the low-priced VIC-20, followed more recently in
the early 1980s by the Commodore 64. Both machines proved remarkably successful,
far outselling other Commodore Business Machine (CBM) computers in volume.
CBM's earlier experience enabled a range of appropriately priced peripherals (tape
drives, disk drives, printers, modems) to be produced at about the same time. All
CBM machines have strong resemblances, and straightforward BASIC programs
which run on one CBM machine are likely to run on others, too. But programs of
any complexity will generally run only on the machine for which they were written.
easier to program. Pairs of paddles (rotary controllers) can be plugged into either
port. A light pen can be plugged into port 1. Chapter 16 has full programming infor
mation on these and other controllers.
The power switch, as you might have guessed, is where you turn the power
on.
The power input socket is where you plug in the connector from the power
supply unit, which requires standard household current.
The Keyboard
Chapter 6 discusses the keyboard in depth. Here, we'll briefly survey the keys and
their functions as they act just after the computer is turned on, before they are modi
fied by a program (this is called their default arrangement). Alphabetic, numeric, and
symbol keys appear as the keytop legend implies, subject to the changes mentioned
below.
SHIFT selects uppercase lettering or the right-hand graphics set, depending on
the character set in use.
Commodore key accesses the left-hand graphics set or, with the color keys, se
lects one of eight additional colors not named on the standard 64 keyboard. Where
these don't apply, the Commodore key acts as an alternative SHIFT key.
SHIFT-Commodore key (SHIFT and Commodore key together) changes the
display from uppercase, with the full keytop graphics set, to upper- and lowercase
lettering, plus the left-hand graphics set.
CTRL (Control) acts with the color keys and reverse keys to change the color of
the characters or to turn reverse video printing on and off. CTRL-A through CTRL-Z
generate CHR$(1) through CHR$(26), acting as a conventional control key—see
Chapter 3. CTRL also slows screen scrolling; this is useful when listing a program.
Function keys (f1 through f8) display nothing. In BASIC, GET is the easiest
way to detect them. (See Chapter 6 for program information.)
RUN/STOP interrupts BASIC programs. The command CONT allows resump
tion of BASIC, subject to certain conditions.
SHIFT-RUN/STOP (SHIFT and RUN/STOP together) loads, then runs the
next BASIC program on tape. Note that almost any key followed by SHIFT-
RUN/STOP runs the BASIC program in memory. A normal disk LOAD command
followed by a colon, then SHIFT-RUN/STOP will load the specified program from
disk, then run it.
RUN/STOP-RESTORE acts like a panic button; the system returns to its nor
mal state, retaining the BASIC program in memory. Chapter 6 explains both RE
STORE and RUN/STOP in detail.
CLR/HOME places the cursor at the top left of the screen; SHIFT-CLR/HOME
also erases the screen, leaving it blank, with the cursor flashing in the home
position.
INST/DEL (insert/delete) is part of the screen editing system—the set of opera
tions which allow the alteration of any part of the screen. Both keys repeat, although
INST has little effect if it isn't followed by characters on its BASIC line. The screen
editing is powerful and easy to use, despite having a few small quirks in quote
mode. To delete characters to the left of the flashing cursor, press the key
unSHIFTed; to insert characters to the right of the cursor, press SHIFT-INST/DEL.
10
Getting to Know the 64
CRSR keys (cursor keys) move the flashing cursor in the directions printed on
the keytops. UnSHIFTed, the cursor will move in the direction printed below the let
ters CRSR on the key. SHIFTed, the cursor will move in the direction shown above
CRSR. These keys automatically repeat to save time. Movement down the screen
eventually causes the text on the screen to scroll up.
RETURN is the key used by the 64 to signal that information on the screen is
ready for processing. For example, if you type:
PRINT 'HELLO"
on the screen, pressing RETURN causes HELLO to appear. Similarly, RETURN sig
nals that data typed at the keyboard in response to an INPUT statement is ready for
the 64 to process.
SHIFT-RETURN or Commodore key-RETURN moves the cursor to the next
BASIC line (not the same as the next screen line—see below), but without causing
the 64 to take in the information. (For example, if you begin to correct a line of
BASIC, but change your mind, SHIFT-RETURN leaves the line as it was.)
Quotation marks (SHIFT-2) are important in BASIC; quotation marks designate
the start or end of a string (a group of characters) that you may want to print, assign
to a variable, or manipulate in some other way. When in quote mode (more on this
later), several special characters which follow double-quotes are stored as reversed
characters. See SHIFT-RETURN above.
The space bar repeats if held down. Chapter 6 gives full information.
Apparently, nothing happens. In fact, however, the line is stored in the 64's memory
as a line of a BASIC program; any line beginning with a number (up to 63999) is
interpreted as a program line and stored. This is called program mode or deferred
mode. LIST displays the BASIC program in memory; RUN executes it. If you run the
above example, you should see HELLO on the screen.
Quote mode. In quote mode, the 64's special characters are stored for future
use. Quote mode enables you to use the 64's powerful screen control features,
including cursor moves and screen clearing, from within programs.
BASIC Terms
Variables are algebraic in nature; X=10: PRINT X prints the number 10 on the
screen, since the variable, X, has been given the value 10. The value can be altered,
so X is referred to as a variable. The next chapter explains this more fully.
Keywords are the commands recognized by BASIC; PRINT is one. All the
11
Getting to Know the 64
keywords are listed, with examples, in the next chapter. Note that most keywords
can be entered in a shortened form. For example, PRINT can be abbreviated with a
question mark.
10?
12
Chapter 3
BASIC Reference
Guide
• BASIC Syntax
• BASIC Keyword Dictionary
• BASIC Error Message Dictionary
Chapter 3
BASIC Syntax
BASIC is now the most popular language for personal computers. It's easy to write,
test, and edit BASIC, and simple programs can be written by people with very little
computing experience, which is exciting and encouraging. BASIC is sometimes de
scribed as being like English, but the resemblance is tenuous.
Almost every machine has its own version of BASIC. As a result, expressions
work differently on different machines. The information in this book applies to the
version of BASIC contained in the 64.
Variables
A variable is an algebraic symbol used to represent a number or string of characters.
X, X%, and X$, respectively, are numeric (values between ±2.93873588E-39 and
±1.70l4ll83E38), integer (whole numbers between -32768 and +32767), and
string (up to 255 characters) variables. If the variables haven't been assigned values,
numeric and integer variables default to 0, strings to the null character, a string of
zero length.
A variable, as the name implies, can be changed at will. The direct mode line
X=l: PRINT X: X=2: PRINT X
15
BASIC Reference Guide
• Any further alphanumerics are allowed, but not considered part of the variable
name.
Operators
Binary operators take two items of the same type and generate a single new item of
the same type from them. Unary operators modify a single item. The numeric op
erators supported by BASIC on the 64 are standard, much like those supported by
other computer languages, while the string and logical operators are less similar.
When a string or arithmetic expression is evaluated, the result depends on the
priority assigned to each operator and the presence or absence of parentheses. In
both string and arithmetic calculations, parentheses insure that the entire expression
within the parentheses is evaluated as a unit before the other operations are per
formed. The rules for using parentheses dictate levels of priority, so that an ex
pression in parentheses within another set of parentheses will be evaluated first. In the
absence of parentheses, priority is assigned to operators in this order, starting with
the highest level:
t Exponents
+ or — Unary plus or minus sign—positive or negative number
• or / Multiply or divide
+ or — Binary plus or minus—addition or subtraction
< = or > Comparisons—less than, equal to, greater than
NOT Logical NOT—unary operator
AND Logical AND—binary operator
OR Logical OR—binary operator
Logical operators are also called Boolean operators. In an expression like A AND
B, A and B are called operands.
16
BASIC Reference Guide
Functions
Some BASIC keywords are valid only when followed by an expression in paren
theses; they may be used on the right of assignment statements or within ex
pressions. These are functions: They return a value dependent on the expression in
parentheses. Numeric functions return numeric values and include SQR, LOG, and
EXP; string functions, which include LEFT$, MID$, RIGHTS, and CHR$, return
string values. (The last character of all string functions is a $, like that of string vari
able names.) PEEK, though not a function in the mathematical sense, has the syntax
of a numeric function and is considered one. Some functions (like FRE) take a so-
called dummy argument: an expression required by the interpreter's syntax-checking
routine, but ignored by the code which evaluates the function. Typically, the dummy
parameter is a 0, for convenience. The line below is an example:
PRINT FRE(O)
Expressions
A numeric expression is a valid arrangement of numbers, numeric functions, real and
integer variables, operators and parentheses, or logical expressions. Numeric ex
pressions can replace numbers in many BASIC constructions, for example, the right
side of the assignment statement:
X=SQR(M)+PEEK(SCREEN + J)
17
BASIC Reference Guide
looks for two zero bytes, then ends, which is the desired result, but:
IF PEEK(X) AND PEEK(X+1)=O THEN END
Statements
A statement is a syntactically correct portion of BASIC separated from other state
ments by an end-of-line marker or a colon. All statements begin with a BASIC
keyword, or, where LET has been omitted, with a variable. The different types of
statements are discussed below.
• Assignment statements. LET variable = expression. (LET is optional; but its presence
makes the intention behind arithmetically impossible statements, like X=X+1,
clearer for the beginner. Languages like Pascal indicate assignments with the sym
bol :=, which is read as "becomes.")
• Conditional statements. IF logical expression THEN statement.
• Program control statements. For example, GOTO, GOSUB, RETURN, STOP.
• Input statements. Fetch data from device or from DATA statement: INPUT, GET,
INPUT#, GET#, READ.
• Looping statements. FOR-NEXT loops, for example.
• Output statements. Send data to screen, disk, cassette, or other device: PRINT,
PRINT#.
• REM statements. These allow the programmer to include comments for documenta
tion. The interpreter detects the REM statement and ignores the remainder of the
line when the program runs. Program lines which are never run and lines that con
tain only colons can be included in this category.
• Conversion statements. These convert between string variables and literals, real vari
ables and numbers, and integers and numerals. Functions like ASiC, CHR$, STR$,
and VAL are examples.
BASIC programs are made up of numbered program lines; each program line is
made up of statements, separated from eacft other by colons where two or more
statements are used on the same program jihe. Spaces generajjy are ignored outside
quotation marks, as are multiple colons.
18
BASIC Reference Guide
ABS
Type: Numeric function
Syntax: ABS(numeric expression)
Modes: Direct and program modes are both valid.
Token: $B6 (182)
Abbreviated entry: A SHIFT-B
Purpose: ABS returns the absolute value of the parenthesized numeric expression. In
other words, ABS makes a negative number or expression positive.
Examples:
1. 50 IF ABS(TARGET-X) <.01 THEN PRINT "DONE": END
This shows how to check for approximate equality; when TARGET is 6, the
program ends only if X is between 5.99 and 6.01. This kind of text is typically
used in iterative computations in which a calculated value is expected to converge
to a given value.
2. 100 IF ABS(X1-X2)<3 AND ABS(Y1-Y2)<3 GOTO 90
From a game program, this recalculates starting positions on screen for two
players if randomly generated starting positions are too close.
19
BASIC Reference Guide
AND
Type: Logical operator
Syntax: Logical or numeric expression AND logical or numeric expression
Modes: Direct and program modes are both valid.
Token: $AF (175)
Abbreviated entry: A SHIFT-N
Purpose: AND applies the logical AND operator to two expressions. For the pur
poses of the AND comparison, numeric expressions are evaluated as 16-bit signed
integers, so each operand must be in the range —32768 to 32767. Values outside this
range result in an 7ILLEGAL QUANTITY ERROR. Each of the 16 bits in the first op
erand is ANDed with the corresponding bit in the second operand, resulting in a 16-
bit, two-byte integer. The four possible combinations of corresponding individual bits
are:
0 AND 0 = 0
0 AND 1 = 0
1 AND 0 = 0
1 AND 1 = 1
20
BASIC Reference Guide
ASC
Type: Numeric function
Syntax: ASC(string expression at least one character long)
Modes: Direct and program modes are both valid.
Token: $C6 (198)
Abbreviated entry: A SHIFTS
Purpose: This function returns a number in the range 0-255 corresponding to the
ASCII value of the first character in the string expression. It is generally used when
this number is easier to handle than the character itself. See the Appendices for a
table of ASCII values.
Note that the converse function to ASC is CHR$, so ASC(CHR$(N)) is the same
as N, and CHR$(ASC(//P//)) is the character P. All keys except RUN/STOP, SHIFT,
CTRL, the Commodore key, and RESTORE can be detected with GET and ASC.
Examples:
1. X = ASC (X$+CHR$(0))
Calculates the ASCII value of any character X$. Adding CHR$(0) allows
detection of the null character, which otherwise gives 7ILLEGAL QUANTITY
ERROR.
2. X = ASC(X$)-192
Converts uppercase (SHIFTed) A-Z to 1-26. Useful when computing
checksums, where each letter has to be converted to a number.
3. 1000 IF PEEK(L)=ASC("*") THEN PRINT "FOUND AT" L
Shows how using ASC can make your programs more readable; the example
is part of a routine to search memory for an asterisk.
ATN
Type: Numeric function
Syntax: ATN(numeric expression)
Modes: Direct and program modes are both valid.
Token: $C1 (193)
Abbreviated entry: A SHIFT-T
Purpose: This is the arc tangent, or inverse tangent, function. This function returns,
in radians in the range — tt/2 to + v/2, the angle whose tangent is the numeric ex
pression. The expression may take any value within the valid range for floating-point
numbers, approximately ±1.7E38.
To convert radians to degrees, multiply by 180/tt. This changes the range of val
ues of ATN from - n/2 through tt/2 to -90° through 90°.
In some cases, ATN(X) is a useful transformation to apply, since it condenses al
most the entire number range into a finite set from about —1.57 to +1.57.
Examples:
1. R=ATN((E2-E1)/(N2-N1))
From a program for surveyors, this computes a bearing from distances east
ing and northing.
21
BASIC Reference Guide
2. DEF FN AS(X)=ATN(X/SQR(1-X*X))
DEF FN AC(X)=t/2-ATN(X/SQR(1-X*X))
These function definitions evaluate arc sine and arc cosine, respectively.
Remember that the arc tangent can never be exactly 90 degrees; if necessary, test
for this extreme value to avoid errors.
CHR$
Type: String function
Syntax: CHR$(numeric expression)
Modes: Direct and program modes are both valid.
Token: $C7 (199)
Abbreviated entry: C SHIFT-H (This includes the $.)
Purpose: CHR$ converts a numeric expression (which must evaluate and truncate to
an integer in the range 0-255) to the corresponding ASCII character. It is useful for
manipulating special characters like RETURN and quotes which are CHR$(13) and
CHR$(34), respectively. Check the Appendices for a table of ASCII values. Note that
ASC is the converse function of CHF*
Examples:
1. A$=CHR$(18)+NAME$+CHR$(146)
This adds {RVS} and {OFF} around NAME$, so PRINT A$ prints NAME$
in reverse video.
2. FOR J=833 TO 848: PRINT CHR$(PEEK(J));:NEXT
This prints the name of the most recently loaded tape program, by reading
the characters from the tape buffer, assuming the buffer hasn't been altered by a
program.
3. PRINT#4, CHR$(27)"E08"
The above command sends the ASCII ESC (escape) character, plus a com
mand, to a printer. Special printer features are often controlled like this, and the
codes will vary from one brand of printer to the next.
4. OPEN 2,2/0/CHR$(38)+CHR$(60)
A command which opens a file to a modem. The two CHR$ parameters are
required in this format by BASIC.
CHR$(0) represents the null character, but, unlike the null string, "", it has a
length of one, and can be added to strings. See ASC for an application. Embedded
null characters, as in Y$="12"+CHR$(0)+"34" can cause strange results.
CLOSE
Type: Input/output statement
Syntax: CLOSE numeric expression
Modes: Direct and program modes are both valid.
Token: $A0 (160)
Abbreviated entry: CL SHIFT-0
22
BASIC Reference Guide
Purpose: CLOSE completes the processing of the specified file and deletes its file
number, device number, and secondary address from the file tables.
A numeric expression may be used as a logical file number; it must evaluate to a
number in the range 0-255. No error message is given if the file is not open. (Ac
tually CLOSE shares OPEN's syntax checking, so four parameters are valid after
CLOSE, but only the first is used.)
Notes:
1. Files opened for reading do not have to be closed, but files opened for saving to
tape or disk should always be closed, or tape files will lose the last portion of data
held in the buffer, while disks may be corrupted. Chapters 14 and 15 have details.
(OPEN 15,8,15: CLOSE 15 is an easy way to correctly close disk files, perhaps
after a program stops with 7SYNTAX ERROR while writing to disk.)
2. CLOSE is a straightforward command, but it is made more complicated by the
behavior of CMD, which must be followed by a PRINT# command to switch out
put back to the TV or monitor.
Example:
OPEN 4,4: PRINT#4/'HELLO": CLOSE 4
The line above opens a file, sends data to a printer through the file, then closes
the file. The second number in the OPEN command is a device number, which selects
the printer (device 4).
CLR
Type: Statement
Syntax: CLR
Modes: Direct and program modes are both valid.
Token: $9C (156)
Abbreviated entry: C SHIFT-L
Purpose: CLR clears the memory area currently allocated to variables, leaving the
BASIC program, if there is one, unchanged. Any machine language routines in RAM
are left unaltered. Additional effects are noted below.
Note: CLR is actually part of NEW, and does most of the things NEW does, while
keeping the current program intact. CLR operates by resetting pointers, and doesn't
actually erase variables, so in principle these could be recovered. It has other func
tions, too. Following is the complete list:
• The string pointer is set to top-of-memqjy, and the end-of-variables and end-of-
arrays pointers are set to end-of-BASIC. AH variables and arrays are thus effectively
lost.
• The stack pointer is reset, but the previous address is retained; therefore, all FOR-
NEXT and GOSUB-RETURN references are lost, and also, if CLR executes within a
program, that program continues at the same place.
• The DATA pointer is set to start.
• Input/output activity is aborted.
• Files are aborted (but not closed), and keyboard and screen become the
input/output devices.
23
BASIC Reference Guide
Examples:
1. POKE 55,0: POKE 56,48: CLR
Sets the top of the BASIC program storage area to 48*256=$3000, typically
to reserve space for graphics in VIC bank 0.
2. 1000 CLR: GOTO 10
This sort of operation is useful in some simulation programs; all existing
variables are erased and the program continues. RUN 10 has a similar effect.
CMD
Type: Output statement
Syntax: CMD numeric expression [, any expression]
The numeric expression, a file number, must evaluate to a number in the range
1-255. The optional expression does not include the brackets shown above, but must
follow a comma; it is printed to the specified file and can be used to put a header on
a printout.
Modes: Direct and program modes are both valid.
Token: $9D (157)
Abbreviated entry: C SHIFT-M
Purpose: CMD is identical to PRINT#, except that the output device is left listening.
Therefore, a CMD statement followed by a device number redirects printed output
from TV to the specified device. The effect usually lasts until PRINT# unlistens the
device.
Notes:
1. CMD is a convenient way to cause a program with many PRINT statements to di
vert its output to a printer. This is easier than changing all PRINT statements to
PRINT# statements. However, CMD has bugs; GET and sometimes GOSUB will
redirect output to screen. Where this is a problem, use PRINT#.
2. CMD is necessary in order to list programs to printers.
Examples:
1. OPEN 4,4: CMD4,"TITLE":LIST
This will list the current program (or disk directory file, if present in mem
ory) to a printer. Follow this with:
PRINT#4: CLOSE4
to return output to the screen.
2. 100 INPUT "DEVICE NUMBERED: OPEN D,D: CMD D
Allows PRINT to direct output either to device 3 (screen), device 4 (printer),
or elsewhere.
24
BASIC Reference Guide
CONT
Type: Command
Syntax: CONT
Modes: Only direct mode is available. (In program mode CONT enters an infinite
loop.)
Token: $9A (154)
Abbreviated entry: C SHIFT-O
Purpose: CONT resumes execution of a BASIC program stopped by a STOP or END
statement, or by the RUN/STOP key. CONT cannot be used to restart a program
that has stopped due to any sort of error. Also, CONT cannot be used if you edit
any program lines after the program stops.
For debugging purposes, STOP instructions may be inserted at strategic points in
the program, and variables may be PRINTed and modified after the program has
stopped. CONT will continue, provided you make no error. ?CAN'T CONTINUE ER
ROR has several causes. In such cases, GOTO a line number serves a similar purpose
as CONT.
Note: Because STOP aborts files, CONT may be accepted, but not actually continue
as before; for example, output which ought to go to a printer may be displayed on
the screen after CONT.
Example:
10 PRINT J: J=J + 1: GOTO 10
Run this, then press the RUN/STOP key. The BASIC command CONT will
cause the program to continue. You can change J, by typing J=10000 in direct mode,
for example, and CONT will resume (using the new value).
COS
Type: Numeric function
Syntax: COS(numeric expression)
Modes: Direct and program modes are both valid.
Token: $BE (190)
Abbreviated entry: None
Purpose: COS returns the cosine of the numeric expression, which is assumed to be
an angle expressed in radians.
Examples:
1. PRINT COS(45*tt/180)
The above statement prints the cosine of 45 degrees (conversion from radi
ans to degrees is accomplished by multiplying the value in radians by tt and
dividing by 180).
2. FOR J=0 TO 1000 STEP ?r/10: PRINT COSfl): NEXT
This shows the cyclical nature of COS. Large values of the argument don't
introduce significant error, because COS uses only the remainder in the range 0 to
2*7T.
25
BASIC Reference Guide
DATA
Type: Statement
Syntax: DATA list of data separated by commas
Modes: Only program mode is available.
Token: $83 (131)
Abbreviated entry: D SHIFT-A
Purpose: DATA enables numeric or string data to be stored in a program. The READ
statement retrieves the data in DATA statements in the same order it's stored in the
program.
Notes:
1. DATA statements to store ML programs can be generated automatically: See
Chapter 9.
2. A 7SYNTAX ERROR in a valid DATA statement line means that the READ and
DATA statements don't match properly.
3. Unnoticed or omitted commas can cause baffling bugs:
DATA R,O,Y,G,,B/P,
contains eight data items, two of them (between G and B, and following P) null
characters.
4. Because DATA statements are handled in sequence (RESTORE restarts the se
quence), take care when adding more data (for example, by appending a sub
routine) in case data from a wrong routine is read.
Examples:
1. 100 DATA "7975, LAZY RIVER ROAD"
This shows that quotes enable commas, colons, leading spaces, and other
special characters to be included in strings.
2. 1000 DATA CUCOPPER^e^FEJRON^S.l
This illustrates how sets of data can be stored. Typically, a loop with READ
A$,M$,W inside might be used to read each set of three items.
3. 10000 DATA SUB1 :REM MARKS START OF SUBl's DATA
Here's a trick that might be used to insure that the correct data is being read.
Use the following line to locate SUB1:
1000 READX$: IF X$<> "SUB1" GOTO 1000
DEFFN
Type: DEF FN, statement; FN, numeric function
Syntax: DEF FN valid variable name (real variable)^ arithmetic expression
Modes: Only program mode is available.
Token: DEF: $96 (150); FN: $A5 (165)
Abbreviated entry: DEF: D SHIFT-E (FN has no abbreviated form)
Purpose: DEF FN sets up a numeric (not string) function, with one dependent vari
able, which can be called by FN. Function definitions help save space where an ex-
26
BASIC Reference Guide
DIM
Type: Statement
Syntax: DIM variable name [, variable name..]
Modes: Direct and program modes are both valid.
Token: $86 (134)
Abbreviated entry: D SHIFT-I
Purpose: DIM is short for DIMension. It sets up space above the BASIC program in
memory for variables in the order the variables are listed in the DIM statement. This
command is automatically carried out when a variable is given a value (for example,
a line that contains the expression X=l), so there's no need for DIM, unless arrays
27
BASIC Reference Guide
with dimensions greater than 10 are needed. All variables set up by DIM are set to 0
(if numeric) or null (if strings).
Notes:
1. Arrays can use numeric expressions in their DIM statements, so their size can be
determined by some input value; they don't have to be of fixed size. Arrays start
with the zeroth element, so DIM X(4) sets up a numeric array with five storage
locations, X(0) through X(4). Dimensions can have a maximum of 32767 elements,
and not more than 255 subscripts may be used in multidimensional arrays; in
practice, an ?OUT OF MEMORY ERROR will result long before you reach these
limits.
2. Arrays are stored in memory above regular variables. Chapter 6 explains the con
sequences in detail, but here are a couple:
• New variables introduced after an array has been set up cause a delay.
• Arrays can be deleted with:
POKE 49,PEEK(47): POKE 50,PEEK(48)
So if intermediate results are computed with a large array, this can be deleted
when you are finished.
3. Integer arrays are efficient, while the efficiency of string arrays depends on the
lengths of the strings.
PRINT FRE(O)
gives a quick indication of spare RAM at any time. RAM space occupied by arrays
is explained in Chapter 6.
4. Large string arrays are vulnerable to garbage collection delays, also explained in
Chapter 6. The total number of separate strings, not their lengths, is the signifi
cant factor in garbage collection.
Examples:
1. 100 INPUT "NUMBER OF ITEMS";N: DIM IT$(N)
This might be used in a sorting program, where any number of items may
be sorted.
2. DIM X,YJ,L,P$ :REM SET ORDER OF VARIABLES
Ordering variables, with the most frequently used ones dimensioned first,
will help increase the speed of BASIC programs.
3. 100 DIM A(20): FOR J=l TO 20: INPUT A(J): A(0)=A(0)+A(J): NEXT
The above line uses the zeroth element to keep a running total.
4. DIM X%(10,10,10)
This sets up an array of 1331 integers, perhaps to store the results of three
11-point questionnaires.
END
Type: Statement
Syntax: END
Modes: Direct and program modes are both valid.
28
BASIC Reference Guide
EXP
Type: Numeric function
Syntax: EXP(numeric expression)
Modes: Direct and program modes are both valid.
Token: $BD (189)
Abbreviated entry: E SHIFT-X
Purpose: EXP calculates e (2.7182818...) to any power within the range -88 to +88,
approximately. The result is always positive, approaching 0 with negative arguments,
becoming large with positive arguments. EXP(0) is 1.
Note: EXP is the converse of LOG. Sometimes logarithms of numbers are used in
calculations; EXP transforms the results back to normal. EXP(Q) could be replaced by
2.7182818TQ, but the shorter form is more readable.
Examples:
1. PRINT EXP(LOG(N))
The above line prints N (possibly with rounding error), demonstrating that
EXP and LOG are converse operations.
2. 100 P(N) = MtN * EXP(-M)/FACT(N)
This is a typical statistical formula, for the probability of exactly N rare
events happening when the average is M. FACT(N) holds N! for a suitable range
of values. (EXP is important for its special property that it equals its own rate of
growth; it tends to turn up in scientific calculations.)
FOR-TO (STEP)
Type: Statement
Syntax: FOR simple numeric variable=numeric expression TO numeric expression
/STEP numeric expression]
29
BASIC Reference Guide
is treated as a single FOR statement. Now, 18 bytes are put on the stack, if there's
room. Once they are placed there, they won't change, so the upper limit of the
loop FOR X=l TO N won't change after the looping starts, even if the value of N
is changed within the loop.
10 FOR X=489 TO 506: PRINT PEEK(X): NEXT
lists 18 bytes from the stack; these are the FOR token, the two-byte address of the
loop variable, the STEP size in floating-point format, the sign of the STEP, the
floating-point value of the upper limit of the loop, the line number of the FOR,
and the address to jump to after the loop is finished. The STEP value defaults to 1.
Because NEXT determines whether the loop will continue, every FOR-NEXT
loop is executed at least once, even FOR J=l TO 0: NEXT. NEXT also checks the
loop variable, so NEXT X,Y, for example, helps insure correct nesting of loops—it
must be preceded by FOR Y and FOR X statements. NEXT adds the STEP size to
the variable value; if the result exceeds the stored limit (or is less, if a negative
STEP size was used), processing continues with the statement following NEXT.
There's no way the system can detect a missing NEXT; if a set of loops is un
expectedly fast, this may be the reason.
When the STEP size is held exactly, there is no loss of accuracy in using
loops. So:
FOR J=l to 10000 STEP .5
30
BASIC Reference Guide
31
BASIC Reference Guide
GET
Type: Input statement
Syntax: GET variable name [, variable name...]
Mode: Only program mode is available.
Token: $A1 (161)
Abbreviated entry: G SHIFT-E
Purpose: GET reads a single character from the current input device, usually the
keyboard, and assigns it to the named variable. If the keyboard buffer is empty,
string variables are assigned a null, and numeric variables are given a value of 0.
GET (unlike INPUT, or GET on some other computers) doesn't wait for a keypress,
so BASIC can test for a key and continue if there isn't one. GET X$ is more powerful
than GET X, which crashes when it detects a nonnumeric key; so the string form of
GET is nearly always used, and conversions are made when necessary. The string
GET works with any ASCII character, but the RUN/STOP, Commodore, SHIFT,
CTRL, and RESTORE keys aren't detected by GET.
Chapter 6 explains the keyboard buffer and associated keyboard features in
depth. Chapter 4 explains how GET may be used to write reliable INPUT-like
routines.
Examples:
1. 5 GET X$: IF X$="" GOTO 5: REM AWAIT KEY
10 PRINT "{CLR}" X$ ASC(X$): GOTO 5
This short program waits for a key to be pressed, then clears the screen and
prints the key pressed and its ASCII value. There are a few exceptions, like quotes
and the color controls. You'll see how RETURN is read, plus all the normal keys.
Chapter 4 discusses this in more depth.
2. 100 DIM IN$(200): FOR J=l TO 200: GET IN$fl): NEXT
This line gets 200 characters into an array, most of which will be nulls.
3. 200 GET A$,B$,C$
This is a syntactically valid statement, but this format is more appropriate
with the GET# statement. The syntax is accepted because GET, GET#, INPUT,
and READ largely use the same Kernal routines, and the 64's designers felt it was
not worth removing the relatively useless alternatives.
GET#
Type: Input statement
Syntax: GET# numeric expression, variable name ^variable name...]
32
BASIC Reference Guide
The numeric expression, a file number, must evaluate and truncate to a number
in the range 1-255.
Mode: Only program mode is available.
Token: $A1 (161) then $23 (35). This is GET then #; GET# has no token of its own.
Abbreviated entry: G SHIFT-E #
Purpose: GET# reads a single character from the specified file, which must be open
to an input device or a ?FILE NOT OPEN ERROR will result. Unlike the INPUT#
statement, GET# can read characters like colons, quotes, and RETURNS. GET# can
read files character by character in a way impossible with INPUT# and is not limited
to 88 characters per string.
Notes:
1. GET# can read from screen or keyboard, although there's usually no real advan
tage in this.
2. GET# from tape sets the status variable (ST) to a value of 64 when it reaches the
end-of-file, so programs can evaluate ST to test for the end of data if no special
marker was used. ST is immediately reset, so the test is needed after each GET#.
Chapter 14 has full details.
3. GET# from disk also sets ST=64 at end-of-file; from then on, ST is set to 66,
which indicates end-of-file plus device not responding. Chapter 15 has full details.
Examples:
1. 1000 IN$=""
1010 GET#1,X$: IF ASC(X$)=13 GOTO 2000 :REM RETURN FOUND
1020 IN$=IN$+X$: GOTO 1010 :REM BUILD STRING
This program extract reads in a string, character by character, from tape or
disk, building IN$ from each character, and exiting to the next part of the program
when RETURN indicates the end of a string. A routine at line 2000 would handle
the string after the GET# process was complete.
2. 100 GET#8,X$: IF ST=64 GOTO 1000: REM END OF DATA
The above line shows how to use ST to detect that no more data is on file,
and how to jump to another part of the program based on that information.
3. 100 GET#1,X$,Y$
This example program line GETs a pair of consecutive characters from file 1,
which has already been opened to an input device.
GO
Type: Dummy statement
Syntax: Always part of GO TO
Modes: Direct and program modes are both valid.
Token: $CB (203)
Abbreviated entry: None
Purpose: The sole function of GO is to allow GO TO as a valid form of GOTO,
which occasionally gives problems. For example, some renumbering programs ignore
it, some early CBM machines don't have it. Chapter 8 shows how you can modify
GO to suit your own purposes.
33
BASIC Reference Guide
GOSUB
Type: Statement
Syntax: GOSUB line number
Modes: Direct and program modes are both valid.
Token: $8D (141)
Abbreviated entry: GO SHIFT-S
Purpose: GOSUB jumps to the specified BASIC line, saving the address of the orig
inal line on the stack, so that the RETURN statement in a program can transfer con
trol back to the statement immediately following the GOSUB statement. This means
a subroutine can be called from anywhere in BASIC while keeping normal program
flow. IF or ON allows conditional calls to be made to subroutines.
Notes:
1. Testing subroutines in direct mode. It is often simple to test parts of a large program
while in direct mode. For example:
L=1234: GOSUB 500
34
BASIC Reference Guide
Part of a simplified error message routine, this excerpt prints an error (EM$
must be set before GOSUB 2000) in reverse at the top of the screen.
2. 500 GOSUB 510
510 PRINT"*"
520 RETURN
This shows how a subroutine can have several entry points. Here, GOSUB
510 prints an asterisk, while GOSUB 500 prints it twice.
GOTO; GO TO
Type: Statement
Syntax: GOTO line number; GO TO line number
Modes: Direct and program modes are both valid.
Token: GOTO: $89 (137). Separate GO and TO tokens are also accepted.
Abbreviated entry: G SHIFT-0
Purpose: GOTO jumps to the specified BASIC line. IF and ON allow conditional
GOTO statements.
Notes:
1. Using GOTO in direct mode. Direct mode GOTO executes the program in memory
without executing CLR, so if the program has been previously run, the variables
are retained. This is similar to CONT, except that any line can be selected as the
starting point. Variables can be changed, but these will be lost if any BASIC pro
gram lines are edited.
2. Line numbers are read by the same routine that handles GOSUB's line numbers,
and similar restrictions apply.
Examples:
1. TI$="235910": GOTO 1000
This is a direct mode example; the clock is set just short of 24 hours, then
the program in memory is executed from line 1000 on, retaining the value of TI$.
2. 100 GET A$: IF A$="" GOTO 100
This simple loop awaits a keypress.
IF-THEN
Type: Conditional statement
Syntax: IF logical expression THEN line number
IF logical expression GOTO line number
IF logical expression THEN statement [: statement...]
Modes: Direct and program modes are both valid.
Token: IF: $8B (139); THEN: $A7 (167)
Abbreviated entry: IF: None; THEN: T SHIFT-H
Purpose: IF-THEN statements allow conditional branching to any program line or
conditional execution of statements after THEN.
35
BASIC Reference Guide
The expression after IF is treated as Boolean (that is, if zero it's false, if nonzero
true). If the expression is true, the statement after THEN is performed; if it is false,
the remainder of the line is ignored, and processing continues with the next line. (If
the expression is a string, the effect depends on the last calculation to use the
floating-point accumulator, so IF X$ THEN may be true or false.)
Examples:
1. 1000 LC=LC+1: IF LC=60 THEN LC=0: GOSUB 5000
This program excerpt increments the line count (LC); if LC is 60, it resets the
count value to 0 and calls a form advance subroutine at 5000 before continuing.
2. 700 IF X=l THEN IF A=4 AND B=9 THEN PRINT "*"
This is a composite IF statement, identical in effect to:
IF X=l AND A=4 AND B=9 THEN PRINT "*"
INPUT
Type: Input statement
Syntax: INPUT [string prompt in quotes;] variable name ^variable name...]
Modes: Only program mode is available.
Token: $85 (133)
Abbreviated entry: None
Purpose: INPUT is an easily programmed command which takes in data from the 64
and assigns it to a variable. INPUT echoes the data to the screen, so editing features
like DEL can be used. Pressing the RETURN key signals the end of INPUT.
Notes:
1. INPUTS prompts. INPUT N$ and INPUT "NAME";N$ illustrate the two forms of
INPUT. Both print a question mark followed by a flashing cursor, but the second
version also prints NAME, giving NAME? as the prompt. When you use an IN
PUT statement with multiple variables—for example, INPUT X$,Y$—two consec
utive question marks (??) will be displayed on the screen if only the first string is
entered, followed by RETURN. In response to the double question marks, you
simply enter the remaining data. Typing the two entries, separated by a comma
(FIRST, SECOND) assigns both strings, with no further prompt.
These demonstration lines show how the prompt string can be used:
100 INPUT "{CLR}{DOWN}{RIGHT}";X$
This clears the screen and moves the cursor (and the question mark prompt)
down and to the right of the home position.
100 INPUT "{2 SPACES}-{LEFT} {LEFT} {LEFT} {LEFT}",X$
This illustrates the technique of indicating the expected length of the input
data. There should be two spaces after the first quote, and two more cursor-lefts
than hyphens.
36
BASIC Reference Guide
37
BASIC Reference Guide
Examples:
1. INPUT "ENTER NAME";N$: PRINT "HELLO, " N$
This is a straightforward string input of N$, followed by a personal greeting.
2. FOR J=l TO 10: INPUT Xfl): NEXT
The above line inputs ten numbers into an array.
3. 100 INPUT "DEVICE{2 SPACES}3{LEFT}{LEFT}{LEFT}";D
This example shows one method to allow a default value for INPUT. In this
case, simply pressing RETURN assigns the value 3 to D, which saves time for the
user. Another method, which doesn't print the default under the cursor, is this:
4. 100 X$="YES": INPUT X$
If the user simply presses RETURN, X$ retains the value "YES".
INPUT#
Type: Input statement
Syntax: INPUT# numeric expression, variable name ^variable name...]
The numeric expression, a file number, must evaluate and truncate to a number
in the range 1-255.
Mode: Only program mode is available.
Token: $84 (132)
Abbreviated entry: I SHIFT-N (This includes the #.)
Purpose: INPUT# provides an easy method to read variables from a file, usually on
tape or disk. The format is the same as with PRINT#; the data consists of ASCII
characters separated by RETURN characters. Provided INPUT# matches PRINT#,
this command should be trouble-free.
Note: INPUT# is closely similar to INPUT. Below is a list of differences between the
two:
is read by INPUT# as two strings, because the colons are treated as separators.
Usually, PRINT# with straightforward variables will help you avoid these bugs.
• INPUT# can't take in a string longer than 88 characters, as this results in a
7STRING TOO LONG ERROR. Screen input doesn't have this problem, since part
of an overlong string is simply ignored.
• ST signals the end-pf-file point, as it does with the GET# statement.
Example:
10 OPEN 1 :REM READ TAPE FILE
20 DIM D$(100): FOR J=l TO 100: INPUT#1,D$(J): NEXT
This example reads 100 strings from a previously written tape file into an array.
38
BASIC Reference Guide
INT
Type: Numeric function
Syntax: INT(numeric expression)
Modes: Direct and program modes are both valid.
Token: $B5 (181)
Abbreviated entry: None
Purpose: INT, the integer function, converts the numeric expression to the nearest
integer less than or equal to the expression. Two sample results of the integer func
tion are INT(10.4) is 10, and INT(—2.2) is -3. The expression is assumed to be in
the full range for numerals, between about -1.7 E38 and +1.7 E38. So
L=INT(123456.7) is valid. But L%=INT(123456.7) gives an error, since the result is
too large for an integer variable.
Examples:
1. 100 PRINT INT(X+.5) :REM ROUND TO NEAREST WHOLE NUMBER
The above example rounds numbers—including negative numbers—to the
nearest whole numer.
2. 100 PRICE = INT(.5 + P*(l+MARKUP/100))
This calculates price to the nearest cent from percentage markup and pur
chase price in cents.
LEFT$
Type: String function
Syntax: LEFT$(string expression, numeric expression)
Modes: Direct and program modes are both valid.
Token: $C8 (200)
Abbreviated entry: LE SHIFT-F (This includes the $.)
Purpose: LEFT$ returns a substring consisting of the leftmost characters of the orig
inal string expression. The numeric expression (which must evaluate to 0-255) is
compared with the length of the string; the smaller of the two determines the length
of the substring.
Examples:
1. FOR J=0 TO 20: PRINT LEFT$("HELLO THERE",J): NEXT
This prints 20 strings, "", "H", "HE", "HEL", and so on. As J increases, the
number of characters printed does, too. However, the number of characters
printed never exceeds 11, the length of the original string. Thus, the eleventh
through twentieth strings printed will be identical.
2. PRINT LEFT$(X$ + " ",20)
This formatting trick pads X$ to exactly 20 characters with hyphens. This
way the output is always 20 characters long.
3. PRINT LEFT$(" ",20-LEN(X$)); X$
This line right justifies X$, preceding it with hyphens. (X$ is assumed not to
be longer than 20. Other characters, notably spaces, are usable in the same way to
format output.)
39
BASIC Reference Guide
LEN
Type: Numeric function
Syntax: LEN(string expression)
Modes: Direct and program modes are both valid.
Token: $C3 (195)
Abbreviated entry: None
Purpose: LEN determines the length of a string expression. The result is always be
tween 0 and 255 (see Chapter 6 for more details).
Examples:
1. 10 PRINT SPC(20-LEN(MSG$)/2);MSG$
This line will center a short message on the computer screen by adding lead
ing spaces.
2. 50 IF LEN(IN$)oL THEN PRINT "MUST BE" L "DIGITS": GOTO 40
The above excerpt rejects an input string of the wrong length, sending the
program back to line 40, where another attempt may be tried.
3. 100 FOR J=l TO LEN(W$): IF L$=MID$(W$,J,1) GOTO 200: NEXT
110 PRINT "NOT FOUND"
This checks word W$ for the presence of letter L$. The use of LEN(W$) al
lows the program to set the loop counter for any length of W$.
LET
Type: Statement
Syntax: [LET] numeric variable = numeric or logical expression
[LET] integer variable — numeric expression in range —32768 to +32767 or logical
expression
[LET] string variable = string expression
Modes: Direct and program modes are both valid.
Token: $88 (136)
Abbreviated entry: L SHIFT-E
Purpose: LET assigns a number or string to a variable. The statement LET is usually
omitted, since the 64 assumes LET by default. Simple or array variables may be
used, and if a simple or array variable doesn't already exist, LET makes room for it
in the variable storage area in memory. LET will dimension an array to the default
size of 11 (elements 0 through 10) if it has not been previously dimensioned with a
DIM statement.
Notes:
1. Chapter 6 has full details on variable storage; it also has a routine, VARPTR,
showing how LET can be used from ML. Since LET is rarely used, it can be
modifed by the user (Chapter 7 demonstrates this).
2. Variables can be reassigned freely, so be careful not to try using a variable for two
purposes simultaneously. This is often a problem when using subroutines, because
it is harder to keep track of variables.
40
BASIC Reference Guide
Examples:
1. X=123456: LET X=123456
Both of these statements set X to 123456.
2. Q%=Q/100
The above line sets Q% equal to the integer portion of Q/100, so if
Q = 1234, Q% = 12.
3. LET QH%=Q/256: LET QL%=Q-QH%*256
This sets QH% and QL% equal to the high and low bytes of the number Q.
LIST
Type: Command
Syntax: LIST [line number] [-] [line number]
Modes: Direct and program modes are both valid.
Token: $9B (155)
Abbreviated entry: L SHIFT-I
Purpose: LIST displays part or all of BASIC in memory to the screen, or (with CMD)
to disk, tape, or other output device.
Notes:
1. Line numbers must be ASCII characters, not variables.
2. LIST uses many RAM locations; it always exits to READY mode if used within a
program.
3. LOAD errors and other errors may show up in LIST. For example, if a machine
language program designed for some other part of memory is loaded into the
BASIC program area, an attempt to LIST will show only random characters.
4. Chapter 6 lists BASIC tokens and has examples of BASIC storage in memory.
Also, a LIST reference has programs to modify LIST in useful ways. (Chapter 8
shows how it's done.) The entry for REM has notes on the way LIST interprets
screen-editing and other characters. TRACE is a modified LIST which works while
a program runs. UNLIST shows ways to protect your programs.
Examples:
1. LIST 2000-2999
The line above displays the BASIC lines from 2000 through 2999.
2. LOAD "$",8 followed by LIST
This displays a disk directory, which is stored in memory as though it were a
BASIC program.
3. 1000 LIST -10
This lists all lines up to and including line 10 of a BASIC program. As
shown in this example, LIST can be included within a BASIC program line. How
ever, execution of this line will stop the program, and CONT will not restart it.
4. LIST 1100-
This displays all lines in the current BASIC program with line numbers of
1100 or greater. If there is no line 1100 in the current program, the listing begins
with the first existing line greater than 1100.
41
BASIC Reference Guide
LOAD
Type: Command
Syntax:
Tape: LOAD [string expression [,numeric expression^ numeric expression]]]
All parameters are optional. The first numeric expression, if used, must evaluate
to 1 (device number). The second normally evaluates to 0 (BASIC LOAD) or 1
(forced LOAD). Chapter 14 has full details.
Disk: LOAD string expression, numeric expression [, numeric expression]
A name and a numeric expression, typically 8, which is the device number, are
required. The second numeric parameter has the same meaning as in tape LOAD.
Chapter 15 has full details.
Modem: LOAD cannot be used with a modem. Attempting to use device number
2, therefore, will result in an error.
Modes: Direct and program modes are both valid. (See "Notes" below.)
Token: $93 (147)
Abbreviated entry: L SHIFT-O
Purpose: LOAD reads from an external device, filling RAM with a BASIC program,
ML, graphics, or other data. In its simplest form, LOAD {RETURN}, then RUN {RE
TURN} loads BASIC from tape and runs it. (SHIFT-RUN/STOP does this, too.)
Notes:
1. LOAD is followed by a standard set of messages, like PRESS PLAY ON TAPE, OK
when the cassette starts, and so on. These are listed in the chapters on tape and
disk usage. Program mode LOADs don't have these messages (apart from PRESS
PLAY ON TAPE, which can't be avoided), so the screen layout can be kept tidy.
2. Tape LOAD blanks the TV screen to the border color during any tape reading.
When a program or file header is found, a FOUND message is displayed on
screen for about ten seconds, after which loading takes place if the program name
is acceptable, and the screen temporarily blanks again. Otherwise, the process of
searching goes on. Pressing the Commodore key, or one of several other keys, cuts
the ten-second pause short.
3. A BASIC program LOAD nearly always requires that LOAD'S third parameter be
0. This allows LOAD to relink BASIC, so that any start-of-BASIC position is
acceptable. For example:
LOAD "BASIC PROG"
loads that program from tape into the 64 with any memory configuration and pre
pares it for RUN. In fact:
POKE 43,LO: POKE 44,HI: POKE HI*256+LO,0: NEW
followed by the correct disk or tape LOAD can put a BASIC program anywhere
you choose, if there's room for it.
4. Loading ML, graphics definitions, and other data is generally trickier than loading
BASIC programs, and needs a LOAD format like this:
LOAD "CHARSET",1,1
42
BASIC Reference Guide
to insure that the data is put back where it came from. Supermon's .L load com
mand (see Chapter 7) does this. Block LOAD in Chapter 6 explains how blocks of
bytes can be loaded without disturbing BASIC.
5. Program mode LOADs generally chain BASIC; see CHAIN in Chapter 6, and also
OLD, which explains how to chain a long program from a shorter one.
Examples:
1. LOAD: LOAD "",1
These are tape LOADs and have identical effects. Either loads the first
BASIC program found on tape.
2. LOAD "PROG"
This loads the program with the filename PROG from tape. Actually, be
cause of the filename checking scheme used, the first program encountered on
tape having a name beginning with PROG (PROGRAM, or PROGDEMO, for ex
ample) will be loaded.
3. LOAD "PROG",8
This line will load only PROG from disk. No other program with a name
beginning with PROG will be loaded if PROG is not found; instead, a ?FILE NOT
FOUND ERROR will be reported.
4. LOAD "PAC*",8
This illustrates a typical disk pattern-matching LOAD command, which will
load PACMAN, PACKER, or the first program beginning with PAC.
5. 10000 PRINT "PLEASE WAIT": LOAD "PART2"
The above line loads and then runs the tape program PART2 from within
BASIC. If the correct key on the tape deck is pressed, no message appears on the
TV.
6. 10 IF X=0 THEN X=l: LOAD "GRAPHICS",1,1
20 REM THE PROGRAM CONTINUES HERE AFTER THE LOAD
This loads the graphics into a fixed area of memory. A LOAD command
from within a program causes that program to be run again from the start. The
variable, X, is used as a flag, which prevents GRAPHICS from being loaded
repeatedly and allows the program to continue.
LOG
Type: Numeric function
Syntax: LOG(numeric expression)
Modes: Direct and program modes are both valid.
Token: $BC (188)
Abbreviated entry: None
Purpose: LOG returns the natural logarithm (log to the base e) of a positive
arithmetic expression. This function is the converse of EXP.
Logarithms transform multiplication and division into addition and subtraction;
for example, LOG(l) is 0 since multiplication by 1 has no effect. Logarithms are used
mainly in scientific work; their susceptibility to rounding errors makes them less suit
able for commercial work.
43
BASIC Reference Guide
Examples:
1. PRINT LOG(X)/LOG(10) :REM LOG TO BASE 10
PRINT LOG(X)/LOG(2) :REM LOG TO BASE 2
PRINT EXP(LOG(A)+LOG(B)) :REM PRINTS A*B
These are all standard uses of the LOG function.
2. LF=(N+.5)*(LOG(N)-1) + 1.41894 + 1/(12*N)
This defines LF, an approximation of LOG(N!), so that EXP(LF) approxi
mately equals N!. This illustrates how LOG helps when using very large numbers.
MID$
Type: String function
Syntax: MlD$(string expression, numeric expression ^numeric expression])
Modes: Direct and program modes are both valid
Token: $CA (202)
Abbreviated entry: M SHIFT-I (This includes the $.)
Purpose: MID$ extracts any required substring from a string expression. The first nu
meric parameter is the starting point (1 represents the first character of the original
string, 2 the second, and so on). The final parameter is the length of the substring to
be extracted. If this isn't used, the substring extends to the end of the original string.
Examples:
1. N$=MID$(STR$(N),2) :REM REMOVE LEADING SPACE FROM N
This is useful when a number's leading spaces aren't wanted. It works with
any positive numbers in the correct range.
2. 10 INPUT X$: L=LEN(X$)
20 FOR J=l TO L: PRINT MID$(X$,L-J+1,1);: NEXT
This inputs a string, then prints it out backward, one character at a time.
NEW
Type: Command
Syntax: NEW
Modes: Direct and program modes are both valid.
Token: $A2 (162)
Abbreviated entry: None
Purpose: NEW allows a new BASIC program to be entered, by ignoring any pre
vious program. It corrects pointers after a forced (nonrelocating) LOAD like:
LOAD "ML",1,1
44
BASIC Reference Guide
aborts files, among other things. OLD in Chapter 6 will recover BASIC after NEW
(or after resetting by the method described in Chapter 5), provided new program
lines haven't been entered.
2. NEW may sometimes generate 7SYNTAX ERROR. (See the error message notes.)
Examples:
1. NEW
In direct mode, NEW readies the 64 for a new program. (Without NEW, the
program would be simply added to the one currently in BASIC as extra or replace
ment lines.)
2. 20000 NEW: REM PROGRAM NO LONGER WANTED
This exits to READY mode. The program won't LIST and appears erased.
NEXT
Type: Statement
Syntax: NEXT [numeric variable][,numeric variable...]
Modes: Direct and program modes are both valid.
Token: $82 (130)
Abbreviated entry: N SHIFT-E
Purpose: NEXT marks the end of a FOR-NEXT loop. See FOR, which has a detailed
account of loop processing.
Examples:
1. FOR 1=1 TO 10: FOR J=l TO 10: PRINT I*J;:NEXT J: PRINT: NEXT
This prints an unformatted multiplication table for values up to 10 X 10.
Note that NEXT:PRINT:NEXT works, too. In fact, it's a little faster. NEXT J: NEXT
I can be replaced with NEXT J,I. Once a program is debugged, the variables
following NEXT statements can generally be removed; however, they do improve
readability.
2. 80 FOR J=l TO 2000: GET X$: IF X$="" THEN NEXT
81 FOR J=0 TO 0: NEXT
This delays approximately ten seconds, unless a key is pressed; if it is, line
81gets rid of the still active J loop.
3. 10FOR J=l TO 3: GOTO 40
20NEXT K
30NEXT J: END
40FOR K=l TO 2: GOTO 20
NEXT can appear anywhere, allowing clumsy constructions like the one
above.
NOT
Type: Logical operator
Syntax: NOT logical or numeric expression
Numeric expressions must evaluate after truncating to —32768 to +32767.
45
BASIC Reference Guide
is identical to:
(NOT A) AND B
ON
Type: Conditional statement
Syntax: ON numeric expression GOTO line number [,line number...]
ON numeric expression GOSUB line number [,line number...]
The numeric expression must evaluate and truncate to 0-255.
Modes: Direct and program modes are both valid.
Token: $91 (145)
Abbreviated entry: None
Purpose: ON allows a conditional branch to one of the listed line numbers, depend
ing on the value of the expression after ON. If it is 1, the first line number is used; if
it is 2, the second is used, and so on. If the value is 0 or too large for the list, the
line is ignored and processing continues with the next statement. This provides a
readable method of programming multiple IFs, provided a variable takes consecutive
values 1, 2, 3, ...
Examples:
1. ON SGN(X)+2 GOTO 100,200,300
The above line branches three ways, depending on X being negative, zero, or
positive.
2. 90 ON ASC(IN$)-64 GOTO 100,200,300,400
This line jumps to one of the lines listed, depending on IN$ being A, B, C,
or D.
46
BASIC Reference Guide
OPEN
Type: Input/output statement
Syntax:
Tape: OPEN numeric expression [,numeric expression [.numeric expression [.string
expression]]]
The first numeric expression, the file number, must evaluate to 1-255; the sec
ond is the device number, which is 1; the third sets read or write type; and the op
tional string expression is the filename. Chapter 14 has full details.
Disk: OPEN numeric expression, numeric expression, numeric expression [,string ex
pression].
Here, the file number must be 1-255, the device number is usually 8, the
secondary address is usually between 2 and 15, and the string expression a com
mand like "SEQ FILE,W", which the disk drive itself, not BASIC, processes. Chapter
15 has full details.
Modems and other RS-232 devices: The same as for a disk drive, except that the
device number is 2, and the string expression is a pair of bytes which set transmit/
receive features. Chapter 17 has full details.
Printers Hfid other write-only devices: These require file and device numbers. The
string expression is irrelevant with these devices, while the third numeric parameter
may or may not matter. See Chapter 17.
Tape and disk filenames can't exceed 16 characters.
Modes: Direct and program modes are both valid.
Token: $9F (159)
Abbreviated entry: O SHIFT-P
Purpose: OPEN sets up a file to write or read (sometimes both) data to or from ex
ternal devices like tape or disk drives. For example, the statement:
OPEN 1,1,1/TAPE FILE"
opens logical file 1, called "TAPE FILE", to the cassette. After this, the statement:
PRINT#1
leaves a complete new file called "TAPE FILE", which can be read back later, typi
cally by OPEN 1 and INPUT#1,X$ or similar statements.
As many as ten files (enough for almost any purpose) can be open at once; each
must have a different logical file number (the first parameter of OPEN) so that they
47
BASIC Reference Guide
are distinguished. In addition, the secondary addresses of disk files must all be dif
ferent. Three tables in RAM store the file numbers along with their device numbers
and other information.
Note: Opening a file to tape blanks the screen during tape read/write activity. OPEN
1 in direct mode (valuable for reading a program header's information) pauses for
ten seconds before returning to READY unless the Commodore key, space bar, or
one of a few other keys is pressed. OPEN 1 in program mode does not result in a
pause.
Examples:
1. OPEN 2,l,0,"TAX"
The above example opens a file from tape called TAX (or TAXI, TAXI
DERMY, etc.) for reading (since the third parameter is 0), and assigns it logical file
2, so INPUT#2 or GET#2 will fetch data from the file. This is identical to OPEN
2, except that the file is asked for by name; OPEN 2 opens the first file it finds.
With tape, OPEN reads tape until it finds a header.
2. OPEN 1,8,3/'ORDINARY FILE,S,R"
The line above opens a sequential file (specified by the ,S after the filename)
on disk called ORDINARY FILE for reading (specified by the ,R after the filename)
by INPUT#1 or GET#1 statements.
3. OPEN 2,2,0,CHR$(6)
This is an OPEN which prepares the modem (device 2) for PRINT#2 and
INPUT#2. The string expression is used to set modem parameters such as parity
and data transfer rate.
4. OPEN 4,4: REM OPENS FILE#4 TO DEVICE#4
This line opens a file to a printer, assuming that the printer (and interface, if
one is used) operate like a standard Commodore printer.
OR
Type: Logical operator
Syntax: Logical or numeric expression OR logical or numeric expression
Numeric expressions must evaluate after truncating to -32768 to +32767.
Modes: Direct and program modes are both valid.
Token: $B0 (176)
Abbreviated entry: None
Purpose: OR calculates the logical OR of two expressions, by performing an OR on
each of the bits in the first operand and the corresponding bits in the second. For the
purposes of the OR comparison, numeric expressions are evaluated as 16-bit signed
binary numbers. The four possible combinations of single bits are:
0 OR 0 = 0
0 OR 1 = 1
1 OR 0 = 1
1 OR 1 = 1
48
BASIC Reference Guide
though verifying this by finding the binary arithmetic forms of 380 and 75 is some
what tedious.
Examples:
1. 560 IF (A<1) OR (A>20) THEN PRINT "OUT OF RANGE"
This is a typical validation test; A must be a value from 1 to 20.
2. POKE 328,PEEK(328) OR 32
The above POKE and PEEK combination sets bit 5 of location 328 to 1,
whether or not it was 1 before, leaving the other bits unaltered. OR can set bits
high; AND can clear them to 0.
Notes:
1. PEEK (like POKE) is unusual in that it is easily replaced by ML routines. Chapter
17, for example, has ML routines to read joystick values, which are much faster
than using PEEK in BASIC.
2. A number of locations are modified by hardware and therefore have values which
vary: joystick, paddle, and keyboard locations are obvious examples. Some loca
tions vary as a result of software modification; much of the memory area from
locations 0-255 (called the zero page) is used by BASIC as it operates, and numer
ous locations in the range 256-1023 are used from time to time during BASIC
execution. Most of the memory above 32768 can be reallocated for different uses
and is largely controlled by the byte in memory location 1, as Chapter 5 explains.
Examples:
1. PRINT CHR$(34);: FOR J=2048 TO 2147: PRINT CHR$(PEEK(J));: NEXT
This prints 100 characters PEEKed from the start of the 64's normal BASIC
program storage area. (The quotation mark generated by CHR$(34) is an attempt
to prevent spurious control characters from clearing the screen, etc.)
2. 500 IF (PEEK(653) AND 1)=1 THEN PRINT "SHIFT KEY"
This tests bit 0 of location 653—the byte that stores the SHIFT, Commodore
key, and CTRL key flags—to determine if the SHIFT key is pressed.
49
BASIC Reference Guide
POKE
Type: Statement
Syntax: POKE numeric expression, numeric expression
The first numeric expression is an address, and the second is a one-byte value,
so the ranges must be 0-65535 and 0-255, respectively.
Modes: Direct and program modes are both valid.
Token: $97 (151)
Abbreviated entry: P SHIFT-O
Purpose: POKE stores the byte specified by the second expression into the address
given by the first. POKE can store ML routines into memory from DATA statements,
alter BASIC pointers, alter hardware registers, and perform other useful functions.
Notes:
1. POKE (like PEEK) can be replaced by simple ML routines; replacing a POKE to
the screen with the ML equivalent is aij ideal introduction to ML (see Chapter 7).
2. A careless POKE to an uninitialized variable, like:
POKE A0,0
in place of:
POKE A,0
will gltfr the direction register at location 0; this affects tape and the 64's
RAM/ROM bank switching.
Examples:
1. POKE 53281,9
This changes the screen background color by altering a VIC chip register.
2. Chapter 6 has a large number of programs which READ values from DATA state
ments, then POKE them into RAM.
3. FOR J=0 TO 499: POKE 1024+J, PEEK(2048+J): POKE 55296+J,0: NEXT
This puts |0P bytes of BASIC program data onto the screen in j^lack, by
POKEing values "to both screen and color memory. '*;
4. FOR J=40960 TO 49151: POKE J, PEEKg): NEXT: POKE 1,54
This moves the BASIC ROM ($A000-$BpFF) into RAM, then switches that
part of memory to RAM, so the BASIC interpreter is now held in RAM. Normally,
POKE J, PEEK(J) has no effect, of course. The example works only because the 64
is designed so that POKEd information goes into RAM which is underneath ROM.
(This means that either the usual ROM or alternative RAM may be used. This
technique will not work with most other computers.)
POS
Type: Numeric function
Syntax: POS(numeric expression)
The numeric expression is a dummy expression, as with FRE.
Modes: Direct and program modes are both valid.
50
BASIC Reference Guide
PRINT
Type: Output statement
Syntax: PRINT [expression]
The expression may be any type, separated by one or more of the following:
SPC(numeric expression), TAB(numeric expression), space, comma, semicolon, or no
separator (where this causes no ambiguity).
Modes: Direct and program modes are both valid.
Token: $99 (153)
Abbreviated entry: Question mark (?)
Purpose: PRINT evaluates and prints string, numeric, and logical expressions to an
output device, usually the TV or monitor. The punctuation of the material after the
PRINT statement affects the appearance of the output, which also depends on the
internal character set being used. (See "PRINT USING" in Chapter 6, for infor
mation on ML formatting of numbers.)
Notes:
1. Built-in graphics. The entire character set can be printed, but {RVS} is necessary to
complete the set using PRINT statements (POKE, of course, does not require the
use of {RVS}). Color and other controls are easy to include in strings, either in
quote mode or with CHR$. PRINT "{RED}HELLO{BLU}" and PRINT
CHR$(28)"HELLO''CHR$(31) are equivalent. Because {RVS} is necessary to print
some graphics, it's not always easy to convert a picture on the TV into PRINT
statements in a program. Homing the cursor, inserting spaces, and typing line
numbers followed by ?" and RETURN will sometimes work. This method, how
ever, won't accept reversed characters, so be careful when designing graphics di
rectly on the screen. Chapter 12 has detailed information on graphics.
Note that the SHIFT-Commodore key combination normally toggles between
two different character sets, one with lower- and uppercase (good for text), and
one with uppercase and extra graphics characters. Printing CHR$(14) selects the
51
BASIC Reference Guide
• Semicolons prevent print position from skipping to the next line, and therefore
act as neutral separators. Try this line:
PRINT 1;2;3;: PRINT 4
Remember that numbers are output with a leading space (in case there is a neg
ative sign) and a trailing space. Often the semicolon isn't needed, as in:
PRINT X$ Y$ "HELLO" N% A
where the interpreter will correctly identify everything.
• Colons end the statement, and in the absence of a semicolon move print position
to the next line. The following line advances the print position two lines:
PRINT: PRINT
PRINT#
Type: Output statement
Syntax: PRINT# numeric expression ^expression]
There must be no space between PRINT and #; the numeric expression is a file
52
BASIC Reference Guide
number—the file must be open; and subsequent expressions should use format simi
lar to PRINT.
Modes: Direct and program modes are both valid.
Token: $98 (152)
Abbreviated entry: P SHIFT-R (This includes #, and ?# does not wort)
Purpose: PRINT# sends data to an output device, usually a printer, tape, disk drive,
or a modem.
Notes:
1. Punctuating PRINT*. The effect of punctuation in PRINT# statements is identical
to PRINT, except for a few cases: Eleven spaces are always written after a comma,
and expressions in TAB or POS will not work.
PRINT#4,X$
writes X$ alone.
PRINT#128/X$:
writes X$ followed by a carriage return and linefeed; this feature of files numbered
128 or more is useful with certain non-Commodore printers.
2. PRINT* and INPUT*. Remember that INPUT# cannot handle strings longer than
88 characters, so this limit must be observed when setting up data files with
PRINT#.
3. PRINT* and CMD. PRINT#4,;: unlistens the device using file 4, while CMD4,;:
leaves it listening, so these expressions are opposites. See Chapter 17 for full de
tails on printers and modems.
Examples:
1. OPEN 1,1,1/TAPE FILE": INPUT X$: PRINT#1,X$: CLOSE 1
This opens TAPE FILE to tape and, after allowing the user to enter a string,
writes the string to tape. Chapter 14 has full details on tape; Chapter 15 has infor
mation on disk files.
2. 100 FOR J=32768 TO 40959: PRINT #l,CHR$(PEEKg));: NEXT
This prints the bytes in RAM or ROM in the plug-in area to file 1. Note the
semicolon to prevent characters having a RETURN character following each one
(making the file twice as long). The resulting file must be read back with GET#,
since it is ML and not formatted for INPUT# to handle.
READ
Type: Statement
Syntax: READ variable [,variable...]
Modes: Direct and program modes are both valid.
Token: $87 (135)
Abbreviated entry: R SHIFT-E
53
BASIC Reference Guide
Type: Statement
Syntax: REM [anything]
Modes: Direct and program modes are both valid.
Token: $8F (143)
Abbreviated entry: None
Purpose: REM allows documentation to be included in a program. Everything on the
BASIC line after REM is ignored by the BASIC interpreter. REM statements take
space in memory, and a little time to execute, so final versions of programs will gen
erally have the REM statements removed.
Note: See the section on REM in Chapter 6 for some special effects. Chapter 7 ex
plains how ML can be stored in REM statements.
Examples:
1. GOSUB 51000: REM PRINT SCREEN WITH INSTRUCTIONS
Above is a sample REM comment.
2. 70 FOR J=l TO 1000: REM MAIN LOOP
80 Ag)=J*A: NEXT
This shows poor placing of REM, because the REM executes 1000 times.
Move the REM to line 69 to increase speed.
3. 15998 REM
15999 REM *** SUB 16000 PRINTS TITLE ***
These lines show how REM statements can be made easy to read in long
programs.
54
BASIC Reference Guide
RESTORE
Type: Statement
Syntax: RESTORE
Modes: Direct and program modes are both valid.
Token: $8C (140)
Abbreviated entry: RE SHIFTS
Purpose: RESTORE resets the data pointer, so that subsequent READs retrieve data
starting from the first DATA statement. NEW, RUN, and CLR all perform RESTORE
as part of their functions.
Note: This command has no connection with the RESTORE key.
Examples:
1. 2000 READ X$: IF X$="**" THEN RESTORE: GOTO 2000
This example is the first line of a loop to read the same block of DATA
continuously—perhaps the notes for a tune to be repeated. When ** is en
countered as the last element of the data, the RESTORE resets the data pointer,
and the line executes again.
2. 130 RESTORE: FOR L=l TO 9E9: READ X$: IF X$o"MLROUTINEl"
THEN NEXT
140 FOR L=328 TO 336: READ V: POKE L, V: NEXT
9000 DATA 27, 32, SUB, MLROUTINE1,169, 0, 141, 202, 3, 162,1, 160, 20
This shows how data can be labeled to insure that the correct section is read;
line 130 reads the data until it reaches the label MLROUTINE1, which is followed
by the desired data.
3. RESTORE: GOTO 100
This is a direct mode command of the sort helpful in testing programs which
contain DATA statements, since after the RESTORE statement, all the data will be
reread from the start.
RETURN
Type: Statement
Syntax: RETURN
Modes: Direct and program modes are both valid.
Token: $8E (142)
Abbreviated entry: RE SHIFT-T
Purpose: RETURN transfers program control to the statement immediately after the
most recent GOSUB statement. GOSUB and RETURN therefore permit subroutines
to be automatically processed without the need to store return addresses in
programs.
Notes:
1. See GOSUB for a full account of subroutine processing.
2. This command has no connection with the RETURN key.
55
BASIC Reference Guide
Example:
10 INPUT L: GOSUB 1000: GOTO 10 :REM TEST SUBROUTINE 1000
1000 L=INT(L+.5)
1010 PRINT L: RETURN
This example repeatedly inputs a number and calls the subroutine at 1000 to
process it; the RETURN causes execution to resume with the GOTO 10 statement.
RIGHT$
Type: String function
Syntax: RlGHT$(string expression, numeric expression)
Modes: Direct and program modes are both valid.
Token: $C9 (201)
Abbreviated entry: R SHIFT-I (This includes the $.)
Purpose: RIGHTS returns a substring made up from the rightmost characters of the
original string expression. The numeric expression (which must evaluate to a value
between 0 and 255) is compared with the original string's length, and the smaller
value determines the substring's length.
Examples:
1. FOR J=l TO 7: PRINT SPC(8-J) RIGHT$("AMAZING",J): NEXT
prints seven substrings of "AMAZING", aligned using SPC.
2. 100 PRINT RIGHT$(" "+STR$(N),10)
is another method for right justification; each string is padded with leading spaces,
making a total length of ten.
RND
Type: Numeric function
Syntax: RND(numeric expression)
Modes: Direct and program modes are both valid.
Token: $BB (187)
Abbreviated entry: R SHIFT-N
Purpose: RND generates a pseudorandom number in the range 0-1, but excluding
these limits. RND can help generate test data, mimic random events in simulations,
and introduce unpredictability in games.
Notes:
1. RND's argument The argument used in the parentheses as part of the RND state
ment affects the way the number will be generated:
• Positive. The value of the number is irrelevant. RND(l) and RND (1234) behave
identically. The sequence of numbers generated is always the same, starting with
.185564016 immediately after the computer is turned on.
• Zero. This causes RND to take values from CIA timers; the result is more truly
random, although short ML loops, for example, may show repetitiveness.
56
BASIC Reference Guide
• Negative. The random number is reseeded with a value dependent on the argu
ment. A negative argument always returns the same value; RND (—1) is always
2.99 E—8, for example. Chapter 8 has information on RND and explains why
negative integers give very small seed values.
• Programming with RND. During program development with random numbers,
start with, say, X=RND(-1.23) to seed a fixed value, then use RND(l) while
testing the program, which will always follow the same sequence. The final ver
sion of the program might use X=RND(0) to start seeding with a random value.
2. To obtain a random number between A and B (but excluding the exact values of A
and B), use:
A + RND(1)*(B-A)
For example:
-1 + RND(1)*2
For example:
1 + INT(RND(l)*10)
RUN
Type: Command
Syntax: RUN [line number]
The line number must be ASCII numerals; anything else is ignored.
Modes: Direct and program modes are both valid.
57
BASIC Reference Guide
This starts the program from scratch after Y, or YES, is typed in.
SAVE
Type: Command
Syntax: SAVE [string expression ^numeric expression^ numeric expression]]]
Identical to that for LOAD. The interpretation of the final parameter is different,
however, when using a tape drive: 0 allows a relocating LOAD, so a BASIC program
can work whatever its starting address; 1 forces LOAD to put the program where it
was saved from; 2 and 3 are like 0 and 1 but additionally write an end-of-tape
marker. Chapter 14 has full details on saving to tape, and Chapter 15 discusses disk
SAVEs.
Modes: Direct and program modes are both valid.
Token: $94 (148)
Abbreviated entry: S SHIFT-A
Purpose: SAVE writes the BASIC program in memory to tape or disk, so the pro
gram is stored for future use. Programs must be saved to disk by name, but tape
programs need not have names (although names can be useful in identifying tape
contents).
ML, graphics characters, and other continuous blocks of RAM can be saved, too.
Only two pointers have to be changed (in four memory locations), effectively
redefining the position of BASIC'S program area. The pointers are locations 43 and
44 (start), as well as 45 and 46 (end). See Block SAVE in Chapter 6. Saving BASIC
with its variables is also possible. For example, BASIC followed by integer arrays
holds data in a very compact form, and both variables and BASIC can be saved to
gether, although this is tricky (and strings are best excluded). BASIC followed by
58
BASIC Reference Guide
graphics definitions can be saved like this, too—see Chapter 12. (In each case only
the pointer in 45 and 46 need be altered before saving.)
Note: As with LOAD, standard messages prompt the user when saving to tape.
PRESS PLAY AND RECORD ON TAPE is the first. The system can't distinguish
these keys from PLAY on its own, so if you're careless you may find you've recorded
nothing.
Examples:
1. SAVE :REM SAVES BASIC TO TAPE WITH NO NAME
SAVE "PROG",1,2:REM SAVE TO TAPE, WITH END-OF-TAPE MARKER
Above are two BASIC program SAVE commands for use with tape. (SAVE
with forced LOAD address is generally used only with ML, where the correct loca
tion of the program in memory is essential.)
2. SAVE "PROGRAM"+TI$,8
Here is a sample disk SAVE command. This adds a clock value to keep track
of several versions of a given program. The third parameter is ignored when sav
ing to disk; there is no SAVE with forced LOAD address for disk.
SGN
Type: Numeric function
Syntax: SGN(numeric expression)
Modes: Direct and program modes are both valid.
Token: $B4 (180)
Abbreviated entry: S SHIFT-G
Purpose: SGN computes the sign of a numeric expression; it yields — 1 if the ex
pression is negative, 0 if it has a value of zero, and +1 if the expression is positive.
This is related to logical expressions and to ABS and the comparison operators. For
example, SGN(X-Y) is 0 if X=Y, 1 if X exceeds Y, and -1 if X is less than Y.
Examples:
1. ON SGN(X)+2 GOTO 400,600,800
This statement causes program execution to branch to 400 if X is negative, to
600 if X is 0, and to 800 if X is positive.
2. FOR J= -5 TO 5: PRINT J;SGNa);SGNg)*J;SGN(J)*INT(ABS(J)): NEXT
This example prints several results; the last is like INT but rounds negative
numbers up.
SIN
Type: Numeric function
Syntax: SlN(numeric expression)
Modes: Direct and program modes are both valid.
Token: $BF (191)
Abbreviated entry: S SHIFT-I
59
BASIC Reference Guide
Purpose: SIN returns the sine of the numeric expression, which is assumed to be an
angle in radians. (Multiply degrees by V180 t0 convert to radians.)
See ATN for the converse function.
Examples:
1. FOR J=0 TO 90: PRINT J SIN(J* tt/180): NEXT
The above line prints sines of angles from 0 to 90 degrees in one degree
steps.
2. 120 X=A+SIN(A)/2: Y=A+SIN(A)*3/2
This calculates the x and y coordinates of a geometrical shape.
SPC(
Type: Special output function
Syntax: SPC(numeric expression)
SPC appears only in PRINT and PRINT# statements. The numeric expression
must evaluate to a value in the range 0-255.
Modes: Direct and program modes are both valid.
Token: $A6 (166)
Abbreviated entry: S SHIFT-P (This includes the open parenthesis mark.)
Purpose: SPC helps format screen or printer output. The name is misleading: With a
TV or monitor, 0 to 255 cursor-rights can be printed, but these aren't spaces. Try the
following:
PRINT SPC(200)"HI!"
However, with any other device, spaces are output, since cursor-rights are not in
the ASCII system. TAB is exactly the same except that it moves from the leftmost
column, rather than moving from the current position.
Examples:
1. 100 PRINT "{HOME}";: FOR J=0 TO 21: PRINT "X" SPC(38) "X";: NEXT
This prints a border down each side of the screen, without disturbing the
screen characters between the borders.
2. 90 OPEN 1,3: CMD 1
Add this line to the previous; note how an open file causes spaces, not
cursor-rights, to be output.
SQR
Type: Numeric function
Syntax: SQR(numeric expression)
The numeric expression must be positive, or an 7ILLEGAL QUANTITY ERROR
will result.
Modes: Direct and program modes are both valid.
Token: $BA (186)
Abbreviated entry: S SHIFT-Q
60
BASIC Reference Guide
Purpose: SQR calculates the square root of a positive argument. This is a special case
of the power (up-arrow) function. SQR actually works faster than XT .5, though, and
is also more familiar to many people.
Examples:
1. PRINT SQR(2) :REM PRINTS 1.41412356
This prints the square root of 2.
2. Xl=(-B + SQR(B*B - 4*A*C)) / (2*A)
X2=(-B - SQR(B*B - 4*A*C)) / (2*A)
These are both solutions of the equation AX2 + BX + C = 0.
ST
Type: Reserved variable
Syntax: ST is treated like a numeric variable, except that no value can be assigned to
ST. (For example, X=ST is correct, but ST=X is not allowed.)
Modes: Direct and program modes are both valid.
Token: Not applicable
Abbreviated entry: Not applicable
Purpose: ST indicates the status of the system after any input or output operation to
tape, disk, or other peripheral. ST is set to 0 before GET, INPUT, and PRINT as well
as CMD, GET#, INPUT# and PRINT#, so ST is rather ephemeral; where it is used it
should be used after every command.
ST is a compromise method of signaling errors to BASIC without stopping it. It
can often be ignored. Table 3-1 shows the meaning of different values of ST for dif
ferent devices. (Where more than one error occurs, they are ORed together, so
ST=66 combines the conditions indicated by 64 and 2.) Chapters 14, 15, and 17
provide details on ST with tape units, disk drives, and modems, respectively.
Note: ST for tape and disks is stored in location 144; ST for RS-232 devices is held
in location 663. ST (like TI and TI$) is checked when a variable is set up; normally,
no ST variable exists in RAM, and ST is processed by special routines. ST isn't a
tokenized keyword or even a normal variable; this is why BEST=2 is accepted, and
means BE=2, despite the apparent presence of ST.
61
BASIC Reference Guide
ST (like TI and TI$) can be POKEd to any value in the legal range. ST can be
used from ML. See Chapter 8, which deals with Kernal routines for information on
this and on methods for reading errors from the disk drive.
Examples:
1. OPEN 11,11: PRINT#11,X$
The above line opens a file to a nonexistent device: This sets ST= —128.
2. 150 INPUT#8,X$: IF ST=64 GOTO 1000
This is a typical end-of-file check, for use when reading data from disk or
tape. Line 1000 might be an exit routine to print totals of all the data, then finish.
STOP
Type: Statement
Syntax: STOP
Modes: Direct and program modes are both valid.
Token: $90 (144)
Abbreviated entry: S SHIFT-T
Purpose: Like the RUN/STOP key, the STOP statement returns the program to
READY mode and prints a BREAK message showing the line number at which the
program stopped. Like END, STOP can set breakpoints in BASIC, but it's better be
cause the line numbers allow you to insert as many STOPs as you want. See CONT
(and GOTO if CONT can't continue) for information on using breakpoints.
Example:
80 GET X$: IF X$="" GOTO 100
90 IF X$="*" THEN STOP :REM STOP IF ASTERISK PRESSED
STR$
Type: String function
Syntax: STR$(numeric expression)
Modes: Direct and program modes are both valid.
Token: $C4 (196)
Abbreviated entry: ST SHIFT-R (This includes the $.)
Purpose: STR$ converts any floating-point number into a string, so that the number
can be edited. It formats numbers as PRINT does, so STR$(10.0) is " 10" (with a
leading space), and STR$(-123) is "-123".
Examples:
1. FOR J=l TO 100: PRINT STR$g)+".0" NEXT
This line prints 1 as 1.0, 2 as 2.0, and so forth.
2. PRINT "0" + MID$(STR$(X),2)
62
BASIC Reference Guide
outputs X as "0.57", etc., where X is between 0.01 and 1.0; MID$ and STR$ to
gether remove the leading space. (Remember that numbers from 0 to 0.01 are out
put in exponential notation.)
SYS
Type: Statement
Syntax: SYS numeric expression
The expression must evaluate to a number between 0 and 65535.
Modes: Direct and program modes are both valid.
Token: $9E (158)
Abbreviated entry: S SHIFTY
Purpose: SYS transfers control to ML at the address following SYS. The ML is exe
cuted and will return to BASIC and execute the statement after SYS if the machine
language routine ends with an RTS instruction (or the equivalent). The registers A,
X, Y, and SR are loaded with values from locations 780-783 by SYS, and their val
ues after the subroutine call are replaced in 780-783. This offers a useful way to
check short ML routines for errors.
Notes:
1. Chapter 7 introduces ML programming on the 64; Chapter 6 has many examples
which use SYS. Many of them end with a DATA value of 96, which is the decimal
value of RTS. A jump to a ROM subroutine ending with RTS (DATA 76,XX,XX)
also works, and RTI is sometimes used, too (a decimal value of 64).
2. Careless SYS calls may crash or corrupt BASIC, and perhaps cause odd results
sometime later in the same programming session. Try:
SYS 47175
as an example. (This sets the decimal flag in the chip.)
3. ROM occupies locations 40960-49151 and 57344-65535 in the Commodore 64,
unless one or both ROMs have been switched out (Chapter 5 explains this).
Usually, SYS calls into these regions have repeatable and predictable effects with
the 64, as explained in Chapter 11. Such calls will not work with other computers,
which means they are machine-specific.
Examples:
1. 10 SYS PEEK(43) + 256*PEEK(44) + 30
The above SYS calls ML stored within BASIC; this form works regardless of
BASIC'S start address. Chapter 9 explains these techniques in depth.
2. SYS 64738
This well-known ROM routine call resets the 64 as though it were turned
off, then on again. Chapter 11 lists ROM call addresses, and some (such as the
screen routines in Chapter 6) are listed elsewhere.
3. POKE 780,ASC("$"): SYS 65490: REM KERNAL ROUTINE
This puts the dollar character in the storage location for A, then calls a
Kernal output routine at $FFD2. (Kernal routines are explained in depth in Chap
ter 8.) The effect is to print a dollar sign.
63
BASIC Reference Guide
TAB(
Type: Special output function
Syntax: TAR(numeric expression)
TAB appears only in PRINT or PRINT# statements. There must be no space be
tween B and ( and the expression must evaluate to a number in the range 0-255.
Modes: Direct and program modes are both valid.
Abbreviated entry: T SHIFT-A (This includes the open parenthesis.)
Purpose: TAB prints an expression at the desired position on the line (values be
tween 0 and 255) specified by the parameter, unless this position is to the left of an
earlier TAB in the same PRINT statement (like typewriter TABs, TAB doesn't work
from right to left).
Note: TAB is nearly identical to SPC; the difference is that TAB subtracts its current
position on the line from the TAB value, then issues that number of moves right.
TAB's use of cursor-rights and spaces is the same as SPC's. In reverse mode, cursor-
rights show as square brackets.
Example:
FOR J=l TO 10: PRINT J TAB(4)J*J TAB(10)J*J*J: NEXT
TAN
Type: Numeric function
Syntax: TAN(numeric expression)
Modes: Direct and program modes are both valid.
Token: $C0 (192)
Abbreviated entry: None
Purpose: TAN calculates the tangent of any numeric expression, which is assumed to
be an angle in radians. The values tt/2 (90 degrees) and other equivalent values
cause 7DIVISION BY ZERO errors and should be tested for to avoid program
crashes. TAN divides SIN by COS; it is slower and less accurate than either.
Example:
90 A=ATN(TAN(A))*180/tt
This converts any radian measurement into its equivalent from —90 to +90
degrees.
Tl and Tl$
Type: Reserved variables
Syntax: TI is treated like a numeric variable, and TI$ like a string variable, except
that no value may be assigned to TI (TI=X is never allowed). TI$ = string expression
of length 6 is allowed, but expressions with more or fewer than six characters cause
an error.
64
BASIC Reference Guide
where the last of the three bytes changes fastest. Try writing a loop that repeatedly
PEEKs location 162 for a demonstration. The easiest way to change the clock setting
is with the statement:
TI$="101500"
The above line would set the clock to a quarter after ten. Note that ML can be
used to set and read TI$; Chapter 8's section on Kernal ROM routines gives full
information.
Note: Like ST, these variables are intercepted by BASIC, not set up in the normal
variables section of memory located above BASIC program space. TIME and TIME$
are treated like TI and TI$, but ANTIC is treated as AN, and the characters TI are
ignored.
Examples:
1. 50 TI$=HH$ + MM$ + SS$
This program line combines three previously entered two-digit strings into
TI$
2. T$=TI$: PRINT MID$(T$,1,2) + ":" + MID$(T$,3,2) ":" + MID$(T$,5,2)
The above line prints TI$ in the format HH:MM:SS. Note that T$ stores the
value in case TI$ changes while the strings are being calculated (for example, from
11:59:59 to 12:00:00).
3. 10 DIM T1/T2J: T1=TI
20 FOR J= 1 TO 1000:NEXT
30 T2=Tl-58 :REM FIGURE MAY VARY A BIT
40 PRINT T2/60 'THOUSANDTHS OF SEC"
Shows how individual BASIC operations can be timed. The first line insures
that TI, T2, and J are placed at the start of variables; the number in line 30 must
be set so that the program as it stands prints a value of 0. This means that any
additional commands put in the loop in line 20 are timed exactly. You'll find that:
POKE 7680,123
takes 8/1000 second, a colon takes about 1/10,000 second, and so on. See Chap
ter 6 for more on this topic.
65
BASIC Reference Guide
USR
Type: Numeric function
Syntax: USR(numeric expression)
Modes: Direct and program modes are both valid.
Token: $B7 (183)
Abbreviated entry: U SHIFTS
Purpose: USR allows you to define a function in machine language. This requires a
thorough understanding of ML; in BASIC, it's nearly always easier to use a DEF FN
expression, and not much slower. In Chapter 8, the section on calculations has a
complete explanation of this function with examples.
VAL
Type: Numeric function
Syntax: VAL(string expression)
Modes: Direct and program modes are both valid.
Token: $C5 (197)
Abbreviated entry: V SHIFT-A
Purpose: VAL converts a string into a number, so calculations can be performed on
it. If the string is not a valid number representation, as much as possible is con
verted, and the remainder ignored with no error message. Valid characters are
spaces, signs, numerals, unSHIFTed E, and periods in certain combinations. VAL is
the converse of STR$.
Example:
PRINT VALr 0.77") :REM PRINTS .77
PRINT VAL(//1.72E3//) :REM PRINTS 1720
PRINT VALC' +773 DOLLARS'') :REM PRINTS 773
IN$=//1.2.3//: PRINT VAL(IN$) :REM PRINTS 1.2
PRINT VAL("12"+"."+"01") :REM PRINTS 12.01
IF VAL(IN$)<0 OR VAL(IN$)>10 THEN PRINT "ERROR"
These should be self-explanatory. Note that the last of these tests an input num
ber, avoiding bugs caused by comparing strings with each other.
VERIFY
Type: Command
Syntax: VERIFY [string expression [.numeric expression^ numeric expression]]]
Identical syntax as LOAD. Syntax should match that of the LOAD or SAVE
statement preceding VERIFY.
Modes: Direct and program modes are both valid.
Token: $95 (149)
Abbreviated entry: V SHIFT-E
66
BASIC Reference Guide
Purpose: VERIFY reads and compares a BASIC or ML program from disk or tape
with the program already in memory. If they aren't identical, 7VERIFY ERROR is re
ported. When VERIFY is used at all (it often isn't), it's generally to verify that a pro
gram has saved correctly. (It can be used in program mode, so a program can verify
itself.)
Because programs may load into different addresses depending on LOAD's
parameters, VERIFY should match the parameters of LOAD. Even so, BASIC can
generate spurious 7VERIFY ERROR messages, as explained in the notes at the end of
this chapter.
VERIFY cannot be used with most data files, since these cannot be loaded like
programs.
Examples:
1. SAVE "NEWPROG",8
VERIFY "NEWPROG",8
The above commands save a program to disk, then verify that it has been
saved correctly.
2. 10 PRINT "REWIND TO VERIFY"
20 GET X$: IF X$="" GOTO 20: REM WAIT FOR KEY PRESS TO VERIFY
30 VERIFY
At the start of a tape program, this verifies in program mode.
3. LOAD "VERSION6",8
VERIFY "VER 6",8
If you have two programs which you believe may be identical, VERIFY will
compare them. OK means that your two programs are indeed identical. This is
often useful when a disk contains lots of versions of a program, including security
duplicates, saved during program development.
WAIT
Type: Statement
Syntax: WAIT numeric expression, numeric expression [.numeric expression]
The first parameter is an address (in the range 0-65535); the others are in the
range 0-255 and will be converted to integers. The optional third parameter defaults
to 0.
Modes: Direct and program modes are both valid.
Token: $92 (146)
Abbreviated entry: W SHIFT-A
Purpose: This statement waits until one or more bits of the memory location are
cleared (given a value of 0) or set (given a value of 1) in the way specified by the
two parameters. The contents of the location are Exclusive-ORed with the third
parameter, then ANDed with the second parameter. If all bits are still 0, the
comparison is repeated; otherwise, BASIC continues with the next instruction.
Notes:
1. The location read by WAIT must be one whose contents can change, or the pro
gram will wait indefinitely. Chapter 11 has a list of locations which WAIT might
67
BASIC Reference Guide
use. Note, however, that WAIT commands don't usually work on other computers.
In fact, they're often better replaced, as they always can be, by an equivalent
statement using PEEK.
2. The operation of WAIT can be hard to explain. First, consider Exclusive-OR (EOR
is the 6502 mnemonic, so we'll use it as an abbreviation). Its truth table is:
0 EOR 0 = 0
0 EOR 1 = 1
1 EOR 0 = 1
1 EOR 1 = 0
If both bits tested are the same, the result of an EOR is 0. If the bits are different,
the result is 1. To put it another way, EOR is true if either but not both bits are set.
The statement:
WAIT address,a,b
first EORs the byte in address with b. This allows any bit, or bits, to be flipped (set
bits will be cleared and clear bits will be set). The result is ANDed with a, which
allows any bit to be turned off (in this case, ignored). Since a zero result makes
WAIT loop again (continue waiting), we can select a and b to respond so that any
single bit changing either to on or off, can cause the program to exit from WAIT
and execute the next statement. In the special case:
WAIT address,a
there is no EOR parameter. (Actually, the value in address is EORed with a zero
byte, so the result is always the same as the original value in address.) Thus:
WAIT address,16
waits until bit 4 is set. If it never is, WAIT continues forever. This is why WAIT
addresses should only be in RAM or in a hardware register which can change.
Examples:
1. POKE 162,0: WAIT 162,16
This line causes the computer to wait until the jiffy clock TI counts to 16
(about 1/4 second).
2. 100 POKE 198,0: WAIT 198,1
This example waits for a keypress (until one character is in the keyboard
buffer).
3. WAIT 56321,32,32
This waits until bit 5 of location 56321 is off. This happens when the Com
modore key is pressed.
4. 10 WAIT 53265,128: POKE 53281,RND(1)*16: GOTO 10
The above line waits until bit 8 of the raster line becomes 1, indicating bot
tom of screen is reached, before changing screen color. Chapter 12 has more
examples of these techniques.
68
BASIC Reference Guide
?BAD SUBSCRIPT
The value given an array subscript is negative, or larger than that in the DIM state
ment (larger than 10 if the array has not been explicitly dimensioned). This message
is also given if the wrong number of subscripts is used.
?BREAK
The RUN/STOP key was pressed before LOAD or SAVE was complete.
?CAN'T CONTINUE
The program cannot be continued using CONT because of one of the following
conditions:
• The program halted due to a SYNTAX ERROR, instead of the RUN/STOP key,
STOP, or END;
• CLR has erased its variables;
• the program was edited after it stopped, effectively erasing variables;
• a direct mode error occurred, which the system can't distinguish from a program
error; or
7DIVISION BY ZERO
An attempt has been made to divide by zero, which BASIC does not allow, generally
when a denominator underflows to zero. TAN(7r/2) contains an implicit division by
zero.
?EXTRA IGNORED
Given when the response to INPUT contains more items than asked for by INPUT'S
parameter list. The extra items are lost. INPUT# behaves identically, but doesn't
print the error message. Often this is caused by the inclusion of commas or colons in
an input string; avoid this with leading quotes.
?FILE DATA
The type of data in a file doesn't match the variables to which it is assigned by
GET# or INPUT#. This happens when INPUT#X tries to read a string.
69
BASIC Reference Guide
?FILE OPEN
This means that a logical file number referred to in an OPEN statement has already
been opened.
?ILLEGAL DIRECT
This indicates that a statement requiring the input buffer has been entered in direct
mode, typically GET or INPUT, or that DEF FN was entered in direct mode.
?ILLEGAL QUANTITY
An expression used as the argument of a function or in a BASIC command is outside
the legal range. Attempting a POKE with either parameter negative, using a logical
file number greater than 255, and asking for the square root of a negative number
are examples.
?LOAD
A tape or disk program was not loaded successfully. See Chapter 14 (tape) or Chap
ter 15 (disk) for information on how to read the status byte to determine the cause of
the error.
70
BASIC Reference Guide
?OUT OF DATA
There were no remaining unread DATA items when a READ statement was en
countered. Pressing RETURN over the READY prompt generates this message. RE
STORE resets the data pointer.
?OUT OF MEMORY
This message indicates one of the following:
• The 64 does not have enough RAM for the program and its variables (especially if
dimensioning large arrays or inputting long strings);
• temporary storage on the stack has run out, having been filled with GOSUBs
(about 24 maximum), FOR-NEXT loops (about 10 maximum), and intermediate
calculation results:
PRINT (l+(2+(3+(4+(5+(6+(7+(8+(9+(10+(ll+(12)))))))))»)
71
BASIC Reference Guide
7OVERFLOW
The value of a calculation is outside the valid range for floating-point numbers,
approximately — 1.7E38 to +1.7E38. If a result is within the valid range, this error
may be avoidable by restructuring the computation using, for example:
PRINT (5/4)tl00
instead of:
5U00/4T100
?REDIM'D ARRAY
An attempt has been made to dimension an array that has already been dimen
sioned. It may have been dimensioned automatically. A reference to X(8), for ex
ample, implicitly performs DIM X(10) if the array doesn't yet exist in memory.
?SYNTAX
This indicates that a BASIC statement is unacceptable. There are many causes. The
64 anticipates a sequence of statements; if a statement doesn't start with a keyword
or the equivalent of LET, if a variable name isn't ASCII, if a statement isn't ter
minated with colon or null, or if parentheses, commas, and other symbols are mis
placed, 7SYNTAX ERROR often results. This message is also given after NEW if the
first byte of BASIC is nonzero.
POKE PEEK(44)*256,0: NEW
72
BASIC Reference Guide
?TYPE MISMATCH
This message is output if the interpreter detects a numeric expression where a string
expression is expected, or vice versa.
?UNDEF'D FUNCTION
An undefined function has been used in an expression; it should first have been de
fined with DEF FN.
?UNDEF'D STATEMENT
The target line number of a GOTO, GOSUB, or RUN does not exist.
7VERIFY
The program in memory isn't identical to the disk or tape file it is being compared
with by VERIFY. Spurious VERIFY errors occur if BASIC programs are loaded into
64 memory at different addresses from where they were saved; the link pointers be
tween lines are different, but the BASIC statements may be the same.
73
Programming
^^^ 0^^
Chapter 4
77
Effective Programming in BASIC
78
Effective Programming in BASIC
broadly dividing people who do not have programming skills and those who do.
The first group sees, with some relief, a rdomful of unruly children settle down to
play a number game, and thinks, "It is remarkable to see them working in an or
derly way for hours. Increased equality of education is possible. Unmotivated stu
dents find their intejr^st reawakened, and their confidence grows."
The other group's argument is, "Computers are unparalleled at teaching logical
thought. They provide great oppbrtunities for students to display their creativity, and
this may be the most important part of their schooling." When the experts disagree,
it is hard to know what makes good educational software.
Multiple-choice tests, with question-and-answer programs, graded by year and
subject, make a potentially attractive package. In principle, dozens of programs could
be used as refreshers and tests in a range of subjects. Multiple-choice questions are
easy to program, since the only reply needed is typically 1, 2, 3, or 4, without the
need to interpret a verbal answer. To discourage guessing, wrong answers could
score —1/4 point, so completely random answers would score around zero.
Single-concept programs, like children's counting programs and alphabetic-
recognition programs, are becoming available commercially. Good graphics can add
a lot of appeal and help to hold the user's attention longer. More advanced examples
include foreign language vocabulary and translation tests; economics concepts like
price elasticity, supply-and-demand curves, and marginal costs; musical relationships
between frequency and pitch; population simulations; and math techniques and con
cepts like graph plotting, limits, sums of series, calculus, and simulations of random
ness with coins, roulette, and so on. (See "Dice" page 97.)
Personal. This rapidly expanding area of the market includes games and educa
tional and home business programs. Several magazines are currently being published
which include programs in the magazine that can be typed in at home, and therefore
are practically free.
Program Design
We've distinguished programs from systems, and noted that systems require more
planning and knowledge; in the commercial world this is reflected in the job separa
tion between analysts and programmers. On the relatively mddest scale of the 64,
it's equally true; experienced programmers can almost unconsciously plan ambitious
projects out of reach of beginners. This section covers the sort of thought processes
necessary in programming and in design, with a concrete example of each to give
substance to the generalities. Bear in mind that many programmers write in an un
organized, ad hoc fashion and don't always worry about tidy, theoretical schemes. If
your programs are messy and patched together, don't worry too much—many other
people's programs are, too.
79
Effective Programming in BASIC
Understand the problem. The example is quite simple: Many computer prob
lems are not.
Express it in a computerizable way. This is where programming experience is
essential. For example, if you haven't grasped the idea of computer fU.es, you'll obvi
ously not be able to appreciate their use in storing data. If you haven't understood
that the computer has to count lines of print to know where it is on a page, you
won't be able to print titles on page tops. Knowledge of the logic of programming
equips you with methods and tricks to process data, but experience is probably the
best way to learn the physical limitations and capabilities of a particular computer.
This flow chart expresses an approach to our game in a form that can be written
as BASIC. Entries in the boxes are shorter than usual to avoid clutter. You should be
able to trace how the variable, N, records the number of guesses, and how all three
possible outcomes of the comparison between the correct number, X, and the current
guess are processed.
80
Effective Programming in BASIC
Flow charts generally use diamonds to indicate options and rectangles for opera
tions; direction of flow is usually down, with loops and branches generated by the
options arranged clockwise, as in this diagram. Many other, less common symbols
are also used. Virtually all programs have loops and decision points, and flow charts
show these clearly. However, they are hard to modify and they take up space, so
many people prefer to make outlines and notes—stylized lines of English resembling
programs. There's no correct notation; and the sad fact is that any complex program
remains complex in whatever way it is written down.
Write it in BASIC. If it's a complex program, write parts of it and test them in
dividually as subroutines. This is where past practice is invaluable, not only because
of skill in BASIC per se, but because experience suggests efficient ways of getting
results.
Algorithms are rules with explicit instructions and no exceptions, which generate
correct results. Math algorithms can be used by anyone, without understanding any
of the underlying theory. For example, linear programming (solving such problems
as finding the least expensive combination of foods which supply all known nutri
ents) involves long calculations, which give the right answer. At a simpler level,
arranging dates in the format YYMMDD makes them sort numerically into chrono
logical sequence, while MMDDYY requires more work to sort properly. A version of
3-D tic-tac-toe requires the winner to avoid a line; the algorithm start in the center
and make opposite moves always wins for the first player.
Algorithms can be used to deal with very complex situations: often the rule is
found to give good results and is therefore used in lieu of anything better.
Warnsdorf's rule in chess, to generate knight's tours around the whole board, illus
trates this. The rule is: Move the knight to the square with fewest exit squares. This
often (but not always) gives a solution. Many games—bridge, for example—are in
effect often played algorithmically, as the players follow rules that sum up the expe
rience of good players. Chess openings can be generated with simple algorithms as
well; a common example is moving to maximize the area under attack by your
pieces, while minimizing the opponent's range of replies. Reversi (or Othello™)
played on an 8 X 8 board, with pieces white on one side and black on the other,
can be played by the following simple algorithm: For about 10 moves, occupy cen
tral squares, reversing as few of the opponent's pieces as possible; for another 10
moves, keep the total number of your pieces to about ten; after this, go for maxi
mum points.
Our number game, Program 4-1, is too simple to require such intricate al
gorithms, though.
81
Effective Programming in BASIC
Lines 0-30 correspond exactly to the first boxes of the flow chart; after this, be
cause IF allows only two options, the lines cannot exactly match boxes, but the logic
is identical. Note that line 60 doesn't need to test IF G=X, since no other possibility
exists.
Test and improve. Our example could include:
70 FOR J=l TO 3000: NEXT: GOTO 10
effectively replacing the box END with a delay loop and a branch back to the PRINT
TITLE box. Values could be checked to insure that they are integers in the correct
range, and you could add color.
Testing is difficult, and many commercial programmers spend most of their time
removing bugs from programs. Ideally, with good planning, bugs would not appear,
but in practice it's seldom possible to foresee every potential problem.
System Design
In addition to the normal programming concerns, system planning takes three major
steps, which are discussed below.
Ask if the project is feasible. Time may be a problem; sorts, searches, graphics,
and tape processing may be too slow; the program's response time may be inad
equate; the data may take too long to key in. You may want to write a test program
to check the feasibility of the task. Machine language always outperforms BASIC,
but is often more difficult to program.
Generally, if much data is to be processed, estimate the total storage needed in
bytes and estimate whether it can coexist with BASIC, or whether stored files would
help. Perhaps splitting a program into smaller subprograms would be advisable. Less
tangible problems might be user attitudes, reliability, and recovery of lost data if
problems should occur. A little time spent in advance on all these questions is
usually worthwhile. Even so, there will be cases where a feasibility study requires a
lot of work.
82
A
Read
Open Read
Until Modules
Files Header
File End
/ \
"Live" "Dead" "Live" "Dead"
Header Trailer
Record Record
/ \
Read a Print a
Record Record
Subroutines
83
Effective Programming in BASIC
illustrates soft coding as well. When N=3, output is to the TV, and when N=4, out
put is to a printer. A program may include a menu of parameters at the start, so the
84
Effective Programming in BASIC
user's own requirements can be keyed in. For example, modem programs often begin
with a menu for setting baud rate, parity, and stop bits.
Error messages. These signal that a mistake has been made and should indicate
the error. Program 4-3 is a subroutine that handles error messages. Before sending
the program to the subroutine, place the error message text in the variable EM$:
EM$="TOO LONG": GOSUB 10000
This will print the message on the screen in reverse video to attract attention. You
could use this in a large program before resetting cursor position and returning for
reinput.
Easy data input. The BASIC INPUT statement is fine in many cases, but doesn't
give the programmer full control. To make a program as easy to use as possible, un
desirable keys should be blocked out or ignored. Integer input (see the section below
on string and integer input) will accept only numbers, not cursor keys, color keys, or
alphabetic characters. RUN/STOP and RESTORE may need to be disabled (see
Chapter 6), and the length of the integer checked if there's a maximum value. None
of this is very difficult, but it takes time and memory.
How easy a program is to use is important. Prompts, telling the user what to
type, and instructions, providing an overview, are helpful. The programmer must al
ways balance the program's features with memory usage and execution speed. The
lines below illustrate how to combine PRINT and INPUT into a relatively friendly
input routine and show that this requires extra memory.
100 PRINT "ENTER THE DISCOUNT"
110 PRINT "PERCENTAGE (E.G., 13.25)"
120 INPUT "AND PRESS RETURN"; PC
Menus. These are elaborate prompts, which help the user select his or her own
path through a program; often a help option is available from the menu. Data entry
can be simplified by presenting a summary of input at appropriate places in the pro
gram, allowing for easy corrections.
The best menu design allows the user to indicate the desired option by pressing
a single number or letter. If a menu program stands alone, it has to load and run a
new program (see "Chain" in Chapter 6). Of course, all or most of the options may
exist as one program in memory, if there is room. Tape units don't have the flexibil
ity of disk drives when it comes to loading one program of several, of course, be
cause tape stores programs in sequence, rather than allowing equally rapid access to
each one.
Program 4-4 is a menu which calls one of three routines based on user input.
85
Effective Programming in BASIC
86
Effective Programming in BASIC
87
Effective Programming in BASIC
Examples in BASIC
The following sections illustrate some of the fundamentals of BASIC programming.
One of the best ways to learn BASIC is by looking at program examples and modify
ing them to suit your needs. Most of your programming will involve the elementary
skills discussed here, in one way or another.
Input
Programs 4-5 and 4-6 use GET to build an input string, IN$. In Program 4-4, the
cursor flash POKEs in lines 110 and 130 simulate the way BASIC'S INPUT looks to
the user. The program gets individual characters into X$ in line 120. Line 140 allows
the INST/DEL key to operate. All other special keys are disallowed, except
RUN/STOP and RUN/STOP-RESTORE, which can be disabled if you wish (see
Chapter 6). line 150 defines the range of acceptable characters, so for integer input
the line should be changed (by placing a 0 inside the first pair of quotation marks
and a 9 inside the second pair).
Decimal input is a bit more complicated, as Program 4-6 illustrates, and extra
programming is needed to insure that only one decimal point can be entered. This
version allows only two digits after the decimal (this can be modified at line 152).
All these features can, of course, be changed, but be sure to test the results.
88
Effective Programming in BASIC
GET can build strings in any format. Machine part numbers might be of the
form ###XXX (that is, three digits followed by three letters), and a routine to input
these should test for the correct input and ignore anything else. Where an
INST/DEL key is allowed, this is a little more difficult. An input string might be ac
cepted, then tested for correct format; if an error were found, the program would
loop back for data reentry, perhaps after displaying an error message.
The discussion of INPUT in Chapter 3 explains some tricks, like forcing quotes
after the prompt.
Pressing RETURN on INPUT leaves everything unchanged; so a line like:
100 X=50: INPUT "NEW X (OR RETURN=50)"; X
Output
Many times you will want to print information in some special format. This is
especially important in financial calculations. Program 4-7 is an error-trapping and
output-formatting routine for use with numeric data.
89
Effective Programming in BASIC
Line 20 demonstrates the routine by producing test values and going to the sub
routine at line 100, where the number is converted to a string. Lines 105-115 test for
an E in the string equivalent of the value, V, and check for under- or overflow. Line
120 retains the minus sign, where applicable, so every possibility is tested for. Lines
125-150 handle the decimal point and trailing zeros, and control the length of the
string, V$, in its processed form.
Routines like this are valuable for such purposes as printing invoices, receipts,
and reports. Chapter 6 (see "PRINT USING") contains a machine language im
plementation of the same idea. The following line of BASIC is a simple method for
rounding a number to two decimal places:
X=INT(100*X 4- .5)/100
Calculations
Below are some examples of calculation and report programs. The first of these pre
dicts weight change based on information entered by the user (weight, sex, calorie
intake, and level of activity).
Lines 400-450 calculate weight change per week. Line 410 contains the formula
to determine the number of calories needed to maintain the same weight; lines 420
and 430 calculate DW, the change in weight for one day. The results of 16 weeks are
printed out. The algorithm makes standard assumptions that one pound of fat is
equivalent to 3500 calories, and that a fairly constant ratio exists between total
weight and static weight calorie intake.
Program 4-9 works out the smallest bill and coin combinations to pay the sepa
rate amounts of a payroll. Line 60 is a DATA line, which can be changed, for ex-
90
Effective Programming in BASIC
ample, to eliminate hundred dollar bills, add twenty dollar bills, or convert to other
currencies (the first value on this line is the number of different denominations
used). Line 130 adds a small correction to each figure so there is no chance of round
ing errors.
60 DATA 11,100,50,10,5,2,1,.5,.25,.1,-05,.01
:rem 46
70 READ NUMBER OF DENOMS: DIM NC(NU),QU(NU) :rem 6
80 FOR J=l TO NU: READ NC(J): NEXT :rem 72
110 INPUT M{CLR}# OF EMPLOYEES"; EMPLOYEES: DIM SA
LARIES OF (EMPLOYEES) :rem 83
120 FOR J=l TO EM: PRINT "EMPLOYEE #"J; :rem 122
130 INPUT SALARY OF (J): SA(J)=SA(J) + NC(NU)/2
: rem 6
140 NEXT :rem 212
210 FOR J=l TO EMPLOYEES :rem 136
220 FOR K=l TO NUMBER :rem 160
230 X=INT(SAL(J)/NC(K)): SAL(J)=SAL(J)-X*NC(K): QU
(K)=QU(K)+X :rem 4
240 NEXT K :rem 32
250 NEXT J :rem 32
310 PRINT "{CLR} ANALYSIS:" :rem 150
320 FOR J=l TO NU: IF QU(J)=0 THEN 340 :rem 183
330 PRINT QU(J) "OF $" NC(J) :rem 141
340 NEXT :rem 214
91
Effective Programming in BASIC
String Handling
Words are handled by BASIC as strings; this allows constructions like:
10 INPUT "NAME";N$: PRINT "HELLO, " N$
This feature is useful for games, especially text adventures, and can be used to
personalize the replies the computer makes to the user. Typing trainer programs use
the same principle. At a more involved level, any individual characters can be se
lected at will, using MID$, LEFTS, or RIGHTS (actually, MID$ is enough), and any
combination of characters can be generated with the aid of the string concatenation
92
\
operator (+). Program 4-7, "Rounding," showed how to scan a string for the charac
ter E. Program 4-12 shows how a number can be scanned (in its string form) to re
place the number 0 with the letter O, which many people prefer.
When storage space is short, data compression may be necessary, and Program
4-13 illustrates how long numbers can be packed into about half their normal length,
using string handling:
Analogous tricks include collecting similar characters together and selecting from
them with MID$. For example, there's no simple connection between color keys and
their ASCII values, but:
C$="{BLK} {WHT} {RED} {CYN} {PUR} {GRN} {BLU} {YEL}"
is a character string holding eight of them, and PRINT MID$(C$,J,1) prints the Jth
color, where J is 1-8.
Program 4-14, a simple version of the word game Bingo, illustrates a small-
system program that evaluates five-letter words by giving each letter a point value
(which you may vary between runs).
93
Effective Programming in BASIC
The first part of the program accepts five-letter words (note the check in line
120) and writes them to disk, stopping when the end-of-file indicator (END**) is
typed in. RUN 200 runs the second phase: 26 values corresponding to A-Z are en
tered by the user, and the Commodore 64 reads back all the words on file and prints
word values. Line 360 converts each letter into a number from 1 (for A) to 26 (for Z).
The variable, S, in line 370 is the total for the word which has been read from file;
this shows how MID$ can analyze the individual letters in a word.
The program can be refined by categorizing the words, for example, into those
beginning B, I, and so on, and rejecting words whose total value is less than the
highest so far, or by using a different file for each type of word.
94
Effective Programming in BASIC
Number of data items 50 100 200 500 1000 2000 4000 9000
Shuffling is the converse of sorting. Program 4-15 prints a randomly dealt whole
deck of electronic cards:
95
Effective Programming in BASIC
Lines 100-140 of the above program generate numbers from 1 to 52, without
producing the same number twice. Lines 300-410 print the suit and set the correct
color, while lines 500-600 convert the number to the card's value. Although Pro
gram 4-15 is fast, it is not as easy to understand as Program 4-16, a simpler method
of shuffling the cards.
"Simpler Shuffler" puts each of the 52 numbers into an array element. If the
random array position is already occupied, it tries again with another random num
ber. This way, every possible number is used and none is repeated.
Randomizing is using somewhat unpredictable (pseudorandom) numbers for
games, simulations, problem solving, and so forth. Program 4-17 uses random num
bers to find the chessboard positions of queens, such that no queens attack each
other. Rather than testing completely random boards, the program retains most of an
unsuccessful test, moving an attacking queen at random, and producing results quite
rapidly. The speed decreases greatly with larger boards; analysis of a 20 X 20 chess
board could take hours.
Lines 10-50 generate the starting positions of the queens, and lines 100-150 test
the board to see if any queens are attacking each other. Lines 200-300 generate the
printout of the positions.
96
Effective Programming in BASIC
Random numbers can be used to solve simulation problems of many kinds. Pro
gram 4-18 prints the sum of the values of two dice and also keeps a running score. It
keeps track of the average number of throws required to produce a 7 (the answer is
it takes an average of six throws to score a 7).
Data Structures
BASIC'S data structures are files, DATA statements, variables, and RAM storage.
Files, which store data externally on tape or disk, aren't limited by available RAM
and are necessary in handling large amounts of data. Disk files have more scope
than tape, since several files can be accessed at once and search time is greatly re
duced. Chapters 14 and 15 give full details of tape and disk programming, respec
tively. "Wordscore" (above) is an introductory example of file handling.
We've seen examples using DATA statements as well. Obviously, data cannot be
changed in the same way variables can, and we needn't say more about it here.
Simple variables are used often in BASIC. Chapter 6 explains exactly where they
are placed in memory and how their values are stored.
Arrays (subscripted variables) offer a powerful extension to the concept of vari
ables and are worth mastering for many serious applications. They provide a single
name for a whole series of related strings or numbers, using one or more subscripts
to distinguish the separate items or elements.
97
Effective Programming in BASIC
One-dimensional arrays have a single subscript, which may take any value from
0 to the value used in the DIM statement that defined the array (or 10 if DIM wasn't
used). The line:
DIM A$(50), N%(100), SX(12)
defines three arrays: string, integer, and real number, respectively. Space is allocated
in memory for them, except for the string arrays. Arrays can be visualized as a set of
consecutively numbered pigeon holes, each capable of storing one value, and initial
ized with contents 0. A typical application is the lookup table. A string array might
hold values like this:
A$(0)="ZERO", A$(1)="ONE",
would print the value of J as a word, provided J fell into the correct range. It might
hold a list of names, ready for sorting, so that A$(0), A$(l), and so on, would be ar
ranged alphabetically after sorting. Numeric arrays can be used to store the results of
calculations. Many of the examples in this section use such arrays. For example,
numbers ranging from 1 to 52 can represent playing card values; numbers from 1 to
8 can represent the position of queens on a chessboard, indicating the row on which
that column's queen is placed. It's often worthwhile to set up tables of the results of
calculations, which can be looked up rather than recalculated. Chapter 13's sound
calculations illustrate this technique.
Array variables are slower than simple variables, because of the processing re
quired, but they are versatile. DIM A$(50) makes room for 51 variables (remember
the zero element) and assigns each a unique name. Without this facility you'd have
to define 51 individual names, and the resulting slowing effect would be
considerable.
Two-dimensional arrays have two subscripts.
DIM C(8,8)
defines a number array with room for 81 numbers, which might be used to record a
chess position, pieces being represented by positive or negative numbers, with sign
representing color, and magnitude, the type of piece. Two-dimensional arrays are
valuable for storing data for business reports. (Three dimensions or more are concep
tually a bit more difficult, but are occasionally useful.) For example, sales figures
may be available for ten items in 12 different outlets. An array can keep the sets of
data distinct. Subtotals and overall totals can be conveniently stored in the often ne
glected zeroth elements.
Integer arrays, which store numbers from —32768 to +32767 in slightly more
than two bytes apiece, are particularly efficient in storing data and can be loaded
from disk as a block of memory, as the following section explains. It's possible to
telescope surprisingly large amounts of data into memory like this, although the
programming is likely to be difficult.
Multi-dimensional arrays—at least those with more than two or three dimen
sions—aren't used much, probably because of the difficulty of visualizing the data's
storage pattern. Three-dimensional arrays can be pictured as rooms in a building,
98
Effective Programming in BASIC
having height, width, and depth. After this, depiction becomes progressively more
complicated. In practice, large numbers of dimensions soon exhaust the 64's
memory.
RAM storage: Data may be poked into RAM for future use, or loaded from disk
or tape, although this is not strictly BASIC. Chapter 6 discusses this technique and
gives many examples.
BASIC data can be treated in the same way, although generally this is worth
doing only when integer arrays store data to be saved directly to disk or tape—
which is far more efficient than writing to a file. Chapter 6 explains block SAVEs, the
relevant area being that from PEEK(47)+256*PEEK(48) to PEEK(49)+256*PEEK(50).
Control Structures
Some BASIC enhancement utilities offer structures like REPEAT-UNTIL and DO-
WHILE. It's possible to simulate these forms with BASIC; see FOR in Chapter 3, and
this example:
100 FOR J=0 TO -1 STEP 0
110 REM INSERT UNTIL COMMANDS HERE
120 J= (A=B)
130 NEXT
This has the same effect as REPEAT-UNTIL A=B, since J becomes -1 only
when the logical expression in line 120 sets J true.
IF-THEN-ELSE is another structure missing from the 64's BASIC. ON-GOTO or
ON-GOSUB is the nearest approach. Where ON isn't suitable, because an expression
evaluating to 1, 2, 3, ... doesn't exist, GOTOs will probably be necessary to process
both the THEN and ELSE parts of the program.
Processing Dates
Dates are sometimes difficult to handle; this section has routines to help validate
them, to compute the day of the week given the date, and to calculate the number of
days between two dates. (Note that leap years are, of course, allowed for, but the
years 2000 and 1600, which don't count as leap years, have not been corrected for.)
Program 4-19 is a date validation routine, which checks to make sure the day,
month, and year combination is valid. D, M, and Y should be input as one- or two-
digit numbers.
If OK is true, D, M, and Y are acceptable. Line 1005 expects M to be from 1 to
12, and Y to be 85 or 86; you can modify the limits for your own purposes. Line
1010 checks that the day does not exceed 28, 29, 30, or 31, whichever applies to its
month and year.
99
Effective Programming in BASIC
Program 4-20 calculates the day of the week from the date. The weekday is
found by an algorithm usually called Zeller's Congruence.
Program 4-21 calculates the number of days between two dates by taking the
difference between days elapsed from an arbitrary early date to the two requested
dates.
10 DATA 0,31,59,90,120,151,181,212,243,273,304,334
:rem 137
20 DIM D(12): FOR J=l TO 12: READ D(J): NEXT
:rem 193
100 INPUT " FIRST DATE (M,D,Y)M; M,D,Y :rem 27
110 GOSUB 1000: DX=DE :rem 111
120 INPUT "SECOND DATE (M,D,Y)"; M,D,Y :rem 81
130 GOSUB 1000: DY=DE :rem 114
200 PRINT DY-DX "DAYS": END :rem 11
1000 DE = D + D(M) + 365*Y + INT((Y-l)/4) - ((INT(
Y/4)*4=Y) AND (M>2)) :rem 146
1010 RETURN :rem 162
Flne-Tuning BASIC
The following methods individually have little effect, but collectively can be useful.
They're arranged in approximate order of ease of implementation:
• Turning off the VIC chip takes about 5-1/2 percent off running time (providing you
don't need to watch the TV or monitor). POKE 53265,0 turns off the chip; POKE
53265,27 is the usual value to reenable the screen. See Chapter 5.
100
Effective Programming in BASIC
• Reducing the interrupt rate by POKE 56325,255 gives the 6510 more time to pro
cess BASIC, less to scan the keyboard. Speed is increased by a couple of percentage
points.
• You should DIM variables in their order of importance at the start of the program.
This has some effect on speed, depending on the number of variables in the pro
gram; Chapter 6 explains why. New variables defined after arrays have been set up
cause a one-time delay, too.
► Using large numbers of strings causes garbage collection delays; these can be cut
down only by reducing the number of strings or by using fewer string operations.
See Chapter 6.
• Crunching (removing spaces from BASIC programs, except within quotes, and
collecting the program into as few lines as possible) improves speed by an amount
which depends on the waste in the original program.
» Altering BASIC to run efficiently comes with practice, but a typical example is mov
ing REM statements outside loops; if they're inside, the REM statements are exe
cuted many times. Streamlining unnecessarily repetitive programs will generally
provide significant speed improvement.
• The discussion in Chapter 8 about moving BASIC into RAM includes a few simple
commands to alter the operation of RUN. As a result, BASIC programs run about
3-1/2 percent faster.
101
Chapter 5
Commodore 64
Architecture
• Introductory Hardware Topics
• The 64's Memory Configurations
• Commodore 64 Ports
• Programming the CIAs
• Program Recovery and Resetting
• Commercial Software and Hardware
Chapter 5
Commodore 64 Architecture
Binary Numbers
A bit, or frmary digit, is a single, tiny electronic switch, which can be either on or off.
It can be pictured as an ordinary switch, which either passes or blocks current. It is
actually a tiny area of a computer chip that either holds an electrical charge or
doesn't. Hundreds of thousands of these switches are contained in a Commodore 64,
inside the black rectangular integrated circuit chips.
Each bit has a choice of only two values: on or off. According to convention, the
binary voltage values are assigned numbers (no voltage is represented by 0, and
voltage high is represented by 1). The systems are then structured so that binary
arithmetic works correctly. The values aren't actually 0 or 1, but this provides a
convenient way of talking about them.
In principle, three values could be used, making trinary arithmetic possible, but
binary hardware is by now so firmly established that this is not likely to become im
portant. As several computer manufacturers have found already, there is often little
economic sense in introducing hardware based on such novel processes.
All Commodore 64 operations are binary. In hardware terms this is reflected in
the large number of electronic lines needed to carry data within the Commodore 64.
The expansion port, for example, has 44 separate lines. Every line, apart from those
which supply power or are grounded, is treated by the system as carrying either
high or low voltage values. Each additional line roughly doubles the system's poten
tial for information handling.
A full understanding of programming requires a grasp of the relation between
binary numbers and ordinary numbers. Fortunately, this is not difficult, although it
can look forbidding at first. Binary arithmetic uses a notation of 0's and l's only.
However, it represents ordinary numbers and is merely a different way of writing
them, just as MCMLIX is a different way of writing 1959.
A digit's position within a decimal number determines the magnitude of that
digit; thus, 123 and 1230 mean different things. In the same way, the positions of 0's
and l's within a binary number determine the value of that number. Binary
10101100 is different from 00101011, with the leftmost number being the most
significant. And just as decimal digit positions increase in value by powers of ten (1,
10, 100, 1000, 10000, . . .), from right to left, binary digits increase in value by pow
ers of two (1, 2, 4, 8, 16, . . .).
To avoid confusion, a binary number will be written as a series of 0's and l's
105
Commodore 64 Architecture
prefaced by a percentage sign (%). This lets us be sure, for example, that the decimal
number 10 is not confused with %10, which represents the decimal value 2 in binary
notation.
The word byte is a derivation of the word bit. It is supposed to imply a larger,
scaled-up version of a bit, and that is more or less what it is. A byte is a collection of
8 bits which are dealt with by the system as though they were a single unit. This is
purely a hardware matter: IBM invented 8-bit bytes, but other numbers such as 4 or
6 bits are in use, too. A 4-bit binary number is called a nybble.
The 6502-based (6510) microprocessor which operates your Commodore 64 is
called an 8-bit chip because it is designed to deal with data 8 bits at a time. Its
addressing uses 16 bits, but only by dividing up each address into two sets of 8 bits
each, called the low byte (the less significant) and the high byte (the more significant).
Since each of the 8 bits can be either on or off, you have 28 = 2*2*2*2*2*2*2*2
= 256 potential combinations for each byte. That is the reason that PRINT
PEEK(address) always yields a value between 0 and 255. It also explains why a 16-bit
address cannot exceed 65535, which is 216 — 1 (since 0 is one of the combinations).
There is a total of 65536 addressable memory locations, with addresses ranging from
0 to 65535.
Table 5-1 shows some binary numbers and their decimal equivalents and also
demonstrates certain features of the bit pattern of binary numbers. For example, if
bit 7 is 1, the number must have a decimal value of 128 or more. If bit 0 is 0, the
number must be even because only bit 0 can take the value 1 and make a number
odd. If a bit pattern is moved as a whole to the right one position, its value is exactly
halved (provided the smallest bit isn't a 1 and therefore lost when the shift takes
place). Finally, if a bit pattern is inverted (so that all l's become 0's and all 0's be
come l's), the two individual values will always add to 255 (because 255 is
%11111111). Observations like these, which are analogous to ordinary base 10
arithmetic, are crucial to the understanding of ML programs.
106
Commodore 64 Architecture
Hexadecimal Numbers
Hexadecimal (base 16, called hex for short) is another notation that is useful when
programming. BASIC programmers usually ignore hex, but ML programmers find it
useful. It relates directly to the internal design of the computer, and this relation
helps ML programmers and hardware designers.
Hex uses 16 different numeral symbols for its arithmetic—the ordinary numeral
symbols 0-9 and the letters A-F. To avoid confusion with ordinary numbers, hex
numbers are preceded by the dollar sign ($). Thus, $1 is a valid hex number, as are
$A000, $1234, $21, $ACE, and $BEER The decimal equivalents of these numbers are
40960, 4660, 33, 2766, and 48879. They can be looked up in conversion tables or
converted with a programmer's calculator.
It is important to notice the relationship between binary and hex numbers,
which results from the fact that 16 (hex's base) is a power of 2 (binary's base). It
turns out that any 16-digit binary number (2 bytes) can be represented in only 4 hex
digits. This saves space and is easier to remember.
Because each memory location in the 64 can hold one 8-bit byte (having a deci
mal value 0-255), it is common to write single bytes in hex using two digits, even if
the leading digit is 0. Thus, the range is $00-$FR That makes for neater programs,
because the numbers line up evenly. Similarly, since memory addresses can only
range from 0 to 65535, they are written as 4-digit hex numbers. It is not necessary
that leading zeros be included ($033C and $33C are the same number), but many
ML programs are written to expect such an arrangement.
107
Commodore 64 Architecture
To convert $1234 into decimal, multiply 1 by 163 (or 4096), adding 2 times 162
(or 256), adding 3 times 161 (or 16), and finally adding 4 times 16° (or 1). Conver
sion from decimal to hex can be done by dividing by 4096 first, then repeatedly mul
tiplying remainders by 16 to find the next digit.
Computer memory is measured in kilobytes (K), where IK is 1024 (210) bytes.
Note that $1000 is 4096, exactly 4K, and that there are exactly sixteen 4K blocks of
memory in the 64, $0000-0FFF, $1000-$lFFF, and so on.
108
Commodore 64 Architecture
manufacturing problems, and are therefore not designed for direct use of the average
consumer.
On the other hand, an EPROM (Erasable Programmable Read Only Memory)
chip resembles a ROM chip, but is made in much smaller quantities. Any program
can be burned in with inexpensive, widely available equipment. EPROMs have a
window in the top, usually covered by a label. If this label is removed, exposure to
strong ultraviolet light will erase the EPROM for reprogramming. This is how bugs
in an EPROM program can be corrected. A PROM (Programmable Read Only
Memory) is similar to an EPROM, but not erasable. Many cartridge products, printer
interfaces, and other products under development use EPROMs or PROMs.
There is an intermediate form of memory in which data stored in RAM can be
in effect converted to ROM, by disabling the RAM chip's write-enable line. Battery
backup allows such a package to store data even when disconnected from the usual
house current. This capability could become important, in view of the relatively low
price of RAM.
The 64's Input/Output (I/O) chips are the VIC-II (Video Interface Chip), which
generates the signal for the color TV; SID (Sound Interface Device), which controls
the 64's sound output; and two CIAs (Complex Interface Adapters), which are ex
plained later in this chapter. They control most timing and input/output (keyboard,
tape, and disk). All these chips have special addresses in the memory map.
Like the 6510, the PLA (Programmed Logic Array) is invisible, or transparent, to
the programmer. It supervises hardware operations within the 64. For example, it
turns off RAM when a ROM cartridge is plugged in (so the cartridge can be read)
and turns off the 6510 at intervals to allow the VIC-II to generate the TV picture.
Most computer hardware has these features. For example, a typical printer has
an I/O chip to read input, a CPU with a program in ROM to process and decide
what to do with its input, RAM for temporary storage of text, and character ROM to
select dot-matrix characters. Printers will behave differently according to the ROM
fitted inside. Another example which illustrates how simple hardware fixes work is
Chapter 15's modification to change disk drive device numbers.
Another interesting if not useful feature of the 64 is incomplete address de
coding, which happens when some address lines are left unconnected. Since the
VIC, SID, and CIA chips all have repeating images in memory, there's actually a
choice of many equivalent addresses for the same functions on these chips. For ex
ample, POKE 53600,0 has the same effect as POKE 53280,0—it changes the border
color to black.
109
Commodore 64 Architecture
contribute to its overall operation. They are discussed below (see Chapter 11 for a
detailed look at Commodore 64 ROM).
Tables. These contain data, not programs, and have innumerable uses. The
screen link table and ROM keywords are typical examples. The screen table is in
RAM because it has to be able to change to reflect the screen's organization. The
high bit of the last character of each ROM keyword is on, which makes the character
appear reversed on the screen when using Program 5-1 below. File tables, which
hold details about each currently open file, are another example.
Buffers. A buffer is a section of RAM reserved for input or output. Buffers in the
64 include the input buffer, the keyboard buffer, and the 192-byte tape buffer at
$033C-$03FB (828-1019), which is important when reading from and writing to
tape.
Pointers. Zero page (memory locations 0-255) contains many pairs of adjacent
bytes that are pointers to special locations. Information about the top and bottom of
BASIC text, arrays, and strings is held in this manner, for example. The pair of bytes
forms an address in the low-byte/high-byte format described above. For example,
locations 43 and 44 are the pointer to the beginning of BASIC program storage. On
the 64, the normal values held in these locations are 1 ($01) and 8 ($08), indicating
that program storage starts at location 1+(8*256)=2049 ($0801).
Vectors. These resemble pointers, as they are also pairs of bytes that constitute
addresses. However, while pointers merely hold address information, vectors are
used to tell the computer where to find routines to perform important operations.
Each vector is set up to point to a routine within BASIC or the Kernal operating sys
tem when the system is turned on or reset. Altering these values enables many func
tions of the 64 to be modified.
The memory examination program described below changes the vector to the
interrupt routine, which looks at the keyboard every 1/60 second. Sometimes ROM
contains vectors; the Kernal jump table is a good illustration. It is different, though,
in that each address is preceded by a 6502/6510 JMP instruction and therefore occu
pies three bytes instead of two.
Flags. These keep track of a wide variety of events while a program is run,
ranging from whether the machine is in immediate mode to the position of the
cursor on the screen.
Programs. Most of ROM is subdivided into the BASIC interpreter and the
Kernal, a collection of related machine language routines. The only substantial pro
gram outside ROM is CHRGET, a routine at locations $73-$8A (115-138) that
fetches individual BASIC characters. CHRGET is copied out of ROM into RAM when
the system is turned on or reset. Having the routine in RAM is faster than using a
ROM routine, and it permits new BASIC keywords to be added using a program
called a wedge, which will be explained later.
Accumulators. Several number storage areas exist in RAM: two floating-point
accumulators, where numbers are added, multiplied, and so on ($61-$70), and the
realtime clock ($A0-$A2). You can use Program 5-1 to view the three bytes of the
clock changing.
The stack. The stack is discussed in more detail in later chapters. Essentially, it
is 256 bytes of RAM from $100 to $1FF (256-511) that are used by the 6510
microprocessor to store temporary information, particularly information relating to
110
Commodore 64 Architecture
subroutines. It is normally best left alone. Short machine language routines can be
stored in the lower portion of the stack; if tMpe drive is in use, a safe starting location
is $013F.
You can see how 40-column lines are linked by a table into 80-character lines or
watch the activity in zero page. PRINT USING (Chapter 6) relies on the number out
put routine. Watch ten characters (normally) line up in the keyboard queue (which
starts at 631), as you press keys quickly. For more interesting places to peek around,
check a memory map.
111
Commodore 64 Architecture
64 Hardware Tidbits
The Schematic. This is a useful diagram in the Commodore 64 Programmer's Ref
erence Guide. It corresponds, more or less, to the internal arrangement of your 64.
The power input is defined in terms of a 5-volt DC line, two 9-volt AC lines, and
ground. Since these are supplied outside the 64, the same 64 can be used in coun
tries with different house current types, provided a local Commodore adapter is
used, and the crystal, jumper, and VIC-II chip match the TV. The VIC-II's power
supply is separate from the SID's.
112
Commodore 64 Architecture
A system reset connects all the major chips' RESET pins and is triggered by a
timer. The address bus (labeled A0-A15) and the data bus (D0-D7) appear as solid
lines. The game control ports have power supplied to them, and joysticks are wired
with the keyboard, which has two pairs of eight wires, plus a RESTORE key. Note
how CIA l's interrupt request line connects to IRQ on the 6510, while CIA 2's con
nects to NMI. Port A of CIA 2 controls the serial bus and also selects the VIC-II's
bank; port B is connected to the user port and therefore handles RS-232
communications.
The PLA. Inputs are drawn on the left, outputs on the right. The 16 input lines
allow 65,536 combinations, all of which are processed within the chip to give 8 out
puts, though, as we've seen, only 5 input lines determine most of the memory
configuration, selecting BASIC or Kernal ROMs, the character generator, or some
other area of memory. The PLA controls which banks of memory are active and
distinguishes between reading from and writing to chips, allowing otherwise
meaningless BASIC statements like POKE J, PEEKfl) which PEEK from ROM but
POKE to underlying RAM at the same address.
Different 64s. All computers are subject to redesign as improved layouts and
technology are introduced and errors removed. This process has helped Commodore
reduce prices while generally improving the hardware.
At present there are three main types of 64s. All have Microsoft BASIC 2.0
(which CBM owns the rights to). The BASIC statement PRINT PEEK(65408) returns
the Kernal release number.
The earlier 64s (5-pin audio-video socket) have a 1982 printed circuit board with
two CIAs, BASIC ROM 01, Kernal ROM 01 (release 0), character-generator ROM
(socketed), 6510, and PLA. The SID and VIC are enclosed in a box. RAM chips oc
cupy the bottom left, with the fuse at the right. BASIC had a few bugs, notably an
occasional lockup on screen editing, and an INPUT bug.
The 1983 revision (8-pin audio-video socket) has Kernal ROM 03 (release 2),
which removed most bugs (and also made screen POKEs invisible). The design is
somewhat different, with improved video layout (under a perforated screen) and a
better TV modulator and power supply. Most chips, except VIC-II and SID, are sol
dered, not socketed.
The SX-64 (release 96) has a restyled casing and small monitor, but is very simi
lar to the 64. Its power-up message is different and it has a white background de
fault color, since light blue on blue is unsuitable for the small monitor. Tape
software, although mostly still in ROM, is branched over, so device 1 no longer ex
ists, since there is no tape port. SHIFT-RUN/STOP puts LOAD "*",8 (with a RE
TURN character), then RUN (with another RETURN) into the keyboard buffer, so it
will load and run the first program from disk. The rest of BASIC is unchanged.
MAX. The ULTIMAX was to have been a cartridge-compatible games machine.
It never appeared. It was to have no keyboard, BASIC or Kernal ROM, or character
generator, a different VIC-II memory map, and only 2K RAM. Some 64 hardware
features were designed with it in mind.
VIC-II and SID Chips. Chapter 12 explains how to program the VIC-II chip.
The SID chip is explained in Chapter 13.
From the hardware viewpoint the VIC-II chip is important because it sometimes
disturbs the timing of other chips, since the calculations it has to do are so complex,
113
Commodore 64 Architecture
particularly with sprites enabled. This can affect the use of tape, disk, and RS-232,
though not SID's sound output. VIC-II has two on/off bits, one (DEN) in register 17,
which turns off screen processing (but not sprites), and another (RESET) in register
22, which in later VIC chips has no effect. To cut out this effect, sprites must be off
and DEN set. The BA (Bus Acknowledge) line on the cartridge port allows for this
effect when, for example, an external Z80 microprocessor controls the 64. The fact
that VIC-II doesn't work instantaneously, but is continually calculating whicji dots to
put on the screen, can be important to grasp. For example, sprite collision registers
cannot be instantly updated after they've been read; some time has to pass before
the next screen scan.
No Cartridge Connected
When you turn on the 64, you have 38K BASIC RAM plus 4K free RAM at $C000-
$CFFF. BASIC ROM can be switched out, giving 52K RAM plus the Kernal, or both
BASIC and Kernal can be switched out. Chapter 11 deals thoroughly with the tech
niques for modifying BASIC in RAM.
114
Commodore 64 Architecture
Software Hardware
CHAREN HIRAM LORAM EXROM GAME 0 A000 C000 D000 E000 FFFF
1 1 1 1 1
RAM BASIC RAM I/O Kernal
0 1 1 1 1 Chr.
RAM BASIC RAM ROM Kernal
1 1 0 1 1
RAM I/O Kernal
1 0 1 1 Chr.
0
RAM ROM Kernal
1 0 1 1 1
RAM I/O RAM
0 0 1 1 1 Chr.
RAM ROM RAM
1 0 0 1 1
RAM
0 0 0 1 1
RAM
If EXROM is grounded with no cartridge present, the 64 will print 30719 bytes
free when turned on; it loses 8K of ROM, so $8000-$9FFF is read as garbage, but
written as RAM. If GAME alone is grounded when the computer is turned on, the
64 crashes, since the Kernal is deactivated. The examples in Figure 5-2 show how
the PLA detects cartridges.
115
Commodore 64 Architecture
Software Hardware
CHAREN HIRAM LORAM EXROM GAME 0 8000 A000 C000 D000 E000 FFFF
1 1 1 0 1 8K
RAM Cartridge BASIC RAM I/O Kernal
8K Chr.
RAM Cartridge BASIC RAM ROM Kernal
1 1
116
Commodore 64 Architecture
Software Hardware
CHAREN HIRAM LORAM EXROM GAME 0 8000 A000 C000 D000 E000 FFFF
1
1 1 1 0 0
RAM 16K Cartridge RAM I/O Kernal
0 1 1 0 0 Chr.
RAM 16K Cartridge RAM ROM Kernal
1 1 0 0 0 8K
RAM Cartridge RAM I/O Kernal
0 1 0 0 0 8K Chr.
RAM Cartridge RAM ROM Kernal
1 0 1 0 0
RAM I/O RAM
0 0 1 0 0 —Same—
1 0 0 0 0 RAM
0 0 0 0 0 —Same—
MAX
Intended to allow a 16K autostart cartridge, including its own I/O routines with 4K
of RAM.
Software Hardware
CHAREN HIRAM LORAM EXROM GAME 1000 8000 A000 D000 E000 FFFF
_ANY — 8K 8K
RAM Unused Cartridge Unused I/O Cartridge
117
Commodore 64 Architecture
Commodore 64 Ports
CBM computers are designed so that similar ports are compatible and dissimilar
ports, incompatible. For example, the 64's cartridge port is a different size from the
VIC-20's and the Plus/4's, since ML programs can't usually run on more than one of
these computers. Tape ports on the 64 and VIC-20, but not the Plus/4, are compat
ible. And the 64 and VIC-20 user ports are similar enough for VICModem to operate
correctly with either. Commodore reversed the VIC's pin numbering on the cartridge
socket of the 64.
Audio/video ports. All 64s have TV-modulated output through a phono-plug.
Early 64s have five-pin audio/video sockets, with luminance, audio in, audio out,
composite video, and ground; later models have eight pins, partly to avoid confusion
with VIC-20's five-pin socket (6V, ground, audio, two videos) which has to go
through a modulator. Connecting the audio signal and ground to a hi-fi is quite easy,
but putting signals into the port requires some electronics experience.
Cartridge port. This is the port at the left of the 64, looking from the rear. It has
44 connections, 22 on each side, all of which are connected. Two tracks, usually
wired together, carry the +5-volt power supply to the cartridge; these are pins 2 and
3, near the top right, from the back of the 64. (As mentioned, the VIC-20 has a re
versed numbering convention.) The tracks are rather close, and the possibilities of a
short-circuit or arcing make it inadvisable to insert or remove cartridges when the
118
Commodore 64 Architecture
power is on, though with care it is generally safe. Note that edge connectors are de
signed for the replacement of faulty computer parts during maintenance; they aren't
really ideal for cartridges.
The pinouts on the cartridge circuit board (not viewing the computer, but the
cartridge) are as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
119
Commodore 64 Architecture
GND +5V RESET CNT1 SP1 CNT2 SP2 PC2 ATN +9VAC+9VAC GND {
Top I "
! 2 3 4 5 6 7 8 9 10 11 12
ABCDEFHJK L M N ^
Top I "
GND FLAG2 PBO PB1 PB2 PB3 PB4 PB5 PB6 PB7 PA2 GND
120
Commodore 64 Architecture
121
Figure 5-7. CIA Memory Map
4 Timer A-Lo
—
16-bit timer A —
5 Timer A-Hi
6 Timer B-Lo
16-bit timer B -
7 Timer B-Hi
f:8 TOD-Tenths 0 0 0 0 This nybble 0-9
A.M. = 0
11 TOD-Hour P.M. = 1 0 0 Oorl This nybble 0-9 or 0-2
Write:
0=clear, l=set not used Flag SPline Alarm Timer B Timer A
13ICR
Read: interrupt interrupt interrupt interrupt
0=noint, l=int
TOD AC Freq.
14CRA 0=60 Hz 0=Serial Reg. In 0=TA counts 02 0=no effect 0=TA continuous 0=PB6 pulse 0=PB6 normal 0=Stop TA
1=50 Hz 1=Serial Reg. Out 1=TA counts CNT l=load latch in TA 1=TA one-shot 1=PB6 toggle 1=PB6 outputs TA 1=Start TA
0=Normal TOD 00 TB counts 02 / 01 TB counts CNT 0=no effect 0=TB continuous 0=PB7 pulse 0=PB7 normal 0=Stop TB
1=Latch Alarm 10 TB counts TA / 11 TB counts TA 1 = load latch in TB 1=TB one-shot 1=PB7 toggle 1=PB7 outputs TB l=StartTB
when CNT high
123
Commodore 64 Architecture
underflow (when they decrement below 0), but the interrupt must be enabled to ac
tually occur.
Timers have two functions. One is timing in the usual sense; the other is count
ing. The first provides a regular measure of time, for instance, when sending out bits
at accurate intervals; the other can perform such tasks as counting eight incoming
bits, even when these are received irregularly.
Each timer has its own 8-bit control register. This includes a bit to start the time
(if the bit is set to 1) or stop it (if it is cleared to 0). Any POKE into a timer is latched
(the value is kept): Once the timer underflows or is started, it reloads with the
POKEd value, so the timer's interrupt rate can be altered. However, a strobe bit al
lows a new value to be loaded instantly, without waiting for the timer to pass 0.
A timer can run in continuous mode (generating regular interrupts) or one-time
mode (counting only once instead of continually repeating). The first mode is used to
scan the keyboard; the second is used for such purposes as tracing ML commands
one at a time, or detecting the presence of hardware by timing its response.
If a timer counts down with the 64's clock, the maximum time interval between
interrupts is about $FFFF millionths (roughly 1/15) of a second. Timer B can count
timer A, though, extending this interval considerably. Another feature allows timer B
to count timer A, but only when CNT is held high.
The serial register. This 8-bit register is connected to the line SP. On com
mand, it performs eight shifts, either moving the byte in the serial register out onto
SP, as eight single bits, high bit first, or (if configured for input) reading eight bits
from SP one at a time into the serial register. A shift register within the CIA does the
work. When a whole byte has been shifted, an interrupt flag is set, so serial-parallel
conversion can be made automatic.
The shift register is controlled by TA. It can be timed by the 64's clock or by the
CNT line, which therefore can be used in handshaking.
Control registers of the CIA. Three bytes control the configuration of every
thing about the CIA. Register 13, called the interrupt control register (ICR), controls
the five sources of CIA interrupts.
Writing to the ICR with bit 7 low clears sources of interrupts whose bits are set;
this is why POKEing the register with 127 disables all interrupts. Writing with bit 7
high enables interrupts whose bits are POKEd high, so POKEing with 129 enables
timer A's interrupts, but no others.
After reading the register, if the high bit is set, an interrupt has been triggered
by that CIA. The bit pattern will indicate the cause. If the high bit is not set, no
interrupt took place, but it's still possible for one or more of the five bits to be high,
since they register even if their interrupt isn't enabled. There are examples in Chap
ter 8, Chapter 12 (on VIC-II), and later in this section.
CIAs in the 64
CIAl
Port A is normally set for output and port B defaults to input. Timer A generates
IRQ interrupts at regular intervals to service the keyboard, which shares lines with
the joysticks and paddles. CIA 1 reads tape, using the FLAG line.
CIA 2
This chip controls NMIs, the serial bus, and RS-232 processing.
124
Commodore 64 Architecture
10 Port 1 Paddles
01 Port 2 Paddles BTN2 Joy 2/E Joy 2/W Joy 2/S Joy 2/N
125
Commodore 64 Architecture
DATA In CLK In DATA Out CLK Out ATN Out RS-232 VIC-II Bank Select
Tx&Rx
DD08-DD0B TOD
RS-232
DD0D=56589ICR Read
DDOE=56590 CRA
DD0F=56591 CRB
126
Commodore 64 Architecture
CIA 1 and the keyboard. Chapter 6 explains how CIA 1 reads and decodes
information from the keyboard. Setting PB6 or PB7 for output makes the keyboard
unreadable and prevents the use of RUN/STOP-RESTORE to reset the machine.
Joysticks, light pens, and potentiometers. All these are shared with the key
board, under the control of CIA 1. See Chapter 16 for full details.
Interrupt handling. Each CIA has an IRQ (Interrupt ReQuest) line, which is
normally set, but can be cleared. CIA 1 connects to the 6510's IRQ line, and CIA 2
to the NMI line; each chip generates a different type of interrupt.
You should temporarily turn off IRQs to alter the IRQ vector to insert your own
ML program or to read the character-generator ROM. POKE $DC0D (56333) with
127 to turn off all CIA 1 interrupts. Later, POKE with 129 (%1000 0001) to turn on
timer A interrupts, which normally control the IRQ rate. Chapter 8 shows how NMI
interrupts can be used with BASIC.
Tape drives. Only tape reading is controlled by a CIA (see Chapter 14 on writ
ing tape, checking the cassette keys, and controlling the motor). CIA l's FLAG line
reads tape. Tape input is signaled on FLAG, and any negative transition sets an
interrupt. Reading the interrupt register clears the interrupt flag, and as long as it's
reset a low condition exists.
Timers. Program 5-2 allows you to see the timers updating on the screen. Timer
Abnormally controls the IRQ rate, which is why POKEs to $DC04 and $DC05 (56324
and 56325) alter the cursor flash rate. TA counts down with the system clock about
60 times per second, too fast for Program 5-2 to show a pattern. Note, though, that
register 5 ($DC05 or 56335) never exceeds 66; this latched value was selected be
cause 65*256/1,000,000 is about 1/60 second. When the computer is turned on,
$DC0D (56333) has bit 0 set high, enabling an IRQ interrupt to the 6510, although
PEEKing won't show this.
To link timers, set $DC0F (56335) to 65 (%0100 0001). This sets timer B to fol
low timer A (decrementing once each time A underflows) and starts timer B. Now
timer B counts down from $FFFF relatively slowly—about 60 times per second—tak
ing about four seconds for the low register to reach 0, and therefore about 15 min
utes for the whole register B to count down.
If a timer isn't started, latching has no visible effect. Set $DC06 (56326) to 0,
$DC07 (56327) to 1, so CIA 1 timer B's latched value is $0100. Now set $DC0F
(56335) to 81 (%0101 0001), which follows timer A, forces in timer B's value, and
starts timer B. Now timer B still counts down with timer A, but is reloaded with $100.
127
Commodore 64 Architecture
Enter direct mode, and type the following line to set $DC0D to %1000 0010:
POKE 56333,127: POKE 56333,130
This turns off timer A and enables timer B as an interrupt source. You'll see that the
cursor is flashing much slower than it normally does. The first POKE is necessary,
since without it both interrupt sources are enabled.
The CNT line. Line 4 of the user port connects to CIA l's CNT line. Latch
timer B with $FFFF by POKEing 255 ($FF) into $DC06 and $DC07 (56326 and
56327). Now POKE $DC0F (56335) with 49 (%0011 0001), which sets timer B to
count CNT, forces $FFFF into timer B, and starts timer B. Now, timer B remains at
$FFFF until pin 4 is grounded. Note that proper debounce circuitry must be used or
the count will decrement very rapidly.
Time-of-day (TOD) clocks. Every CIA has a TOD clock, but we'll use CIA l's.
Four registers hold hours (1-12, high bit for p.m.), minutes, seconds, and tenths of
seconds in binary coded decimal (BCD) format. The clock starts only when the
tenths register is POKEd or PEEKed, and it stops whenever the hours register is
POKEd or PEEKed. This prevents errors when reading the time. For example, if the
time in the clock registers happened to be 10:59:59.9, the clock could advance be
tween the time you read the hours and the time you read minutes. This means your
reading could be wrong by an hour. With CIAs, read the hours register first and the
tenths register last.
You'll need BCD conversion equations to use the TOD with BASIC. This func
tion converts a number from 0 to 59 into its BCD form:
DEF FN BCD(T) = INT(T/10)*16 + T - INT(T/10)*10
The following function converts a BCD value into the corresponding number in the
range 0-59:
DEF FN TD(P) = INT(P/16)*10 + (P AND 15)
128
Commodore 64 Architecture
To use the alarm feature, POKE the registers from BASIC to start the clock, have
interrupt processing ready, and set register 15's alarm bit high before POKEing in
the alarm value.
POKE 56333,127: POKE 788,0: POKE 789,192: POKE 56333,129: POKE 56335,PEEK(56335)
OR 128
This checks the alarm interrupt flag. In this way, lapse of a measured amount of
time can end a game, display a score, or whatever. TOD alarms have a minor bug:
the alarm is often triggered a second or third time, so it makes sense to turn off the
alarm bit in register 15 when it's not needed.
Users of 50-cycle house current should note that the TOD is timed by house
alternating current. Its accuracy relies on the electricity supplied to it, rather than
interrupts. In register 14, 50 Hz or 60 Hz is selectable. But all 64s set 60 Hz even
with PAL TVs. Press RUN/STOP-RESTORE or execute a SYS 64738 to return to 60
Hz. To insure 50 Hz:
POKE 56334, PEEK(56334) OR 128
129
Commodore 64 Architecture
Serial bus problems can cause a lockup. For example, the printer may be in a
loop or disabled; turn it off, then on.
In early 64s (release 0), the screen editor can corrupt a CIA, locking the key
board. (Enter a long BASIC line starting at the screen bottom. Now type a line num
ber at the bottom left and delete back. The keyscan sequence may not work now.
Press #, then PLAY on tape, then RUN/STOP after a few seconds to recover.)
When you have an X2 crash caused by an internal loop in the 6510, only a reset
switch (see below), followed by OLD can recover BASIC. SYS 40965 is an example.
(See Chapter 6.) A so-called X2 crash occurs when the microprocessor tries to exe
cute any opcode (other than $A2) that ends with 2 (see Appendices).
130
Commodore 64 Architecture
SYS 64738. OLD can then be used to restore BASIC, and ML in memory remains in
tact. SYS 64738 is therefore better than turning the 64 off, then on again, but it has a
few odd effects when BASIC'S ROMs are switched to RAM, as detailed in Chapter 8.
Autostarting
Autostart cartridge ROMs usually start at $8000, like this:
$8000-$8001: Startup jump address in cartridge.
$8002-$8003: RESTORE key jump address in cartridge.
$8004-$8008: Standard five-byte identifier sequence, $C3, $C2, $CD, $38, $30
(CBM80).
If the reset routine detects the correct identifier sequence, the jump address is taken.
Typically, it still goes through most of the normal initialization routines before
continuing with its own program.
A cartridge starting at $A000 will autostart without needing identifying bytes,
unless it finds the five-byte sequence at $8004. Obviously, such a cartridge must re
place the BASIC ROM by grounding GAME and EXROM.
Bypassing cartridge autostart. If you want to run BASIC or ML normally, but
have a cartridge which you don't want to unplug, you may want to reset the 64 to its
normal state by bypassing autostart. This is easy if you have a good expansion
board: just switch the unwanted cartridge off and use SYS 64738.
Cartridges that return you to BASIC are fairly easy to disable. Try the following:
• RUN/STOP-RESTORE.
• SYS 64760.
• Mimic the reset sequence without the cartridge test, by POKEing these bytes into
49152 (and subsequent locations): 120, 162, 255, 154, 232, 76, 239, 252. Call the
routine with SYS 49152.
This leaves the cartridge in memory, so SYS 64738 will act like turning the com
puter on and SYS 49152 will return the machine to normal. Thus, with OLD, BASIC
could be switched at will to run with or without some BASIC utility.
Cartridges that don't return you to BASIC can't be bypassed without an expan
sion board (which has a switch or other means to disconnect EXROM or GAME or
both). It is not recommended that you plug in a cartridge while the power is on; you
may damage the computer or at least cause it to crash. If this is done, however, the
cartridge may become fully present in memory without autostarting.
Cartridge Programs
ROM programs plug into the cartridge port and usually autostart when the computer
is turned on—this is convenient and fast.
131
Commodore 64 Architecture
Because cartridges, even with EPROMs, are much more expensive to make than
the easily duplicated tape or disk software, major programs are often supplied on
disk. Cartridges therefore often contain games. Sometimes there's a compromise:
plug-in software may immediately load another program from disk. Since many
applications require a disk anyway, this isn't a problem, and it has the advantage of
permitting corrections to be made in the code if bugs are found in the disk program.
The 64's design insures that cartridges compete for the same parts of memory,
so some form of expansion board is necessary if you want a choice of easily acces
sible cartridges. The only exception is some interface cartridges, which relocate their
ML and then turn themselves off. But even these can't really be made with an exten
sion piggyback socket, since other cartridges may have incompatible software. In
other words, be prepared for your cartridges only to operate individually.
Most cartridges have software protection to prevent a RAM copy of the cartridge
from working properly. For instance, an ML instruction like INC $8300 increments
the contents of $8300 in RAM, but has no effect on ROM, so an ML program that al
ters memory locations within itself will work in ROM but not RAM.
It's actually possible to store BASIC programs in cartridge form, with their vari
ables lower in RAM, so that they can autostart without having to be loaded. This is
rarely done, since there's only space for 8K.
Cartridges tend to look rough when taken apart. Often there is just a printed cir
cuit board with a ROM or EPROM, plus some other circuitry. If you open the car
tridge, you'll be able to follow the tracings and infer the function of much of the
hardware. Note how the dominant direction of tracks on top is often perpendicular
to that below, allowing connections between the surfaces to be made more easily.
The casing provides protection, but isn't necessary to the working of most of these
devices, which can be plugged in as plain printed circuit boards.
Examples. Many games and utility programs are packaged as autostart car
tridges, as are programming languages like COMAL. Most languages switch BASIC
out completely.
BASIC extensions and aids are available on cartridges as well. Simons' BASIC is
16K of ROM from $8000 to $BFFF. It switches the BASIC ROM in and out by
controlling the GAME line. Other utilities have features like fast tape operating sys
tems, additions to the disk operating system, extra graphics, and so on. Unfortu
nately, the programs you write using these cartridges may not run without the
cartridge in place. And watch for subtle changes in BASIC—the ',8' may no longer
be needed to use the disk drive, or you may find tape no longer works.
Voice and music synthesizers, with extra commands like SPEAK, generally use
the SID chip if they don't add hardware to the audio-video socket.
Eighty-column software can be prepared as a cartridge as well. It intercepts vec
tors, controlling screen output and graphics format to produce characters four dots
wide. (There are 320 pixels across the screen to use, so 64 columns are possible, too,
with characters five dots wide.) All the normal colors may not be available. Eighty-
column output usually requires a monitor, not a TV.
CP/M is an operating system which runs on the Z80 chip. The 64's DMA (Di
rect Memory Access) line allows a Z80 or other chip to control the 64: when held
low, the Z80 controls data, addressing, and input/output, though VIC-II still has ac-
132
Commodore 64 Architecture
cess to and controls the display. The Z80's memory addresses are offset $1000 bytes
from 6510 addresses, seeing $1000 as $0, and so on.
CP/M has three parts: console processing (CPP), disk operating system (BDOS),
and input/output (BIOS), all at the high end of memory. The programs start at $100.
But the disk and input/output is specific to the 64 and not transferrable. The Kernal,
of course, can be run only by the 6510, not the Z80. In the Commodore package, a
hardware switch at $DE00 selects the processor. A CP/M cartridge is necessary to
use the Z80; then software is usually loaded from the 1541 disk drive. You may need
to transfer information to other machines by modem, since the 1541 disk drives are
not compatible with those of most other manufacturers. If you want to try CP/M
with minimum hassle, be sure that the programs you'd like to run are available for
the 64.
Programs on Disk
Disk programs are often long and slow to load. Because these programs go into
RAM, copying can be relatively easy, and this is a problem for the software publish
ers. Various methods are used to deter copying, like making the directory unreadable
and causing the program to force-load in memory, then decode itself in complicated
ways (explained in Chapter 15). Don't be surprised if the disk directory looks
strange.
Examples. Most cartridge programs of the types listed above are available on
disk as well: games, 80-column software, terminal software, some BASIC enhance
ments. Exceptions include the rare, very long ROM programs, which require bank
switching and can't fit into the 64's RAM.
Applications software (word processing, spreadsheets, and data bases) is often
available on disk. Unfortunately, the 64 can't autoboot (automatically load and run)
software from the disk when the computer is turned on. You have to type in at least
a LOAD command. Commodore's EasyScript word processor is a typical disk-based
application package. LOAD "ES",8,1 forces a load which automatically runs.
EasyScript's ML actually starts at $8000.
Programs on Tape
Tape is perhaps the cheapest storage medium, and programs of most types listed
above come on tape. Tape access is also usually very slow. But the 64 has plenty of
room for alternative tape LOAD programs. Some commercial tapes first load a fast
tape-read routine, which reads the specially formatted main program very rapidly.
There are several recording methods like this, which are often harder to audio-copy
because of the higher frequencies they use.
Another example is BASICODE, which uses a subset of Microsoft BASIC in or
der to make each program run on a number of different computers. It includes stan
dard routines, different for each computer (for example, GOSUB 100 to clear the
screen), and is without significant color, sound, or graphics. Programs can be broad
cast by radio and recorded with an ordinary cassette recorder. A special program for
the 64 reads this as normal BASIC, which can be saved in conventional 64 tape or
disk format.
Transferring tape software to disk. Straightforward programs can be read from
tape, then stored on disk. There are public domain utilities available which help with
133
Commodore 64 Architecture
this. Chapter 14 explains how to load standard tape programs anywhere in RAM.
But protection methods make this more difficult. For example, programs which use
the tape buffer can cause problems. And programs that load and run another pro
gram won't work unless the device number is changed from 1 to 8. Nonstandard for
mats are difficult to put on disk as well: If the loaded program disables RESTORE,
mimics a ROM cartridge at $8000, and also uses RAM around $300, resetting with
EXROM grounded will sometimes work, but will wipe out the software in low
memory.
Hardware Devices
These peripherals perform functions which software alone can't. There's a wide vari
ety, made somewhat confusing because there are often several different ways to
achieve the same result. Here is a quick look at the most important broad categories.
Interfaces. Interfaces connect the 64 to non-Commodore equipment so that the
external hardware device will work properly. The problem is that since the 64 has no
standard interface, it must use an interfacing device with a 64-compatible socket on
one end and a socket to fit standard equipment on the other.
If you want to connect a daisywheel typewriter or non-CBM matrix printer to
the 64, you'll find this equipment typically uses either Centronics or RS-232 inputs,
or both. Be warned that there may well be problems with incompatibility: Test
equipment before you buy it to make sure that your software works correctly with it.
Another interfacing problem is CBM's IEEE disks and printers. The double disk
drives, like the 8050, are much faster and store far more data than the 1541, but they
won't plug in directly to the 64.
The Centronics interface is a 36-pin parallel system found on most good-quality
printers and also on some typewriters. It has its own handshaking system and has
no need for a baud rate to be set. Some of these interfaces have special features rele
vant to Commodore machines, like printing BASIC control characters as {RED} or
{HOME}, so program listings are more readable.
User port interfaces use port B (plus two lines to control handshaking) as a par
allel port. All that's required in hardware is a Centronics cable fitted with an edge
connector to plug in the user port. Software controls the user port.
Serial port interfaces connect the serial socket in the 64 (or at the disk) to the
Centronics device via hardware, which usually includes an external power supply,
some way to define its device number, and some control over the type of ASCII it
passes to the printer.
The cartridge port can be used as well, but of course is likely to conflict with
other software on cartridge.
RS-232 is a standard for transmitting serial data, which tends to be slower but
less expensive to implement than parallel transmission. The RS-232 connector has 25
pins arranged in two rows. The 64 requires voltage conversion to convert its user
port output to the correct levels of —12 and +12 volts; Chapter 17 has more details
on CBM interfaces.
RS-232 devices are assigned device number 2 when a file is opened with a state
ment like OPEN 10,2,secondary address,baud rate, and subsequent input or output
uses two buffers at the top of BASIC memory for storage. Software has to be written
134
Commodore 64 Architecture
with this in mind; if it assumes a printer is device 4, it won't work without modifica
tion. Also, the baud rate must be set. (Generally, Centronics interfaces are likely to
be easier to work with.)
Some IEEE interfacing cartridges plug into the main socket. One type prints its
sign-on message, relocates its ML to start at $C000, alters BASIC vectors, then dis
connects EXROM so that it disappears from the memory map. Finally, it loads and
runs a program from disk. The 24-pin IEEE edge connector protrudes from the car
tridge to run CBM IEEE disks and printers.
Another type also has an IEEE connector, but adds BASIC 4.0 commands like
CATALOG (which can be found on the later CBM/PET machines) and an ML mon
itor program, making it highly compatible with other CBM products. The extra soft
ware is relocatable. While this is versatile, to make full use of it, you need to know
how to determine where programs are stored in memory.
It is also possible to move BASIC into RAM and alter the output routines. This
way you can customize and enhance BASIC to meet your needs.
Linking devices. Interpod is a CBM product which plugs into the serial port,
converting the 64's serial bus signals into a form acceptable to IEEE devices, and vice
versa. Since it has an external power supply, ROM and RAM, an IEEE socket and an
other serial socket, either or both types of hardware can be connected to the 64's se
rial bus. This system has the advantage of being transparent, not interfering with
normal operations. But since 64 software will not nominally expect IEEE devices to be
present, programs which use the system may need to be specially written.
Interpod is an example of linking; however, some adapters are designed to allow
more than one 64 to share the same printer or disk drive. This can be useful in a
classroom environment. Some caution is required, though; the simplest interfacing
methods don't allow for simultaneous operation, so users must warn each other
when they're about to use the shared devices.
Expansion boards. To save wear on the 64's ports, you may want to purchase
an expansion board. A typical board has three to five slots, in which cartridges fit
upright, facing the user. Each slot has an on/off switch. To work properly this must
disconnect the power line and also GAME and EXROM. In this way, several car
tridges designed for the same area of memory can be in position simultaneously,
though only one can be in operation at any one time. Some boards have a fuse, and
a reset switch is a good idea. These boards are relatively simple, and if you have
experience with printed circuit board equipment, you could make one using ribbon
connectors, rather than rigid boards.
EPROM boards. These are printed circuit boards, with edge connectors to fit the
cartridge port, wired to sockets for Erasable Programmable Read Only Memory
chips. Some boards allow you to switch between EPROMS with a POKE to a spe
cially reserved location, as well as to select the address of the EPROM. Essentially,
they allow you to design and run your own cartridge software. There's considerable
scope, since the expansion port has access to all the bus signals.
An EPROM programmer is a hardware device that puts your software into semi
permanent form in an EPROM. You will also need a special ML monitor program
with a command to start the burn-in. Self-contained units are sold which fit the user
port, drawing the high voltage required from it, and in effect using RS-232 to control
135
Commodore 64 Architecture
EPROM burning. An EPROM can be read back through the user port, but not run as
a program; to run the EPROM, it should be mounted on a board.
Communications devices. Modems fit the user port and require terminal soft
ware to control communications. Cartridge port modems include their own sign-on
message, request to connect, and menu of options. All types, of course, have a
connection to the phone line or to an acoustic coupler. Chapter 17 discusses terminal
software.
Controllers. Another type of adapter is a controller, or I/O board. This set of
relays, or digital/analog converters, connected to the user port, allows the 64 to con
trol external equipment—lighting, machinery, test equipment, alarms, whatever. A
set of BASIC subroutines often is adequate to read and drive the controller. Obvi
ously, hardware and software experience are both needed here.
Other devices. A tape interface can connect the 64 to an ordinary recorder, but
this is more difficult and not much less expensive than using the Commodore tape
units. An ordinary recorder isn't wired to sense keys, so it can't prompt with PRESS
PLAY or PRESS PLAY AND RECORD, and the recorder must be switched on just
before use. Some recorders play back an inverted signal, which sounds identical but
cannot be read properly. See Chapter 14 for more on this.
Cartridges are sometimes fitted with their own video chips (so the TV connec
tion comes from the cartridge, not the 64). This allows 80-column display, variable
line spacing, digital clock display, and so on, but at a price, of course. And cartridges
can take speech synthesis chips, again with output derived from the cartridge, either
bypassing or mixing with the sound from the SID chip.
Special hardware modifications are becoming available for the 64 as well. One
autostart cartridge causes a 1541 disk drive to work at several times the normal
speed, subject to some serial bus problems. (It switches itself out of ROM, leaving
BASIC modified with connectors to two lines within the 64.) Another reported
modification allows the 64 and Apple to share some software.
136
Chapter 6
Advanced
BASIC
How BASIC Is Stored in Memory
Special Locations and Features of
BA^IC
DiGtionary of Extensions to BASIC
Chapter 6
Advanced BASIC
This chapter explains advanced BASIC methods for programming the 64. It should
help you learn new techniques and avoid programming bugs. The following sections
introduce some special features of BASIC, like programming the function keys, for
example. There's a dictionary of enhancements to BASIC, some of them utilities to
assist programming, others subroutines which can be incorporated into programs.
The main set of pointers occupies 14 consecutive bytes, from location 43 to 56,
an arrangement found in all Commodore BASICs. Most of these pointers mark a
boundary between one type of item and the others, which is why END OF PRO
GRAM + 1 = START OF VARIABLES, for example.
All this means is that the program ends one byte before the pointer and that the
variables start exactly at the pointer, so there's no overlap. Such a convention is ob
viously necessary, and this pattern is common among Commodore machines; this is
why the last byte can easily be lost if you are not careful when saving ML programs.
These pointers are most important when considering BASIC on its own, and
much of this chapter is devoted to them. For completeness the list also includes
other pointers set by the 64 when it is turned on or reset. The start and end of
BASIC RAM are stored in an extra set of these pointers when the position of the
screen is set, but these have little function and are less useful than the main pointers.
The screen and character generator pointer values are dependent on the VIC chip,
and setting their values is relatively complex; they aren't ordinary two-byte pointers.
find that the BASIC RAM extends from 2048 ($0800) to 40960 ($A000), and the
screen starts at 1024 ($0400). The 64 allows 1024 bytes for the screen, because
40 X 25 = 1000, and 1024 (210, or IK) is a convenient amount of space to allocate
for it. The screen ends at 2023 ($07E7) and there are 24 bytes left over, 2024-2047.
The final 8 of these store sprite data, while the rest are free.
You will also find that the actual start of BASIC is one byte beyond the start of
BASIC RAM. In other words, locations 43 and 44 together point to 2049 ($0801).
This is because BASIC always starts with a zero byte. Usually, therefore, PRINT
PEEK (2048) will print a 0 on the screen. The BYTES FREE message in this case has
already calculated that bytes from 2048 to 40959 (a total of 38,911) are available to
BASIC. The pointers to the end of program text, simple variables, and arrays are all
set to 2051. BASIC programs have two consecutive zero (or null) bytes marking the
end, and since there are no variables yet, all these pointers are in their starting po
sitions, just after the program, which is where variables will be stored. Zero bytes
(with a PEEK value 0) are convenient for markers because they are easily tested for
in ML. So PEEK (2049) and PEEK (2050) both return 0 at present.
BASIC is stored as a set of numbered lines. As stated above, the first byte of a
program is a zero, or null. Each line begins with a two-byte forward link address, a
two-byte line number, the BASIC line itself, and a zero byte which marks the end.
The link address is simply a pointer to the next line—in fact, it points to the next
line's link address, forming a chain which can be scanned at high speed. Each link
needs two bytes, and line numbers also have two bytes, enabling line numbers
greater than 255. Figure 6-1 illustrates the concept of linked lines in BASIC and
shows that a line link of 0 0 (two consecutive nulls) is used to indicate the end of the
program.
0 Link Line# BASIC Line 0 Link Line# BASIC Line 0 Link Line# BASIC 0 0 0
i
The built-in operating system automatically arranges BASIC as lines are typed in
and entered (by pressing the RETURN key) at the keyboard. When you understand
how lines are entered, you can modify BASIC to produce nonstandard effects. You
could insert lines longer than usually possible, add normally unavailable line num
bers, or arrange a line of BASIC to contain things it ordinarily couldn't.
Here's an easy way to look at BASIC line storage in practice. Type in this simple
program:
140
Advanced BASIC
When you run this, you will see the following numbers on the computer screen
(without the comments):
2048 0 ; Zero byte at the beginning
2049 15 ; Link address; points to start of next line
2050 8 ; at 2063 = 15 + 8 * 256
2051 1 ; Line number; this line number is 1
2052 0 ; = 1 + 0 * 256
2053 153 ; Tokenized form of PRINT
2054 32 ; Space
2055 34 ; Quote and following characters in PETASCII
2056 72 H
2057 69 E
2058 76 L
2059 76 L
2060 79 O
2061 34 "
Program 6-1 shows the contents of every byte of a single typical line of BASIC,
with notes to show their function. Incidentally, you will get slightly different results
if you make small changes to line 1. For example, if you remove the single space
character between PRINT and the first quotation mark, this will be missing from
location 2054, as you would expect. A link address pointer will point to 2062 instead
of 2063, because the next line starts a byte earlier. Similarly, try a different line num
ber in place of 1, and see it reproduced in locations 2051 and 2052 when the pro
gram is run.
As noted above, BASIC lines have five bytes of overhead, consisting of a two-
byte pointer, a two-byte line number, and a null byte. The end of BASIC is marked
by two zero bytes—that is, when a link address is found to be 0, RUN and LIST will
automatically treat this as an END, and return to direct mode with READY.
Before performing the following POKEs, save Program 6-1, if you want to be sure to
have a copy. Try POKE 2063,0: POKE 2064,0 with the program (in the exact form
shown above) in memory. It will now LIST only one line. Now POKE 2063,37:
POKE 2064,8 which will replace the original values, if the spacing was identical to
the version above. LIST now shows the entire program again.
Normally the end-of-BASIC pointer in 45 and 46 will point just beyond these
three consecutive zeros. If you think about it, you will realize that only the second of
the pair of terminating bytes is necessary to signal an end; it is, of course, possible
that the low byte of a link address could validly be zero, but any normal BASIC line
will be in $0800 at the absolute minimum, so its high byte will never be less than
eight.
141
Advanced BASIC
Line 20 moves the screen to $0800, where BASIC'S program storage area begins.
Line 30's REM and line 40's loop activity will both be visible onscreen. Try adding
new lines of BASIC and new variables. The result is rather unpredictable—clearing
or scrolling the screen will remove or alter BASIC.
Another way to display a BASIC program's contents is to POKE it, byte by byte,
into the screen, as Program 6-3 demonstrates:
Table 6-1 shows the significance of each BASIC byte (apart from the links and
line numbers). Note that all BASIC keywords are stored as a single byte with bit 7
set (which means they have a value of 128 or more in decimal). This makes it easy
for the ML routine to detect a keyword. It also means that when BASIC is POKEd
into the screen, BASIC keywords appear as single reverse-video characters on the
screen. Generally, BASIC as stored in RAM looks strange, partly because of this
compression and partly because the pointers and line numbers become visible.
The LIST instruction has the function of presenting this stored collection of
bytes in the familiar form, by expanding each token into its correct keyword. Of
course, compressing BASIC like this is a very valuable space-saving feature. This
special coded form is different from the 64's ASCII and also different from the
screen display system—which may cause some confusion. Table 6-1 shows all valid
bytes as held in BASIC. Some of those not shown will list as apparently meaningful
BASIC, but will not run properly.
142
Advanced BASIC
Note: This table shows all valid bytes as they are held within BASIC. Bytes not listed will list as appar
ently meaningful BASIC, but will not run. Within quotes, the full range of 64 ASCII characters can be ob
tained; see Appendix H for a table.
Use this when PEEKing BASIC or modifying BASIC with an ML monitor.
It's easy to show how a table like the one above can be compiled. Type NEW
and enter this one-line program:
OX
143
Advanced BASIC
or something similar, and POKE values from within a loop. The results may be un
expected; if you POKE a value of 5, this will change the color of the characters
printed on the screen after that to white.
If non-BASIC bytes are POKEd in, LIST will often list them as something appar
ently sensible, but the line will crash with a 7SYNTAX ERROR message when you try
to run the program. However, literals within quotes, or after REM or DATA state
ments, can generally take any value (except a null byte, which will be treated as an
end-of-line). This is why REM lines are a favorite place for simple anti-LIST meth
ods, as we 11 see.
Relatively few values out of the possible 256 comprise valid BASIC. Note that
numbers are stored without any attempt at compression, so the 10000 in GOTO
10000 takes five bytes and 123.456 in PRINT 123.456 takes seven; all the compo
nents are stored in ways which prevent ambiguity, and there is no way that numbers
could be compressed without making them resemble tokens or other BASIC features.
Note also that the operators +,—,*,/, and the up-arrow (T) don't appear in the
ASCII list; they are not stored in this form, but as tokens. Finally, note that BASIC
punctuation includes the comma, the colon, and the semicolon, but not the period,
which is treated as the decimal point.
144
Advanced BASIC
Mantissa
ASCII
Floating-point ASCII Exponent
orO
| Ml M2 M3 M4
t
Sign bit
ASC+128
Integer ASC + 128 Hi Byte LoByte 0 0 0
or 128
t
Sign bit
ASC+128 Pointer
String ASCII Length 0 0
or 128
LoByte | Hi Byte
For example, HI=0 and LO=100 correspond to 100; HI=255 and LO=156 repre
sent — 100. The two expressions add to 0 with overflow.
Strings (X$). Strings cannot be fitted into seven bytes. To allow freedom in
assigning strings (without needing to specify their lengths, as some languages re
quire), they are stored dynamically in RAM. Three of the seven bytes allotted to a
string variable are relevant; two are wasted. One byte holds the length of the string;
LEN (X$) simply PEEKs this value. Another pair of bytes points to the starting ad
dress of the string. Between them, these provide a complete definition. This storage
system explains why any string has a maximum of 255 characters. It also explains
why CHR$(0) gives no SYNTAX ERROR in cases where a null string (" ") does; the
former character has length 1, but the latter has length 0.
145
Advanced BASIC
Most strings are stored in the area of free RAM high above the BASIC program
and variable storage. String storage begins fit the top of the BASIC program space,
and grows downward. As strings are redefined (or as new string variables are de
fined), they fill progressively lower memory locations until a so-called garbage
collection is forced. To save space, some strings aren't stored after BASIC but reside
within the program itself; X$'s pointer in the assignment:
10 X$="HELLO"
points back jyjthin the BASIC program, where HELLO is already stored.
Generally; any string involving calculation is stored after BASIC. For example,
10 X$="HELLO" + " " assigns exactly the same string to X$, but it is stored after
BASIC. Again, this has consequences which will be examined shortly. For the mo
ment, note that because of this internal storage feature:
10 DIM X$ (200): FOR J=0 TO 200: X$ (J) - "1234567890": NEXT
uses 2000 bytes less memory than the same program with:
X$g)="12345"+"67890"
In the first case, every string pointer points to the same string inside the BASIC pro
gram. In the second case, every pointer points to a separate ten-byte string stored
above the program. Any string with any element of calculation is stored after BASIC
(for example, one defined via INPUT or GET or even by A$=B$).
Function definitions. These appear in the variables table, too, and it's quicker
to store them here than to search the whole program for a definition.
Like strings, function definitions store the solid information elsewhere. A func
tion definition has two pointers, one pointing to the defining formula and one point
ing to its principal variable, which is set up in the table if it doesn't yet exist.
Running:
0 DEF FN Y(X) = XT2+5*X+3
creates two entries in the variable table (X and of the function definition Y). Actually,
the formula pointer holds the address of the equal sign (=) in the function defi
nition, and the variable pointer marks the address of the first byte of the floating
point value of its argument. The five bytes are also used as temporary storage for
calculations when running.
Storage of Arrays
Arrays (subscripted variables) are stored after the simple variables; since they can be
of any length, they don't lend themselves to the normal seven-byte memory
allocation.
All three array types—real, integer, and string—have a similar layout, except for
the data, which is stored in five-byte batches for real numbers, two-byte batches for
integers, and three-byte pointers plus characters for strings. Figure 6-3 summarizes
how arrays are stored.
146
Advanced BASIC
Arrays are stored in the order they are first used. The array defined last is there
fore immediately below free memory. Because of this, it's possible to erase an array
which is no longer needed; this can be a useful memory-saving trick if an array is
used for some utility purpose (like sorting or merging). The general approach is:
AL=PEEK(49): AH=PEEK(50)
Then DIMension a new array and use it in the program. When finished with the ar
ray, delete it with:
POKE 49,AL: POKE 50,AH
This method simply stores the low and high bytes of the previous top-of-arrays
pointer, then restores them after using a new array in some way.
The DIM (DIMension) command defines the size of arrays. Obviously, this is
necessary unless you have unlimited RAM, since the computer can't know in ad
vance how much storage you'll need. DIM defaults to 10, so it is not necessary with
small arrays. Without DIM:
X(8)=l
is accepted, but:
147
Advanced BASIC
The position of any one item of an array can be calculated. For example, X(a,b,c)
is at a+b*(l+DIMl)+c*(l+DIMl)*(l+DIM2) elements along the array, where
DIM1 is the number of elements for which a was dimensioned and DIM2 is the
dimension of b.
Figure 6-4 shows a typical BASIC program during a run, followed by its vari
ables and arrays. This illustrates how memory sections are allocated for each dif
ferent kind of BASIC information.
Maximum Space
Available for Strings
Add lines 11, 12, 13, and 14, like line 10; the program as it stands prints the last
of them. But if E is altered to PEEK(55)+256*PEEK(56), which is the top of memory
available to BASIC, you'll see how each string is stored and the top-down way each
is positioned. Figure 6-5 illustrates this, and it is important to note that if a string is
redefined, the old string is still left behind in memory; redundant data like this is
called garbage.
Some loops are heavy users of memory space. For example, FOR J=l TO 40:
X$=X$ + " ": NEXT could be used to generate a string of 40 spaces. But the first
time the loop executes, X$ is defined as a string of one space character; the next
time, as two space characters, and so on; so the total memory used is 1 + 2 + 3 +
. . . + 40 bytes, or 820 bytes, of which 780 are garbage.
148
Advanced BASIC
x$ Y$
X$ is stored only in a BASIC program line (for example, 10 X$="HELLO"). Strings which must be built
irom other strings are stored in high memory (for example, 20 Y$=H$+Z$).
Loops to input data in a controlled way, using GET, do something very similar.
They rely fundamentally on routines like this one:
10 GET X$: IF X$="" GOTO 10
20 Y$=Y$+X$
30 GOTO 10
and use a lot of memory. A short word like SIMON processed like this leaves
SIMONSIMOSIMSIS in memory.
Corruption of data in RAM. Provided the end of BASIC is correctly set, this
will not be a problem. For example, user-defined characters are popularly stored
from 12288 ($3000). Provided POKE 55,0: POKE 56,48: CLR is executed early in the
BASIC program, and it is shorter than 10K, the program will run perfectly because
all strings are separated from the graphics definitions.
Garbage Collection
Programs using many strings are subject to garbage collection delays. It is fairly easy
to see why. Figure 6-6 shows a simplified situation where RAM contains several
strings, some of which are now redundant.
c|a T D O | G E k E P H A N T
^ 1 i
t
Top of
BASIC RAM
free RAM A$ B$
149
Advanced BASIC
Let's suppose BASIC tries to set up a new string but finds there's insufficient
room. It calls a routine, the same as FRE(O), to find out which of the strings after
BASIC are still being used. Strings stored within BASIC itself are outside the scope
of this algorithm and are ignored.
The routine has to check every string variable to determine which is nearest the
top end of memory. This string is moved up as far as possible. The process is
repeated with the remaining strings until the whole collection is cleaned up. The
number of strings is important, not their lengths; generally, with N strings this takes
time proportional to N plus N-l plus N-2, etc. Mathematically inclined people
will realize this adds up to an expression on the order of N squared. What this
means is that, like the well-known bubble sort, a process that is acceptably fast with
a small number of items can become painfully slow with a larger number. In fact,
the whole process is analogous to the bubble sort: intermediate results are thrown
away, which saves space but wastes time.
The 64 takes roughly 0.00075 seconds times the number of strings squared to
free memory. The actual relationship is a quadratic, while this is only an approxima
tion. For instance, 100 strings take 0.9 seconds, 200 take over three seconds, 300
take over seven seconds, and so on.
Note that, during garbage collection, the keyboard locks in an apparent hang
up. This is normal; if a long ML routine runs, the RUN/STOP key has no chance to
work. RUN/STOP-RESTORE will interrupt the collection if you find it necessary. In
practice, you'll be likely to encounter garbage collection only if you're using string
arrays; 100 ordinary strings will cause an occasional delay of less than a second. Try
the following example, which calculates the time required to perform an FRE(0); as
stated above, this uses the garbage collection routine.
10 INPUT D:DIM X$(D):FOR J=l TO D:X$(J)=STR$(RND(1)):NEXT
20 T=TI:J=FRE(0):PRINT(TI-T)/60 "SECONDS''
If garbage collection is a problem, you must rewrite the program to reduce the
number of strings. There is no other easy solution. For example, pairing strings to
gether roughly divides the delay by 4. Note that performing FRE(0) whenever there's
time available can help (by shifting the delay to an acceptable period). It's also pos
sible to do a limited FRE on part of the strings, altering the pointers at 55 and 56
down or 53 and 54 up.
150
Advanced BASIC
Examples:
1. X°/o(500) has one dimension, and DIMl=500. Therefore, it occupies 5 + 2 +
501*2 = 1009 bytes.
2. AB(10,10) has two dimensions; DIMl = 10 and DIM2=10. This much data will oc
cupy 5 + 4 + 121*5 = 614 bytes.
3. X$(100) defined with strings on average 10 bytes long occupies about 5 + 2 +
101*3 + 101*10 = 1320 bytes.
LOAD
BASIC Program
Note: LQAD command on first program causes new BASIC program to load, then run. In the
diagram, the new program is shorter than the old, so variables' values are mostly retained.
151
Advanced BASIC
152
Advanced BASIC
The examples in Table 6-2, PEEKed from 64 memory, will help to clarify this:
1.5 129 64 0 0 0
3 130 64 0 0 0
4 131 0 0 0 0
5 131 32 0 0 0
6 131 64 0 0 0
7 131 96 0 0 0
8 132 0 0 0 0
153
Advanced BASIC
Note that numbers from 4 to 7.9999 . . . have the same exponent; their bit pat
terns run from 00000 ... to 11111 ... as the value increases. Adding 1 to the expo
nent doubles the value, subtracting 1 halves it, and so on. Note how negative
numbers have the sign bit set. Note also that an exponent of zero always indicates a
zero value with this number system.
To decode a number, the easiest method is to start at the lowest significant byte,
divide by 256, add the next, divide by 256, add the next, divide by 256, add Ml
(less 128 if necessary), divide by 128, and add 1. Scale up the result (which will be
from 1 to 1.999 . . .) by 2"129.
Conversely, if you wish to express a number in this format, either PEEK the val
ues from RAM or (if you can't access a computer) use the method outlined below.
Example. Expressing —13.2681. The minus sign means you must set the high
bit of Ml. The nearest power of 2 below 13 is 8 (23), so the exponent is
129+3=132. 13.2681/8 is equal to (1.6585125), and the fractional portion is the
number stored by the 31 bits in the mantissa:
.6585125 * 128 = 84.2896
.2896 * 256 = 74.1376
.1376 * 256 = 35.2256
.2256 * 256 = 57.75.
Thus, the nearest floating-point approximation of —13.2681 is 132 I 212 I 74 I 35 I 58.
Storage errors. Typically, a number giving aberrant results is stored with the fi
nal bit, or bits, incorrect. For instance, X=3/5*3 stores X as 130 I 64 I 0 I 0 11, and the
final bit makes X unequal to 3.
Integers and fractions. Any whole number between 1 — 232 and 232 — 1 is held
exactly by the 64, without any error. This is why loops like FOR J=l TO 100000:
PRINT J: NEXT can continue without error, while the same loop with STEP .9 soon
prints numbers with rounding errors.
Note that 232-l is stored as 160 1127 I 255 I 255 I 255 I 255 and is the highest ac
curately stored integer; 232 is stored as 161 I 0 I 0 I 0 I 0. Similar rules apply to frac
tions; provided they are combinations of 1/2, 1/4, 1/8, . . . 1/231, they can be held
exactly. Because of this, it may be best (particularly in financial calculations) to store
values as integers.
Buffers
The input buffer, keyboard buffer, and tape buffer occupy locations 512-600 ($0200-
$0258), 631-640 ($0277-$0280), and 828-1019 ($033C-$q3FB), respectively. During
normal operations, each of these areas has a specific function. The program "Micro-
Scope" (from the preceding chapter) allows you to watch the first two of these in
action.
Input buffer. Program 6-4 demonstrates the use of the input buffer.
154
Advanced BASIC
An ASCII string, terminated by a null (zero) byte and POKEd into the buffer,
behaves exactly as the same line would if typed in from the keyboard. Lines 40 and
50 execute the command in the buffer, after first setting a pointer to $01FF (one less
than the start of the buffer). The buffer would also be executed if the end of the pro
gram were reached, or if an END statement were encountered, but using the SYS
shown in Program 6-4 is often more useful.
Keyboard buffer. It's easy to show that the 64 has a queuing system for key
strokes. The short routine:
1 GET X$: PRINT X$: FOR J=l TO 2000: NEXT: GOTO 1
prints characters which have been typed faster than the computer can process them.
Up to ten characters can be stored here. You can POKE 649 ($289) to change this,
but if the value exceeds 10, you could corrupt some pointers (it is usually possible to
use up to 15, however).
Location 198 ($C6) independently stores the number of characters in the buffer.
POKE 198,0 therefore causes all characters to be ignored; it has the same effect as
FOR J=l TO 10: GET X$: NEXT. The key combination, SHIFT-RUN/STOP, puts
LOAD, carriage return, RUN, carriage return into this buffer, using a routine at
$E5EE.
Many examples in this chapter (AUTO, DELETE, LIST) rely on this buffer. The
following short routine shows how POKEs into the buffer work. This is another im
portant programming technique.
10 DATA 72,69,76,76,79
15 FOR J=631 TO 635:READX:POKE J,X:NEXT
20 POKE 198,5
POKEing one or more RETURN characters, CHR$(13), into the queue is also a
popular trick, since it allows messages printed on the screen to be input later. In ef
fect, that extends the range of the command beyond ten characters.
The next example, Program 6-5, puts a quote in the line just before INPUT. This
is very useful when a string which is to be input may contain commas, colons, or
other separators. A quote allows the entire string to be input without error. Run this
program, typing in something like A, B, C, and contrast the result with that achieved
by an unadorned INPUT statement.
155
Advanced BASIC
Program 6-5 moves the characters along the buffer, just as the 64's operating
system does.
A more exotic use is to transfer BASIC programs to the 64 from another com
puter by inputting them in ASCII via a modem, printing individual lines on the
screen, and inputting each line, adding a RETURN at the end. It is quicker than typ
ing them in, although the work of conversion is likely to be a problem.
Tape buffer. The 64's operating system reserves this area for tape use, and it is
therefore a popular place to put ML routines once no more tape activity is expected
(after a program has been loaded and is running). It is not actively programmable
like the two previous buffers. In addition, it is overwritten whenever tape is written
to or read from; don't put ML here if you're using tape to load or save data, or if you
are chaining programs.
Spare RAM areas. These aren't buffers in the usual sense. The 64 has IK of
RAM at the low end of memory for its own use, but some isn't allocated and can be
used safely. Locations 251-254 ($FB-$FE), 679-767 ($02Al-$02FF), 784-787
($0310-$0313), 820-827 ($0334-$033B), and 1020-1023 ($03FC-$03FF) are avail
able. The second of these areas is 95 bytes long; the tape buffer has 192 bytes, but
820-1023 are free for noncassette users (204 bytes). BASIC does not use the 4K of
RAM from 49152-53247 ($C000-$CFFF), which is also free for ML programs or
other purposes.
Clock
The three-byte jiffy clock is stored at locations 160-162. Location 162 is the fastest-
changing byte. At each interrupt (about every 1/60 second, a unit of time called a
jiffy—hence the name, jiffy clock), that location is incremented, with overflow when
necessary into the higher bytes. Thus, location 161 is incremented every 256/60 sec
onds, or about every 4.2667 seconds; location 160 is incremented every 65536/60
seconds, or about every 18.204 minutes. The PAUSE routine, later in this chapter,
shows a possible use of the jiffy clock.
The TI and TI$ reserved variables discussed in Chapter 3 are derived from these
bytes by a straightforward conversion. TI equals PEEK(162) + 256*PEEK(161) +
256*256*PEEK(160); for TI$, the value of TI (in jiffies) is converted into hours, min
utes, and seconds. Although the speed of the clock is constant, it is not identical to
that of real clocks, since the interrupts aren't at precise 1/60-second intervals. The
error varies with power sources, and between VICs and 64s, but the maximum error
will not be more than one part in 33,000 (a couple of minutes a day).
156
Advanced BASIC
Function Keys
The simplest programming method is to use a simple GET; the range of ASCII val
ues for fl-f8 are 133, 137, 134, 138, 135, 139, 136, and 140. Program 6-6 is a BASIC
loader which enables all eight function keys to be defined with individual strings up
to 32 characters long.
0 DATA 32,253,174,32,158,173,32,141,173,32,247,183
,136,152,10,10,10 :rem 204
1 DATA 10,10,133,253,169,29,133,254,32,253,174,32,
158,173,32,143,173 :rem 12
157
Advanced BASIC
2 DATA 160,0,177,100,240,22,170,200,177,100,133,25
1,200,177,100,133 :rem 179
3 DATA 252,160,0,177,251,145,253,200,202,208,248,1
38,145,253,169,28 :rem 228
4 DATA 141,144,2,169,75,141,143,2,96,24,165,215,23
3,132,201,8,144,3 :rem 213
5 DATA 76,72,235,170,189,124,28,133,253,169,29,133
,254,160,0,177,253 :rem 40
6 DATA 240,237,201,95,240,6,32,210,255,200,208,242
,166,198,169,13,157 :rem 71
7 DATA 119,2,230,198,208,216,0,64,128,192,32,96,16
0,224 :rem 152
10 REM PROGRAMMABLE FUNCTION KEYS FOR THE 64
:rem 146
20 REM TYPICAL EXAMPLES OF SYNTAX: :rem 113
30 REM SYS 40448,1,"THIS IS FUNCTION KEY 1":CLR
:rem 134
40 REM SYS 40448,2,"[CTRL-RED]M:CLR :rem 41
50 REM SYS 40448,3,"LIST 50-100V:CLR * ADDS RETUR
N :rem 79
60 REM SYS 40448,5,"LOAD"+CHR$(34)+"$"+CHR$(34)+",
8":CLR:REM LOADS DIRECTORY :rem 205
90 REM Sl=49192 TO PUT ROUTINE AT $C000 : rem 101
100 POKE 56, PEEK(56)-2: CLR: REM LOWERS MEMORY BY
512 BYTES :rem 63
110 S=PEEK(56):S1=256*S :rem 24
120 FOR J=S1 TO S1+131:READ X:POKE J,X:NEXT
:rem 202
130 POKE S1+22,S+1:POKE S1+65,S;POKE S1+90,S:POKE
{SPACE}S1+94,S+1 :rem 19
140 FOR J=Sl+256 TO S1+511:POKE J,0:NEXT:REM SET A
LL FN KEYS NULL :rem 174
150 PRINT M{CLR}USE SYNTAX:- :rem 19
160 PRINT "SYS" SI ",N,STRING:CLR" :rem 199
170 LIST 20-60 :rem 201
The program reserves 512 bytes at the top of memory for the ML and the eight
32-character strings. Strictly speaking, only 31 characters are available for each key
definition, because each has a null byte as a terminator. When Program 6-6 is first
run, all function definitions are initially blank. To define a function key, use a state
ment of the form:
SYS address, n, "string":CLR
where address is the start of the ML routine (the program will tell you the proper
value to use), n is the number of the function key you wish to define, and string is
the string of up to 31 characters you wish to assign to that key. CLR sets the pointers
properly. The program has REM statements which include examples.
Typically, SYS 40448,5,"NAME": CLR calculates the starting point where the
string is to be stored and copies it there. CLR sets BASIC pointers correctly. (This ex
ample makes f5 print NAME on the screen.)
158
Advanced BASIC
The vector at $028F-90 is central to the method. Provided it isn't changed and
provided the strings and ML aren't overwritten, the function keys will operate as
programmed indefinitely.
The software is written so that the left-arrow key can be used to insert a carriage
return into the keyboard buffer:
SYS 40448,l,"LIST 100-30<K':CLR
This defines function key 1 to type LIST 100-300 and press RETURN so that those
lines will be listed. Typical applications of user-defined keys are POKEs into hard-to-
memorize locations, SYS calls to routines in memory, and printouts of current values
of variables.
Keyboard
Commodore keyboards are very reliable. The 64 keyboard has 66 keys, which can be
pried off and replaced. It is possible to rearrange the keys, perhaps into German
GWERTZ style or the Dvorak layout with economically arranged letters. The soft
ware which decodes the keyboard can allow for this. Single-key entry of BASIC
keywords is possible, and as illustrated above, the function keys can be programmed
to output strings.
The CTRL key delays screen scroll and acts with keys 0-9 to set the major colors
and reverse on and off. CTRL-A through CTRL-Z also output CHR$(1) through
CHR$(26); for example, CTRL-E sets white characters, CTRL-N lowercase, and
CTRL-S cursor home. Also, CTRL-H locks the keyboard mode and CTRL-I unlocks
it, something otherwise tiresome to achieve. A few other keys act with CTRL, too.
An especially useful combination is CTRL-[ (open bracket, or SHIFTed :), which is
CHR$(27). This is a special printer code, called ESCape, which triggers features like
underline, double strike, and emphasized.
Reading the keyboard. When the 64 is operating normally, its interrupt routine
performs several functions. The clock is updated and a RUN/STOP key location up
dated, the cursor may be flashed, the cassette motor is turned off unless a flag is set,
the keyboard is scanned, and the keyboard buffer is updated on a keypress. This can
be traced in a ROM disassembly. When an interrupt is generated, the 6510 finishes
its instruction, saves a few values, and jumps to the address in $FFFE at the very top
of memory. It is tested to see if it's an interrupt (not a BRK instruction), and jumps to
the address held in $0314-$0315 (788-789), which is normally $EA31. The first
instruction is JSR $FFEA (UDTIM: increment clock, save RUN/STOP status), fol
lowed by screen and tape handling, and a call to the Kernal's SCNKEY routine at
$FF9F which, as the label implies, scans the keyboard.
Only 64 keys of the 66 are detectable by the keyscan. RESTORE is unreadable; it
causes a non-maskable interrupt (NMI) by making a circuit when pressed and isn't
decoded with the other keys. SHIFT/LOCK is the other undetected key. The 64 keys
are wired into a matrix of eight rows and eight columns, which can be scanned using
only two bytes. The arrangement of keys within the matrix is different on the VIC-20
and the 64.
Briefly, two ports of CIA 1, at $DC00 (columns) and $DC01 (rows), are exam
ined for bits set to 0. These bits are almost all 1; only the grounding action of a key
sets a zero value. This is why Table 6-3 below has values of 127, 191, etc.—the bit
patterns are 01111111, 10111111, and so on.
159
Advanced BASIC
When the scan begins, $DCOO is set for output and $DC01 for input, by the de
fault configuration of their data direction registers $DC02 and $DC03 (which hold
$FF and $00, respectively). In turn, the row register is rotated to take one of 8 val
ues, and each bit of the column is tested each time. A bit will have a 0 value if its
corresponding key is pressed, or 1 if the key is not pressed. The value is $FF when
no keys are pressed. A counter increments with each loop; it is this counter value
(not the value read from the matrix) which is stored when a keypress is found. Since
there are eight rows and columns, 64 different values are theoretically possible, and
the 64 uses all of these.
Reading more than one key simultaneously is generally possible only with ML;
Chapter 13 has an example. Even with ML, it is difficult to detect more than 2 of the
64 keys at once; for example, with keys 9 and K pressed, neither the + nor the : key
can be distinguished.
o Right
$BF (191) / t =
sh!ft ; * £
U
$DF (223) ,
@ - L P +
$EF (239) N O K M 0 J I 9
$F7 (247) V u H B 8 G Y 7
$FB (251) X T F C 6 D R 5
Left
$FD (253) SHIFT E S Z 4 A W 3
$FE (254)
3 f5 £3 fl 17 <=> RETURN w
The column is always set to 127 (at $EB42 in ROM, to be precise) apart from
during the actual reading; so RUN/STOP can be detected merely by testing whether
$DC01 has bit 7 clear. Left SHIFT, X, and several other keys can easily be checked
like this, too. Machine language is necessary to read the keyboard. The following
BASIC program and ML routine illustrate the way the rows and columns interact. At
this level, SHIFTed and unSHIFTed keys aren't distinguished.
10 POKE 808,234 :REM DISABLE STOP
20 INPUT "COLUMN",C :REM USE 127,191, ETC
30 POKE 829,C
40 SYS 828: GOTO 40
160
Advanced BASIC
1 2 3 4 5 6 7 8 9 0 + £ fl
57 56 59 8 11 16 19 24 27 32 35 40 43 48 51 0 4
CTRL Q W E R T Y U I O P @
*
t RESTORE f3
62 9 14 17 22 25 30 33 38 41 46 49 54 5
RUN/ A S D F G H J K L [ RETURN f5
STOP
63
BBS 10 13 18 21 26 29 34 37 42 45 50 53 1 6
o SHIFT z
12
X
23
c
20
V
31
B
28
N
39
M
36
<
47
>
44
?
55
SHIFT
$ <=> £7
3
7 2
Space
60
161
Advanced BASIC
which of the three keys is pressed. For example, if SHIFT and the Commodore key
are held down simultaneously, the value should be 3 (since 1 + 2 = 3).
RUN/STOP. Location $91 (145) stores a copy of the normal keyboard row and
is used to indicate that RUN/STOP is pressed. If you PRINT PEEK(145) in a loop
with STOP disabled, the result is 127.
Converting these values into ASCII is the final stage. The 64 has four character
tables built into ROM, for unSHIFTed, SHIFTed, Commodore key, and CTRL sets,
starting at $EB81, $EBC2, $EC03, and $EC78, respectively. Each is 65 bytes long and
converts $C5's contents into ASCII values, making allowance for the SHIFT or Com
modore keys. The last byte in each table contains $FF, the value used to show that
no key is pressed.
After copying the key value (0-64) into $CB, an indirect jump is executed via
$028F to $EB48. This routine's function is to set the address in ($F5) to point to one
of the four keyboard matrices, depending on the SHIFT keys in effect. It has a
subsidiary function, that if $0291 (657) is less than 128, SHIFT-Commodore key will
switch graphics sets from lowercase with uppercase to uppercase with graphics, or
vice versa.
Now, with $F5 pointing to one of the four keyboard tables, the routine at $EAE0
is entered, and the key's ASCII value is determined. The keyscan routine then deals
with keyboard repeats, cursor control, and other special keys. Finally, the ASCII
value of the key is put into the keyboard buffer (if room is available there), and con
trol returns to BASIC until the next IRQ interrupt.
The vector at $028F can be changed to point to your own RAM routine so that
keys can be intercepted and their effect changed. This is an ML technique only; an
earlier example shows how to program the function keys to print out strings of
characters.
Intercepting Keys
Generally, if you wish to intercept keys to trigger an activity, like printing a message,
the technique is to test either $CB or $D7 for your key, or keys. $D7 (215) stores the
ASCII value of the last key printed to the screen. Check $CB if you're only con
cerned with the physical key, and check $D7 if you need to distinguish between
unshifted and shifted keys. Left SHIFT can be tested with $91; SHIFT, CTRL, and
Commodore key can be separately detected at $028D. Jump to $EB48 if the looked-
for key isn't pressed; end your own routine typically with JMP $EB42, which reloads
$DC00 with the correct value for the next keyscan. In this way, keyboard processing
is exactly as normal, SHIFT keys and all, apart from your own specially inserted rou
tine. This very simple example changes the 64's background color whenever the left-
arrow key is pressed:
($028F should point to the starting address)
LDA $CB
CMP #$39 ;Left-arrow key
BEQ LABEL
JMP $EB48 ;Continue normal keyboard operation
LABEL INC $D020 increment border color register
JMP $EB42 ;This exit doesn't print <-. Allows repeat.
162
Advanced BASIC
The routine described above is listed below in the form of a BASIC loader.
0 DATA 173,141,2,201,2,240,3,76,72,235,164,203,192
,64,240,247,196
1 DATA 197,240,243,132,197,200,162,0,232,189,156,1
60,16,250,136
2 DATA 208,247,232,189,156,160,48,7,32,210,255,48,
245,16,243,41
3 DATA 127,32,210,255,76,72,235
20 S=49152:REM S=828 WORKS ALSO
163
Advanced BASIC
Variable S in Program 6-7 controls the place in memory into which the routine
is POKEd; any free RAM area is acceptable since the routine is relocatable.
Redefinition of Keyboard
If you wish to redefine the keyboard, the most elegant way to do this with the 64 is
to transfer BASIC and the Kernal into RAM, as explained in Chapters 5 and 8. This
leaves the keyboard tables free to be redefined in any way you choose. The four
tables, at 60289 ($EB81, unSHIFTED), 60354 ($EBC2, SHIFTED), 60419 ($EC03,
Commodore key), and 60536 ($EC78, CTRL), each have 64 bytes and a terminating
byte that holds $FF. Unused keys, such as CTRL-function keys, also appear as $FF
and can be redefined. Special keyboards can be saved and reloaded later, and turned
on or off at will by switching ROM out or in.
As a simple example, these four POKEs with RAM under ROM activated cause fl
and f5 to output CTRL-Black and CTRL-White, and f2 and f6 to output CTRL-
RVS/ON and CTRL-RVS/OFF:
POKE 60293,144: POKE 60295,5: POKE 60358,18: POKE 60360,146
It is also possible to cause BASIC to process keys differently; for example, CTRL-
G could be used to set a graphics mode. This of course involves work beyond simple
key redefinition. Another possibility is to extend the character set for printing foreign
characters to the screen.
The keyboard as a device. The keyboard is treated as device number 0 by the
operating system. We can open a file to the keyboard and treat it as an input device:
10 OPEN 5,0: REM OPEN FILE 5 FOR USE WITH DEVICE 0 (KEYBOARD)
20 INPUT#5,X$
30 PRINT X$: GOTO 20: REM LOOK AT WHAT'S BEEN INPUT
Commas and other punctuation symbols which BASIC treats as separators won't
now give 7EXTRA IGNORED, because a file is considered open, but parts of a string
may be lost. The normal question mark prompt isn't printed.
Repeat Keys
Location 650 controls which keys, if any, repeat when the key is held down. For ex
ample, the following POKEs easily modify the way the keyboard functions:
POKE 650,0 :REM SPACE BAR AND CURSOR CONTROL KEYS REPEAT.
POKE 650,64 :REM NO KEYS REPEAT.
POKE 650,128 :REM ALL KEYS REPEAT.
With BASIC in RAM, the rate of repeat and delay before repeat takes place can be
controlled by POKEing 60189 and 60138, respectively. Otherwise, an easy way to al
ter the repeat rate is to change the rate at which interrupts occur.
Location 652, the repeating key delay register, can be used to step from one
value to another through a range of values which may be very large. Program 6-8 is
a simple example of the method.
164
Advanced BASIC
The above program can cause some strange results, so take the disk out of the disk
drive (if you are using one) before running the program. The plus key increases Jhe
value printed to the screen at an increasing rate if the key is held down, but shorter
keypresses step forward in smaller increments. The minus key steps down. Chapter
13's "SIDmon" has a similar method for controlling values put into sound locations;
the values there can range from almost 0 to 65535.
165
Advanced BASIC
the whole area up to $D000 as RAM (Chapter 8 explains in depth). The only other
problem with $C000-$CFFF is that everyone's ML tends to start at $C000. If several
routines are to coexist, some will have to be relocated; Chapter 9 shows how to do
this.
The 64 also has RAM beneath ROM from $A000 to $BFFF and from $E000 to
$FFFF. However, this RAM cannot be used by BASIC without ML, except in the
sense that BASIC has some redundancy, so parts of BASIC or the Kernal could be
used for storage. For example, if BASIC and the Kernal are both in RAM, locations
$E4B7-$E4D9 can be used freely; on a larger scale, $E264-$E377 can be used, pro
vided the trigonometric functions (COS, SIN, TAN, ATN) are avoided.
Use LDA #$35:STA $01 to flip out the BASIC and Kernal ROMs, and access
their underlying RAM. To switch the ROMs back in, use LDA #$37:STA $01.
Because of the way the VIC chip works, this hidden RAM can also store graph
ics information, provided the screen is moved to the same general area. For example,
the screen might start at $C000, and up to 352 sprite definitions or 11 sets of charac
ter definitions could be stored simultaneously, ready for access. Chapter 8 provides
more information about how to access the RAM under ROM.
Small areas of free RAM. The 64 has IK of RAM at the start of memory which
is largely allocated for BASIC. In zero page, the four bytes from 251 to 254
($FB-$FE) are left untouched by BASIC. Locations 2-6 are rarely used (predomi
nately by ML number conversion programs), and 247-250 ($F7-$FA) are used for
RS-232 pointers. The stack (256-511, $100-$lFF) can be used with caution at the
low end; 318 ($13E) is a safe starting point if tape is going to be used. However,
don't store ML in the stack if you don't understand it; for example an ?OUT OF
MEMORY ERROR may delete your ML.
679-767 ($2A7-$2FF) has 89 spare bytes; 784-787 ($310-$313) has 4; and
820-1023 ($334-$3FF) has 204, of which the tape buffer (828-1019, $033C-$03FB)
takes 192.
Screen
The screen is treated as device number 3, so files can be opened to the screen for in
put and output with a statement like OPEN 3,3. This provides INPUT without the
normal prompt and can occasionally be useful in lines like the following one, which
reads the top line from the screen, subject to the usual rules governing INPUT.
OPEN 3,3: PRINT {HOME};: INPUT #3,X$
Screen handling can be complicated; each ASCII value has to be converted into
a POKE value, and if it has some special purpose like clearing the screen or setting
reverse mode, a subroutine must carry this out. Color RAM and start-of-screen must
be allowed for.
ML programmers can trace this process at $FFD2, the Kernal routine CHROUT,
which prints a character (which jumps to $F1CA and $E716). From here, an entire
range of processes is traceable, including delete and insert, cursor movements, screen
scrolling, and placing the character and its color into the screen.
Sixteen bytes just after the screen (usually 2024-2039, $7E8-$7F7), and 16 color
RAM nybbles, can store ML or data. (Eight bytes of sprite shape pointers follow
166
Advanced BASIC
this.) Clearing the screen leaves the area intact; but obviously incorrect POKEs to the
screen area can easily overwrite this storage position.
Table 6-5 is a quick reference list for screen locations and ROM routines.
ROM Routines:
$E544 58692 Clear screen
$E5A8 58792 Set VIC chip to normal values
$E632 58930 Input from screen (or keyboard) comes here
$E8CB 59595 Converts CHR$(color) in A into 0-15
$E8EA 59626 Scrolls screen up 1 row
$E981 59777 Scrolls screen down 1 row (contents of 677 may affect this)
$E9FF 59903 Clear entire row (e.g., POKE 781,X:SYS 59903, when X is 0-24)
$EA13 59923 Plots character and color in screen. A=Char, X=Color (0-15)
$EA24 59940 Finds color RAM relevant to current cursor position
167
Advanced BASIC
APPEND
This BASIC system command can either add one file to the end of another, making a
composite file, or link two BASIC programs end to end in a single program. Machine
language can be linked like this, too. Disk files can be appended as well (see Chap
ter 15). And tape files can be appended, but since the 64 has only one tape port, the
process is more difficult.
BASIC programs are easy to append because the LOAD address is easily altered.
Standard subroutines with high line numbers can be put onto the end of programs
without the usual need to list the subroutines to the screen, load the program, enter
some subroutine lines, save, and repeat. If the line numbers of the programs overlap,
the normal editing won't work and you'll have unremovable lines of BASIC.
Figure 6-10 shows a program in memory, plus two of its pointers. Note how a
link address of two zero bytes (following the normal end-of-line zero byte) signifies
the end of the program. If the new program loads and overwrites the double zero
link address with its own link address, the programs append perfectly.
BASIC Program 1 0 0 0
4- Append this:
Li ik BASIC Program 2
Start-of-Program
New End-of-Program
The easiest approach is to first enter POKE 43, PEEK(45)-2:POKE 44, PEEK(46)
in direct mode; then load or type in the new lines of BASIC and POKE 43,1: POKE
44,8 to start BASIC at $800. Perfectly appended BASIC should be the result. Ac
tually, this method is a shortcut; if PEEK(45) happens to be 0 or 1, you'll get an IL
LEGAL QUANTITY ERROR and will need to edit your instructions to POKE 43,
PEEK(45)+256-2: POKE 44, PEEK(46)-1, then continue as before.
AUTO
AUTO is a system command, not available in BASIC, which automatically generates
line numbers. Many utility packages include this command. This example is a BASIC
subroutine, which uses the keyboard buffer to take in complete lines. The POKE in
line 60010 flashes the cursor; line 60040 prints the current values of S and I, and
line 60050 puts two carriage returns in the keyboard buffer. Obviously, this would
be better if implemented in ML, wedging (for example) into the main BASIC loop
IMAIN, at $A480. r
168
Advanced BASIC
169
Advanced BASIC
SYS57812"Hr,8/l:PRINTPEEK(186);PEEK(183);PEEK(187)+256*PEEK(188);PEEK(185).
BLOCK LOAD. To load a machine language routine into memory, the easiest
way is simply to use LOAD "NAME",!,! (or ,8,1). Within a program, a flagging
technique is needed to avoid the automatic chaining feature:
0 IF X=l GOTO 20
1 X=l: LOAD "NAME",1,1
2 REM CONTINUE FROM HERE...
Listed below is a simple, trouble-free method of loading, which works within pro
grams without interrupting their flow:
1000 POKE 147,0 : REM THE LOAD/VERIFY FLAG. 0 IS LOAD
1010 SYS 57812 "SCREEN",8,1 : REM SETLFS IN THE KERNAL SETS PARAMETERS
1020 SYS 62631 : REM NOW LOAD
Program 6-10 saves a screen of information and reloads it. The technique can be
useful for many applications, including games, notepads and word processors (which
allow viewing two files on alternate screens).
Lines 10-30 save $0400-$07FF to disk under the name "SCREEN". Line 20
saves the corresponding color RAM, from $D800 to $DBFF, and line 30 saves the
VIC registers. If user-defined characters were used, they must be saved, too. Between
them, they completely define any picture starting at $0400; for example, the border
color and background are controlled by the VIC chip.
170
Advanced BASIC
Lines 500-520 reconstruct the picture. Try typing this program into a 64 at
tached to a disk drive, then put a few random colored characters on the screen. RUN
will store the screen's information on disk, if connected. Clear the screen; type RUN
500. You'll see the screen reconstruct itself.
Tape is equally simple: Just change each device number from 8 to 1, and remove
the redundant @: from within the filename. The screen files are always stored and
reloaded in the correct sequence.
CHAIN
Chaining is the process by which one program loads and runs another. For example,
a set of programs may exist on disk, each separately accessible by a menu, so that
only one program is in memory at a time, and the menu is reentered on exit from
any called program. Commodore 64 BASIC (and PET/CBM and VIC BASICs) chains
whenever LOAD takes place inside a program. A LOAD is automatically followed by
RUN without CLR (to retain previous variables).
Although simple, this is not quite as straightforward as it seems. Earlier in this
chapter you saw how problems can occur when the chained program is longer than
the program which loaded it. You may also encounter occasional difficulties with
strings and function definitions.
Try the following tape illustration:
1. Save this on tape:
10 PRINT "FIRST PROGRAM"
20 A=10: B%=100: C$="HELLO" + " "
30 LOAD "SECOND PROGRAM": REM CHAINS SECOND PROGRAM
Compile
Compilation is a process by which a language like BASIC is converted into pure ma
chine language. A program which performs this conversion is called a compiler.
171
Advanced BASIC
BASIC, the source code, is translated into ML, the object code. Typically, it will LIST
as a single SYS command, which is followed by a large (but unlisted) ML program.
Compilation is on a higher plane of sophistication than the other utilities discussed
here, but a short discussion is justified.
Briefly, any compiler of an unstructured language like BASIC must first build up
a table of all the program's variables and arrange a position for each of them in
memory. Strings need pointers and will be subject to garbage collection problems
unless they are each assigned 256 bytes. When the variables are dealt with, every
BASIC statement must be converted into its ML equivalent; the result is typically a
set of segments which are linked to make up the compiled code.
Speed increases of 10 to 50 or more times are claimed, but in practice even a
tenfold increase is probably optimistic. Some of the improvement is directly due to
the replacement of BASIC statements, with all their overhead and housekeeping, by
relatively straightforward processing.
By itself, this is not a major factor. Well-written compilers have their own
arithmetic routines, using integers where possible to save time. There's considerable
room for ingenuity. For example, a line like 100 GET X$: IF X$="" GOTO 100,
which is often found in BASIC, could be replaced by just five bytes of machine lan
guage. The line 1000 FOR J=0 TO 1000: POKE SC+J,32: NEXT is a loop, and is the
sort of BASIC which a compiler has great difficulty turning into efficient ML. Tiny
compilers working with a restricted set of BASIC (to save the effort of implementing
every command) also exist and are interesting educational tools.
Compilers invariably need disk drives, for speed and because multiple files are
required. If you don't have a disk drive, someone else can compile your BASIC for
you, in which case, the result will need to be transferred to tape; some compilers
have a feature to permit this.
Typical commercial compilers are PETSPEED and the DTL compiler. Each has a
limit on the size of BASIC program that is compilable and the number of variables in
it. You may find it necessary to shorten a very long program to compile it. The ML
object code is often longer than the original BASIC because it has to include a long
library of standard subroutines.
172
Advanced BASIC
With BASIC in RAM, all that's required is to intercept BASIC at $A8A0 and in
clude a routine to evaluate a formula, rather than simply take in an ASCII line num
ber. This version copies BASIC from ROM to RAM and stores the extra ML into a
part of the RAM which isn't used by the copied-down BASIC.
While ordinary BASIC runs a little slower in RAM, you can now have ex
pressions like GOTO DATE or GOSUB CHECK, where perhaps DATE=1000 was
previously defined, and line 1000 starts the routine called DATE.
173
Advanced BASIC
DEEK
This double-byte PEEK returns the value in two consecutive addresses, assuming
they follow the 6510 convention of low byte, then high byte. Use this formula:
DEF FN DEEK(X) = PEEK(X) + 256*PEEK(X+1)
DELETE (DEL)
This command allows deletion of unwanted BASIC lines. DEL a-b is the syntax,
which is similar to that of LIST (except that DEL alone should not delete every
thing). DEL seems to have been omitted from Commodore's original BASIC
specifications. This subroutine in BASIC, designed to sit at the end of a program,
works by searching for line numbers within a specified range, then deleting the line
by using a trick with the keyboard buffer, which simulates entry of the line number
at the keyboard.
Line 61020 skips through link addresses until a line number in range is found,
line 61030 stops either out of the range or at the end of a program. Lines 61040-50
print to the screen, and the rest of the subroutine simulates keypresses for HOME,
RETURN, and another RETURN.
DOKE
This double-byte POKE puts a value from 0 to 65535 into any two adjacent ad
dresses, assuming the standard 6510 convention of low byte/high byte. There's no
way to write this as a function without writing a SYS routine of the form SYS m,n or
using a wedge. Instead, DOKE ADDRESS, VALUE can be represented by POKE AD,
VA-INT(VA/256)*256: POKE AD + 1, VA/256.
DUMP
Screen dump. This prints a duplicate of the screen onto paper. It is relatively
easy to print normal characters, when user-defined characters aren't used, since all
that's needed is a PEEK into RAM followed by printout of the corresponding charac
ters. Complications include high-resolution graphics, color (where conversion to
174
Advanced BASIC
black-and-white may lose detail), and the fact that Commodore printers have the
Commodore character set, while others may not.
Program 6-13A is a BASIC screen dump which asslimes the uppercase character
set and correctly prints all characters, including SHIFTed and Commodore key sym
bols; however, the quotation mark character is not processed properly by some
printers:
Line 40010 opens a file to the printer. Line 40100 starts a loop, which PEEKs
every individual screen location, and line 40110 looks for reverse characters; use the
reverse character appropriate to your printer, if one is available. CHR$(18) is Com
modore's reverse character printing signal, with CHR$(146) also needed (in line
40160) to turn it off. Chapter 17 has more information on the use of printers.
Variable dump. This lists the current values of variables. Often array variables
are ignored by these routines, because they are more difficult to handle. Of course
values can simply be PRINTed by inserting a program line, so a dump of this kind is
not essential to debugging BASIC. There's no difficulty writing dumps in BASIC;
we've seen how variables and their types are stored, so variables' names and values
can be deciphered and printed out. They're printed in the same order that BASIC de
fined them, which is the sequence in which they are stored after BASIC.
An alternative procedure which gives a sorted list is to cycle through all the
variable names and types from A, A0-A9, AA-AZ, . . . B%, and so on; each variable
can be sought by the ROM routine (like VARPTR) and printed with its name. This
last method is the one used by Program 6-13B.
0 DATA 165,22,72,160,32,162,11,189,185,3,157,32,2,
202,16,247,140,41,2 :rem 45
1 DATA 192,32,240,28,140,36,2,208,23,142,34,2,32,2
25,255,234,234,208 :rem 253
2 DATA 8,104,133,22,104,104,76,116,164,162,48,142,
35,2,169,32,133,122 :rem 49
175
Advanced BASIC
3 DATA 169,0,133,72,32,134,174,165,72,240,19,169,3
2,133,122,173,34,2 :rem 12
4 DATA 141,39,2,173,35,2,141,40,2,32,157,170,174,3
5,2,232,224,58,144 :rem 0
5 DATA 211,224,65,144,247,224,91,144,203,174,34,2,
232,224,65,144,251 :rem 12
6 DATA 224,91,144,171,160,36,204,41,2,240,174,144,
139,200,208,136,34 :rem 6
7 DATA 32,32,65,146,61,34,32,65,32,59 :rem 30
10 REM SYS 828 (DIRECT MODE) DUMPS NON-ARRAY VARIA
BLES1 VALUES :rem 199
20 REM RELOCATE BY POINTING 185,3 IN LINE 0 TO 34
{SPACE}AT END OF LINE 6 :rem 237
100 FOR J=828 TO 963:READ X:POKE J,X:NEXT :rem 68
FIND
See SEARCH.
LIST
One of the most used commands in BASIC is LIST. Luckily, it can be modified
easily. The two routines below are examples of modified listing. Program 6-14 cre
ates a window on 12 lines of BASIC at a time, which can be scrolled up or down.
This is helpful when examining BASIC without the benefit of a printer. Program 6-
15 is in machine language; it alters LIST to expand the reverse characters of 64 list
ings into a more readable text form. Printer owners may like to list their programs in
this format.
Window LIST. Append Program 6-14 to the end of your BASIC programs to
use it. If you RUN 63000 with this subroutine in memory, it will list several lines on
the screen—the number of lines listed can be adjusted in line 63010. Pressing the fl
key causes the listing to move upward past the stationary window, while pressing
the i7 key causes the listing to move downward. Obviously, since any single logical
line of BASIC can take two physical screen lines, 13 lines of BASIC may be too
much for the screen to hold.
Lines 63020 and 63030 are printed on the screen, and they list several lines in
white before returning to test for f1 or f7. The current starting line is the Mth line
number, and subroutine 63300 scans the program, finding which line numbers to
list. After LIST, the keyboard buffer is POKEd to simulate {CLR} RETURN {CLR}
{DOWN} RETURN.
176
Advanced BASIC
BASIC can't be edited with Program 6-14 running—the entire process is under
program control, and listing is all that's allowed. However, it would be possible to
write a line-editing program with this method, plus parts of AUTO.
Legible LIST. Program 6-15 is a transparent ML program which locates itself in
memory.
177
Advanced BASIC
Most of Program 6-15 consists of two tables, one of ASCII character values and
the other of their translated form within brackets. Therefore, the program can easily
be modified to allow for graphics characters or to output your own alternative forms.
The ASCII values of the brackets [ and ] are 91 and 93. The ASCII values and special
characters of the program in its current form are listed below:
Program 6-15, activated by SYS 49152, modifies a LIST vector to point within
the special ML routine, which checks all characters in quotation marks. This part of
the program is quite small. The program then outputs the special characters in brack
ets. Printers can use this program, but lines containing special characters will be
longer than usual. SYS 49152 also turns off the special listing function, acting as a
toggle.
Chapter 8 has a short ML routine which checks for colons and is able to LIST
separate statements on new lines. If you wish to modify LIST in your own way, but
178
Advanced BASIC
have little ML experience, Program 6-16, an outline BASIC program, will help; it
reads BASIC with PEEKs and is easy to understand. Append it to BASIC when you
want to use it. Add your own selection of special characters at the end of the
program.
179
Advanced BASIC
MERGE
A program that combines two BASIC programs into a single program with the lines
sorted correctly is called a merge. Standard subroutines, for example, can be inserted
without the need for reentering them. Many BASIC extension packages have
MERGE. Because of the flexible way merging is done, this command can also per
form some extra functions, such as loading PET/CBM tapes more easily into a 64.
Tape merge. The following procedure involves storing the subroutines to be
merged as sequential files, not as tokenized programs, then reading them back using
the keyboard buffer to simulate entry of each line.
Use this line to save a subroutine on tape as a sequential file:
OPEN 1,1,1,"NAME OF SUBROUTINE": CMD 1: LIST [OPTIONAL LOW-HIGH LINES]
When the cursor returns, type the following line to close the file and write the last
portion of data to tape:
PRINT#1:CLOSE 1
Merging can be carried out whenever you have a program in memory. The re
sult will be a fully merged program, as if the lines had been separately typed at the
keyboard. Note that lines entered with any BASIC abbreviations which are ab
normally long when listed may need to be divided into shorter lines.
Use the following procedure to merge program lines. Start with a program in
memory and the tape in the cassette drive, then:
POKE 19,1: OPEN 1,1,0, "NAME OF SUBROUTINE"
to read the tape until it finds the correct header. This will be signaled by FOUND.
At that point, it will wait for the file to be read. Type {CLR} and
{DOWN}{DOWN}{DOWN}. Then POKE 153,1: POKE 198,1: POKE 631,13: PRINT
CHR$ (19) and press RETURN, and the tape file will be automatically read and
merged. Eventually, a 7SYNTAX ERROR message appears; this is not a mistake, but a
result of either the tape or the program having no more lines left. It means that the
merge is finished.
Disk merge. Program 6-17 alters BASIC, allowing it to merge new lines into a
BASIC program in memory. It has a driver routine starting at $033E (830) which
fetches single characters of BASIC, building them into the input buffer.
180
Advanced BASIC
Use this program by entering LOAD, RUN, and NEW. Load or type a program
into memory, and additional programs can be merged into it with OPEN
8,8,8/TROGRAM NAME": SYS 830. Turn off the disk light with OPEN 15,8,15/T':
CLOSE 15.
MOD
This is an arithmetic function, found in some BASICs, which calculates the remain
der when one integer is divided by another. MOD is an abbreviation of modulo, a
mathematical term used in number theory. The statement, "4=19 modulo 5," means
that 4 and 19 have the same remainders when divided by 5. The simplest BASIC
version is DEF FN MOD(N) = N-INT(N/D)*D, where D is the divisor. This
formulation may be of use when converting other BASICs to CBM BASIC.
Examples of the use of MOD are D=12:H=FN MOD(16), which converts 16
hours to 4:00, and D=256: PRINT FN MOD (50000), which prints the low byte
of 50000.
OLD
Originally, OLD was used to restore a program which had been inadvertently re
moved by NEW. However, the 64 offers two other important uses, which can be
considered under the heading BASIC recovery.
181
Advanced BASIC
OLD for program recovery. This restores BASIC because NEW leaves most of
the program intact. NEW simply arranges pointers as though no program were
present, and puts a zero link address at the very start of BASIC; it also sets GETCHR
and the RESTORE pointers, clears variables, and closes files. Once Program 6-18 is
in memory, to recover from an unwanted NEW, type SYS 53000: LIST. The program
is restored, and its variables are even retained. If BASIC has not been NEWed, or
even if it's running, the SYS call does no harm.
OLD after chaining. When used with LOAD in a program, this restores BASIC
when the new program is longer than the old one. As explained under CHAIN, in
order to pass variables from one chained program to the next, the end-of-program
pointers are not set. Thus, if the newly loaded program is too long, its top end will
be corrupted. However, if the ML routine has been POKEd in by the loader pro
gram, 0 SYS 53000:CLR at the start of the new program will prevent corruption.
(CLR is needed to remove garbage after the program, which may appear as
pseudodata; it is not possible to recover the overwritten variables.)
OLD restores BASIC after SYS 64738. Old can be used after a hardware reset
has occurred, returning the machine to its startup state. Both these routines leave
BASIC RAM unaltered and in effect perform NEW, so SYS 53000 is just as effective
as with NEW. Any BASIC program, including one which disables RUN/STOP and
RESTORE, can be reset and recovered by this method if you have a hardware reset
switch.
How OLD works. OLD makes use of the ROM routine which links BASIC lines.
A nonzero value is POKEd into the first link address, correcting for the zero bytes
NEW inserts, and JSR $A533 relinks the lines of BASIC. The end-of-program point
ers must also be reset. OLD also corrects location $A000, without which a hardware
reset can corrupt BASIC if it is stored in RAM.
BASIC version of OLD. A BASIC equivalent of the above is simple, but the
end-of-program pointers get lost and take some effort to retrieve. If the end-of-
program isn't moved up, variables will overwrite your program when it runs.
POKE PEEK(44)*256+2/l:SYS 42291:POKE 46, PEEK(56)-1:CLR
This assumes that BASIC starts in one of the normal places and that the end-of-
program pointer's position isn't critical (it becomes set to a location 256 bytes below
the end of BASIC memory). The program will LIST properly.
ONERR
The 64 has an indirect vector at $300-$301 (768-769) to process error messages.
Usually, this is set to $E38B and the actual error is dictated by the byte in the X reg
ister. POKE 781, number from 1 to 30: SYS 42042 will print a message to the screen.
ONERR usually works by specifying a line number to GOTO in the event of
error. The advantage is that the program cannot crash, while the drawback is that
processing ONERR properly is liable to take a lot of memory space and slow execu
tion time.
PAUSE
There are two versions of this command: one waits for a timed delay and the other
temporarily freezes BASIC or ML.
182
Advanced BASIC
Timed delays are useful with some types of music programs. BASIC delay loops
(FOR J = 1 TO 500: NEXT) work well, though the actual timing varies with the
stored position of the loop variable in memory. If J is set up as the first variable, this
solves the problem. The 64's internal clock is another obvious way to get accurate
timing. The clock is stored in 160, 161, and 162, with 162 changing fastest. One
short routine is POKE 162,X: WAIT 162,2tN, which has a maximum delay of
128/60, or about two seconds. To explain the formula, note that WAIT stops until
just one bit is set. So POKE 162,0: WAIT 162,64 delays until location 162 reaches
64, pausing for 64/60 seconds. The timing is reasonably constant, although the first
POKE could occur any time between interrupts, so there's 1/60 second maximum
difference in pauses. Unless the interrupt rate is changed, resolution below about
1/60 second isn't possible. Delays longer than about two seconds require the use of
location 161. POKE 161,0: POKE 162,0: WAIT 161,2 pauses for 2*256/60 seconds
(or about eight seconds).
The easiest implementation of a system pause (halting execution until some
event occurs) is to intercept the interrupt routine and check for a keypress. The
SHIFT key is useful, because SHIFT/LOCK can pause indefinitely. However, any
SHIFTed entry will then temporarily stop the program. The following ML routine
will do the trick with normal keys, for example, the left-arrow. You could modify the
program to check the keyboard twice, so the key could toggle the function on and
off, or to test for Commodore, SHIFT, or CTRL keys:
Send interrupt routine here: PAUSE JSR $FF9F ;SCAN KEYBOARD
LDA $C5 ;LOOK AT KEYPRESS
CMP #$39 ;CHECK FOR BACK-ARROW
BEQ PAUSE ;PAUSE WHILE PRESSED
JMP $EA31 ;NORMAL IRQ ROUTINE
POP
This command discards a RETURN from the stack; this erases the effect of the pre
vious GOSUB so that if RETURN is encountered, the address returned to will be that
of the next-to-last GOSUB, or 7RETURN WITHOUT GOSUB will be signaled. This is
useful in providing a premature escape from BASIC subroutines, which otherwise
causes problems. To explain, GOSUB causes the computer to store a return address
on the stack. When the subroutine is finished, RETURN pulls this address off the
stack, using it to resume execution at the correct spot in your BASIC program. If you
repeatedly exit a subroutine without performing a RETURN (using GOTO, for ex
ample), the stack eventually fills up with unused return addresses, causing an ?OUT
OF MEMORY ERROR. You can cure this problem and others like it with Program 6-
19 below.
POP is relocatable, but this version starts at 830 and is called by SYS 830 from
within a program. RUN and test with SYS 830 in direct mode; you should get a
7RETURN WITHOUT GOSUB ERROR.
POP mimics RETURN in all respects apart from the actual change in program
control. With this routine in memory, use SYS 830 immediately before any pre
mature exit from a subroutine.
A more thorough POP, using a part of CLR, clears away all loops and sub
routines within a program by resetting the stack pointer, thus deleting all evidence
of FOR-NEXT and GOSUB. Variable values, DATA pointers, and so on are retained.
On an abort or escape, this routine cuts through any tangle of loops and subroutines.
With the 64, machine language is required to perform this POP:
PLA ; REMOVE SYS ADDRESS
PLA
JMP $A67E ; ENTER CLR TO RESET THE STACK
PRINT @
This moves the cursor rapidly to any place on the screen, as specified by horizontal
and vertical parameters (HTAB and VTAB are other forms of this command). Graph
ics in BASIC can often be much improved with one of these methods, in place of
printing {HOME} and many cursor moves. The fastest versions contain their own
ML routines and therefore require storage space. Slightly slower versions use ROM
routines and are more convenient.
To use the fast ML version below, type in the lines and run the program. SYS
828,H,y takes in horizontal and vertical parameters and puts them into the Kernal
PLOT routine vectored at $FFF0.
0 DATA 32,155,183,138,72,32,155,183,104,170,164,101,24,76,240,255
10 FOR J=828 TO 843: READ X: POKE J,X: NEXT
PRINT USING
Some computer languages allow you to specify the format in which numbers are
printed. This 64 program allows easy and fast output in a variety of formats,
(rounded to two decimal places, or including a leading $ symbol, for example). The
overall length of the output (including leading spaces) is programmable, so lining up
columns of figures is made simpler. Also, output can be directed to a printer.
Program
m 6-20. Print Using
■proof program
For mistake-proof program entry,
entry, be
be sure
sure to
to use the "Automatic Proofreader," Appendix C.
1,10,2,32,162,0,221,0,1,240,6,232,224,12,20
>,24,96,169,69,32,-162 :rem 113
176,90,173,-166,240,94,173,2,1,208,11,172,-
l/TA AC% 1 CO O 1 1 OC • Y»ATT1 00\Q
184
Advanced BASIC
3 DATA 46,32,-162,172,-164,232,136,208,252,236,-16
5,176,33,172,-165,169,0 :rem 3
4 DATA 153,1,1,189,0,1,201,32,208,3,169,32,234,153
,0,1,202,16,6,173,-163,136 trem 114
5 DATA 16,244,136,16,231,169,0,133,97,160,1,132,98
,96,169,0,32,-162,144,240 :rem 106
6 DATA 138,168,173,2,1,240,9,169,46,32,-162,144,2,
138,168,152,170,202,16,181 :rem 156
7 DATA 0,32,158,173,32,221,189,32,-148,32,30,171,9
6 :rem 202
30 T=49318 srem 253
40 L=T-166 jrem 11
50 FORJ=L TO T-l ,rem 116
60 READ X%:IF X%<0 THEN Y=X%+T:X%=Y/256:Z=Y-X%*256
:POKE J,Z:J=J+1 :rem 198
70 POKE J,X%:NEXT : rem 2
80 GOTO 130 :rem 53
100 X%=L/256: Z=L-X%*256 :rem 241
110 POKE 55,Z:POKE 53,Z:POKE 51,Z :rem 93
120 POKE 56,X%:POKE 54,X%:POKE 52,X% :rem 202
130 PRINT "{DOWNjSYS " L+153 " FOLLOWED BY ANY NUM
ERIC :rem 153
131 PRINT "EXPRESSION IN PARENTHESES" :rem 79
132 PRINT "PRINTS FORMATTED VALUE. :rem 118
134 PRINT :rem 37
140 PRINT L{3 SPACES}"=DEC/INT FLAG" :rem 239
150 PRINT L+l "=OUTPUT LENGTH" :rem 255
160 PRINT L+2 "=DEC. PLACES" :rem 0
170 PRINT L+3 "=LEADING CHRS" :rem 116
180 PRINT L+98 "=+VE LEAD CHR" :rem 72
190 PRINT "{DOWNjEG SYS" L+153 "(-1234.567) PRINTS
-1234.56" :rem 227
200 PRINT "{DOWN}SET UP NOW WITH LENGTH 11, 2 DEC.
PLACES, & LEADING SPACES." :rem 156
210 PRINT "{DOWN}SAVE FROM" L "TO" T-l; :rem 248
220 PRINT "($"?:GOSUB 500:PRINT " TO $";:L=T-1:GOS
UB 500:PRINT ")" :rem 64
230 END :rem 108
500 L=L/4096:FORJ=1TO4:L%=L:L$=CHR$(48+L%-(L%>9)*7
):PRINTL$;:L=16*(L-L%):NEXT :rem 210
510 RETURN :rem 118
Once you enter, save, and run the program, the screen display should include this:
49152 = DEC/INT FLAG
49153 = OUTPUT LENGTH
49154 = DEC. PLACES
49155 = LEADING CHARACTERS
49250 = +VE LEAD CHR
SET UP NOW WITH LENGTH 11, 2 DEC. PLACES, & LEADING SPACES.
185
Advanced BASIC
Executing a SYS 49305(X) will print the current value of X, formatted (where
possible) in accordance with the values in the five locations listed.
Decimal/integer flag. A value of 0 in this location means the result will be
treated as an integer (no decimal point symbol will be printed), while 1 means it is
decimal.
Output length. This location specifies the total length of the output string -1.
It allows tables of numbers to be constructed easily.
Decimal places. This controls the number of figures after the decimal point. If
the number is an integer, this is ignored.
Leading characters. This location holds the ASCII character printed before the
number begins. This enables printing in formats like ****100 or 000123,23. The
usual leading character is the space character (32).
Positive symbol. Numerals are preceded by a space or minus sign with BASIC'S
unmodified PRINT statement; this routine permits a substitute for the space charac
ter to be printed (for example, $), so all positive numbers will appear preceded by
the substitute character.
Note that X is truncated; if you wish to round the output value to two decimal
places, use SYS (7667) X + .005.
Using PRINT USING. Program 6-21 prints formatted columns of figures. Lines
20, 30-31, and 40 print the first, second, and third columns, respectively. Meaningful
variable names should help to make the POKEs more understandable.
10 PRNT=49305:SWITCH=49152:LNGTH=49153:DECPTS=4915
4:CHAR=49155:LDGCHR=49250
15 FORJ=-10 TO 100 STEP 10:PRINT
20 POKE SWITCH,0:POKE LNGTH,4:POKE CHAR,42:POKE LD
GCHAR,42:SYS (PR) J
30 POKE SWITCH,1:POKE LNGTH,7:POKE CHAR,32:POKE LD
GCHAR,32
31 POKE DECPTS,4: SYS (PR) l/(l+j)
40 POKE LDGCHAR,ASC(W$M):POKE DECPTS,2: SYS (PR) 1
00 + J
50 NEXT
100 REM SYS 49305 (400.00) ETC.
186
Advanced BASIC
RECONFIGURE
Chapter 5 explains how BASIC configures itself on switch-on. However, there are
many ways memory can be allocated on the 64. The pointers at 43 and 44, and 55
and 56, show the entire BASIC area is normally $0800-$A000. To lower the top of
BASIC memory to $8000, POKE 55,0: POKE 56,128. Now, CLR will reset all the
string pointers correctly, but stored variable values will be lost. POKE 51,0: POKE
52,128: POKE 53,0: POKE 54,128: POKE 55,0: POKE 56,128 has the same effect, but
retains variables, and is therefore sometimes better.
Program 6-22 allows the start or end of BASIC (or both) to be changed, so that
PRINT FRE(0) returns different values from usual. The screen RAM can also be
moved, within IK boundaries; if it's moved to overlap BASIC, a program or its vari
ables may be displayed in the screen, generally with odd side effects.
Program protection methods sometimes make use of this feature. For example,
you can move the screen to $C000 and write ML starting at the normal screen area
of $0400. When the ML is loaded, the screen fills with what is apparently garbage,
but which is necessary to run the program. This makes a program relatively safe
from being copied.
Another use is to simulate other machines, mainly the VIC-20 and CBM/PET.
For example, the CBM/PET simulator in the Appendices moves BASIC and the
screen to the CBM/PET positions and adds some other CBM-like features. All these
examples keep BASIC in ROM; Chapter 8 explains how BASIC in RAM can be used
to reconfigure BASIC more fundamentally.
Note that BASIC must have a zero byte at the position immediately before that
indicated by the pointers 43 and 44. If it does not, NEW or RUN will cause a ?SYN-
TAX ERROR.
187
Advanced BASIC
To make BASIC start at $1200 and end at $1400, with the screen at $2000, run
Program 6-22, enter 4609, 5120, and 8192 at the prompts, and then enter NEW.
Following this PRINT FRE(0) shows 509 free bytes, and POKE 8192,6: POKE
55296,5 prints a green F at the home position, showing that the POKEs have worked
correctly.
Boots. The ability of a computer to load and run a program automatically is
called booting. Some microsystems require the disk operating system (DOS) to be
loaded in when the computer is turned on. The term came about because the com
puter is said to be "pulling itself up by its own bootstraps." The Commodore 1541
disk drive, however, uses proprietary software (the DOS is contained in the disk
drive's ROM chips). And the 1541's built-in software does not autoboot. Neverthe
less, the keyboard buffer and input buffer can be used to solve this deficiency. For
example, the commands can be printed to the screen and the keyboard can be filled
with the characters needed to input them; this, however, assumes that the position
of screen memory doesn't change.
Tape boot. When using tape, autobooting is easy. Simply press SHIFT-RUN/
STOP. If you want to see how to do the same from within a program, add line 1045
to Program 6-22:
1045 POKE 631,131: POKE 198,1
These POKEs have the effect of typing SHIFT-RUN/STOP (the ASCII value is
131). Since 198 holds the number of characters in the keyboard queue, POKEing a 1
into that location simulates a single keypress. Now, the program reconfigures BASIC,
loads the next tape program, and runs it.
Disk boot. The keyboard queue can't easily hold much more than ten charac
ters, which is insufficient to load a disk program since, unlike tape, a name is usually
needed. LOAD"*",8:RUN in its short form just fits. One solution is to use the input
buffer as in the following lines:
61 CLR :REM NEW NOT NEEDED AT END (AS NEW PROGRAM IS TO BE LOADED)
62 N$="LOAD" + CHR$(34) + "NAME" + CHR$(34) + ",8" + CHR$(0)
63 FOR J = 1 TO LEN(N$): POKE 511+J, ASC (MID$(N$,J)): NEXT
64 POKE 198,3: POKE 631,82: POKE 632,213: POKE 633,13
65 POKE 781,255: POKE 782,1 :REM POINTER TO $01FF
66 SYS 42118 :REM INPUT LINE
Line 62 sets up a string ending with a null byte; this exactly mimics a line input
from the keyboard. Line 63 POKEs the characters into the input buffer at 512
($0200). Line 64 puts R SHIFT-U RETURN in the keyboard buffer, to cause the pro
gram to run after loading. Lines 65 and 66 process the line in the buffer, loading the
program called "NAME".
REM
REM is, of course, one of the 64's normal statements. It deserves a place here be
cause of the unique status of REM statements outside the normally strict rules of
BASIC syntax.
REM with SHIFT and quotes. SHIFTed characters have their high bit set and
are interpreted as tokens, so LIST converts these into reserved words, expanding the
188
Advanced BASIC
line. Cursor control characters, {CLR}, {HOME}, etc., can be inserted after an open
ing quotation mark. {DEL} (delete) characters can be used by opening up space in
side quotes with the {INST} (insert) key. A hidden line can be created by following
it with :REM" ", expanding the space in quotes, and filling the space with {DEL}
characters, though this maneuver won't hide the line when it's listed on a printer.
You can use REM statements to produce colorful listings, too. For example, you
could list the initialization section of the program in white, the main loop in yellow,
and subroutines in other colors. This way you could find the section you wanted to
view easily. To change the color of the listing, type REM " " and delete the second
quotation mark, then press {RVS} (CTRL-9) followed by SHIFT-M. Next, press
{INST} (SHIFT-DEL) once, and select the color by pressing CTRL or Commodore
key and the correct numbered key. After this, press the RETURN key to enter the
line. REM stores some characters differently inside quotes than outside. Thus, util
ities which search for strings may not find them in REM statements.
Inserting characters into REM statements. REM is tokenized as 143 in decimal.
The following short routine puts two RETURN characters immediately after REM in
a REM line, and also immediately before the end of the REM line, so 100 REM**
REMINDER COMMENTS * will list remarks neatly onto new lines.
63000 L=43
63010 L=PEEK(L) + 256*PEEK(L+1): IF L=0 THEN ENDrREM SKIP THROUGH LINKS
63020 IF PEEK(L+4)<>143 GOTO 63010:REM IF REM NOT FOUND TRY NEXT LINE
63030 POKE L+5,13: POKE L+6,13:REM POKE TWO RETURNS
63040 FOR J=L+5 TO 9E9: IF PEEK(P>0 THEN NEXT:REM FIND END-OF-LINE,
63050 POKE J-1,13: GOTO 63010 :REM AND POKE ONE RETURN
RENUMBER
Renumbering a BASIC program has some cosmetic advantages and is valuable
where BASIC line numbers are too close to allow more BASIC to be added, or when
a program is finished and you want to renumber by ones starting at line number 0
(which causes the program to run slightly faster). Program 6-23 is a short BASIC
subroutine that changes line numbers only, between a selected range, by POKEing in
new values.
189
Advanced BASIC
190
v?Advanced BASIC
One difficulty with renumbering is that line numbers within programs are stored
as ASCII strings, so if a renumbered line is different in length, the program's length
may have to be changed. Another difficulty concerns syntax; Program 6-23 simply
assumes correct syntax, mainly to use less space.
To use "Renumber," RUN 60000. You may renumber lines 0-59999, but not
above. Lines 60000-60120 of the program build an array; L(J,O) holds original line
numbers, L(J,1) holds new numbers, and L(J,2) holds pointers to the start of each
line. The numbers at the start of each line are renumbered at this stage. J counts the
number of lines in the program; not all these are needed, of course, since RE
NUMBER itself should be left alone.
Lines 60200-60210 scan all the relevant program lines, searching for keyword
tokens, which are processed by the lines that follow. Line 60305 looks for TO; this
allows GO TO to be renumbered, not just GOTO. Spaces after a keyword are
counted, allowing variation in the renumbered line number lengths. Line 60320
searches for lines in the table and signals if they're not found. Lines 60400-60415
POKE in the new line number, where possible. And 60500 processes constructions
like ON X GOTO 100,200 and LIST 10-30.
RESET
SYS 64738 resets the 64, giving a result similar to switching on the machine. RAM
from $0 to $0400, except for the stack, is completely cleared out, and BASIC is in ef
fect NEWed, but the rest of memory is untouched and BASIC can be recovered with
OLD.
SYS 64738 is useful whenever the 64 has been reconfigured or pointers have
been set in unusual ways. For example, after loading ML high in memory, RESET
will leave it there by return to the normal condition of BASIC on startup. When
BASIC is in ROM, a hardware reset (see Chapter 5) has the same effect as this soft
ware reset; other CBM machines behave similarly.
However, if BASIC is in RAM, SYS 64738 acts differently from a hardware reset
and may show an unusually large number of bytes free, because the software SYS
call, unlike hardware, doesn't necessarily switch BASIC into ROM, if the Kernal has
been modified. Chapter 8 explains in depth.
Note that some CBM publications contain a wrong SYS call for this feature.
SEARCH
Searching BASIC is reasonably straightforward, given an understanding of the way it
is stored in memory. The following ML search hunts for a match with the contents
of the first BASIC line.
191
Advanced BASIC
12 DATA 2,230,143,164,143,177,251,240,28,72,164,14
2,177,253,240,15,104 :rem 107
13 DATA 209,253,240,6,230,142,160,4,208,222,230,14
2,208,226,104,104,170 :rem 136
14 DATA 104,208,193,160,2,177,253,170,200,177,253,
32,205,189,169,32,32 :rem 120
15 DATA 210,255,201,0,208,231,96 :rem 25
Run Program 6-24, then enter 0 DATA and type SYS 830. All the line numbers
of lines with DATA statements will list. You can search for lines containing the
number 240 with 0"240; 0"SYS will find SYS as a word, not as a BASIC keyword.
The ML relocates, and can be moved to any free RAM area.
SET
SET (and UNSET) are graphics commands in some BASICs which allow a point or
small square to be drawn at any specified positions on the screen. Chapter 12 has a
lot of information on this, including a high-resolution plotting routine.
SORT
Sorting means arranging a list in order, usually alphabetically or numerically. Many
sorting methods exist, but only three major ones are discussed here: two BASIC sorts
and one ML sort, which includes a demonstration to illustrate the syntax. The ma
chine language version is far faster than BASIC.
BASIC sorts. The Shell-Metzner Sort is a fast sort, which is also easy to pro
gram. The version given in Program 6-25 sorts items 1 to N of an array dimensioned
with A$(N). The sort is written as a subroutine to be added to your programs, and it
assumes that array A$ and number of elements N have both been established before
you GOSUB to the routine. Upon return from the routine, the contents of array A$
will be arranged in ascending order.
The Tournament Sort, so called because it pairs together items for comparison,
starts to give answers almost immediately, rather than waiting for the entire array to
be sorted. In addition, since numbers rather than strings are moved, garbage collec
tion (which can otherwise be a problem with BASIC) is not a factor.
Program 6-26 illustrates the Tournament Sort. Lines 10 and 20 allow you to set
up the array N$, which will be sorted. A numeric array, I, is also required, and it
192
Advanced BASIC
must be dimensioned for twice as many elements as N$. Lines 200-330 perform the
sort, printing each element as it is sorted into its proper position and ending when
the sort is complete.
Machine language sort. This ML sort is far faster than either of the two BASIC
sorts above. Program 6-27 loads the program into free RAM at $C000, although it is
relocatable and can be put anywhere in free RAM. It sorts string arrays in ascending
order, using an ordering algorithm identical to the 64's, and it is initiated using a
simple SYS call. It lets you sort strings from the second, third, or any other charac
ter, and it works with any memory configuration.
0 DATA 32#115,0,133,97,169,128,133,98,32,115,0,240
,7,9,128,133,98,32,115 :rem 213
1 DATA 0,165,47,133,99,165,48,133,100,160,0,165,97
,209,99,208,7,200,165,98 :rem 79
2 DATA 209,99,240,20,24,160,2,177,99,101,99,72,200
,177,99,101,100,133 :rem 71
3 DATA 100,104,133,99,144,221,160,5,177,99,133,102
,200,177,99,133,101,208 :rem 3
4 DATA 2,198,102,198,101,24,165,99,105,7,133,99,16
5,100,105,0,133,100,165,101 :rem 192
5 DATA 208,2,198,102,198,101,208,4,165,102,240,18,
133,105,162,0,134,103,134 :rem 82
193
Advanced BASIC
6 DATA 104,165,99,133,106,165,100,133,107,240,224,
240,114,24,165,106,105 :rem 198
7 DATA 3,133,106,165,107,105,0,133,107,230,103,208
,2,230,104,160,2,177,106 :rem 17
8 DATA 153,109,0,136,16,248,160,5,177,106,153,109,
0,136,192,2,208,246,170 :rem 7
9 DATA 56,229,109,144,2,166,109,160,255,232,200,20
2,208,8,165,112,197,109 :rem 16
10 DATA 144,10,176,34,177,113,209,110,240,238,16,2
6,160,2,185,112,0,145 :rem 142
11 DATA 106,136,16,248,160,5,185,106,0,145,106,136
,192,2,208,246,169,0,133 :rem 49
12 DATA 105,165,101,197,103,208,152,165,102,197,10
4,208,146,165,105,240,138,96 zrem 1
100 FOR J=49152 TO 49394:READ X:POKE J,X:NEXT
:rem 18
110 PRINT "USE SYS 49152:X TO SORT ARRAY X$(), FOR
EXAMPLE:-" :rem 163
1000 INPUT "SIZE OF ARRAY";N :rem 109
1010 DIM XY$(N) :rem 16
1020 FOR J=l TO N: XY$(J)=STR$(RND(1)*100): NEXT
:rem 66
1030 PRINT "SORTING.••" :rem 69
1040 SYS 49152:XY :rem 180
1050 FOR J=0 TO N:PRINT XY$(J):NEXT :rem 5
Program 6-27 is a version of the Bubble Sort, which operates on the pointers of
string arrays and produces no garbage collection delays. It operates in direct or pro
gram modes, but to save space it doesn't include a validation routine, so don't try to
sort an array that does not exist.
Speed is maximized if new items are added at the beginning of an array before
sorting. Note that the zeroth element isn't sorted—it can hold a title if desired. If the
255 in line 9 is changed to 1, strings are sorted from the second position; if it is 2,
sorting begins from the third, and so on.
Provided spaces pad out the strings correctly, it's possible to resort an array in
different ways. For an example, see the disk directory sorting program in Chapter 15,
which sorts on the initial of each program or file.
Strings are sorted in ASCII order. This can produce apparent anomalies: 12.3
comes before 2.87, which comes before 29.67. HELLO! precedes HELLO; and strings
0-25 emerge as 0, 1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 2, 20, 21, 22, 23, 24, 25,
3, 4, 5, 6, 7, 8, 9. Computer sorting often produces effects like these, but they should
not pose too much of a problem in practice.
In fact, programming can often be simplified by careful choice of the way in
which items to be sorted are arranged. For instance, a date held as YYMMDD auto
matically sorts into the correct order. Similarly, the fact that the comma has a lower
ASCII value than any letter insures that names held with commas sort correctly. Wil
liams, P. will come before Williamson, A.
Lines 1000-1050 in Program 6-27 provide a demonstration of the sort. Lines
194
Advanced BASIC
1000 and 1010 establish array XY$, and line 1020 fills the array with random nu
meric characters. Line 1040 calls the ML sort routine, and 1050 prints the values to
the screen. Note that you specify XY to sort array XY$—the $ is not used. If you
wish to add this sorting routine to your own programs, lines 1000-1050 should not
be included.
10 DATA 169,76,133,132,169,19,133,133,169,192,133,
134,96,255,0,254,15 :rem 84
11 DATA 0,252,72,138,72,152,72,173,136,2,141,148,1
92,166,197,224,4,208 :rem 122
12 DATA 12,228,197,240,252,173,13,192,73,255,141,1
3,192,173,13,192,240 :rem 116
13 DATA 38,224,5,208,61,228,197,240,252,160,0,140,
14,192,132,198,32,66 :rem 117
14 DATA 241,240,251,24,105,198,141,15,192,165,57,1
64,58,205,16,192,208 :rem 126
15 DATA 5,204,17,192,240,92,173,15,192,141,18,192,
162,128,160,128,165 :rem 74
16 DATA 197,201,3,240,22,201,5,240,200,173,14,192,
208,162,208,74,202 :rem 250
17 DATA 208,236,136,208,233,238,18,192,208,228,120
,162,0,181,0,157,76 :rem 75
18 DATA 193,202,208,248,162,79,169,160,157,0,4,202
,208,250,32,102,229 :rem 71
19 DATA 165,57,164,58,141,16,192,140,17,192,133,20
,132,21,32,207,192 :rem 18
20 DATA 162,0,189,76,193,149,0,202,208,248,32,108,
229,88,104,168,104 :rem 31
21 DATA 170,104,76,179,227,224,6,208,137,142,14,19
2,228,197,240,252,208 :rem 180
22 DATA 180,32,19,166,160,1,132,15,177,95,240,67,3
2,44,168,234,234,234 :rem 122
23 DATA 200,177,95,170,200,177,95,197,21,208,4,228
,20,240,2,176,44,132 :rem 117
24 DATA 73,32,205,189,169,32,164,73,41,127,32,71,1
71,201,34,208,6,165 :rem 73
25 DATA 15,73,255,133,15,200,240,17,177,95,208,16,
168,177,95,170,200 :rem 30
26 DATA 177,95,134,95,133,96,208,181,96,234,234,23
4,234,234,16,215,201 :rem 141
195
Advanced BASIC
27 DATA 255,240,211,36,15,48,207,56,233,127,170,13
2,73,160,255,202,240 :rem 111
28 DATA 8,200,185,158,160,16,250,48,245,200,185,15
8,160,48,178,32,71 srem 36
29 DATA 171,208,245,96 :rem 70
100 FOR J=49152 TO 49483:READ X:POKE J,X:NEXT
:rem 17
110 SYS 49152 :rem 150
Program 6-28 puts the ML for TRACE into memory starting at $C000. Load or
type in a BASIC program and run it. As stated above, whenever you press fl, the
trace begins; f7 traces fast, f5 single-steps, and f3 waits for a keypress from 0 to 9
before continuing. At this stage f1 will turn TRACE off, leaving BASIC running nor
mally, but fl is still tested for, so tracing can be resumed at any time. SYS 58260
NEWs BASIC and turns off TRACE completely; SYS 49152 reinstates it if desired.
This combination of features offers maximum flexibility in examining BASIC
programs.
Programs with graphics may list illegibly, with some BASIC characters appear
ing as graphics; and programs using the function keys, of course, may present prob
lems. These are typical difficulties in designing TRACE routines.
TRACE works by wedging into BASIC. It performs various operations before
returning to BASIC, which as far as possible is untouched. First, the key fl is
checked, and if it's pressed, a flag is reversed. If this flag is off, the program control
is returned to BASIC. If the trace flag is on, f3 is checked, and, if pressed, a number
key from 0 to 9 is awaited. When the number is received, it is inserted into a delay
loop. Also, f5 is tested, and if the single-step flag is on, the program loops indefi
nitely waiting for f5. When this key is found, the program runs BASIC until it finds
a new line number. The new line is listed on the screen and the indefinite loop re-
entered. If i7 is pressed, the delay loop is bypassed, so BASIC lines are listed as rap
idly as possible. In this way, there is maximum keyboard control over the trace.
The program is not relocatable as it stands, but it isn't difficult for an experi
enced ML programmer to move it. If you disassemble the routine, note the routine at
$C083, which lists lines. This routine saves the entire zero page (so LIST can't cor
rupt any locations), homes the cursor and blanks the first two lines of the screen,
lists the line using a modification of LIST, and restores the zero page values and pre
vious cursor position.
UNLIST
This system command prevents LISTing of BASIC to reduce the risk of unauthorized
copying or modification. UNLISTing is successful in proportion to the difficulty of
acquiring detailed knowledge of a system. No widely sold microcomputer yet has
foolproof protection. Nevertheless, temporary and makeshift expedients may be bet
ter than nothing. A collection of suggestions follows. Note that disabling
RUN/STOP and RUN/STOP-RESTORE is dealt with earlier in this chapter.
Machine language routine to run BASIC. This method is given first because it
is usable by anyone, works with any memory configuration, saves normally, and is
very puzzling to the uninitiated. It also disables RUN/STOP and RUN/STOP- RE-
196
Advanced BASIC
STORE, so if the program has no errors, no explicit or implicit END, and no STOP
statement, it can't be stopped at all by a user with an unmodified 64. BASIC runs
normally but lists as 0 SYSPEEK(44)*256+23 without any further lines. To use this
routine, follow these steps:
1. Be sure that the program has no line numbered 0 or 1. Change the numbering if it
does.
2. Enter line 0, with no spaces, in exactly this way: 0SYSPEEK(44)*256+23
3. Enter line 1 with exactly 21 asterisks (or any other character) and no spaces, like
this* i*********************
7. Save the program, list it, and run it to be sure that UNLIST is working correctly.
Now show the result to a friendly hacker and see if he or she can list it.
Simple ML run. Here's another method, with an explanation of how it works.
Enter a program with no line 0 or 1, and add 0SYS2063 and l********** (ten as
terisks). Next, perform the following ten POKEs:
POKE 2063,169: POKE 2064,26: POKE 2065,133: POKE 2066,43
POKE 2067,32: POKE 2068,89: POKE 2069,166: POKE 2070,76
POKE 2071,174: POKE 2072,167
In addition to the above POKEs, POKE 2059,0: POKE 2060,0 to put end-of-program
bytes after line 0. This lists as 0 SYS 2063. It should run as normal. The ten ML
bytes disassemble in this way:
$100F LDA #$1A
$1011 STA $2B ; MOVES START-OF-BASIC TO THE TRUE START AFTER ML
$1013 JSR $A659 ; CLR SETS POINTERS
$1016 JMP $A7AE ; RUNS PROGRAM FROM START
The effect is identical to POKE 43,31: RUN. All that's needed is to add some
UNLIST features and disable RUN/STOP and RUN/STOP-RESTORE to get an
effective UNLIST.
Special characters in REM statements. Since characters in the same line after
REM don't affect a program's performance, there is plenty of scope for POKEing in
or otherwise entering confusing characters. See the discussion of REM earlier in this
section for some simple ideas.
Five leading tokens method. This method, once considered for commercial use,
causes a program's line numbers to LIST, but nothing else. It is easy to use. Add five
colons (or any five characters or tokens) at the start of every line of BASIC. Then
197
Advanced BASIC
add these lines to the program, choosing your own line numbers if 50000 to 50002
are already taken:
50000:::::S=PEEK(43)+256*PEEK(44): FOR J=l TO 9999
50001:::::IF PEEK(S+4)>0 THEN POKE S+4,0:S=PEEK(S)+256*PEEK(S+l): NEXT
50002:::::END
RUN 50000 will put null bytes into the start of each line. Upon trying to LIST, you
should see a set of line numbers and nothing else—but the program should work
fine. Next, simply delete lines 50000-50002 and the process is complete.
The following lines can put the colons back, so the lines will LIST again:
S=PEEK(43)+256*PEEK(44)
FOR J=l TO 1E8: POKE S+4,58: S=PEEK(S)+256*PEEK(S+1): IF S THEN NEXT.
With this method, about the best you can hope for is that users of your pro
grams haven't read this book. You can also set traps, like using :::NEW: or ::::X
before a variable, rather than five colons, before UNLISTing the program. If the pro
gram is made listable again but these entries pass unnoticed, the program will be
NEWed on running, or variable A may be mysteriously converted into XA.
Overlong lines. All of a line that is longer than about 250 characters cannot be
LISTed. LIST expects each line to be pointed to by a single-byte pointer and will
loop indefinitely if the line is longer. However, some other commands, like READ,
also fail to work.
To combine lines, replace the null byte at the end of each line with a colon (ex
cept the last one in the group), then move the lines down in memory to overwrite
the link addresses and line numbers. The very first link of the series must be set to
span the completed giant line, and all the later link addresses (which are now
wrong) must be corrected.
If the idea interests you, put the following routine at the beginning of a program
and run it. Type in two line numbers; when the program has finished they'll be
joined together. Each line number is printed as its line joins onto the first line se
lected; this ends up as a composite line, so the lines listed on the screen disappear
from the program.
198
Advanced BASIC
When this program is run, line numbers are printed, as is a value (see line 13)
which is the new, lower end-of-BASIC. It isn't necessary to POKE this in, but if you
wish to save memory, you can do so. If, for example, 4567 is printed, type in POKE
45, 4567 AND 255: POKE 46,4567/256:CLR. Be sure to type it correctly; otherwise,
there will be problems. Incorrectly linked BASIC benaves in odd ways and may
refuse to accept new lines or delete old ones. Remember not to include lines ref
erenced by GOTO or GOSUB, or lines with IF statements or REM statements, which
will cause later parts of the newly joined line to be bypassed.
Self-modifying BASIC. If a program has only a few GOTOs and GOSUBs, this
is an excellent way to get simple list protection. LIST needs a correct link address for
each line of the BASIC program. However, RUN doesn't, except to process GOSUB
or to GOTO a lower destination line than the current one (10000 GOTO 100).
You can make use of this to get another type of UNLIST. Type in some lines of
BASIC, PRINT PEEK(2049), and write down the value, then POKE 2049,255 or some
other random value. LIST will probably show garbage, but RUN should be satisfac
tory. Before a GOTO or GOSUB of the sort just described, you'll need to POKE 2049
with the correct value for the program, then afterward POKE in the wrong value
again.
VARPTR
VARPTR finds the location of any variable stored in RAM. Its main use is to investi
gate variables, exactly as in the first part of this chapter. Program 6-30 loads a ma
chine language routine which will find the starting location of a variable name,
whether simple or subscripted. To be conveniently usable with BASIC, it uses ROM
routines not only to find the variable, but (with LET) to assign the resulting address
to another variable.
After this is typed in and run, to put the ML into memory, the syntax SYS
828:AB$:L (for example) assigns to variable L the value of the address where AB$'s
seven-byte description starts in memory. Below is an example that finds and prints
the value of X.
199
Advanced BASIC
200 N=123
210 SYS 830:N:X
220 FOR J=X to X+6: PRINT PEEK(J);: NEXT
These lines print the seven bytes which store X. In the same way, pointers or
any string can be found, and so on. (Note that arrays move if new simple variables
are defined; if you're investigating arrays, be sure not to add variables after VARPTR
has found the current array position.)
This routine can't find TI, TI$, or ST, which are not stored as conventional vari
ables. The machine language for the VARPTR routine follows this flow:
JSR $0073 ; JSR CHRGET (IGNORES SEPARATING COLON)
JSR $B08B ; WITH JSR PTRGET FINDS THE VARIABLE
LDY $5F
LDA $60
JSR $B391 ; CONVERTS POINTER BYTES TO FLOATING-POINT
JSR $0073 : IGNORES COLON
JSR $B08B ; FINDS SECOND VARIABLE
STA $46
STY $47
LDA $08
PHA TWO ENTRIES ON STACK NEEDED
LDA $07 TO ASSIGN VALUE TO VARIABLE
PHA
JMP $A9BA ; EXIT THROUGH LET
200
Chapter 7
6510 Machine
Language
• Introduction to 6510 ML
Programming
• Description of the 6510 Chip
• 6510 ML Techniques
• Monitors for the 64
• Monitor Command Dictionary
• Assemblers for the 64
Chapter 7
203
6510 Machine Language
then the first instruction. After you press RETURN, the monitor will print the next
free memory location for you.
.A C000 LDA #$00 *S
.A C002 STA $0400 C~
.A COOS BRK —>
Press RETURN twice after typing BRK, to return to the period prompt. You can enter
the same program by typing a colon, followed by eight hexadecimal values.
.:C000 A9 00 8D 00 04 00 —any—
This puts the designated values into the eight memory locations from $C000 to
C007. Another way to do this is with the M command. Type M C000 C007 to dis
play the contents of those addresses, then cursor over and type in the new value for
each byte.
You'll find that .D C000 C005 disassembles the bytes, translating the contents of
memory back into mnemonics. At left is the address where each instruction starts; to
the right are the hexadecimal values which make up the instruction, and finally the
mnemonic. The Supermon D command always prints an entire screen of disassembly;
other monitors may display only the specified range of addresses.
Note that, looking at the six bytes of the program, the screen start $0400 is held
with the low byte first and the high byte second—with 00 preceding 04. This feature
is common to all three-byte commands of the 6510 and other 6500 series chips.
.G C000 executes this short program, then returns to Supermon. Its effect is to
print an @ symbol in the top left of the screen, unless the screen scrolls and loses it
or unless there was no character there already, so color RAM is the background
color, making the @ invisible.
This is an easy program to understand, since $0400 is the first screen position,
and POKEing 0 to the screen generates the @ symbol. In fact, we can read the ML
like this: Load the accumulator with 0 (the number zero), store the byte in the accu
mulator in $0400, then BRK (break) to return to Supermon. The accumulator is an
eight-bit location, and it can be loaded with any value $00-$FF; essentially, it is a
one-byte buffer. The above example, therefore, has the same effect as POKEing
$0400 with 0, using the BASIC command POKE 1024,0.
Here's another idea. If we cursor-up and alter the first line to LDA #$01, then G
C000 has the effect of POKEing a 1 into the screen top, so the letter A appears. To
make this change in the Supermon disassembly, type over the value 00 shown in the
middle of the screen, to the left of the mnemonic. Other monitors may let you
change the value in the mnemonic field. You can now put any character into any
screen location, after a certain amount of calculation to determine the address, and
with the screen POKE value from the Appendices.
From BASIC, FOR J=49152 TO 49157 PRINT PEEK(J): NEXT prints the six
bytes of ML in decimal form, much like Supermon's M command. ML programs can
be POKEd into memory as well, and Chapter 9 includes a program which converts
ML into BASIC DATA statements for that purpose.
To illustrate the fact that BASIC can POKE in and use ML programs, enter:
.A C005 RTS
(press RETURN twice)
.X
204
6510 Machine Language
The X command allows you to leave the monitor. Now that you are in BASIC, enter:
FOR J=l to 255: POKE 49153J: SYS 49152: NEXT
This prints all 256 characters in quick succession at the top left of the screen. Each
loop alters the ML program, then executes it in its new form. To disassemble the
program in its final form, enter:
SYS 8
.D C000
This illustrates how the second byte of the six in the sequence contains $FF, or 255,
the last value we POKEd in from BASIC. Note that your program is no longer the
same one that you first typed in. Beginners are ordinarily discouraged from writing
self-modifying programs (which change their own instructions as they run), because
they can be confusing and difficult to debug. Until you have gained more expe
rience, it is probably best to avoid self-modifying code.
This nine-byte program will disassemble with .D C000 C009 into exactly the
same form; try this to confirm that it was entered correctly. Entering .M C000 C009
gives this (the hyphens represent bytes that don't matter):
.:C000 A9 00 8D 00 04 8D 00 D8
.:C008 00
.G C000 executes the program; @ appears, at the top left; it is black because 0
indicates black in the color RAM. Cursor up and replace LDA #$00 with LDA #$02.
Now a red B will appear when you enter .G C000.
refers not just to address $0400, but to address $0400 plus the value of the byte in the
205
6510 Machine Language
X register. That is, the eight-bit value contained in X is added to the sixteen-bit ad
dress $0400, and the result is the address used in the command. Since X has eight
bits, the range of addresses must be within $0400 to $04FF in the example, with
similar figures applying to the color RAM area.
.A cooo LDA #$00 ;LOAD A WITH 0
.A C002 TAX ;TRANSFER A TO X
.A C003 STA $0400,X ;STORE ACCUMULATOR IN SCREEN + X
.A C006 LDA #$00 ;LOAD A WITH 0
.A C008 STA $D800,X ;STORE A IN COLOR RAM + X
.A C008 BRK ;BREAK
Now .G COOO prints @ in black, exactly like the previous program. The dif
ference only appears on cursoring up, and altering LDA #$00 to LDA #$05, for ex
ample. Executing this prints E in black in the fifth screen position past the @
symbol. And any value in place of $00 prints a character offset from the screen start.
Change BRK to RTS, type X to exit to BASIC, and enter:
FOR J=0 TO 255: POKE 49153J: SYS 49152: NEXT
This prints all 256 characters consecutively in black, filling the top part of the screen
and showing clearly how the index, X, operates. POKE 49159 with another color
value, say, 2 for red, to watch the effect of the ML at $C006.
With this ML in memory, .G COOO prints 256 characters in red in the top half of
the screen; it does this far faster than the equivalent BASIC version in Example 3,
taking about 1/200 second.
First, X is loaded with 0 and this is copied into A. (The TXA transfer uses one
fewer bytes than LDA #$00.) Using TXA insures that the offset X corresponds to the
character in A so that after the branch at $C00C, which is taken 255 times, the value
in the accumulator depends on the value in the X register. This shortcut depends on
the use of INX (INcrement X) to increase the value of the byte in the X register by
one. Note that the accumulator (A) value stored in screen memory cycles through
$00-$FF, but the A value stored in color RAM is always $02, so the color of each
206
6510 Machine Language
character stays constant. To understand this program fully, note the values in A and
X at each stage of the program; X increases until it is as large as eight bits can con
tain, at which point it increments from $FF to $00, while A alternates between the
identical, increasing, value of X and $02. (Incrementing an eight-bit register or mem
ory location past $FF flips the value back to $00; similarly, decrementing below $00
gives you $FF.)
At the point that the X register holds a value of 0, the program stops looping
back and executes the BRK instruction. This is because of the BNE (Branch if Not
Equal to zero) instruction. As long as X contained a nonzero byte, the program
branched back to the code at $C002. As soon as the value flips over to 0, no branch
occurs and the next instruction is executed.
Note that the branch command starting at $C00C occupies only two bytes, in
spite of looking as though it would take three bytes. It uses relative addressing,
meaning that if the branch is taken, execution resumes at the address of the follow
ing command plus the byte just after the branch command. The example adds the
offset value of $F4 to the address $C00E (it treats $F4 as negative, or -$0C, since
$F4 + $0C = $00 in a single-byte register). Since $C00E-$0C is $C002, it all
works fine. Don't worry if this arithmetic looks confusing; the monitor will calculate
the right offset value for you, as soon as you enter the destination address for the
branch. Note, however, that such branch commands can reach only 127 bytes for
ward or 128 back.
Now, .G C00F runs Example 4, cycling through the colors until the last color
(light gray) is reached. Because the subroutine is changed by this program, ,G C00F
behaves differently the second time. However, the point is that, like BASIC, sub
routines provide a powerful means of dividing programs into manageable chunks.
CMP (CoMPare) tests the byte in the accumulator with $10 (decimal 16), and if the
two are equal, a special flag called the zero flag is set. The BNE that follows checks
that flag, so if the value in the accumulator is not $10, the branch takes effect.
Comparisons can be followed by other branches than BNE or BEQ (Branch if EQual
to zero—if the zero flag is clear); the illustrations here are used for simplicity.
Because of the speed of ML, the colors on the screen are changed too fast to be
visible. As an exercise, you could add a delay loop after C00F JSR $C000, using up
time without performing significant processing work. Use the X and Y registers; Y is
207
6510 Machine Language
another eight-bit register in the 6510. Construct a loop within a loop, and use DEX
(DEcrement X) and DEY (DEcrement Y), each followed by BNE, so that X decrements
256 times for each decrement of Y. Remember that RUN/STOP-RESTORE generally
returns you to BASIC if your program doesn't work.
The 6510 has 13 addressing modes. Most are easy to understand, but a few are more
difficult. Disassembly treats a given byte in the same way every time, once it has
determined the byte is an instruction; 8D rrt/y is always treated as STA yyxx. In
other words, this is implicit in the chip? Whenever 8D is encountered as an instruc
tion, the following pair of bytes is considered to be an address in low/high byte or
der. A disassembler therefore prints STA in place of 8D and follows it with a 16-bit
address.
Most addressing modes process the contents of memory locations, rather than
using explicit numeric values. This is invaluable in dealing with RAM and ROM
where the processor often is mainly concerned with arranging blocks of RAM. For
instance, in the short programs above, we changed the contents of memory locations
beginning at $0400.
All 6510 instr^tions are^ejther oner two, qy thre^ bytes long. The following dis
cussion examines each type.
Single-byte instructions. Single-byte instructions cannot reference either ad
dress or data, and operate only on features within the 6510 chip itself. The phrase
addressing mode doesn't really apply since there is no address, but for consistency
these instructions are described as possessing implied addressing (the address can be
thought of as an eight-bit location in the processor itself). Instructions which shift or
rotate bits in the accumulator, like ASL (Arithmetic Shift Left), are sometimes said to
use accumulator addressing. Nevertheless, you may encounter monitors which re
quire ASL A rather than just ASL.
Two-byte instructions. These instructions consist of an instruction followed by
a single byte. If this byte is treated as data, the instruction uses immediate mode.
This is usually indicated by a number sign (#) before the data (see the examples
above). Apart from loading the accumulator or X and Y registers with a value, this
addressing mode is used in arithmetic operations, logical operations, and
comparisons.
All other two-byte instructions refer to addresses, not data. There are six dif
ferent types. You have already used one of them, branches, in the previous section.
That addressing mode is usually called relative, because the offset indicates a
destination address relative to the current address.
208
6510 Machine Language
Zero page instructions. Five of the two-byte modes use zero page addressing. The
zero page is not a feature of the chip itself; it is the section of RAM in the 64 which
is wired to addresses $0000-^00FR However, the chip has the facility of enabling
the most significant byte"tobelgnored (since it is a zero anyway), so that LDA $34
can be written in place of LDA $0034, for example. This saves a byte, which short
ens programs and increases execution speed. For this reason, the first 256 bytes are
usually in great demand in 6510 programs, and machine language routines which
coexist with BASIC must be careful to take into account BASIC'S use of these
locations.
In the simplest type, the second byte specifies the address in zero page. For ex
ample, LDAJf^i^Q^^ Sf ^ddressi ,$O,QSfj^r l°cat|on
$55may .hold anyvahieiftom^ $06l^"$F^Noj^ne ditterence"'betwe'^''mi{S and the
^h^LBA^S^^^ih loads Jbhe value j^j^^*
209
6510 Machine Language
indexed by Y; that is, first the indirect address is calculated, then the value in the Y
register is added, and the resulting address is the object of the processing.
To show how this works, suppose again that the four bytes at the very start of
RAM contain 01 80 08 24. Now, LDA ($00),Y loads from $8001 + Y, so the 256
bytes from $8001 to $8100 can all be accessed, depending on Y's value.
Indirect indexing can be done only with the Y register. It's used for pure indirect
addressing when Y is $00, for such purposes as following the link pointers from one
BASIC line to the next; it is also used for processing blocks of data which aren't in
the zero page.
»The difference between indexed indirect and indirect indexed can be confusing
affirst. Put simply, indexed, jndirect—LDA ($00,X)—is often used to access a vector
table (a series of indirect addresses which point to special locations). By changing the
value of X, you can pick different two-byte addresses from the table, and use them
in processing.
In^aggygdoced—LDA ($00),Y—is a far more useful addressing mode; it lets
you access any memory location from $0000 to $FFFF. Typically, you will place the
desired base address in two free zero page locations, and index from there. To use a
common example, suppose that you have loaded locations $FB and $FC with 00 04.
Your base address is $0400, the first byte of screen memory. When the Y register
contains zero, LDA ($FB),Y loads the accumulator with the contents of $0400. If Y is
$01, STA ($FB),Y stores tfie accumulator contents at $0401, and so on.
Three-byte instructions. Three-byte instructions in the 6510 always consist of
an instruction iolll6w5ci'By"a two-byte address. There ar^mi^njgtjiyreta^nnsinf the
address: absolute,)absolute indexed by X| absolute indexed by Yf ancTabsolute
210
6510 Machine Language
7 6 5 4 3 2 1 0
N. V 1 B D I Z c
2 1 0
3 1 B 1 c
6 V 1 2 Z
7 V 1 B
3 Z c
A N 1
4 I
B N 1 B
5 I c
E N V 1
6 I z
F N V 1 B
7 I z c
8 D
9 D c
A D z
B D z c
C D I
D D I c
E D I z
F D I z c
211
6510 Machine Language
Chapter 10 shows which flags are affected by each instruction. LDA, for in
stance, affects the N (negative) and Z flags, but no others. This process is automatic;
it's part of LDA and happens even if you don't need it to. However, a few instruc
tions are specifically for setting or clearing flags: CLC (CLear Carry) clears the C
(carry) flag to 0, and SEC (SEt Carry) sets C to 1.
The logic behind the use of flags can be difficult to follow at first. The V (over
flow) and N flags are tricky, while Z and I (the interrupt disable flag) are much
simpler. With practice, the programmer should find them easy enough or at least be
able to avoid the awkward ones. For instance, V is seldom used.
The N, or negative, flag (bit 7 of SR) is a direct copy of bit 7 of the result of
some other operation. Thus, LDA #$D3 loads $D3 into the accumulator, and since
$D3 is hexadecimal shorthand for binary 1101 0011 (which has bit 7 high), N is
turned on by this instruction. Some hardware ports are wired up to bit 7, so LDA
from the location sets or clears N to reflect the status of bit 7. N is used along with
BMI (Branch on Minus) or BPL (Branch on PLus), the branches being taken if N is 1
or 0, respectively. This special concept of negative is part of twos complement
arithmetic, which is discussed below.
The V, or internal overflow, flag (bit 6 of SR) is seldom used. Like N, it's re
lated to twos complement arithmetic and indicates typically that two numbers added
together give a result outside the acceptable range. See below.
The 1 flag (bit 5 of SR) is unused. Since it is always set to 1, it is referred to in
this book as the 1 flag.
The B, or break, flag (bit 4 of SR) is usually set only when a BRK instruction is
encountered. Its purpose is to enable a BRK instruction to be distinguished from an
interrupt, since both jump to the same address. The address is fixed in ROM. This is
a hardware feature of the 6510, discussed in greater detail later.
The D, or decimal calculation mode, flag (bit 3 of SR) changes the way the
processor handles bytes in general and selects the 6510's binary coded decimal
(BCD) mode of addition and subtraction, instead of the usual binary. The results re
semble ordinary decimal arithmetic. This concept is not a simple one. As an illustra
tion, consider adding 35 to 97. In hex, the result is $CC; in decimal mode, it is 32
with the carry flag set, identical to the normal decimal outcome. The 6510 automati
cally adds 6 to either nybble if a result exceeds 9. For more on BCD representation
of numbers, see Mapping the Commodore 64 and The Second Book of Machine Language
from COMPUTE! Publications.
The I, or interrupt disable, flag (bit 2 of SR), when set with SEI (SEt Interrupt
flag), prevents any IRQ interrupts from taking place—remember, this is the interrupt
disable flag. Chapter 8 explains these interrupts, with examples, but due to their im
portance in handling the keyboard, they are mentioned in other places as well. The
main reason for disabling interrupts is to prevent them from disturbing ML routines
which won't work properly if interrupted; for example, you would not want an inter
rupt to occur while you were changing the interrupt vector to point to your own ML
routine. CLI clears this flag.
The Z, or zero result, flag (bit 1 of SR) is set by most of the instructions which
set N. To derive Z, all eight bits of a result are ORed together; if this process gives a
value of zero, the Z bit is set to show a zero result. Otherwise, when the result is non
zero, Z is zero. The notes to BEQ and BNE in Chapter 10 expand on this.
212
6510 Machine Language
213
6510 Machine Language
crashed in otherwise infinite loops. IRQ is used by the 64 to read the keyboard,
among other things. Chapters 5, 6, and 8 discuss the software side of these hardware
features.
214
6510 Machine Language
Jumps. JMP acts like GOTO in BASIC. JSR acts like GOSUB, with RTS the
equivalent of RETURN. JSR pushes the current address plus two onto the stack, for
use when the subroutine is finished.
Logical operations. AND, EOR (Exclusive-OR the byte in the accumulator), and
ORA (OR the byte in the accumulator) perform binary logical operations on the
accumulator using immediate data or a byte in a specified memory location, retain
ing the result in the accumulator, and setting the N and Z flags. The BIT instruction
sets the Z flag just as AND would, but does not affect the contents of the accu
mulator; it also copies the sixth and seventh bits of the tested value into the V and N
flags.
No operation. NOP does nothing but take space. It is useful for testing, because,
for example, JSR instructions can be masked by inserting NOPs over the JSR and the
two subsequent address bytes.
Return. RTS returns to the instruction following JSR by pulling the stored return
address off the stack, and transferring program control to the next byte after the ad
dress. This has the effect of jumping to the instruction which follows the two-byte
address after JSR. RTI jumps to the address on the stack and also loads the status
register from the stack.
Rotate/shift. ROL (ROtate Left) and ROR (ROtate Right) act on the accumulator
and the C (carry) flag (a nine-bit rotation). For example, an ROL causes all bits in the
accumulator to move one position to the left; the leftmost bit (bit 7) is pushed into
the carry flag, and the old contents of the carry flag wrap around into the rightmost
bit of the accumulator (bit 0). ASL (Arithmetic Shift Left) and LSR (Logical Shift
Right) also involve the accumulator and C (but do not rotate C) so that bit 0 with
ASL and bit 7 with LSR ^re always set to 0. Flags N, Z, and C are set.
Stack operations. These are PHA, PHP, PLA, and PLP and are explicit opera
tions on the stack, but BRK, JSR, RTS, and RTI also use the stack. TSX and TXS
allow the stack pointer to be found and set, respectively.
Transfers between registers. Six instructions allow transfers between any two
registers Y, A, X, and S. The opcodes are TYA. and TAY, TAX and TXA, and TXS and
TSX.
Note: Not all of a 6510 machine language program consists of instructions;
tables of data are a common, and necessary, feature, and these can usually be identi
fied by the fact that they don't disassemble sensibly. Chapter 5 explains about such
tables. BASIC ROM starts with tables, including address tables (that is, tables of 16-
bit numbers), BASIC keywords, and BASIC messages.
Timing
All opcodes take a precise number of 6510 clock cycles; the faster the clock, the
faster the ML executes. The 64's chip runs at about one million cycles per second.
Table 7-2 summarizes timing in the 6510; most instructions are included in the first
column, but a few exceptional instructions are listed in the other columns.
215
6510 Machine Language
Stack PH=3, PL
RTS=6 RTI=6
BRK=7
2 (if no branch)
3 (if branch taken
+ lover page)
6510 ML Techniques
This section uses assembler-style listings in the examples. See the section on assem
blers for information. The following topics are discussed:
• Two-Byte Operations
• Testing the Range of a Byte
• Loops
• Shift and Rotate Instructions
• Logical Instructions
• Twos Complement Arithmetic
• Decimal Arithmetic
• Debugging ML Programs
216
6510 Machine Language
Two-Byte Operations
Incrementing two bytes. The best method to increment two bytes is illustrated
by the following routine:
INC LOBYTE
BNE CONT ;BRANCH UNLESS $FF JUST BECAME $00
INC HIBYTE ;ONLY NEEDED WHEN LOBYTE NOW IS $00
CONT ...
Adding two-byte pairs. The carry flag carries overflow from low to high bytes.
CLC ;START BY CLEARING CARRY
LDA LO1 ;GET FIRST LOW BYTE ...
ADC LO2 ;...ADD IT TO OTHER LOW BYTE
STA LO2 ;AND STORE RESULT
LDA HI1 ;GET FIRST HIGH BYTE ...
ADC HI2 ;...ADD IT AND CARRY TO OTHER HIGH BYTE,
STA HI2 ;AND STORE RESULT
In this example, LO2 and HI2 end up with the contents of LO1 and HI1 added
to them. Chapter 10 has another example.
Subtracting two-byte pairs. The carry flag (C) is set before subtraction (if it is
left clear, the result will be off by 1). If C is clear on exit, the result is negative—that
is, the amount subtracted was larger than the original two-byte amount.
SEC ;SET CARRY FLAG
LDA LO1 ;GET FIRST LOW BYTE...
SBC LO2 SUBTRACT OTHER LOW BYTE
STA LO2 ;STORE RESULTS LOW BYTE
LDA HI1 ;GET FIRST HIGH BYTE...
SBC HI2 SUBTRACT OTHER HIGH BYTE AND CARRY FLAG COMPLEMENT
STA HI2 ;STORE HIGH BYTE OF RESULT.
217
6510 Machine Language
After exiting Supermon, the following BASIC line can be used to test the ML multiply
routine.
10 INPUT X,Y: POKE 252,X: POKE 253,Y: SYS 49152: PRINT PEEK(252)+256* PEEK(253)
This can be tested from BASIC by POKEing locations 252 and 253 with low and
high bytes of the numerator, POKEing 254 with the denominator, SYSing to 49152,
and printing PEEK(252) and PEEK(253) for the solution and remainder.
Comparing two-byte pairs. The trick is to avoid comparison instructions and
use SBC instead, which retains results as well as setting flags. Use the following
routine:
SEC
LDA LO1
SBC LO2
STA TEMP TEMPORARY STORE
LDA HI1
SBC HI2
ORA TEMP ;RESULT 0 ONLY IF A AND TEMP BOTH 0
Z is set if the contents of the first address equal those of the second; C is clear if
the contents of the first are less than the second. Therefore, BEQ, BCC, and BCS test
for =, <, and > respectively.
Other two-byte operations. It's often possible to write compact ML using the X
and Y registers to store two bytes. Suppose locations $FD and $FE contain an ad-
218
6510 Machine Language
dress to be decremented, then stored in locations $0350 and $0351. You can use the
following routine:
LDY $FE
LDX $FD
BNE NO
DEY
NO DEX
STY $0351
STX $0350
Loops
Loops generally use X or Y as a counter and often as an offset, too. There's some
room for timesaving in the design of loops. Also, it's worth checking over their logic.
It's easy to write loops which aren't quite correct, perhaps missing one of the values
at one end of the loop.
The short loop below puts the five bytes for the letters of the word HELLO on
the screen. There are two versions:
LDX #0 LDX #5
LOOP LDA TABLE,X LOOP LDA TABLE-1,X
STA $lE00,X STA $lE00,X
INX DEX
CPX #5 BNE LOOP
BNE LOOP RTS
RTS TABLE .BYTE "HELLO"
TABLE .BYTE "HELLO"
219
6510 Machine Language
an extra difficulty; the LDA instruction cannot be executed with an X value of 0. This
explains why the decrementing version uses LDA TABLE—1,X instead of LDA
TABLE,X; if this were not done, the program would print a garbage character fol
lowed by OLLE.
Note that LDX #$04 : ... : BPL LOOP counts X down from 4 to 0, and the accu
mulator loads from the expected starting point. However, X values larger than $7F
won't cause a branch on BPL (because bit 7 is used to indicate a negative number
with the BPL and BMI instructions), so it's best to avoid BPL at first.
Looking at longer loops, there are again several possible methods. Suppose 512
bytes are to be moved into color RAM from $C000. Different approaches are shown
below:
(A) (B) (C)
LDA #$00 LDY #$00 LDY #$00
STA $FB LOOP LDA $C000,Y LOOP LDA $C000,Y
STA $FD STA $D800,Y STA $D800,Y
LDA #$C0 LDA $C100,Y INY
STA $FC STA $D900,Y BNE LOOP
LDA #$D8 INY INC LOOP+2
STA $FE BNE LOOP INC LOOP+5
LDY $00 LDA LOOP+5
LOOP LDA <$FB),Y CMP #$DA
STA <$FD),Y BNE LOOP
INY
BNE LOOP
INC $FC
INC $FE
LDA $FE
CMP #$DA
BNE LOOP
Loop B is the shortest and fastest. It moves bytes in pairs. The loop will obvi
ously get longer if several thousand bytes are to be moved, perhaps when ML has
been loaded into RAM from tape and needs to be put into its correct RAM area to
run.
Loop C is basically similar but uses self-modifying ML. In the example, the loop
becomes LDA $C100,Y : STA $D900,Y the second time around, then LDA $C200,Y :
STA $DA00,Y, after which the CMP test terminates the loop. Although this is fairly
straightforward, it has the drawback that the ML is different on exit from what it
was at the start. Thus, a second call to the ML gives different results (crashing the
computer in this case), one reason why beginners are often warned against using
self-modifying code.
Loop A is a general-purpose version, suitable in most cases; it's longer than the
others, because of the need to set up $FB-$FC and $FD-$FE with $C000 and $9600.
In each case, these examples assume that the loop ends at a page start address like
$DA00. Obviously, both bytes in the address can be compared if this doesn't apply.
Saving the zero page is sometimes a useful trick, perhaps to optimize ML run
ning with BASIC. TRACE (Chapter 6) does this to allow LIST and BASIC to work
together. The routines are simple enough but require 256 bytes of RAM protected
220
6510 Machine Language
from BASIC (usually, the top of BASIC is lowered). Use the following routine to
save the area, where STORE is the first byte of your protected area:
LDX #$00
LOOP LDA $00,X ; LOAD CONTENTS OF ZERO PAGE LOCATION
STA STORE,X ; STORE IN SAFE LOCATION
INX
BNE LOOP ; FINISH ALL 256 BYTES
Logical Instructions
AND and ORA act like BASIC'S AND and OR, except that only eight bits are in
volved. EOR (Exclusive-OR) doesn't exist in BASIC; the nearest thing is (A OR B)
221
6510 Machine Language
AND NOT (A AND B). As Chapter 11 shows, AND is used to mask out bits, ORA is
used to force bits high, and EOR is used to reverse bits. In each case, any combina
tion of bits can be chosen.
For example, assume you have a byte ($72) which you want to print as the digits
7 then 2. Store the byte, then shift it right four times. Then, AND #$0F to mask off
(erase) the leftmost bytes. ORA #$30 forces $30 into the byte to create the ASCII
value of a numeral ready for printing. Recover the original byte and repeat. This
way, both digits are correctly output.
An example of EOR may be helpful, too. EOR combines bits in repeatable pat
terns, and you can use this to generate a checksum for BASIC or ML programs,
which is helpful in verifying if a program is correct. The following version prints a
number 0-255 and will print the same number whenever the identical program
loads into the identical memory area.
LDA $2B ;COPY START-OF-PROGRAM POINTER
STA $FD ; INTO FD AND FE
LDA $2C
STA $FE
LDY #$00 ;SET Y TO 0
LOOP EOR <$FD),Y
INC $FD INCREMENT ADDRESS IN FD/FE
BNE NOINC
INC $FE
NOINC LDX $FE ;TEST WHETHER FE/FF YET EQUALS 2E/2F
CPX $2E ; 2E/2F, THE END-OF-PROGRAM POSITION
BNE LOOP
LDX $FD
CPX $2D
BNE LOOP
TAX ;END OF PROGRAM. NOW PRINT OUT A'S VALUE
LDA #$00
JMP $BDCD ;USING THIS ROM ROUTINE
All logical instructions (like the arithmetic instructions ADC and SBC) use the
accumulator. EOR #$FF is the equivalent of NOT A, since all the bits in the accu
mulator are flipped; every 1 bit is changed to 0, and vice versa.
The BIT instruction is different from the above three instructions; it sets flags
but doesn't alter the accumulator or any address. It may be of use when some loca
tion is to be tested logically while the accumulator must remain unchanged. The Z
flag is set if the accumulator and the operand of BIT together AND to 0, and bits 6
and 7 of the result are copied into the V and N flags, respectively.
222
6510 Machine Language
Bit 7, the leftmost (highest) bit, can be regarded as a sign bit. It takes one of two
values, with 0 designating a positive number and 1 representing a negative sign; the
seven lower bits in the byte indicate the actual value of the number. The N flag in
the status register is wired to be consistent with this scheme; when N=l, the num
ber is considered negative and BMI's branch is taken. If N=0, BPL is taken. These
branches operate whether or not you're using signed arithmetic.
A number and its negative must add to 0. It follows that a pair of numbers (say,
+ 7 and —7) can be represented by $07 and $F9, because these add to $00, and be
cause the second has its high bit set. The use of numbers like $F9 to represent neg
atives is called twos complement arithmetic, and $F9 is the twos complement of $07.
You'll find with experiment that the largest possible one-byte twos complement
number (%0111 1111) is 127, and the smallest (%1000 0000) is -128. These figures
are identical to the range available to branch instructions and show how a branch's
offset can be stored in just one byte.
Subtraction from 256 gives the twos complement. Another rule, which may be
easier to use, is to flip all the bits in the byte, and add 1. So, the twos complement
of %0101 0101 ($55) is %1010 1010 plus 1, or %1010 1011 ($AB). Again, $55 plus
$AB adds to $00, ignoring the carry flag. Note that $00 is its own negative
complement.
You can generate twos complement numbers with the following routine:
LDA NUMBER
EOR #$FF
CLC
ADC #$01
Since the sign can be stored elsewhere, this type of arithmetic isn't particularly
popular; however, the 64's BASIC integer variables (for example, X%) use a 16-bit
version of twos complement arithmetic in which the highest bit stores the sign, so
integers may range from -32768 to +32767.
The V flag is also associated with this type of arithmetic, showing that an over
flow took place into the sign bit. Consider addition, for example, where V is affected
by ADC. Numbers of opposite signs cannot overflow; even extreme values must fall
in the correct range. But if the signs are the same, overflow is possible. For example,
$44 + $33 gives %77, and V is clear, but $63 + $32 gives $95, which in twos com
plement arithmetic is considered negative; this addition results in V being set. Simi
larly, two negative numbers can appear to add to a positive result, and if this is the
case, V will also be set.
The condition of the V flag is in fact determined internally, by the chip, by
reversing the EOR of the sign bits (giving 0 if they match, 1 otherwise). V is set, in
other words, when the signs are the same; the result (incorrectly) shows a different
sign.
To reiterate, twos complement is an interpretation. Many programmers may
never use it, preferring to work in positive numbers. But you can't fully understand
the N and V flags without grasping the idea of negative bytes.
223
6510 Machine Language
Decimal Arithmetic
Decimal mode arithmetic, with the D flag set, packs two digits into each byte and
adds or subtracts in decimal. This example adds a four-digit number in locations $8B
(high byte), $8C (low byte) to a six-digit number in locations $8D (high byte), $8E
(middle byte), $8F (low byte), leaving the result in the three-byte number. Scoring in
games often uses such subroutines; a score is stored in the smaller location, the sub
routine called to total, and the result printed.
SED ;TURN ON BCD MODE
CLC ;CLEAR CARRY
LDA $8C ;ADD LOW BYTES,
ADC $8F
STA $8F ;STORE RESULT
LDA $8B ;ADD MID BYTES
ADC $8E
STA $8E ;AND STORE
BCC NOINC
INC $8D ;HIGH BYTE
NOINC CLD ;RETURN TO NORMAL MODE
The six-digit number can be printed by looping three times to select a byte, then
shifting it right, using AND #$0F followed by ORA #$30 to convert to ASCII,
outputting with JSR $FFD2, and repeating with the same byte unshifted.
It is often simpler to use individual bytes for totals of this sort. This example
uses the first five locations of the 64's screen to print the score of a game. Start by
putting the screen code for zero—$30(48)—into the first five screen locations. Then
put the score into $8B through $8F as, for example, 00 00 01 00 00 (to represent
100). The result appears directly on the screen.
LDX #$04 ;SET COUNTER FOR 4,3,2,1,0
CLC ;CLEAR CARRY
LOOP LDA $0400,X ;LOAD BYTE FROM SCREEN
ADC $8B,X ;ADD CORRESPONDING BYTE
CMP #$3A ;IS RESULT 10 OR MORE?
BCC CLEAR ;IF NOT, BRANCH,
SBC #$0A ;IF SO, SUBTRACT 10, LEAVING CARRY SET
CLEAR STA $0400/X ;UPDATE SCREEN BYTE
DEX ;COUNT DOWN TO NEXT BYTE
BPL LOOP ;BRANCH UNTIL X IS FF
This is fast and efficient. Change the value of X for more than or fewer than six
digits. This method does not use BCD mode.
Debugging ML Programs
Listed here are many errors common in 6510 ML programming. Program design
should be approached methodically, preferably from the top down, starting with the
writing or reusing of standard subroutines. Careful analysis of the code, perhaps
with flow charting, and testing with typical and abnormal data should insure a
sound program. Your program will be simpler to debug if you build it out of distinct
modules or subroutines that each perform a clearly defined task. Thus, when bugs
appear, you can locate the source of trouble by testing one routine at a time.
224
6510 Machine Language
Careless errors. Errors of oversight may remain undetected for a long time.
Examples include transcription errors (entering 7038 for 703B) and immediate mode
# errors (using LDA 00 instead of LDA #00). You might also use a wrong ROM ad
dress, perhaps one for a different computer, or make branch errors, especially with
simple assemblers where forward addresses must be reentered. Yet another possibil
ity is the use of a Kernal or other subroutine which alters A, X, or Y.
Addressing mode errors. These stem from confusing the order of low and high
address bytes, failure to understand indirect addressing modes, or attempted use of
indexed zero page addressing to extend above location $FF (LDA $AB,X always
loads from zero page, for any value of X). Indirect jumps may also cause problems;
JMP ($03FF) takes its address from $03FF and $0300, due to a bug in the
microprocessor.
Calculation errors. With addition, subtraction, and so on, do not forget to use
the proper flag instruction (CLC before adding, SEC before subtracting, and SED and
CLD with BCD math). Remember, too, that LDA #$02 followed by ADC $FD adds
the contents of location $FD to the 2 in the accumulator, but leaves $FD unchanged.
It's easy to forget that only the accumulator holds the result, and STA $FD may be
needed to return the answer to the desired location. Also, be careful to keep track of
the carry bit with shifts and rotates. That can be tricky, since C is easy to overwrite.
Status flag errors. The logic behind flags may cause difficulties for beginners,
who may not realize (for example) that AND #$00 is identical to LDA #$00. In
crementing from a value of #$7F to #$80 sets the negative flag. The following rou
tine stores the contents of KEY in LOCN, but STA sets no flags:
LDA KEY
CMP #$3A
BNE ERROR
STA LOCN
You might expect it to clear Z, but this is not the case. Z will remain set until cleared
by the execution of some instruction which affects that flag.
Stack errors. Generally, the number of stack pushes should equal the number of
pulls, and the order should match. For example, PHA:TXA:PHA usually requires
PLA:TAX:PLA to retrieve A and X. Stack errors frequently crash the computer by
transferring program flow to an address that contains garbage. Advanced pro
grammers often use the stack for temporary storage, but it is usually safer (and about
as efficient) to use other RAM locations for that purpose. You may find that you can
program for a long time without ever having to use the stack.
Errors in which RAM is overwritten. Programs or their data can be overwrit
ten by BASIC strings or variables, by tape activity, or by subroutines which happen
to access the BASIC pointers (including utilities like Supermon), to name but a few.
The program itself may be at fault: A loop may move some data it shouldn't, a
pointer may be updated while still in use so that it points temporarily to a wrong ad
dress, or a part of the stack may be used for storage but get filled by normal stack
activity.
225
6510 Machine Language
BASIC Monitors
BASIC programs which use PEEK and POKE to program in ML are slow, but they
do have advantages, particularly for beginners. They use familiar INPUT commands,
and can be loaded, run, stopped, and listed without difficulty. As they are BASIC,
they occupy the normal BASIC space in RAM, whereas ML monitors occupy un
familiar areas. They're easily modified; if you'd like decimal addresses with a dis
assembly, or nonstandard opcodes, these are easy to put in.
226
6510 Machine Language
doing this, and if you wish to use BASIC including string processing, you must
remember to lower the top of BASIC—POKE 56,150: NEW is fine. So the BASIC
loader is often the most convenient. Obviously, since loading it will overwrite BASIC
in memory, it's better to load Supermon at the start of a session. Like most BASIC
loaders of its type, repeated running of the BASIC loader will put a series of working
versions of the program next to each other in the top of memory, lowering the
pointer each time, and reducing memory available to BASIC.
Supermon won't normally load into the area starting at $C000, so it's suitable
when you wish to write ML into $C000, which is probably most of the time. It also
locates itself below cartridges at $8000, and is therefore easy to use when examining
ROM programs.
Supermon has these commands:
A, D,F, G, H, L, M, Rf S, T,
(Note: All the monitors discussed here have an ASCII table near the end that lists the
commands.)
It does not have an intelligent relocater, and its memory display command
doesn't print ASCII equivalents of memory, so you cannot scroll through a program
looking for keywords, for example. Disassembly clears the screen, then fills the page;
the result is tidy, but tiresome to move through, particularly backward. But it's still a
useful monitor.
CBM MON is supplied with Commodore's editor/assembler package in two
versions; both use forced loads, one at $8000 and the other at $C000. A call to the
start address enters the monitor—SYS 8*4096 or SYS 12*4096, respectively. Two
versions are supplied so ML can be written into either of the major RAM areas; for
example, the version starting at $8000 must be used when programming in $C000.
POKE 56,128: POKE 55,0: NEW protects the $8000 version against corruption by
BASIC strings.
It's easy to relocate CBM MON. For example, you may use these commands to
move the $8000 version to $1000:
.T 8000 9000 1000 STRAIGHT TRANSFER OF 4K
.N 1000 2000 9000 8000 9000 :ADJUST ADDRESSES BY ADDING 9000
.N 1E66 1E99 9000 8000 9000 :ADJUST ADDRESS TABLE BY ADDING 9000
227
6510 Machine Language
Screen scrolling may be erratic. Monitors with this feature generally use what
ever linked lines already exist, so spacing may be unpredictable, with occasional
skips of a line.
Interaction with BASIC. As we've seen, routines ending with BRK return to the
monitor after .G C000 or a similar command runs them. A routine ending with RTS
typically returns to BASIC. To run such a routine requires that you exit to BASIC
with .X and then enter SYS 12*4096 or something similar. You may find, in fact, that
some ML routines run correctly when called with SYS calls, but don't work from
within a monitor; JSR $BDCD (a ROM routine which outputs a number) has this ef
fect, because the monitor uses zero page routines used by the ROM routine.
After exit to BASIC, you may like to reset with SYS 64738. This leaves your
monitor in RAM, but completely resets pointers and the low part of memory, leaving
everything in order. If you use the above SYS, POKEs to lower the top of memory
will have to be reentered.
What to do if ML crashes. When the computer is caught in a loop or will not
respond for some unknown reason, try the following:
• Try RUN/STOP-RESTORE. If this works, enter a SYS call back to the monitor.
• If RUN/STOP-RESTORE fails (as it will with an X2 crash), the only recovery
procedure is a hardware reset switch, not standard to the 64, as explained in Chap
ter 5. This erases ML from $0 to $102 and from $200 to $400, but leaves ML
higher in RAM unaffected. If you have no reset switch, you'll have to turn the com
puter off and start over.
You may be able to avoid this problem more often if you fill RAM with zero
bytes using the .F command, thus increasing the chance that a wrong command will
end on BRK and return you safely to the monitor.
Getting started. If you're an absolute beginner, load your monitor and enter it;
for example, load, then run Supermon. Try assembling the short demonstration pro
grams at the start of this chapter, using the .A command, then run them with .G,
and disassemble again with .D—the full syntax is explained in the following list of
commands. You'll soon get the feel of it. Note that many monitors output their re
sults using $ to indicate hex, but won't accept a $ as part of the command format.
Don't be discouraged if your first ML programs crash the computer with
distressing regularity. ML instructions are very powerful, and work without the auto
matic error-checking that BASIC provides. Nearly every ML program contains a few
bugs at first, and correcting them is a normal part of the programming process, for
experts as well as beginners.
/^Assemble)
nfhe Assemble command converts 6510 mnemonics and data into the correct form,
inferring the addressing mode from the command's format and storing ML bytes into
229
6510 Machine Language
memory. Labels and other features of true assemblers aren't accepted. There's often
a read-back check in case RAM isn't there; try assembling at $A000 to see this.
Typing RETURN with no ML instructions following the address allows you to exit
the A mode. An example of the use of the A command follows:
.A C000 LDA #$00
.A C002 STA $0400
.A C004 BRK
.A C005
After you enter a line, many monitors will expand it to show the actual hex bytes
which make up the instruction. For instance, after entering the second line above,
you would see:
.A C002 8D 00 04 STA $0400
Screen editing can be used to alter addresses, opcodes, and operands already on
the screen. Cursor to the appropriate place, make the changes, and press RETURN.
C (Compare Memory)
Compare Memory reports any differences between two areas of memory. Syntax is
identical to T.
.C C000 C100 C800, for example, checks whether the bytes in $C000-$C100
match bytes from $C800 to $C900, and prints the addresses of nonmatching bytes.
^^Disassemble)
xfie Disassemble command translates the contents of memory into standard 6510
mnemonics, using $ and # to denote hex addresses and data. The format is compat
ible with that of the assembler. It cannot produce labeled disassemblies and lacks
some other features of true assemblers.
Supermon always prints as much disassembly as will fit on the screen, whether
you specify a single address or a range of addresses. The disassembly begins with
the first specified address.
On other monitors, .D A500 then RETURN disassembles a single address; if
your monitor allows scrolling, you'll be able to continue disassembly by moving the
cursor to the top or bottom of the screen.
.D A46E A471 disassembles between the two limits, producing an output as
follows:
A46E C8 INY
A46F F0 03 BEQ $A474
A471 20 C2 BD JSR $BDC2
Some monitors let you edit a disassembly by typing changes in the mnemonic
field. Supermon disassemblies can only be edited by typing over the hex bytes be
tween the address and the mnemonic.
FjfFill Memory)
Memory fills a region of RAM with identical bytes. For example, .F 033C 03FF
00 fills the tape buffer with zero bytes. This has no syntax or read-back checking, so
230
6510 Machine Language
if you enter it wrongly, nothing will happen and you'll have no warning of this. $EA
(NOP) is a useful space filler.
runs ML from Supermon. The command .G C000, for example, transfers control
to a program starting at location $C000. The G execution continues until a BRK
occurs (which returns to the monitor) or some other irregular event takes place. For
example, RTS may return execution to BASIC or the program may contain a mistake
and crash. The G command can also be entered without an address, to transfer con
trol to the current program counter address (see R command below).
HflHunt Memory)
This command reports all instances of a byte combination or string of characters be
tween two addresses. For example:
.H E000 FFFF 00 90
prints all Kernal uses of the word BASIC. H requires some care in interpretation. A
Hunt for 20 E4 FF will certainly find all instances of JSR $FFE4, but a Hunt for 21
DO, for example, may yield nothing, even though the address $D021 had been used,
because D000,X may have been used to address it. And while JMP $C100 can be
found with .H 4C 00 Cl, a branch command like BEQ $C100 cannot be located like
this.
I (Interpret Memory)
Interpret Memory prints addresses followed by eight ASCII characters per row (or
dots, where ASCII doesn't apply) and their hex equivalents. Some monitors include
this as part of the M command.
Tj(Load ML)
Load ML uses this syntax for tape and disk loads, respectively:
.L "NAME",01
X "NAME",08
Abbreviations are accepted, so .L " ",01 loads the next tape program, L "N*",08
loads the first disk program beginning with N. The program or data is loaded as a
block—after loading, it is not altered in any way.
(M/(Memory Display)
Memory Display prints addresses followed by eight hex bytes, including, with some
monitors, ASCII characters in reverse. Monitors which scroll allow examination of
large areas of ROM or RAM. For example, .M A09E A0EE displays 11 lines of BASIC
keywords, as they are stored in ROM. Readability is improved in lowercase mode.
Nonprinting characters are displayed as reversed periods. The addresses and bytes
can be altered, followed by RETURN to enter the new values.
231
6510 Machine Language
N (Number Adjuster)
Number Adjuster is a command which adjusts absolute addresses, such as sub
routine calls, within ML. It is usually used after moving ML as a block with T.
"Micromon-64" supports the N command. For a detailed description, including
instructions for relocating Micromon-64, see COMPUTERS First Book of Commodore 64.
P (Printer Disassembly)
Disassembly to a printer is supported by some monitors. When using a monitor that
does not support this command, enter OPEN 4,4: CMD 4 from BASIC, then enter
the monitor and type commands blind. The output from the monitor will be directed
to the printer instead of the screen. This will also work if you wish to dump the
bytes in memory with M. To recover the screen display after printing, use X to exit
the monitor, then enter PRINT#4: CLOSE 4. If your printer omits the last instruc
tion, specify an ending address a few bytes past the last byte you want to print.
/ Display)
displays the contents of the program counter (PC), IRQ vector, status register,
bytes in the A, X, and Y registers, and the stack pointer as they were on entry to the
monitor. Typically, any of these can be changed. When .G runs the program, the
modified contents are loaded into PC, IRQ, and so on, before actual running. In this
way, you can change IRQ to point to your own interrupt routine; try different values
of A, X or Y; or experiment with different flag settings in the status register.
(pSaveML)
Save uses the following syntax for tape and disk, respectively:
.S "ML",01,C000,C200
.S "ML",08,C000,C200
Unlike BASIC SAVEs, it's essential to specify the limits of memory to be saved.
These examples save memory from $C000 to $C1FF. The final byte is not saved, due
to the way the pointers in the machine execute the SAVE command; saving from
$E000 to $FFFF is therefore impossible. In some monitors, the device number de
faults to 8 if not explicitly included. Note that there may be no error message if a
disk drive is off.
<Vn<Transfer Memory)
Transfer moves a block of memory. The syntax is identical to C. For example, .T
0400 07E6 0401 moves a screen of bytes along one position. The end point of the
new block is implicit in the three parameters. See N for relocation of programs.
V (Verify)
Most monitors have no Verify command, but BASIC'S VERIFY can be used like this
by exiting the monitor and using BASIC'S VERIFY. Before using VERIFY from
BASIC, you'll need to change the start-of-BASIC and end-of-program pointers to
match the beginning and end of your ML program.
232
6510 Machine Language
<fi)<Exit to BASIC)
X is the command that allows for a safe return to BASIC.
2
4 ; ROUTINE TO AWAIT A KEY, TI
6 ;PONDING CODIi, USING TABLl
Q
These instructions and labels together make up the source code. Around the core
of familiar 6510 opcodes is a collection of symbols, some of which are punctuated to
resemble addressing modes. This code is stored with line numbers, in RAM or on
disk or tape as the source file. Source code may include equates, like SCREEN=
$0400, and may have a comment after each instruction to document the program.
Therefore, source code is usually much longer than object code, often 20 times as
long.
233
6510 Machine Language
The job of the assembler is to convert the source code intd object code—the ac
tual numbers which make up machine language instructions. Note that the object
code is a sequence of bytes identical to that produced by a simple disassembler. This
is necessary, of course, since the 6510 has precise requirements which any utility
program must respect. Object code is often stored on disk as an object file; this is a
machine language program and can be loaded into a specific place in memory and
run with a SYS call.
The versatility of assemblers is illustrated by the pseudo-opcode (a special assem
bler function) which assigns the starting address of the ML program. The starting ad
dress pseudo-op is always used at the beginning of the source code. The command
*=$2000 at the start of the source code causes the assembler to create the ML pro
gram starting at $2000. Simply changing the command to *=$3000, followed by
assembly, generates ML identical in its effect, but positioned to start at $3000. Object
code, on the other hand, isn't usually relocatable without some effort.
But the great advantage of source code is the fact that it can be edited. Inserting
extra instructions in the middle of a program is easy, because assembly simply re
calculates all the addresses and branches. In contrast, monitor users have to shift
parts of the program, alter addresses, and generally rewrite and recheck.
Assemblers also have the advantage of potentially giving ML a very readable
format, provided the reader has a good grasp of ML. Symbols like GETCHR and la
bels like FOUND make ML easier to follow than the object code; and comments
allow the programmer room for thorough explanation of the program.
Figure 7-1 is part of a routine which waits for A, B, or C to be pressed, then
jumps to a corresponding address, using the trick of pushing the destination address
less 1 onto the stack, then using RTS to jump. The column of comments helps in de
ciphering the program. Object code is obviously harder to follow than source code,
but there are reverse assemblers available, which take ML and insert labels. Of course,
it is impossible to reconstruct comments or the original labels.
234
6510 Machine Language
usually kept intact. Symbols are often limited in length to conserve space in the
symbol table.
If the assembler is directed to send the assembled ML to disk, clearly no RAM is
used. Many assembler packages include a monitor to allow disassembly, running un
der ML control, and other convenient functions. An assembler may also include a
text editor to facilitate the task of writing source code. Special loader and relocater
programs are helpful, too. One key to successful use of assembler packages, there
fore, is learning to manage the 64's memory efficiently.
Assembler Features
An assembler either reads a source file into RAM or operates on source text already
in RAM, converting it into object code on a command such as A, ASSEMBLE, or
OPT OO. Assemblers vary in the way they scan source code; some require precise
alignment in columns and signal errors if they don't find them, while others are
more tolerant. Line 18, containing START, may be rejected, because it seems to con
tain the opcode STA. Since standardization is limited, it makes sense to learn to use
just one assembler.
First-time assemblies without errors are rare. Unlike BASIC, which can run with
SYNTAX ERRORS remaining in the unused portions of the code, assembling is not
tolerant of errors. Often, removing errors becomes a goal in itself. The triumph of
achieving a no-errors message may cause the programmer to fail to notice that the
program doesn't do what it should. Because repeated assemblies are the norm, it's
desirable that assemblers and their source files should coexist in RAM; it saves disk
access time. Likewise, you can speed up the process of testing and revision by
assembling directly to RAM when possible, using the disk only to back up your
work and save the final product.
Assemblers for the 6510 typically allow for these features:
Labels. These mark addresses to which branches, jumps, or subroutine calls are
made. Often there is a specified maximum length.
Symbols. These are values like GETIN in the example which are explicitly set.
The assembler must be able to distinguish zero page symbols from others. In prac
tice, the terms label and symbol are often used synonymously.
Opcodes. Standard 6510 opcodes, like LDA.
Operands. These are symbols or arithmetic values punctuated in a standard
way. Line 22's LDX #2 could be written LDX #$2 (hex) or LDX #%00000010 (bi
nary). Line 42 has a symbol used in indirect addressing, but also shows the use of
simple arithmetic; many assemblers allow evaluations like this. Line 53 shows the
use of the quote to generate ASCII. LDA "A is equivalent to LDA #$41 on some
assemblers and is often more convenient. The constructions LDA #<ADDRESS and
LDA #>ADDRESS, loading the low and high bytes of ADDRESS, respectively, are
often used.
Comments. Generally these are signaled by a semicolon, which causes the
assembler to ignore the rest of the line.
Pseudo-opcodes (directives). These are important and, like symbols, essential to
assemblers. Formats vary, so what follows may not apply to your assembler.
Pseudo-ops are commands to the assembler, some of which have housekeeping
functions, like diverting output to a printer rather than to the screen. Others ease
235
6510 Machine Language
programming, allowing, for example, easy entry of ASCII bytes. They are called
pseudo-opcodes because they appear in the source code in the same place as
opcodes; often they begin with a period or some other special symbol, so the assem
bler's parser looks either for an opcode or a period or some other character on each
line. These are typical pseudo-ops:
*= (sometimes .ORG, meaning origin) sets the current address at which object
code should start. Line 12 in the example starts the assembly at $2000. Line 52 re
serves one byte, by adding 1 to the current address. Similarly * = *+500 reserves 500
bytes, and LDA #*—LABEL loads A with the difference between the then-current
address and an earlier label. The term star is often used to denote this symbol.
= (sometimes .EQU, meaning equates) assigns values to symbols. Equates are
usually collected at the start of source code, where they can be easily checked.
Usually, zero page equates must be at the start of the source code.
.BYTE allows bytes to be assembled; this is necessary for any kind of data table.
So .BYT 31,$EA,%00010001,"HELLO puts eight bytes, IF EA 11 48 45 4C 4C 4F,
into the object code.
.DISK NAME outputs object code to a disk file called NAME.
.END marks the end of the source code. Anything later is treated as comment.
.FILE NEXT instructs the assembler to load, then assemble the source file called
NEXT. This pseudo-op is essential for chaining the components of large ML
programs.
.LABELS causes a symbol table (a sorted table of all symbols with their values)
to be printed after the assembly.
.MACRO INC causes a macro to be inserted; see below.
.OUTPUT instructs the assembler to put the object code into RAM as it is
assembled so that it will be ready to run.
.PRINTER diverts whatever output is requested to a printer.
.SCREEN turns on output to screen. With .NS (for example) a part of the assem
bly can be selected.
.WORD puts two bytes into the object code, least significant byte first, to con
form with 6510 convention. Line 54 has an example.
Conditional assembly, library files, macros, and relocatable object files.
These are typical extras of good assemblers. Conditional assembly allows several ver
sions of the object code to be prepared. A simple example follows:
.IF TYPE-1 <
*=$8000
SCREEN=$0400 >
.IF TYPE-2 <
*=$C000
SCREEN=$8000 >
Depending on how the expression after IF evaluates, the source code is assembled at
different locations.
.LIB NAME loads and assembles a file, inserting it into the current assembly.
This is not the same as chaining, but permits a source file to be built from a group of
separate library source files.
236
6510 Machine Language
Assembler Packages
Here's a brief look at the different types of assemblers that are available for the 64.
Assemblers written in BASIC. Everything described so far can be carried out
by BASIC. For example, symbols can be stored in a string array, object files can be
written straight to disk, one byte at a time, and pseudo-ops like * can be im
plemented. The problem with using BASIC is that such assemblers are painfully
slow, and take up more RAM than those written in ML.
Assemblers with BASIC editing. Some cartridge-based assemblers use the
BASIC editor. When you turn the computer on with the cartridge in place, routines
to intercept BASIC are set up, and commands like &A, &S, and &L are used to as
semble, save, and load source files. It may not be possible to write the resulting ob
ject code to disk, but a monitor with the S (Save) command may be included to save
the RAM image as a normal ML program. The instant availability of the assembler is
nice, and some edit features may be present, in fact, like AUTO line numbering. A
programmer's aid utility may be compatible with the assembler, and this can help
edit, though clearly it couldn't be expected to automatically format its output the
way the assembler would like.
Another example of an assembler that uses the BASIC editor is Richard Mans
field's LADS assembler (published in The Second Book of Machine Language, from
COMPUTE! Books). LADS loads into memory beginning at $2AF8 (11000). Its sym
bol table is stored down from the start of its own code, and the BASIC source code is
stored, loaded, and saved in the usual BASIC area from $0800. The area after $C000
is free, and this could hold a monitor or RAM object code. The assembler recognizes
and expands tokenized BASIC keywords if these occur in the source file. The syntax
requirements of the assembler are relatively strict—LOOP LDA #0 needs exactly one
space between each item. LADS produces ML in nonrelocatable form which can
either remain in RAM or be saved to disk or tape as loadable ML. Of course, the
source code allows for locating the program at different places in memory, and the
text describes how to modify LADS, customizing it to your needs.
237
6510 Machine Language
238
Chapter 8
ML Methods
Specific to
the 64
• Kernal Routines
• BASIC ROM Routines
• Using RAM Under ROM
• Modifying BASIC
• Vectors
• Interrupts
Chapter 8
This chapter is a reference to the ROM of the 64, and a guide to using the vectors
that point to that ROM effectively. You can include your own ML routines that
wedge into the normal operations of the computer if you like, and this chapter will
show you how. Other chapters that include specific ML material are Chapter 6 (key
board, screen, etc.), Chapter 12 (graphics), and Chapters 13-17 (sound, tape, disks,
peripherals).
Kernal Routines
The Kernal is the essential core of ML routines that the 64 uses during normal op
eration, and it has a jump table pointing to the routines. While the specific addresses
of the routines may differ from one computer to the next—like in the VIC and 64—
the addresses of the jump table are supposed to remain constant between machines.
In theory, this will allow programs to work on several ROM versions of the same
machine and even on different Commodore computer models. In practice, consis
tency among different models is achievable only to a small extent, because so many
hardware and software differences exist between machines. Still, it does serve a use
ful purpose.
Commodore has upgraded the 64's Kernal ROM in the past, and more changes
are possible (see "Upgrading ROMs" below). ML programs that access Kernal
routines only through the jump table are likely to work correctly on machines with
updated ROMs; programs that jump into ROM routines at other entry points might
work differently (or not at all) after a ROM upgrade.
The jump table listed below is arranged in ascending order by memory location.
The Kernal appears less formidable if you note that more than half is concerned with
opening and closing files and input/output of characters. Table 8-1 lists input/out
put errors that may be returned by Kernal routines.
Note that values shown in parentheses in "The Kernal Jump Table" (below) are
two-byte vectors which contain addresses in standard 6510 low/high byte form. The
value ($0281) can be read as "the vector at $0281/'
241
ML Methods Specific to the 64
The Kernal message I/O ERROR #3 will be printed, as will the BASIC message ?FILE
NOT OPEN ERROR IN 10. Location 157 controls the printing of error messages; in
normal BASIC operation it contains 0, which suppresses Kernal messages.
Kernal Routine
Address Location Name Description
242
ML Methods Specific to the 64
243
ML Methods Specific to the 64
244
ML Methods Specific to the 64
245
ML Methods Specific to the 64
246
ML Methods Specific to the 64
247
ML Methods Specific to the 64
Using GETIN to fetch keyboard characters. The short example below shows a
method for echoing keypresses to the screen. In practice, more constructive uses are
likely. Note the loop branching back to JSR $FFE4; this is similar to the GET loop
waiting for a character. Note also the test for the * key, which allows an exit from
the loop.
LOOP JSR $FFE4
BEQ LOOP ;AWAITKEY
CMP #$2A ;A HOLDS BYTE. COMPARE WITH ♦
BEQ EXIT ;EXIT ON *
JSR $FFD2 ;CHROUT PRINTS TO SCREEN
BNE LOOP ;BRANCHES ALWAYS
EXIT RTS or BRK
GETIN alters X and Y registers, unlike CHROUT. Thus, while you can use X or Y in
a loop with CHROUT alone, you must use a temporary storage location as the
counter when using GETIN and CHROUT together.
Using CHRIN to fetch characters. The routine below shows how a loop inputs
successive characters using CHRIN. If you precede this short program by the cursor
position routine, you can simulate INPUT. The cursor will flash at the selected po
sition onscreen. The program prints the characters at the top of the screen to show
how CHRIN works. Note how ANDing the accumulator contents with $3F converts
the ASCII value into the correct screen display code. $FE is used as a temporary
store for the current offset, since X or Y can be altered by CHRIN. As with BASIC
INPUT, if you wish to validate a string being typed, GETIN is best, but CHRIN is
easier to use.
;POSITION CURSOR BEFOREHAND
LDA #$00
STA $FE ;COUNTER
LOOP JSR $FFCF ;CHRIN
CMP #$0D ;RETURN IS LAST CHARACTER
BEQ EXIT
LDX $FE
$FE ;BUMP COUNTER UP
#$3F ;CONVERT ASCII TO POKE VALUE
STA $0400,X ;STORE CHARACTER TO SCREEN
LDA #$00
STA $D800,X ;SET COLOR RAM
BEQ LOOP
EXIT RTS or BRK
248
ML Methods Specific to the 64
Using LOAD and SAVE. Examples are in Chapter 6 (BLOCK LOAD and
SAVE) and in the chapters on disk and tape. If the precise mechanism of these com
mands interests you, disassemble the routines, following the branches to tape or
disk. Tape LOAD at $F533 prints SEARCHING, loads a header, computes the start
and end addresses, prints LOADING, and continues with the data load. Disk LOAD
reads the first two bytes for its LOAD address.
Using OPEN and CLOSE. Chapter 15 contains disk examples.
Using READST. JSR $FFB7 loads A with the status byte, either RS-232 or
otherwise, depending on which device is used. This simple routine saves you the
trouble of remembering ST's RAM address.
Using SCNKEY. Chapter 6's PAUSE is an example of how this can be used.
The IRQ vector is redirected by altering ($0314) to point to some routine other than
$EA31, its usual destination. The new routine sets the interrupt disable flag (SEI), so
no further interrupts are allowed, and repeatedly reads the keyboard until some
predetermined keypress occurs. At that time, interrupts are enabled (CLI), then JMP
$EA31 carries on as though nothing had happened.
Using STOP. JSR $FFE1 then BEQ EXIT is an easy way to stop ML from the
keyboard. Without it, the RUN/STOP key is generally inactive. STOP is called after
each BASIC statement is executed in a normal RUN, which is why STOP works with
BASIC.
Using SETTIM and RDTIM. Both these commands are very simple. What's
usually more important is converting the result into a readable form. This ML rou
tine (non-Kernal) converts the clock's contents into a form exactly like TI$ (a string
of exactly six numerals, with leading zeros where needed) so that a quarter after
seven is 071500. The string is left in locations $00FF-$0104, as the demo shows by
storing it to the screen top. The six bytes can, of course, be edited and printed (for
example) as 07:15:00.
JSR $AF84 ;READ/SET CLOCK
STY $5E
DEY
STY $71
LDY #$06
STY $5D
LDY #$24
JSR $BE68 ; NOW TI$ IS SET UP IN 00FF-0104
LDX #$05 ; POKE SIX BYTES INTO SCREEN
LOOP LDA $00FF,X ; NOT $FF,X
STA $0400,X ; STORE TO SCREEN
LDA #$00
STA $D800,X ; COLOR RAM
DEX
BPL LOOP
RTS orBRK
249
ML Methods Specific to the 64
This can be useful when ML calls BASIC; see UNLIST in Chapter 6 for an ex
ample. Remember that it's sometimes easier to include some BASIC along with ML,
particularly with tricky programming involving arrays or file handling, which can be
more trouble to convert to ML than they're worth.
250
ML Methods Specific to the 64
isn't used in either example, of course. JSR $B79B and JSR $AD8A fetch the num
bers. There's generally a choice of registers and memory locations for use in transfer
ring data between ROM routines. $B79B returns the value in both $65 and X;
$AD8A evaluates numeric expressions (for instance, VAL(X$)+6*X) and leaves the
result in FAC1, so there's less choice with this. Computed GOTO shows one
continuation with FAC1, namely conversion to integer format using only two bytes.
Calculations
This section explains how to carry out calculations in ML. With the help of Chapter
11, it will be clear that useful results are relatively easy to achieve, so you should
not be held back by problems requiring arithmetic.
Floating-Point Accumulator 1 (FAC1 for short) is a major location for number
work. Occupying six bytes from $61 to $66, the format is slightly different from the
five-byte variable storage of BASIC. Conversion from FAC1 to the memory format
(MFLPT, for short) rounds off the extra bit.
FAC storage can be si^jnarize^J^EMMMMS, having an exponent byte, four
bytes of data (mantissa), and jLsign. If E is set to 0, the number is treated as 0
regardless_pf M's contents. "
Some math routines (like negation) operate only on FAC1. However, many use
EAC2^ including all the binary operations. For example, when adding, FAC1 and
FAC2 are each loaded with a value; when the addition subroutine is called, the
numbers are totaled and the result left in FAC1.
FAC1 can be stored in RAM either by copying the six bytes for later use or by
using one of the routines around $BBC7. You'll see an example in the ML hex-to-
decimal converter later on.
Storing FAC1 in MFLPT format is, of course, part of BASIC, and many of Chap
ter 11 's routines are relevant to BASIC. As an example, $BD7E adds the contents of
A to FAC1, and $BAE2 multiplies FAC1 by 10. Between them, these routines allow
ordinary decimal numbers to be input and stored in FAC1 as each digit is entered.
The ROM routine at $B391 is an easy way to put integers from —32768 to
+32767 into FAC1 as floating-point numbers. The following routine loads 1 into
FAC1; A holds the high byte and Y holds the low byte of the number, in 16-bit
signed integer format.
LDY #1
/LDA #0
/JSR $B391
251
ML Methods Specific to the 64
The first byte controls the magnitude of the number. The others determine its
value, except for the high bit of the first data byte, which handles the sign. This is
handy if you wish to store floating-point numbers in memory. The program works
by directing USR to the following:
033C JSR $BBC7 ;FAC1 INTO MFLPT FORMAT AT $5C
033F LDX #$04 ;MOVE TO MORE PERMANENT RAM AREA
0341 LDA $5C,X
0343 STA $034A,X ;WHERE PEEKS CAN RECOVER
0346 DEX
0347 BPL $0341
0349 RTS ;BACK TO BASIC AFTER USR
Line 20's POKEs direct USR to $033C. Line 40 executes a USR command. First,
whatever number was input is converted to FAC1 format. Then BASIC jumps to
$0310, where it finds JMP $033C. Here, FAC1 is rearranged in RAM, and its five
bytes are moved from their temporary storage (which would soon be overwritten)
into the tape buffer. After RTS, BASIC resumes and MFLPT can be PEEKed.
For example, suppose you want to evaluate — 10*X*X. Enter the following at
$033C:
033C JSR $BC0C ;COPY FAC1 INTO FAC2
033F JSR $BA30 ;MULTIPLY FAC1 BY FAC2; RESULT IN FAC1
0342 JSR $BFB4 ;NEGATE FAC1
0345 JSR $BAE2 ;MULTIPLY FAC1 BY 10; RESULT IN FAC1
0348 RTS
252
ML Methods Specific to the 64
Return to BASIC, then POKE 784,76:POKE 785,60:POKE 786,3 and PRINT USR(8).
You'll get —640, and so on. If you have no ML monitor, POKE the following num
bers using BASIC to locations 828-839: 32, 12, 220, 32, 48, 218, 32, 180, 223, 76,
226, and 218.
Routines can be strung together like this in many ways, though it's helpful to
know ML well enough to appreciate potential problems. For instance, add JSR
$BFED to calculate EXP of FAC1. Alternatively, use temporary storage areas. For in
stance, the following routine puts FAC1 into MFLPT form beginning at $57, then
multiplies FAC1 by the MFLPT number it finds starting at $57. In effect, it is simply
another way of multiplying a number by itself.
JSR $BBCA
LDA #$57
LDY #$00
JSR $BA28
USR is not a very important function, but as these examples show, it can be
useful in testing ML calculation routines.
Hex-to-Decimal Conversion
The program below is a longer program example using ML arithmetic that illustrates
several points. INIT sets FAC1 to 0 and stores 16 in MFLPT form in spare RAM (in
fact, in the random number storage area). GET not only fetches an individual charac
ter, but also flashes the cursor and tests for the RUN/STOP key. PROC is the
processing part; each digit is converted from ASCII ($30 to the character 0, for ex
ample), added to FAC1, and, if a further digit is wanted, multiplied by 16. PRINT
outputs the result.
INIT 033C LDA #$04
033E STA $FE ;COUNT FOUR DIGITS
0340 LDA #$00
0342 STA $61 ;FAC1 NOW ZERO
0334 LDX #$04
0346 STA $8B,X ;LOOP PUTS 16 IN
0348 DEX ;MFLPT FORM INTO
0349 BNE $0346 ;8B-8F (RND AREA)
034B LDA #$85 ;FOR REPEATED USE
034D STA $8B
034F LDA #$00
0351 STA $cc
0353 STA $CF ;CONTROL CURSOR
GET 0355 JSR $FFE1 ;TEST RUN/STOP KEY
0358 BNE $035F
035A LDA #$01 ;IF RUN/STOP PRESSED,
035C STA $cc ;FLASH CURSOR &
EXIT 035E RTS ;RETURN
035F JSR $FFE4 ;GET CHARACTER FROM KEYBD
0362 BEQ $0355 ;WAIT FOR NON-NULL CHR
PROC 0364 PHA ;SAVE CHARACTER ...
0365 JSR $FFD2 ;ECHO TO SCREEN...
0368 PLA ;RECOVER
253
ML Methods Specific to the 64
SYS828 accepts four-digit hex numbers and continues until the RUN/STOP key
is pressed. The routine is relocatable. For binary-to-decimal conversion, POKE 829,8:
POKE 844,130 after running.
Random Numbers
Random numbers are used in simulations and in games. From ML, the easiest
method is to call ROM routines, which have the advantage of being repeatable if
you want them to be. JSR $E0D3 is equivalent to RND(—X) and seeds the random
number storage area with a value dependent on FAC1. The reason RND of negative
integers is always very small is that the FLPT bytes are simply switched around.
$E0D3 can be used to seed a constant value. However, with ML it's quicker to
store your own seed value directly in $8B-$8F. JSR $E0BE uses a formula to cal
culate a new random number from the previous one, leaving the result in both FAC1
and $8B-$8F. The sequence is completely predictable.
JSR $E09E uses CIA timers to generate a true random number, except in the
sense that very short ML loops may start to show regularities.
Typically, during testing, a seed is chosen. Then $E0BE is used to give a repeat-
able sequence (this eases debugging). The seed is replaced by $E09E for use.
Suppose you want something to happen 10 times in every 256. All you need is
CMP #$0A, then BCC to branch when the accumulator holds 0-9.
254
ML Methods Specific to the 64
If you need a random number in ML within a fixed range, say, 0-20, the easiest
method is to use repeated subtraction (rather than to get a decimal, multiply by 20,
take an integer, and add 1):
RANGE CMP #$15 ;COMPARE WITH DECIMAL 21
BCC FOUND ;NUMBER IN RANGE 0-20
SBC #$15 SUBTRACT DECIMAL 21
JMP RANGE ;COMPARE AGAIN
FOUND CONTINUE ;A HOLDS 0-20 DECIMAL
255
ML Methods Specific to the 64
Series Calculations
All of the 64's mathematical functions are evaluated by series summation. Briefly,
the value to be converted is first put into a smaller range. Trigonometric functions,
for instance, repeat regularly, so their input values can be reduced (if large) by
subtracting multiples of pi. Then a series evaluation works out the function's value,
and finally an allowance is made for the initial scaling-down process.
In the 64, the ROM routine at $E059 sums the series. The following short ex
ample shows how:
LDY #$03
LDA #$40
JSR $E059
RTS
256
ML Methods Specific to the 64
A single long loop can't be used, because it sets the VIC chip wrongly. The pro
gram exploits the fact that POKE puts a byte into the RAM underlying ROM, even
when ROM is selected. It's quite slow because of the slowness of BASIC, taking
more than a minute to perform 16384 PEEK and POKE combinations. Program 8-2
uses ML to speed things up:
At this stage, POKE 1,55 and POKE 1,53 can be alternated with absolutely no
visible effect, since the two versions of BASIC are identical.
257
ML Methods Specific to the 64
easiest features of BASIC to alter. For example, BASIC keywords are stored from
41118, starting with END, and these can be changed. It's easiest to keep keywords
the same length as their original form, although it's possible (say) to redefine BASIC
with single-letter and other short keywords, allowing very long (but hard to read!)
lines of BASIC to be entered. Another example is the power-up message; a later pro-
grain shows how this can be altered as far as is possible.
Larger modifications. Significant adjustments to BASIC require some ML
knowledge and the information on BASIC'S structure given in Chapter 11. At the
simplest level, we can alter locations like $EAEA (delay between repeats) and $EB1D
(cursor countdown) to alter cursor control. At a more advanced level, Chapter 6
shows how Computed GOTO and MERGE can be introduced into BASIC, and how
the keyboard's tables can be redefined. These small adjustments are known as
patches; $E4E0 (Filename) and $E4EC (Color) are two patches added to more recent
64 Kernal ROMs, correcting a tape name bug and a screen color effect. As another
example, we can modify RUN to eliminate the test for RUN/STOP, the CONT line
updates, and the end-of-program test (so END becomes necessary) quite easily, with
a small speed increase of 3-1/2 percent. With BASIC in RAM, use these POKEs:
POKE 42960,160: POKE 42961,0: POKE 42962,177: POKE 42963,122: POKE 42964,208
POKE 42965,49: POKE 42966,24: POKE 42967,169: POKE 42968,4: POKE 42987,208
Upgrading ROMs
Earlier CBM computers had to have ROMs changed, at some expense, when
improvements were made to BASIC. With the 64 this is no longer necessary. New
versions of BASIC can be used as they become available. If the changes aren't too
great, a program with ML to move BASIC to RAM, and a series of values to POKE
into RAM to upgrade BASIC will be faster than loading the entire 16K from disk
or tape.
Many 64s have a version number 0; PRINT PEEK(65408) in the Kernal to see
this. Newer ROMs return 3. These have a few improvements: INPUT with a long
prompt string works correctly with wraparound to the next line, and the screen edit
bug is removed (where BASIC lines overrunning the bottom screen line, then back
spaced, crash). Also, like very early 64s, POKEs to the screen are visible after {CLR}
without needing color RAM POKEs.
If you'd like a different version of 64 ROM, an easy way to compare ROMs is
with Program 8-3 or a similar comparison routine:
258
ML Methods Specific to the 64
Program 8-3 compares Kernals, assuming these to have been saved with a mon
itor, and commands like .S "KERNAL",08,E000,FFFF, but BASIC ROMs can be com
pared, too. Program 8-4 can be used at the start of a session; it converts version 0 to
version 3 (there are no BASIC ROM differences):
New Languages
Languages radically different from BASIC which are supplied on disk rather than
cartridge require a forced LOAD into the BASIC area, then POKE 1,54, assuming the
Kernal doesn't need to be altered. This takes more time, of course, and is less
convenient than a cartridge. As an example, CBM BASIC 4 includes disk commands
(CATALOG, DOPEN, etc.). To put BASIC 4 into the 64 as nearly as possible re
quires that its 20K (BASIC 4 fills $B000-$FFFF) be relocated to fit the 64's space as
best it can ($A000-$CFFF, $E000-$FFFF, probably) and that the hardware references
be converted from VIAs to CIAs, and to apply to the VIC and SID chips. In this
way, some compatibility of software is possible, even down to matching zero page
and other locations, but complete compatibility is impossible. ML programs which
use some CBM entry points cannot work on the 64, for instance.
It's conceivable that a language like Apple BASIC might be transferable to the
64, but even if this were done, there'd be difficulties over disk drive and other hard
ware incompatibility.
It's worth noting a potential problem when loading new versions of programs
into the Kernal area. The command .S "ML",08,E000,FFFF doesn't save the very last
byte, so LOAD "ML",8,1 works perfectly except for the final byte. Since this byte
helps determine the IRQ vector, it's important that it should be set correctly.
259
ML Methods Specific to the 64
The first pair alters the 64's default screen color values; the last changes the color in
CINT. Now press RUN/STOP-RESTORE; RAM BASIC is retained and the new col
ors appear. RAM BASIC is made completely secure against RESTORE in this way.
260
ML Methods Specific to the 64
261
ML Methods Specific to the 64
Perhaps the easiest method would be to use READ and DATA statements to
POKE the bytes in. After this, SYS 830 will carry out the ML routine at $A000
(assuming it ends with an RTS) by putting 54 into 1, jumping to $A000, and putting
55 into 1 on return. In this version, the ML mustn't call BASIC routines, of course.
The digits 0 and 160 correspond to $A000 in low/high byte form. A routine at
$B055, for example, would need 85, 176 instead of 0, 160. All this, including POKEs
or LOADs of ML under BASIC, can be done from BASIC.
The following lines of ML allow access to RAM under BASIC ROM while keep
ing the Kernal in ROM:
LDA #$36
STA $01
; PERFORM PROCESSING HERE
LDA #$37
STA $01
6RK
You'll find that some ML monitors will allow you to assemble in the BASIC area
after $A000 if you use M 0001 0001 and enter 36, to turn off BASIC ROM. Don't try
to exit to BASIC without BASIC present—it'll crash!—and don't turn off the Kernal
ROM by putting $35 into $0001, or the keyboard and screen handling will crash.
Modifying BASIC
64 BASIC has a large number of vectors in RAM; these are addresses which BASIC
uses as it runs. If these vectors are altered, BASIC can be intercepted and new com
mands tested for and executed. Alternatively, old commands can be modified slightly
(or completely) as required.
The techniques are simple, but plenty of small problems await the programmer.
In particular, when you alter BASIC, any errors in the added ML are likely to pre
vent BASIC from working normally. Thus, in order to avoid tedious retyping, it's im
portant to save programs as they are written or to use a reset switch for emergency
recovery. All the methods use ML, but this need not stop you, since the 64 can do
most of the work.
262
ML Methods Specific to the 64
There are over 20 such vectors. In addition, the CHRGET routine at $0073
(which fetches BASIC characters) is accessible for programming. As you'll see, this
allows access to BASIC as it runs, so new commands can be added.
Alterations to CHRGET or to vectors called from BASIC are semipermanent.
Once in place, SYS 64738 or switching off and on will remove them, tilt otherwise
even RUN/STOP-RESTORE leaves them untouched. This is intentioiial. On the
other hand, RUN/STOP-RESTORE sets vectors used by the Kernal routines to the
default values.
First, consider examples involving vector alterations and use of wedges. Gen
erally, wedges are probably easier to program; there's only one subroutine to worry
about, and commands can be added almost indefinitely. However, because
tokenization isn't possible, short commands like @X or @Y are generally used.
BASIC vectors allow some effects to be achieved which aren't possible with
wedges—for example, modified LIST. Such large-scale modifications take prior plan
ning and are not for inexperienced programmers. Kernal vectors are easier to deal
with, in the sense that they give convenient access to commands but are not often
used. There are not that many occasions when you would want to reprogram OPEN,
LOAD, or SAVE. Again, Kernal vectors, like the three interrupt vectors, are all set to
normal by RUN/STOP-RESTORE.
The Wedge
To understand the wedge, first look at CHRGET, the RAM routine starting at $0073,
which fetches every BASIC character while BASIC runs:
CHRGET 0073 INC $7A ;ADDS 1 TO CURRENT ADDRESS
0075 BNE $0079 ;ADDS 1 TO CURRENT ADDRESS
0077 INC $7B INCREMENT
CHRGOT 0079 LDA CURRENT
007C CMP #$3A ;COLON (OR GREATER) EXITS
007E BCS $008A
0080 CMP #$20 ;SKIPS SPACE CHARACTERS
0082 BEQ $0073
0084 SEC ;ANYTHING FROM $30 to $39
0085 SBC #$30 ;CLEARS C FLAG;
0087 SEC ;ELSE C IS SET
0088 SBC #$D0
008A RTS
CHRGET is stored in ROM at $E3A2; SYS 58303 from BAStC moves it back to
RAM. This may be useful if you've altered CHRGET, but note that it NEWs BASIC.
A call to CHRGET returns with A holding the next BASIC character, C clear if an
ASCII numeral was found, and the zero flag set if either a colon or null byte was
found. JSR $0073 followed by BCC or BEQ is common in ROM, and BCC applies
when a line number (made of ASCII numerals) is read from a GOTO or GOSUB
statement.
ROM also uses JMP $0073. In this case, RTS uses the address it finds on the
stack, and in fact BASIC keywords are executed in this way. The 6510 requires that
the return address -1 be pushed on the stack. .
263
ML Methods Specific to the 64
Wedge Demonstration
If you replace $0073 INC $7A with $0073 JMP $C000, or some other jump address,
all ROM calls to BASIC characters can be intercepted before they are executed. This
allows us to test for and use new commands in BASIC. A wedge, once inserted, is
quite durable. As mentioned above, RUN/STOP-RESTORE, for example, leaves it
unaltered. That can be important. If your routine has an error, it may be impossible
to POKE in the correct values or enter a SYS call to replace the wedge, since BASIC
itself is behaving differently from usual.
Many utility programs use wedges. This example puts a JMP at $0073; note that
$0073 or subsequent addresses can be used and are sometimes better, since they
may allow another wedge to be used simultaneously. Some wedges test for JMP at
$0073 and allow for them. They also allow zero page RAM (typically $007F-$008A)
to be used in programs.
Program 8-6 adds the single command ! to BASIC. When it executes, the screen
colors are changed. When naming new commands, it's easiest to use a character that
doesn't appear in ordinary BASIC (like !, @, or &) as an identifier. If desired, it is
easy to add further commands, such as !R or !P (with specific functions of their
own), by getting the following BASIC character with JSR $0073 whenever ! is found.
However, to keep the example shorter, it adds only a single command.
264
ML Methods Specific to the 64
Note that! is accepted only in program mode. A line like 100! works perfectly,
but! on its own is an error. This is deliberate. It avoids commands being executed
while a program is being written, when they may not be wanted, although you
could obviously create a wedge (like the DOS 5.1 wedge) that only works in direct
mode. There are several tests for direct mode. TSX then LDA $0102,X to recover the
return address is one. Another is location $9D, which usually holds $80 in direct
mode. The test in the example simply checks the current address used by CHRGOT;
if it's around $0200, it must be a direct mode line.
The only peculiarity of BASIC syntax with wedges is the IF statement. IF X=l
THEN: PRINT "ONE" is correct as far as BASIC is concerned, but the colon can be
omitted. With wedges, the colon can't be left out.
How the wedge works. Program 8-6 loads the following ML into the 64:
SETUP LDA #$4C ;PUTS JMP $C00D INTO CHRGET
STA $73
LDA #$0D
STA $74
LDA #$C0
STA $75
RTS
WEDGE INC $7A ;MIMIC CHRGET
BNE INC
INC $7B
INC JSR $0079 ;A NOW HOLDS BASIC CHARACTER
CMP #$21 ;IS IT '!' ?
BEQ YES
NO JMP $0079 ;NO—JMP BACK TO CHRGOT. WEDGE UNUSED
YES LDA $7B ;YES—CHECK FOR DIRECT MODE
CMP #$02
BEQ NO ;DIRECT MODE—DON'T USE WEDGE
TYA ;PROGRAM MODE. USE WEDGE—
PHA ;SAVE X AND Y
TXA
PHA
INC $D020 ;EXECUTE '!' COMMAND; HERE WE
INC $D021 INCREMENT BORDER AND GROUND COLORS.
PLA PROCESSING OVER. RECOVER A AND X.
TAX
PLA
TAY
JMP $0073 ;CONTINUE BASIC
SYS 49152 activates the wedge. Note that the entire routine is relocatable, apart
from the address in SETUP. Long routines that won't fit the tape buffer can also be
put at the top of BASIC memory.
$C021 jumps to CHRGOT, not CHRGET. This means that! in direct mode is
treated as normal, generating 7SYNTAX ERROR if entered as a command, but in
cluded in BASIC otherwise. If $C021 jumps to CHRGET, there are no SYNTAX ER-
RORs, but the command becomes difficult to include in BASIC. Note as well that
$C031 jumps to $0073. Of course, it immediately jumps back to $C00D, but $0073
always relocates.
265
ML Methods Specific to the 64
Vectors
The main block of vectors starts at $300. ($0300) through ($030A) are vectors to
BASIC. ($0314), ($0316), and ($0318) are vectors to IRQ, BRK, and NMI. ($031A)
through ($0332) are vectors to Kernal routines, except ($032E) is unused.
Earlier RAM has a sprinkling of vectors, including ($028F), used by SCNKEY,
the keyboard-reading routine, which allows keys to be intercepted—see the "Func
tion Keys" definition and other keyboard redefinition programs in Chapter 6. Vectors
($0003) and ($0005) point to floating-to-fixed (and vice versa) number conversion
routines, but neither is called by ROM—either through oversight or perhaps because
the intention was to allow JMP ($0003) and JMP ($0005) to work on both the VIC
and 64 and possibly future machines.
BASIC Vectors
There are six BASIC vectors, each called from the address three bytes before the vec
tor's normal destination. For example, $A437 JMPs to ($0300), which is set normally
to $A43A. Although this seems like useless extra execution time, it is the basis for
the wedging technique. The vectors are listed here in order:
Now POKE 770,60: POKE 771,3. The effect is to move the vector to point to these
instructions:
LDA #$2A ;LOADWITH*
JSR $FFD2 ;OUTPUTIT
JMP $A483 ;CONTINUE AS USUAL
Now the cursor expecting input is preceded by *. In fact, you can tell when the rou
tine is called by the presence of the asterisk. IMAIN allows several possibilities:
automatic BASIC line numbering, output of some message or prompt, and automatic
LOAD and RUN, as Chapter 14 shows.
266
ML Methods Specific to the 64
72, 201, 58, 208, 10,169,13, 32, 210, 255, 169, 32, 32, 210, 104, 76, 26, 167
Now POKE 774,60: POKE 775,3. This simple routine compares the character to be
listed with the colon; if it finds one, it starts a new line and prints a space, so LIST
now puts every statement on a new line. (It doesn't test for colons within strings.)
This sort of thing is useful with printers and could include a test for output device
number 4.
Note that a SYS call is needed to alter the vector, because POKE is processed
using this and gets confused if one byte of the vector changes.
After RUN, LET is redefined so that LET 13, for example, sets the background
light green. LET 13 now, in effect, POKEs $D021 of the 64 with 13, but does this
much faster than POKE. FOR J=0 TO 15 :LET J :NEXT cycles the colors at great
speed. The extra ML is this:
HERE JSR $0073 ;FETCH NEXT BASIC CHR
CMP #$88 ;LOOK FOR LET TOKEN
BEQ LETFND ;BRANCH IF FOUND
JSR $0079 ;GOTCHR SETS FLAGS
JMP $A7E7 ;CONTINUE NORMALLY
LETFND JSR $B79B ;CALCULATE 1-BYTE BASIC PARAMETER
STX $D021 ;PUT IT IN VIC REGISTER (BACKGROUND COLOR)
JMP $A7EA ;CONTINUE, AFTER EXECUTION POINT
LET (and GO) and many rarely used mathematical functions lend themselves to
this treatment, and may be helpful in dealing with some of the more tiresome com
mands needing POKEs and PEEKs, such as when controlling the SID sound chip.
267
ML Methods Specific to the 64
inclusion in the vector table is to allow nonstandard terms, either string or numeric,
to be defined. Thus, hex numbers beginning with $ can be introduced into BASIC, or
binary numbers beginning with %. Examine the following assembly-style listing:
HERE JSR $0073 ;GET FIRST CHR OF TERM
CMP #$24 ;IS IT $?
BEQ YES ;IF YES, BRANCH
LDA #$00 ;IF NO, SIMULATE
STA $0D ;NORMAL BEHAVIOR
JSR $0079
JMP $AE8D
YES JSR $0073 ;GET FIRST CHR AFTER AND
;PROCESS. PUT IN FAC1
JMP $0073
To compute results in the range 0-65535, a modified FXFLPT routine has to be used,
as explained earlier.
Kernal Vectors
Twelve Kernal routines are vectored through RAM addressed from ($31 A) through
($332); all, except ILOAD and ISAVE, are called immediately after entering the
Kernal jump table. Open and close routines, routines to fetch and output characters,
and the RUN/STOP key test all have indirection and allow modifications to be
made to the Kernal even in ROM. LOAD and SAVE store address parameters before
entering their vectors. A short list of the vectors is given below:
($31A) IOPEN: Vector to Kernal OPEN Routine
($31C) ICLOSE: Vector to Kernal CLOSE Routine
($31E) ICHKIN: Vector to Kernal CHKIN Routine
($320) ICKOUT: Vector to Kernal CKOUT Routine
($322) ICLRCH: Vector to Kernal CLRCHN Routine
($324) IBASIN: Vector to Kernal CHRIN Routine
($326) IBSOUT: Vector to Kernal CHROUT Routine
268
ML Methods Specific to the 64
As a fairly simple example, using an asterisk as before, the routine below inter
cepts CHROUT and causes it to output an extra asterisk. POKE these values into 828
and the following:
72,169, 42, 32, 202, 241,104, 76, 202, 241
Now POKE 806,60: POKE 807,3 to alter the vector to $033C. The ML is listed
below:
PHA ; SAVE OUTPUT CHARACTER
LDA #$2A ; LOAD ASTERISK
JSR $F1CA ; OUTPUT ASTERISK, NOT WITH FFD2, OF COURSE
PLA ; RECOVER CHARACTER
JMP $F1CA ; OUTPUT IT, CONTINUE
Following SYS 828, any use of CHROUT prints an asterisk—including all BASIC
messages like READY, which appears as R*E*A*D*Y*.
Interrupts
Defaults for NMI, BRK, and IRQ vectors (Non-Maskable Interrupt, BReaK, and Inter
rupt ReQuest vectors) are permanently stored in the top six bytes of the 6510's
memory. With the 64, NMI jumps to $FE43, BRK to $FE66, and IRQ to $FF43. Hard
ware reset (see Chapter 5) has no indirect vectors apart from the optional cartridge's
start address.
269
ML Methods Specific to the 64
270
ML Methods Specific to the 64
271
ML Methods Specific to the 64
The next step is to write the background ML, which in our example will start at
$C00D and typically exit with JMP $EA31, the normal interrupt routine. Here are a
few things to keep in mind when programming with interrupts.
• A, X, and Y can be used independently of BASIC. Because the BASIC values at the
time the interrupt happens are saved automatically and restored on return from
interrupt, your ML is self-contained.
• The simplest termination of an IRQ routine is JMP $EA31. This is the usual address
in ($0314), and exit to it means that BASIC behaves exactly as normal apart from
the introduced ML. All exits must return properly, or BASIC will crash. It's not
essential to exit via $EA31, though. Note that RTI restores the status register to its
pre-interrupt value, so CLI isn't necessary.
• Keep in mind the effects of repeats. Time dependency is a little hard to get used to.
A command like DEC $FE in normal programs decrements the contents of $FE just
once, from (for example) 9 to 8. But in an interrupt-driven program, this command
decrements whenever interrupts occur, typically 60 times per second. This is how
the TI clock works (except that it increments). Clearly, this is a valuable feature.
• Polling means that, during each interrupt, locations are PEEKed to see if action is
required. Chapter 16 has an interrupt-driven program to read joysticks; at regular
intervals, the joystick hardware addresses are read in ML and transferred to a
convenient location. Processing is far faster than the BASIC equivalent. Another ex
ample is reading location $C5 (197) to see if a key is held down; unlike GET's once
only action, this allows keys to act as long as they are held down.
• POKEs into the background program can be a useful control if a program has sev
eral functions. To illustrate, here is a simple example:
LDA#0
BNE INTML
JMP $EA31
272
ML Methods Specific to the 64
If this is the first ML of an IRQ program, the branch will never be taken and
the effect is negligible. But POKEing a value other than 0 into the location follow
ing the LDA instruction will activate whatever ML has been put in at the location
labeled INTML by the assembler. So completely invisible ML interrupt programs
can be activated from BASIC.
• The speed of background programs can, of course, vary. Suppose we have a rel
atively slow routine, perhaps to fill a screen with graphics. Is there a chance that
the interrupt might itself be interrupted and the program crash? Since an IRQ inter
rupt, in effect, performs SEI whenever it occurs, background programs driven by
IRQ can be slower than 1/60 second. If every repetition is as slow as this, the nor
mal program will hardly get a chance to run.
NMIs have no disable flag; if interrupts occur faster than the background pro
gram takes to execute, the 64 will lock up.
273
Chapter 9
Mixing BASIC
with Machine
Language
• RAM Available for ML Routines
• Combining BASIC and ML
• Relocating ML
Chapter 9
While most programmers are happy to use BASIC, machine language (ML) offers in
creased speed and power. This short chapter explains how ML programs and data
can be incorporated into BASIC, and how BASIC can be used to load in and relocate
your ML routines.
277
Mixing BASIC with Machine Language
Suppose you've written ML starting at 49152. To use Program 9-1, load and run
it, entering the start (49152) and end addresses of the ML you want DATA state
ments made for, the line number of the first DATA statements (perhaps 0 or the
location of your routine), and a maximum line length, say, 60. If you aren't sure of
the end address, put in a figure that is too large, then edit the last few DATA lines.
When the program has finished creating the DATA, delete the lines of the
"DATA Maker" program; the DATA lines to store your ML in BASIC will be left.
You will need to add some BASIC to POKE in the data:
FOR J=SA TO EA: READ X: POKE J,X: NEXT
The above line (where SA is the starting address and EA is the ending address) or
something similar should work fine.
REM statements. ML can be stored in REM statements. To do this, enter a line
like OREMXXXXXXXXXXXXX. Now POKE 2054,238: POKE 2055,1: POKE 2056,4:
POKE 2057,96. Assuming that BASIC starts at $0801, SYS 2054 calls the following
ML, which these POKEs represent:
INC $0401
RTS
278
Mixing BASIC with Machine Language
This increments the character in the second column of the first row of the
screen. A complication is that the ML must not contain any null bytes, or on editing
these will be treated as ends of lines and spoil the ML. (You could use LDX #1:DEX
in place of LDX #0.) Another complication is that BASIC lines normally have a
maximum length of 80 characters. By adjusting the link addresses the limitation is
easy to overcome. A line treated in this way LISTs oddly, and mustn't be edited.
Using quotes after REM and starting at 2055 will cause the program to LIST without
keywords.
Strings. ML can also be stored as a string. The line ML$="4C48D2AAD191D3"
illustrates an alternative tq DATA that's sometimes encountered. It requires
modifications to the DATA writing program to find and print separate hex bytes.
This method saves space compared with ML stored as numbers, but is slower to de
cipher and POKE back in. Since the 64 has a lot of RAM, the earlier method is used
more often.
Block LOADs. Chapter 6 explains how to load a block of ML. For an example,
see Chapter 12's character editor which allows user-defined characters to be saved to
disk for use later. To bypass the 64's attempt to GOTO the first line after a program-
mode LOAD, you'll need something like the following line, which loads the ML file
only once:
0 IF X=0 THEN X=l: LOAD "ML",8,1
As we've seen, block LOADs save time compared with DATA READs and
POKEs, so this technique (or one of those following) is desirable with ML of any
substantial size.
Consolidated BASIC and ML. These programs, sometimes called hybrids, con
tain ML immediately after BASIC. BASIC LISTs normally, but since the three null
bytes marking its end are earlier than the end-of-BASIC pointer, there's space for ex
tra ML which doesn't show on listing. Programs like this can't be edited, or the ML
will be moved in memory and probably will not function properly.
An example of this method is 64 Term. The ML needed to run the 64 modem is
loaded as BASIC, but the BASIC is reduced to a single SYS command. In this ex
treme case, the only use of the BASIC is to run the ML, saving the user from having
to force-load and then use a SYS call. Some games include their graphics definitions
after BASIC and are hybrids in another sense.
Here is an explanation of how to alter an ML program so that it can be loaded
and run. Suppose the ML starts at $0810, just after the start of BASIC. If you have
the source code, you can reassemble the program at $0810, and some monitors have
an .N relocation feature. We want BASIC to LIST as 0 SYS 2064, our ML to start at
$0810 (2064), and both to be loadable simultaneously. The process is shown below
(a simple ML routine which changes the screen color serves as an illustration).
Step 1. With a monitor, like Supermon, load the ML which is located at $0810.
The sample program is shown after being loaded and examined with the M
command:
.M 0810 0810
.: 0810 EE 21 DO 60 00 00 00 00
279
Mixing BASIC with Machine Language
Relocating ML
Moving relocatable ML. Where several utilities might be required in RAM, it makes
sense to write them in such a way that they detect and fill the next highest available
space. Supermon and some of the utilities in Chapter 6 are written like this; they're
not dependent on being put into a fixed place in RAM. The techniques that follow
put ML into the top of BASIC, after first lowering the end-of-BASIC pointer by the
correct amount. These techniques work with any CBM machine. Since the 64 has
RAM available at other places than top of BASIC, it's possible to modify the method,
for example, to allocate $C000-$CFFF to ML routines, but some pointer other than
top of BASIC has to be chosen to locate the individual ML routines correctly. Load
ers to put utility ML into the top of BASIC can start with this:
100 T=PEEK(55) + 256*PEEK(56): REM T=ORIGINAL TOP OF BASIC
110 S=T-N: : REM EG S=T-50 LOWERS BY 50 BYTES
120 POKE 56,S/256: POKE 55,S-INT(S/256)*256: CLR: REM SET NEW TOP
130 S=PEEK(55) + 256* PEEK(56): REM RECOVER VALUE OF S=START OF ML
which lowers BASIC'S top by an amount specified in line 110 and sets S equal to the
start of the new area, ready for a loop of the type:
FOR J=S TO S+49: READ X: POKE J,X: NEXT
280
Mixing BASIC with Machine Language
To convert code into data which this program can use, enter the code into RAM,
then print or write out the disassembled version. (A disassembler giving decimal val
ues of locations is helpful.) Next, mark all the absolute addresses which need chang
ing during relocation, replacing each by its offset from the end of the program; that
is, count backward from the end of program plus one, the result being a negative
number. See the example; this is easier than it might seem.
Convert the bytes into DATA statements and enter them. Note that each new
negative value replaces two bytes as a rule. Enter the value of N in line 110, then
test the loader. Run it several times, and check that each routine is independent and
correctly set up.
The example shown in Figure 9-1 is a short routine which fills the first 256
screen positions with the character stored as the last byte of the routine. It has a sub
routine call, a load from an absolute address, a store to an indexed absolute address,
and two branches. The branches, because of their relative addressing mode, relocate
without modification; so do the implied mode instructions, and the immediate mode
instruction. The store to the indexed absolute address does not have to be relocated
because the target address is not within the code to be relocated. The only addresses
to be relocated are those circled.
Counting back from the end, we find that $02AB is the thirteenth byte and
$02B7 is the first; so —13 and —1 respectively replace all occurrences of these two
addresses. The DATA statement is therefore:
10 DATA 32,-13,96,160,0,173,-1,153,0,4,200,208,250,96,32
281
Mixing BASIC with Machine Language
After relocation, the new starting address of the ML can be found with:
PRINT PEEK(55)+PEEK(56)*256
Relocating ML with ML. This technique is similar and much faster. Supermon
uses this method. ML of course has no out-of-range values in the way BASIC has
negatives; instead use null bytes as markers.
First, mark the absolute addresses needing relocation. Then add a zero byte im
mediately after each such address, and also after every genuine zero byte. This is
much easier to do with an assembler.
Third, replace the addresses by their displacement from the end of the program.
That is, replace the absolute addresses with the twos complement of the number of
bytes from the end of the program. (See Chapter 7 for information on calculating the
twos complement.) In the example above, we found that $02AB was 13 ($000D)
bytes from the end of the program. Using the twos complement of $000D, you
would replace the address with JSR $FFF3. Address $02B7 has a displacement of 1
byte, so we replace LDA $02B7 with LDA $FFFF.
Finally, put in a BASIC call (as shown earlier in this chapter) to the relocator
program, the relocator (from Program 9-2, below), and the ML you wish to relocate
(preceded by a unique marker byte not found anywhere in the ML to be relocated)
together in RAM, and save.
The relocator program works by starting at the end of the ML to be relocated
and working backward, moving the bytes one by one to the top of available memory
(as indicated by the pointer in locations 55 and 56). If a zero byte is found, the
relocator examines the next byte. If it is also a zero, then a zero byte is moved. But if
it is not a zero, then an additional byte is retrieved and these two bytes (the
displacement you calculated) are added to the top-of-memory address to compute
the proper absolute address for the relocated ML.
This continues until the marker byte—which separates the relocator from the
ML being relocated—is encountered. As presented in Program 9-2, 222 is used as
the marker. If the ML you wish to relocate contains the byte $DE (222), you'll need
to change this marker to some other value not found in your code. You can do this
by changing the 222 in line 16 to the desired value.
Finally, the relocator program lowers the value in the pointers to the top of
BASIC program storage (locations 55 and 56) and string storage (locations 51 and
52) to protect the relocated ML from BASIC. It then executes the ML by jumping to
the first byte of the relocated code. As with the BASIC relocator, the starting address
of the relocated code can be found with:
PRINT PEEK(55)+PEEK(56)*256
282
Mixing BASIC with Machine Language
For an example of how to use this relocator program, add the lines shown in
Program 9-3 to Program 9-2. This will create a machine-language relocated version
of the example routine from Figure 9-1. Line 30 creates a program file called RE
LOCATE TEST directly on the disk. Line 40 writes out the data for a BASIC SYS call
from line 5, line 50 writes out the ML relocator program from lines 10-16, and line
60 writes the byte that separates the relocator from the code to be relocated. Line 70
reads the ML to be relocated from the DATA statement in line 20. Notice how this
data differs from that created for the BASIC relocator in the previous section. When
you run the program, it creates a program on disk called RELOCATE TEST, which
you can load and run like a BASIC program. The RELOCATE TEST program will
move the routine to the top of available memory—adjusting addresses in the pro
cess—then lower the top-of-memory pointer and execute the routine.
To use this program for your own ML, replace line 20 with DATA statements
containing your ML (modified for relocation as described above), and change the
value of L (line 25) to reflect the number of items in your DATA statements.
283
Mixing BASIC with Machine Language
284
Chapter 10
Vocabulary of
the 6510 Chip
• The 6510 instruction Set
Chapter 10
This chapter lists each opcode with full details and helpful examples. The following
conventions have been used:
Read as "becomes." For example, A:= X means that the value in A becomes that
currently in X.
x, 0, and 1
Show the effect of an opcode on the status flags. An x means that the flag depends
on the operation's result; 0 and 1 represent flags which an opcode always sets to 0
or 1, respectively. All other flags are left unchanged.
$ and %
Prefix hexadecimal and binary numbers; where these are omitted, a number is
decimal.
A, X, and Y
The accumulator and the two index registers, X and Y.
M
Memory. This may be ROM in the case of 6510 load instructions (like LDA). Note
that immediate addressing mode (#) loads from the byte immediately following the
opcode in memory. All other addressing modes load from elsewhere in memory.
PSR (or SR)
The processor status register. Each bit of the register serves as an indicator (flag) for
a different condition:
bit 7: Negative (N) flag. Matches bit 7 of the result of the operation just com
pleted, which indicates negative numbers in twos complement arithmetic.
bit 6: Overflow (V) flag. Indicates an overflow (result too large for one byte) in
twos complement operations.
bit 5: Unused; always set.
bit 4: Break (B) flag. A BRK instruction was encountered.
bit 3: Decimal (D) flag. When set, all math is performed in decimal (BCD) mode.
bit 2: Interrupt disable (I) flag. When set, interrupts are ignored.
bit 1: Zero (Z) flag. Indicates that all bits are zero in the result of the operation
just completed.
bit 0: Carry (C) flag. Holds the carry bit for addition, or borrow for subtraction.
S
The location within the processor stack (locations $0100-$01FF) currently referenced
by the stack pointer.
SP
The stack pointer.
PC
The program counter; this is composed of two eight-bit registers, PCL (program
counter.low byte) and PCH (program counter high byte).
287
Vocabulary of the 6510 Chip
ADC
Add memory plus carry to the accumulator. A:= A+M+C
Flags:
N V — B D I Z C
X X X X
Operation: Adds together the current contents of the accumulator, the byte ref
erenced by the opcode, and the carry bit. If the result is too large for a single byte, C
is set to 1. If A holds 0 (each bit equals zero), the Z flag is set to 1; otherwise, it is 0.
If bit 7 in A is 1, the N flag is also set 1, to denote a negative value in A.
Uses:
1. Single-, double-, and multiple-byte additions. The carry bit automatically provides
for overflow from one byte to the next. For example:
CLC INSURES CARRY BIT IS 0
LDA $4A WE WISH TO ADD #$0A (10 DECIMAL) TO THE CONTENTS
ADC #$0A OF ($4A), I.E., THE DO0BLE-BYTE ADDRESS WHERE $4A
STA $4A IS THE LOW BYTE AND $4B THE HIGH BYTE
LDA $4B
ADC #$00 ADDS THE CARRY BIT WHERE APPLICABLE
STA $4B RESULT MUST BE STORED, ELSE IT WILL REMAIN ONLY IN A
288
Vocabulary of the 6510 Chip
Notes: In decimal mode, the zero flag doesn't operate normally with ADC because
of the automatic correction (adding 6) which the 6510 carries out. Testing for a zero
result requires (for example) CMP #$00/ BEQ—which is an extra step not required
in hexadecimal arithmetic.
The V flag is important if the twos complement convention is in use, and is set
if the apparent sign of the result (bit 7) is not the true sign. In decimal mode, V is
not used.
AND
Logical AND of memory with the accumulator. A:= A AND M
Flags:
N V — B D I Z C
X X
Operation: Performs logical AND of the eight bits currently in the accumulator and
the eight bits referenced by the opcode. When both bits are 1, the result is 1, but if
either or both bits are 0, the result is 0. The resulting byte is stored in A. If A now
holds 0—that is, all its bits are 0—the Z flag is set to 1; and if the high bit is set (bit
7 is 1), the negative flag N is set to 1. Otherwise, the flag is 0.
289
Vocabulary of the 6510 Chip
Uses:
1. Masking off unwanted bits, typically to test for the existence of a few high bits, or
to test that some bits are 0:
LDA $E081,X ; LOADS ACCUMULATOR FROM A TABLE OF CODED VALUES
AND #$3F ; TURNS OFF BITS 6 AND 7, LEAVING ALPHABETIC ASCII.
ASL
Shift memory or accumulator left one bit.
—| 76543210 [
Flags:
N V -- B D I Z C
X X X
Operation: Moves the contents of memory or the accumulator left by one bit po
sition, moving 0 into the low bit, and the high bit into the carry flag. The carry bit
therefore is set to 0 or 1 depending on bit 7 previously being 0 or 1. Z and N are set
according to the result; thus, Z can be true (that is, 1) only if the location or A held
$00 or $80 before ASL. The N bit can be set true if bit 6 was previously 1.
Uses:
1. Doubles a byte (though not in decimal mode). If signed arithmetic is not being
used, the result can safely reach values not exceeding 254, after which the carry
must be taken into account, often with ROL. This example uses A from 0 to 127
to load two bytes from a table of address pointers and store them on the stack:
ASL A
TAY
LDA ADDHI,Y
PHA
LDA ADDLO,Y
PHA
290
Vocabulary of the 6510 Chip
BCC
Branch if the carry bit is 0. PC:= PC + offset if C=0 *
N V — B D I Z C
After any comparison, C is clear if the value compared was smaller, but is set with
an equal or greater value. Bit 7 is irrelevant.
291
Vocabulary of the 6510 Chip
BCS
Branch if the carry bit is 1. PC:= PC + offset if C=l
Flags:
|n V — B D I Z C
Operation: Identical to BCC, except that the branch is taken if C=l and not C=0.
Uses: Identical to BCC. The choice between BCC and BCS at a branch point depends
on convenience. For example, suppose a hardware port is to be read until bit 0 is set
to 0. This routine:
LOOP LDA PORT ; READ LOCATION UNTIL XXXXXXX0
LSR A
BCS LOOP
BEQ
Branch if zero flag is 1. PC:= PC + offset if Z=l
NV-BD I Z c|
Operation: If Z=l, the byte following the opcode is added, in twos complement
arithmetic, to the program counter, which currently points to the next opcode. The
effect is to cause a jump, forward or backward, up to a maximum of +127 or -128
locations if the zero flag is set. If Z=0, the branch is ignored.
292
Vocabulary of the 6510 Chip
Uses:
1. Common as an unconditional branch. It may be used to make routines relocatable,
where the branch command isn't wide-ranging enough to span the program with
out an intermediate hop. The example inserts a couple of branches at a point
where they will never be taken by the ML immediately before, and so are avail
able as long branches.
LDA #$F5 : NONZERO VALUE
BEQ BACK ; THESE TWO BRANCHES
BEQ FWRD RELY ON Z=l
3. After comparisons. BEQ is popular after comparisons because it's easy to use. For
example, JSR GETCHR/ CMP #$2C/ BEQ COMMA looks for a comma in BASIC.
Notes: When a result is 0, the zero flag Z is made true (1). This point can be confus
ing. BEQ is usually read "branch if equal to zero," but when comparisons are being
made it could read "branch if equal." The zero flag cannot be set directly (there is
no SEZ instruction), but can be set only as the result of a location, register, or dif
ference becoming zero.
BIT
Test memory bits. Z flag set according to A AND M; N flag:= M7; V flag:= M6
Flags
N V — B D I Z C
M7 M6 X
Operation: BIT affects only three flags, leaving registers and data unchanged. Z is
set as if A AND M had been performed. If no bit position is 1 in both the memory
location and A, then A AND M is 0 and Z=l. Also, bits 6 and 7 are copied from
memory to the V and N flags.
Uses:
1 Multiple entry points for subroutines. The three-byte absolute address BIT is the
only instruction regularly used to provide alternative entry points for a routine.
The example loads A with RETURN, space, or a cursor-right depending on the en
try point into the routine.
293
Vocabulary of the 6510 Chip
If the routine is entered with JSR $033C, the accumulator is loaded with $0D and
the two BIT operations are then performed. These will change the settings of the
status register flags, but will not affect the contents of the accumulator. If the rou
tine is entered with JSR $033F, the routine begins with the A9 20 (LDA #$20) op
eration, and the contents of the accumulator will not be affected by the following
BIT operation. A JSR $0342 will leave $1D in the accumulator.
This is a compact way to load values into A (or X or Y). BIT $18, in the same
way, alters three flags, but if entered at the $18 byte clears the carry flag. Both
constructions are common in Commodore ROM, which explains why you may fre
quently see BIT instructions when you disassemble ROM.
2. Testing bits 7 and 6. BIT followed by BMI/BPL or BVC/BVS tests bits 7 and 6.
BIT $0D
BMI ERR
This example tests location $0D, with a branch taken if it holds a negative twos
complement value. Location $0D is in fact used to check for type mismatches. A
value of $FF there denotes a string, $00 a numeric variable, so BMI occurs with
strings.
3. Used as AND without affecting the accumulator. The following example shows
the AND feature in use. CHRFLG holds 0 if no character is to be output, and $FF
otherwise. Assuming the accumulator holds a nonzero value, BIT tests whether to
branch past the output routine, while retaining A's value.
LDA VALUE
BIT CHRFLG
BEQ NOTOUT
BMI
Branch if the N flag is 1. PC:= PC + offset if N=l
N V — B D I Z c|
Operation: If the N flag is set, the byte following the opcode is added to the pro
gram counter in twos complement form. The effect is to force a jump to the new ad
dress. The maximum range of a branch is -128 to +127 locations. When N is clear
the branch command is ignored.
294
Vocabulary of the 6510 Chip
Uses:
1. Testing bit 7 of a location. For example:
LOOP BIT PORT ;TEST BITS OF A HARDWARE PORT (PRESERVING VALUE
; IN A)
BMI LOOP ;WAIT UNTIL BIT 7 OF THE PORT IS 0
2. Conventional use. Like the other flags, N may be used in a purely conventional
sense. As an example, consider BASIC'S keyword tokens. All have values, in deci
mal, of 128 or more, which keeps keywords logically separate from other BASIC
and also permits instructions like this:
LDA NEXT ; LOAD NEXT CHARACTER INTO ACCUMULATOR
BMI TOKEN; BRANCH TO PROCESS A KEYWORD
; OTHERWISE, PROCESS DATA AND EXPRESSIONS
Notes:
1. It's important to realize that the minus in BMI (Branch if Minus) refers only to the
use of bit 7 to denote a negative number in twos complement arithmetic.
Comparisons (for example, with CMP) followed by BMI implicitly use bit 7.
Mostly, it is easier to think of this operation as "branch if the highest bit is set."
2. BPL is exactly the opposite of BMI. Where one branches, the other does not.
Flags:
NV — BDIZC
Operation: BNE operates exactly like BEQ, except that the condition is opposite. If
Z=0, the offset contained in the byte after BNE is added to the program counter, so
the branch takes place. If Z=l, the branch is ignored.
Uses:
1. In unconditional branches. BNE may be used in unconditional branches in circum
stances like those which apply to BEQ.
2. In a loop, where a counter is being decremented. BNE is very often used in a loop
in which a counter is being decremented. This is probably the easiest type of loop
to write. Watch the data's starting address, as offset 0 isn't executed by a loop like
this. The example prints ten characters from a table, their offsets being 10, 9, 8, ...
2,1.
295
Vocabulary of the 6510 Chip
LDX #$0A
LOOP LDA TABLED
JSR OUTPUT
DEX
BNE LOOP
Notes: When a result is nonzero, the zero flag, Z, is made false (set to 0). This can
be confusing. BNE is usually read "branch if not equal to zero." The result of a
comparison is zero if both bytes are identical, because one is subtracted from the
other, so "branch if not equal" is an optional alternative.
BPL
Branch if the N flag is 0. PC:= PC + offset if N=0
|N V — B D I Z C
Operation: BPL operates exactly like BMI, except that the condition is opposite. The
branch is taken to the new address given by program counter plus offset if N=0.
This means that if the result is positive or zero, the branch is taken.
Uses:
1. In testing bit 7 of a memory location. This code, for example waits until the accu
mulator holds a byte with bit 7 on. Such a location must be interrupt- or
hardware-controlled, not just RAM.
LOOP LDA TESTLOCN
BPL LOOP
2. Testing for the end of a loop. Where a counter is being decremented, and the
counter's value 0 is needed, this command can be useful. This simple loop prints
ten bytes to screen:
LDX #$09 ;X REGISTER WILL COUNT 9,8,7,... 1,0
LOOP LDA BASE,X ;//BASE// IS THE STARTING ADDRESS OF THE 10 BYTES
STA $0400,X ;START OF SCREEN (64)
DEX ; DECREMENT X
BPL LOOP ; BRANCH WHEN POSITIVE OR ZERO
296
Vocabulary of the 6510 Chip
Force break. S:= PCH, SP:= SP-1, S:= PCL, SP:= SP-1, S:= PSR, SP:= SP-1,
PCL:= $FE, PCH:= $FF
Flags:
N V — B D I Z C
1 1
Operation: BRK is a forced interrupt, which saves the current program counter and
status register values and jumps to a standard address. Note that the value saved for
the program counter points to the BRK byte plus two (like a branch) and that the
processor status register on the stack has flag B set to 1.
The IRQ service routine behaves like BRK. The break flag is a sort of designer's
patch so that BRK can be recognized as different from IRQ interrupts.
Uses:
1. BRK is mainly used with ML monitors. The ML stops when BRK is encountered,
and the vector points back to the monitor, typically printing the current values of
the program counter, flags' status register, A, X, Y, stack pointer, and possibly
other ML variables.
Whenever the 6510 encounters a BRK, it looks to locations $FFFE and $FFFF
for the address of the next instruction. In the 64's ROM, locations $FFFE and
$FFFF point to a routine beginning at $FF48. If the B flag is set, a jump is made
through a vector at location $0316, so the BRK handling routine can be modified
by changing the values in $0316 and $0317. Altering these locations to point to
the monitor is a function of initialization of the monitor; it isn't inherent in the
system that BRK behaves like that. BRK is valuable when developing ML
programs. ^T^
2. Monitors can be entered from BASIC if $0316-$0317 points to their start. POKE
790,0: POKE 791,96, for example, points this vector .to $6000, and SYS 13 (or a
SYS to any location containing a zero byte) enters a monitor there. Usually,
$0316-$0317 points to a ROM routine used by RUN/STOP-RESTORE which re
sets I/O and Kernal pointers. BRK is not widely used in ML that must interact di
rectly with BASIC.
297
Vocabulary of the 6510 Chip
BVC
Branch if the internal overflow flag (V) is 0. PC:= PC + offset if V=0
|N V - B D I Z c]
Operation: If V is clear, the byte following the opcode is added, as a twos com
plement number, to the program counter, set to point at the following instruction.
The effect is to jump to a new address. If V=l, the next instruction is processed and
the branch ignored.
Uses:
1. As a "branch always" instruction. For instance:
CLV
BVC LOAD
2. With signed arithmetic, to detect overflow from bit 6 into bit 7, giving a spurious
negative bit. This is rarely used since the sign of a number can be held elsewhere
so that ordinary arithmetic can be used without the complication of the V bit.
The following routine adds two numbers in twos complement form; the
numbers must therefore be in the range -128 to +127. CLC is necessary; other
wise, it may add 1 to the result. Overflow will occur if the total exceeds 127 or is
less than —128.
LDA ADD1
CLC
ADC ADD2
BVC OK
JMP OVERFL
3. Testing bit 6. BIT copies bit 6 of the specified location into the V flag of the
processor status register, so BVC or BVS can be used to test bit 6. For example,
the following routine waits until the hardware sets bit 6 of hardware location
PORT to 1.
F103 BIT PORT
F106 BVC $F103
298
Vocabulary of the 6510 Chip
BVS
Branch if the internal overflow flag (V) is 1. PC:= PC + offset if V=l
Flags:
|N V - B D I Z C
Operation: This branch is identical to BVC except that the test logic to decide
whether the branch is taken is opposite.
CLC
Clear the carry flag. C:= 0
Flags:
N V — B D I Z c
Operation: The carry flag is set to 0. All other flags are unchanged.
Uses: The carry bit is automatically included in add and subtract commands (ADC
and SBC) so that accurate calculations require the flag to be in a known state. CLC is
the usual preliminary to additions:
CLC
LDA #$02
ADC #$02
JSR PRINT
After CLC, this routine adds 2 and 2 and prints the resulting byte 4. In multiple-
byte additions, C is cleared at the start, but is subsequently used to carry through the
overflows if they exist.
299
Vocabulary of the 6510 Chip
CLD
Clear the decimal flag. D:= 0
Flags:
N V — 6 D I Z c
Operation: The decimal flag is set to 0; all other flags are unchanged.
Uses: Resets the mode for ADC and SBC so that hexadecimal arithmetic is per
formed, not binary coded decimal. Typically, SED precedes some decimal calculation,
with CLD following when this is finished.
Notes: BASIC uses no decimal mode calculations; when the machine is switched on,
CLD is executed and the flag is left off. ML monitors clear the flag on entry, too.
CLI
Clear the interrupt disable flag. I:= 0
Flags:
N y
B D I z c
Operation: The interrupt disable flag is set to 0. From now on, IRQ interrupts will
take place and be processed by the system.
Notes:
1. Interrupts through the NMI line (non-maskable interrupts) take place irrespective
of the I flag.
2. Typically, CLI is used after SEI plus changes to interrupt vectors. Often, CLI isn't
needed when used with BASIC, as a number of BASIC routines themselves use
CLI.
300
Vocabulary of the 6510 Chip
CLV
Clear the internal overflow flag. V: = 0
Flags:
N V — B D I Z C
Operation: Sets V to 0.
Notes: CLV is used in "branch always" instructions, for example, CLV/BVC. Unlike
C, V isn't added to results, so clearing is not necessary before calculations.
CMP
Compare memory with the contents of the accumulator. PSR set by A—M
Flags:
N V — B D I Z C
X X X
Operation: CMP affects three flags only, leaving registers and data intact. The accu
mulator is not changed. The byte at the address specified by the opcode is subtracted
from A, and the three flags N, Z, and C are set depending on the result. Thus, if the
accumulator holds the same value as the memory location, the result is zero and the
zero flag is set.
Within the chip, what happens is that the value in the accumulator is added to
the twos complement of the data. The result of this determines how the flags are set.
301
Vocabulary of the 6510 Chip
Uses:
1. With the zero flag, Z. This is the easiest flag to use with CMP. Z=0 after a CMP
means the two values were equal.
FF22 JSR $FFCF ;INPUT A CHARACTER
FF25 CMP #$20 ;IS IT A SPACE?
FF27 BEQ $FF22 ;YES. INPUT AGAIN
FF29 CMP #$OD ;IS IT RETURN?
FF2B BEQ $FF47 ;YES. BRANCH ...
FF2D CMP #$22 ;..NO. IS IT QUOTES? ETC.
This is part of a ROM routine to search through BASIC lines from the keyboard
buffer for particular characters, such as spaces, RETURNS, and quotes, which re
quire special handling.
2. With the carry flag, C. If the value of the byte is less than A or equal to A, the
carry flag is set; that is, C=0 (tested with BCC) after a CMP means that A<M,
while C=l (tested with BCS) indicates that A^M. Here, "less than" is in the ab
solute sense, not the twos complement sense. Thus, 100 is less than 190, although
in twos complement notation, 190 (being negative) would count as the smaller
number of the two.
The following example shows how a range of values may be tested for and
processed. Starting with the lowest ranges, comparisons are carried out until the
correct range is found. Each comparison is followed by a branch to Bl, B2, etc.,
where processing is carried out for 0-$lF, $20-$3F, and so on.
LDY #$00
LDA (PTR),Y
CMP #$20
BCC Bl
CMP #$40
BCC B2
3. With the negative flag, N. This is the trickiest flag to use with CMP. The reason is
that twos complement numbers are assumed, and if you are working with these,
CMP operates as expected, subtracting the memory from the accumulator. If both
numbers are positive or both negative, the N flag is set as though absolute
subtraction were being used, and in these circumstances BMI/BPL can be used.
But if the two data items have different signs, the comparison process is com
plicated by the fact that the V bit may register internal overflow. Generally, use
the carry flag.
302
Vocabulary of the 6510 Chip
CPX
Compare memory with the contents of the X register. PSR set by X—M
Flag*
N V - B D I Z C
X X X
Operation: CPX affects three flags only, leaving the registers and data intact. The
byte referenced by the opcode is subtracted from the contents of the X register, and
the flags N, Z, and C are set depending on the result. The value in X is not affected.
Within the chip, X is added to the twos complement of the data, and the result
determines how the flags are set.
Uses:
1. With the zero flag, Z. This flag tests equality.
LDX #$00
LOOP LDA $0278,X
STA $0277,X
INX
CPX $C6
BNE LOOP
The loop in this example is part of the keyboard buffer processing, showing
how the contents of the buffer are shifted one character at a time. Thus, $C6 is a
zero page location, updated whenever a new character is keyed in, which holds
the current number of characters in the buffer. The comparison provides a test to
end the loop.
2. With the carry flag, C. This flag tests for Xs>M and X<M.
LDX $FE
CPX #$27
BCS EXIT; IF X>39 (#$27)
The test routine is part of a graphics plot program; location $FE holds the
horizontal coordinate, which is to be in the range 0-39 to fit the screen. The
comparison causes exit, without plotting, when X holds 40-255.
3. With the negative flag, N. When X and the data have the same sign (both are 0-
127 or 128-255), then BMI has the same effect as BCC, and vibe versa. When the
signs are opposite, the process is complicated by the possibility of overflow into
bit 7. For example, 78 compared with 225 sets N=0, but 127 compared with 255
sets N=l. (Note that 225 = —31 as a twos complement number; thus,
78+31 = 109 with N=0, but 127+31 = 158 with N=l.)
303
Vocabulary of the 6510 Chip
CPY
Compare memory with the contents of the Y register. PSR set by Y—M.
Flags:
N V — B D I Z C
X XX
Operation: CPY affects three flags only, leaving the registers and data intact. The
byte referenced by the opcode is subtracted from Y, and the flags N, Z, and C are set
depending on the result. Apart from the use of Y in place of X, this opcode is identi
cal in its effects to CPX.
Notes: The major difference in addressing between X and Y is the fact that post-
indexing of indirect addresses is available only with Y. This type of construction, in
which a set of consecutive bytes (perhaps a string in RAM or an error message) is
processed up to some known length, tends to use the Y register.
LDY #$00
LOOP LDA (PTR),Y
JSR OUTPUT
INY
CPY LENGTH
BNE LOOP
DEC
Decrement contents of memory location. M:= M—1
Flags:
N V — B D I z c
X X
304
Vocabulary of the 6510 Chip
A zero page decrement takes five clock cycles to carry out; a successful
branch takes three (assuming a page boundary isn't crossed). The inside loop
therefore takes 8*255 cycles to complete, and the whole loop requires a little more
than 8*255*255 cycles. Divide this by a million to get the actual time in seconds,
which is about half a second.
DEX
Decrement the contents of the X register. X:= X—1
Flags:
N V — B D I Z C
X X
305
Vocabulary of the 6510 Chip
Operation: The value in the X register is decremented by 1, setting the N flag if the
result has bit 7 set, and setting the Z flag if the result is 0. As with DEC, the carry
bit is unaltered.
Uses: To count X in a loop. DEX is almost exclusively used to count X in a loop.
Since its maximum range, 255 bytes, is often insufficient, several loops may be nec
essary. This routine moves 28 bytes from ROM to RAM, including the CHRGET
routine.
LDX #$1C
NEXT LDA $E3A2,X
STA $73,X
DEX
BNE NEXT
DEY
Decrement the contents of the Y register. Y:= Y—1
Flags:
N V — B D I Z C
X X
Operation: The value in the Y register is decremented by 1, setting the N flag if the
result has bit 7 set (that is, is greater than 127), and setting the Z flag if the result is
0. As with DEC, the carry bit is unaltered.
Uses: Counting within loops. DEY, like DEX, is almost exclusively used to count
within loops. There are more opcodes which have indexing by X than by Y, so X is
more popular for this purpose. The example uses Y to count from 2 to 0.
LDY #$02
LDA (PTR),Y ;LOAD SECOND BYTE
DEY
ORA (PTR),Y ;ORA WITH FIRST BYTE
DEY
ORA (PTR),Y ;ORA WITH ZEROTH BYTE
BNE CONT ;ENDIFZERO
This inclusively ORs together three adjacent bytes; if the result is 0, each of the
three must have been a zero.
306
Vocabulary of the 6510 Chip
EOR
The byte in the accumulator is Exclusive ORed bitwise with the contents of memory.
A:= AEORM
Flags:
N V — B D I Z C
X X
The example shows how a single bit can be reversed. To reverse an entire
byte, use EOR #$FF; to reverse bit 7, use EOR #$80.
2. In hash totals and encryption algorithms. Hash totals and encryption algorithms
often use EOR. For example, if you have a message you wish to conceal, you can
EOR each byte with a section of ROM or with bytes generated by some repeatable
process. The message is recoverable with the same EOR sequence.
307
Vocabulary of the 6510 Chip
INC
Increment contents of memory location. M:= M+l
Flags:
N V — B D I z c
X X
Where IRQCONT is the interrupts usual routine, this allows some periodic rou
tine to be performed. Here, the zero page location $FE is used to count from $20
up to $FF and $00, so the processing occurs every 255—32=223 jiffies—about
every 3.7 seconds.
Notes:
1. The accumulator can't be incremented with INC. Either CLC/ADC #$01 or SEC/
ADC #$00 must be used; TAX/ INX/ TXA or some other variation may also be
used.
2. Remember that INC doesn't load the contents of the location to be incremented
into any of the registers. If the incremented value is wanted in A, X, or Y, then
INC $C6 must be followed by LDA $C6, LDX $C6, or LDY $C6.
308
Vocabulary of the 6510 Chip
INX
Increment the contents of the X register. X:= X+l
Flags:
N V — B D I Z C
X X
Operation: The byte in the X register is incremented by 1, setting the N flag if the
result has bit 7 set, and the Z flag if the result is 0. These flags may both be 0, or
one of them may be 1; it is impossible for both to be set to 1 by this command. The
carry bit is unchanged.
Uses: As a loop variable. INX is common as a loop variable. It is also often used to
set miscellaneous values which happen to be near each other, for example:
LDX #$00
STX $033A
STX $033C
INX
STX $10
INY
Increment the contents of the Y register. Y: = Y+l
Flags:
N V — B D I Z C
X X
Operation: The byte in the Y register is incremented by 1, setting N=l if the result
has bit 7=1 and setting Z=l if the result is 0. A zero result is obtained by in
crementing $FF. Note that the carry bit is unchanged in all cases.
Uses: To control loops. Like DEX, DEY, and INX, this command is often used to con
trol loops. It is often followed by a comparison, CPY, to check whether its exit value
has been reached.
309
Vocabulary of the 6510 Chip
JMP
Jump to a new location anywhere in memory. PC:= M
CO
JMP absolute 3
$6C (108 %0110 1100) JMP (absolute) 5
Flags:
N V — B D I Z C
The example is part of a subroutine which checks for a comma in a BASIC line; if
the comma has been omitted, an error message is printed.
Notes:
1. Indirect addressing. This is a three-byte command that takes the form JMP ($0072)
or JMP ($7FF0). A concrete example is the IRQ vector. When a hardware interrupt
occurs, an indirect jump to ($0314) takes place. A look at this region of RAM with
a monitor reveals something like this:
0314 31 EA 97 FF 47 FE
So JMP ($0314) is equivalent to JMP $EA31 in this instance. Pairs of bytes can be
collected together to form an indirect jump table. Note that this instruction has a
bug; JMP ($02FF) takes its new address from $02FF and $0200, not $0300.
2. A subroutine call followed by a return is exactly identical to a jump, except that
the stack use is less and the timing is shorter. Replacing JSR CHECK/ RTS by
JMP CHECK is a common trick.
310
Vocabulary of the 6510 Chip
JSR
Jump to a new memory location, saving the return address. S:= PC+2 H, SP:
SP-1, S:= PC+2 L, SP:= SP-1, PC:= M
Flags:
N V — B D I Z C
The example uses a Kernal subroutine which gets a character, usually from
the keyboard. The subroutine is a self-contained unit. Chapter 8 has examples in
which several JSR calls follow each other, performing a series of operations be
tween them.
2. Other applications. See RTS for the PLA/ PLA construction which pops one sub
routine return address from the stack. RTS also explains the special construction in
which an address (minus 1) is pushed onto the stack, generating a jump when
RTS occurs. Finally, see JMP for a note on the way in which JSR/RTS may be re
placed by JMP.
311
Vocabulary of the 6510 Chip
LDA
Load the accumulator with a byte from memory. A:= M
Flags:
N V — B D I Z C
X X
Operation: Loads the accumulator with the contents of the specified memory loca
tion. The zero flag, Z, is set to 1 if the accumulator now holds 0 (all bits loaded are
0's). Bit 7 is copied into the N (negative) flag. No other flags are altered.
Uses:
1. General transfer of data from one part of memory to another. Such transfer needs
a temporary intermediate-storage location, which A (or X or Y) can be. As an ex
ample, this program transfers 256 consecutive bytes of data beginning at $7000 to
an area beginning at $8000. The accumulator is alternately loaded with data and
written to memory.
LDX #$00
LDA $7000,X
STA $8000,X
DEX
BNE -9
2. Binary operations. Some binary operations use the accumulator. ADC, SBC, and
CMP all require A to be loaded before adding, subtracting, or comparing. The
addition (or whatever) can't be made directly between two RAM locations, so
LDA is essential.
LDA $C5 ; WHICH KEY?
CMP #$40 ; PERHAPS NONE?
BNE KEY : BRANCH IF KEY
3. Setting chip registers. Sometimes a chip register is set by reading from it; this ex
plains some LDA commands in initialization routines with no apparent purpose.
312
Vocabulary of the 6510 Chip
LDX
Load the X register with a byte from memory. X: = M
Flags:
N V — B D I Z C
X X
Operation: Loads X from memory and sets Z=l if X holds 0. Bit 7 from the memory
is also copied into N. No other flags are altered.
Uses:
1. Transfer of data and holding temporary values. These applications closely re
semble LDA.
2. Offset with indexed addressing. Register X has two characteristics which distin
guish it from A: It is in direct communication with the stack pointer, and it can be
used as an offset with indexed addressing. There are other differences, too.
Constructions like LDX #$FF/ TXS and LDX #$00/.../ DEX/ BNE are common.
LDY
Load the Y register with a byte from memory. Y:= M
Flags:
N V — B D I Z C
X X
Operation: Loads Y from memory and sets Z=l if Y now holds 0. Bit 7 from mem
ory is copied into N. No other flags are altered.
313
Vocabulary of the 6510 Chip
Uses:
1. Transfer of data and storage of temporary values.
2. Loops. Since Y can be used as an index and can be incremented or decremented
easily, it is often used in loops. However, X generally has more combinations of
addressing modes in which it is used as an index. Therefore, X is usually reserved
for indexing, while A and Y between them process other parameters. When in
direct addressing is used, this preference is reversed, since LDA (addr,X) is gen
erally less useful than LDA (addr),Y.
LDY #$00 ;X HOLDS LENGTH
LOOP DEX DECREMENT IT
BEQ EXIT ;EXIT WHEN 0
LDA (PTR),Y ;LOAD ACCUMULATOR
JSR PRINT ;PRINT SINGLE CHR
CMP #$0D ;EXIT IF
BEQ EXIT ; RETURN
BNE Loqp ;CONTINUE LOOP
LSR
Shift memory or accumulator right one bit.
6 5 4 3210
Flags:
N V - B D I Z C
0 X X
314
Vocabulary of the 6510 Chip
Uses:
1. Similar to ASL. This might well have been called arithmetic shift right. A byte is
halved by this instruction (unless D is set), and its remainder is moved into the
carry flag. With ASL, ROL, ROR, ADC, and SBC, this command is often used in
ML calculations.
2. Other applications. LSR/ LSR/ LSR/ LSR moves a high nybble into a low nybble;
LSR/ BCC tests bit 0 and branches if it was not set to 1. In addition, LSR turns off
bit 7, giving an easy way to convert a negative number into its positive equivalent,
when the sign byte is stored apart from the number's absolute value.
NOP
No operation.
Flags:
N V — B D I Z C
Operation: Does nothing, except to increment the program counter and continue
with the next opcode.
Uses:
1. Filling unused portions of program. This is useful with hand assembly and other
methods where calculation of branch addresses cannot be done easily.
2. When writing machine code. A large block of NOPs (or an occasional sprinkling
of them) can simplify the task of editing the code and inserting corrections. NOP
can also be used as part of a timing loop.
ORA
Logical inclusive OR of memory with the accumulator A:= A OR M
315
Vocabulary of the 6510 Chip
Flags:
N V — B D I Z C
X X
Operation: Performs the inclusive OR of the eight bits currently in the accumulator
with the eight bits referenced by the opcode. The result is stored in A. If either bit is
1, the resulting bit is set to 1, so that, for example, %0011 0101 ORA %0000 1111 is
%0011 1111. The negative flag, N, is set or cleared depending on bit 7 of the result.
The Z (zero) flag is set if the result is zero, and clear otherwise.
Uses:
1. Setting a bit or bits. This is the opposite of masking out bits, as described under
AND.
LDA #ERROR
ORA $90
STA $90
PHA
Push the accumulator's contents onto the stack. S:= A, SP:= SP—1
Flags:
|N V — B PI Z C|
Operation: The value in the accumulator is placed into the stack at the position cur
rently pointed to by the stack pointer; the stack pointer is then decremented. Figure
10-1 illustrates the position before and after the push:
316
Vocabulary of the 6510 Chip
$0100 $01FF
h STACK IN USE
1
SP (STACK POINTER)
Uses: This instruction is used for temporary storage of bytes. It may be used to hold
intermediate values of calculations produced during the parsing of numeric ex
pressions, to temporarily store values for later recovery while A is used for other
processing, for storage when swapping bytes, and for storage of A, X, and Y registers
at the start of a subroutine.
The example shows a printout routine which is designed to end when the high
bit of a letter in the table is 1. The output requires the high bit to be set to 0; but the
original value is recoverable from the stack and may be used in a test for the
terminator at the end of message.
LOOP JSR GETC ;GET NEXT CHARACTER
PHA ;STORE ON STACK
AND #$7F ;REMOVE BIT 7
JSR PRINT ;OUTPUT A CHARACTER
PLA ;RECOVER WITH BIT 7 INTACT
BPL LOOP ;CONTINUE IF BIT 7=0
PHP
Push the processor status register's contents onto the the stack. S:= PSR, SP:
SP-1
Flags:
V — B D I Z c
Operation: The operation is similar to PHA, except that the processor status register
is put in the stack. The PSR is unchanged by the push.
Uses: Stores the entire set of flags, usually either to be recovered later and displayed
by a monitor program or for recovery followed by a branch. PHP/ PLA leaves the
stack in the condition it was found; it also loads A with the flag register, SR, so the
flags' states can be stored for use later.
317
Vocabulary of the 6510 Chip
PLA
Pull the stack into the accumulator. SP:= SP+1, A:= S
Flags:
N V - B D I Z C
X X
Operation: The stack pointer is incremented, then the RAM address to which it
points is read and loaded into A, setting the N and Z flags accordingly. The effect is
similar to LDA. Figure 10-2 illustrates the position before and after the pull:
t | AI STACK IN USE [
SP (STACK POINTER)
1 t | STACK IN USE
SP (STACK POINTER)
Uses:
1. PLA is the converse of PHA. It retrieves values put on the stack by PHA, in the
reverse order. PLA/ PHA leaves the stack unchanged, but leaves A holding the
contents of the current top of the stack. Flags N and Z are set as though by LDA.
2. To remove the top two bytes of the stack. This is a frequent use of PLA; it is
equivalent to adding 2 to the stack pointer. This is done to "pop" a return address
from the stack; in this way, the next RTS which is encountered will not return to
the previous JSR, but to the one before it (assuming that the stack has not been
added to since the JSR).
PLA ;DISCARD ADDRESS STORED
PLA ;BYJSR
RTS ;RETURN TO EARLIER SUBROUTINE CALL
PLP
Pull the stack into the processor status register. SP:= SP+1, PSR:= S
318
Vocabulary of the 6510 Chip
Flags:
N V - B D I Z C
X X X X X X X
Operation: The operation of PLP is similar to that of PLA, except that the processor
status register, not the accumulator, is loaded from the stack.
Uses: Recovers previously stored flags with which to test or branch. See the notes on
PHP. This can also be used to experiment with the flags—to set V, for example.
ROL
Rotate memory or accumulator and the carry flag left one bit.
76543210
Flags:
N V — B D I Z C
X X X
Operation: Nine bits, consisting of the contents of the memory location referenced
by the instruction (or of the accumulator) and the carry bit, are rotated as the di
agram shows. In the process, C is changed to what was bit 7, bit 0 takes on the pre
vious value of C, and the negative flag becomes the previous bit 6. In addition, Z is
set or cleared, depending on the new memory contents.
Uses:
1. Doubles the contents of the byte that it references. In this way, ROL operates like
ASL, but in addition the carry bit may be used to propagate the overflow from
such a doubling. Multiplication and division routines take advantage of this prop
erty where a chain of consecutive bytes has to be moved one bit leftward. ROR is
used where the direction of movement is rightward.
ASL $4000/ ROL $4001/ ROL $4002 moves the entire 24 bits of
$4000-$4002 over by one bit, introducing 0 into the rightmost bit. If there is a
carry, the carry flag will be 1.
2. Like ASL, ROL may be used before testing N, Z, or C, especially N.
ROL A ;ROTATE 1 BIT LEFTWARD
BMI BRANCH ;BRANCHES IF BIT 6 WAS ON
319
Vocabulary of the 6510 Chip
ROR
Rotate memory or accumulator and the carry flag right one bit.
76543210
n
Instruction Addressing Bytes Cycles
Flags:
N V — B D I Z C
X X X
RTI
Return from interrupt. SP:= SP+1, PSR:= S, SP:= SP+1, PCL:= S, SP:= SP+1,
PCH:= S
Flags:
N V - B D I Z C
X X X X X X X
Operation: RTI takes three bytes from the stack, deposited there by the processor it
self when the hardware triggered the interrupt. The processor status flags are re
covered as they were when the interrupt occurred, and the program counter is
restored so that the program resumes operation at the byte at which it was inter
rupted. Note that the contents of A, X, and Y are not saved or recovered automati
cally in this way, but must be saved by the interrupt processing and restored
immediately before RTI. If you follow the vector stored in ROM at $FFFE-$FFFF, you
will see how this works.
320
Vocabulary of the 6510 Chip
Uses:
1. To resume after an interrupt. The techniques presented in Chapter 8 use the
interrupt-processing routine in ROM, which is the simplest approach; it's not nec
essary even to understand RTI. The routines invariably end PLA/ TAY/ PLA/
TAX/ PLA/ RTI because the contents of A, X, and Y are pushed on the stack in
A, X, Y order by CBM ROMs when interrupt processing begins.
2. To execute a jump. It is possible, as with RTS, to exploit the automatic nature of
this command to execute a jump by pushing three bytes onto the stack, imitating
an interrupt, then using RTI to pop the addresses and processor status. By
simulating the stack contents left by an interrupt, the following routine jumps to
256*HI + LO with its processor flags equal to whatever was pushed on the stack
as PSR.
LDA HI
PHA
LDA LO
PHA
LDA PSR
PHA
RTI
RTS
Return from subroutine. SP:= SP+1, PCL:= S, SP:= SP+1, PCH:= S, PC:
PC+1
Flags:
|N y — b D I Z c
Operation: RTS takes two bytes from the stack, increments the result, and jumps to
the address found by putting the calculated value into the program counter. It is
similar to RTI but does not change the processor flags, since an important feature of
subroutines is that, on return, flags should be usable. Also, unlike RTI in which the
address saved is the address to return to, RTS must increment the address it fetches
from the stack, which points to the second byte after a JSR.
Uses:
1. Return after a subroutine. This is straightforward; a batch of ML to be callable by
JSR is simply ended or exited from with RTS. This also applies to ML routines
callable from BASIC with SYS calls; in this case the return address to the loop
which executes BASIC is put on the stack first by the system.
2. As a form of jump. RTS is used as a form of jump which takes up no RAM space
and can be loaded from a table. For example, the following routine jumps to the
address $HILO+1, so put the desired address —1 on the stack.
321
Vocabulary of the 6510 Chip
LDA #$HI
PHA
LDA #$LO
PHA
RTS
Notes: See PLA for the technique of discarding (popping) return addresses. JSR
SUB/ RTS is identical in effect to JMP SUB, since SUB must end with an RTS. This
point can puzzle programmers.
SBC
Subtract memory with borrow from accumulator. A:= A—M—(1 —C)
Flags:
N V - B D I Z C
X X X X
Operation: It is usual to set the carry bit before this operation or to precede it by an
operation which is known to leave the carry bit set. Then SBC appears to subtract
from the accumulator the data referenced by the addressing mode. If the carry flag is
still set, this indicates that the result did not borrow (that is, that the accumulator's
value is greater than or equal to the data). When C is clear, the data exceeded the
accumulator's contents; C shows that a borrow is needed. Within the chip, A is
added to the twos complement of the data and to the complement of C; this con
ditions the N, V, Z, and C flags.
Uses:
1. Single-byte subtraction. The following example is a detail from PRINT. When
processing the comma in a PRINT statement, the cursor is moved to position 0,
10, 20, etc. Suppose the cursor is at 17 horizontally; subtract 10's until the carry
flag is clear, when A will hold —3. The twos complement is 3, so three spaces or
cursor-rights take you to the correct position on the screen. Note that ADC #$01
adds 1 only; the carry flag is known to be 0 by that stage.
322
Vocabulary of the 6510 Chip
Double-byte subtraction. The point about subtracting one 16-bit number from an
other is that the borrow is performed automatically by SBC. The C flag is first set
to 1; then the low byte is subtracted; then the high byte is subtracted, with borrow
if the low bytes make this necessary.
In the following example, $026A is subtracted from the contents of addresses
(or data) LO and HI. The result is replaced in LO and HI. Note that SEC is per
formed only once. In this way, borrowing is performed properly. For example,
suppose the address from which $026A is to be subtracted holds $1234. When
$6A is subtracted from $34, the carry flag is cleared, so that $02 and 1 is sub
tracted from the high byte $12.
SEC
LDA LO
SBC #$6A
STA LO
LDA HI
SBC #$02
STA HI
SEC
Set the carry flag to 1. C: = 1
Flags:
N V — B D I Z c
Operation: Sets the carry flag. This is the opposite of CLC, which clears it.
Uses: Used whenever the carry flag has to be put into a known state; usually SEC is
performed before subtraction (SBQ and CLC before addition (ADC) since the nu
meric values used are the same as in ordinary arithmetic. Some Kernal routines re
quire C to be cleared or set, giving different effects accordingly. SEC/BCS is
sometimes used as a "branch always" command.
323
Vocabulary of the 6510 Chip
SED
Set the decimal mode flag to 1. D:= 1
Flags:
N V — B D I Z c
Operation: Sets the decimal flag. This is the opposite of CLD, which clears it.
Uses: Sets the mode to BCD (binary coded decimal) arithmetic, in which each nybble
holds a decimal numeral. For example, ten is held as 10 and ninety as 90. Two thou
sand four hundred fifteen is 2415 in two bytes. ADC and SBC are designed to op
erate in this mode as well as in binary, but the flags no longer have the same
meaning, except C. The result is not much different from arithmetic using individual
bytes for each digit 0-9, but it takes up only half the space and is faster.
SEI
Set the interrupt disable flag to 1. I:= 1
Flags:
N V — B D I Z c
Operation: Sets the interrupt disable flag. This is the opposite of CLI, which clears it.
Uses: When this flag has been set, no interrupts are processed by the chip, except
non-maskable interrupts (which have higher priority), BRK, and RESET. IRQ inter
rupts are processed by a routine vectored through locations $FFFE-$FFFF, like BRK.
If the vector in the very top locations of ROM is followed, the interrupt servicing
routines can be found. In the 64, these are not all in ROM: The vectors use an ad
dress in RAM before jumping back to ROM.
The example here is a typical initialization routine to redirect the 64's RAM IRQ
vector into the user's own program at $C00D (where it may play a musical tone or
whatever).
324
Vocabulary of the 6510 Chip
C000 SEI
C001 LDA #$C0
C003 STA $0315
C006 LDA #$0D
C008 STA $0314
C00B CLI
C00C RTS
STA
Store the contents of the accumulator into memory. M:= A
Flags:
|n V — B D I Z C
Operation: The value in A is sent to the address referenced by the opcode. All reg
isters and flags are unchanged.
Uses:
1. Intermediate storage. Transfer of blocks of data from one part of memory to an
other needs a temporary intermediate store, usually in A, which is alternately
loaded and stored. See LDA.
2. Saving results of binary operations. Binary operations using the accumulator, nota
bly ADC and SBC, are performed within the accumulator; a common bug in ma
chine language programs is forgetting to save the result.
LDA $90 ;STBYTE
AND #$FD ; BIT 1 OFF
STA $90 ; REMEMBER THIS!
3. Setting the contents of certain locations to known values.
LDA #$89
STA $22 ; SETS VECTOR AT $22-$23
LDA #$C3
STA $23 ;TO$C389
325
Vocabulary of the 6510 Chip
STX
Store the contents of the X register into memory. M:= X
Flags:
N V - B D I z c
Operation: The byte in the X register is sent to the address referenced by the
opcode. All registers and flags are unchanged.
Uses: The uses are identical to those of STA. There is a tendency for X to be used as
an index, so STX is less used than STA.
STY
91 I
Store the contents of the Y register into memory. M:= Y
Flags:
N V — B D I z c
Operation: The byte in the Y register is sent to the address referenced by the
opcode. All registers and flags are unchanged.
Uses: STY resembles STX; the comments under STX apply.
TAX
Transfer the contents of the accumulator into the X register. X: = A
Flags:
N V — B D I Z C
X X
326
Vocabulary of the 6510 Chip
Operation: The byte in A is transferred to X. The N and Z flags are set as though
LDX had taken place.
Uses: This transfer is generally used to set X for use as an index or a parameter or to
temporarily hold A. The example is from a high-resolution screen-plotting routine; it
plots a black dot in a location with a coded value of 1, 2, 4, or 8 in $FB. On entry X
holds the position of the current X in a table. On exit X holds the position of the new
character. Intermediate calculations use the accumulator because there is no "EOR
with X" instruction.
TXA
EOR #$FF
ORA $FB
EOR #$FF
TAX
LDA TABLE,X
Note that registers A, X, Y, and the stack pointer are interchangeable with one
instruction in some cases, but not in others. The connections are shown below:
Y ^ A ^ X ^ S.
TAY
Transfer the contents of the accumulator into the Y register. Y:= A
Flags:
N V — B D I Z C
X X
Operation: The byte in A is transferred to Y. The N and Z flags are set as though
LDY had taken place.
Uses: See TAX.
TSX
Transfer the stack pointer into the X register. X:= SP
Flags:
N V - B D I Z C
X X
327
Vocabulary of the 6510 Chip
Operation: The stack pointer is transferred to X. Note that the stack pointer is al
ways offset onto $0100, so when the stack is accessed, the high byte of its memory
location is $01. The pointer itself is a single byte.
Uses:
1. To look at current values on the stack. TSX/ LDA $0100,X loads A with the con
tents presently at the top of the stack; LDA $0101,X loads the last item pushed on
the stack (one byte higher) into A, and so on. BASIC tests for BRK or interrupt
with PHA/ TXA/ PHA/ TYA/ PHA/ TSX/ LDA $0104,X/ AND #$10 because
the return-from-interrupt address and the SR are pushed by the interrupt before
the system saves its own three bytes. LDA $0104,X loads the flags saved when
the interrupt or BRK happened.
2. To determine space left on the stack. BASIC does this and signals ?OUT OF MEM
ORY ERROR if there are too many GOSUBs, FOR-NEXT loops, or complex
calculations with intermediate results.
3. Processing. Sometimes the stack pointer is stored and a lower part of the stack
temporarily used for processing.
TXA
Transfer the contents of the X register into the accumulator. A: = X
Flags:
N V — B D I Z C
X X
Operation: The byte in X is transferred to A. The N flag and Z flag are set as though
LDA had taken place.
Uses: See TAX.
TXS
Transfer the X register into the stack pointer. SP:= X
Instruction Addressing Bytes Cycles
Flags:
|N V — B D I Z c]
328
Vocabulary of the 6510 Chip
Operation: X is stored in the stack pointer. PHA or PHP will place a byte onto the
stack at $0100 plus the new stack pointer, and PLA or PLP will pull from the next
byte up from this. In addition, RTI and RTS will return to addresses determined by
the stack contents at the new position of the stack.
Uses:
1. As part of the RESET sequence. TXS is always part of the RESET sequence; other
wise, the stack pointer could take any value. CBM computers use the top bytes of
the stack for BASIC addresses. When the 64 is turned on, LDX #$FF/ TXS sets
the pointer to the top of the stack, but if BASIC is to run (that is, if no autorun
cartridge is in place), SP is moved to leave locations $01FA-$01FF ready for use
by the RUN command.
SP has high values to start with because it is decremented as data is pushed
onto the stack. If too much data is pushed, perhaps by an improperly controlled
loop, SP decrements right through $00 to $FF again, crashing its program.
2. Switching to a new stack location. This is a rarely seen use of TXS. As a simple
example, the following routine is an equivalent to PLA/ PLA which you have
seen (under RTS) to be a "pop" command which deletes a subroutine's return ad
dress. Incrementing the stack pointer by 2 has the identical effect.
CLC
TSX
TXA
ADC #$02
TAX
TXS
TYA
Transfer the contents of the Y register into the accumulator. A:= Y
Flags:
N V — B D I Z C
X X
Operation: The byte in Y is transferred to A. The N flag and Z flag are set as though
LDA had taken place.
Uses: See TAX. The transfers TAX, TAY, TXA, and TYA all perform similar functions.
329
Chapter 11
64 ROM Guide
64 Memory Map
Chapter 11
64 ROM Guide
64 Memory Map
This chapter maps in detail the first few hundred RAM locations, the BASIC ROM,
and the Kernal ROM. It will be especially valuable to programmers who want to
make full use of Commodore 64 BASIC.
Locations are listed for both the Commodore 64 and the VIC, since many loca
tions are the same on the two computers.
Commodore 64 BASIC is stored in ROM from $A000 to $BFFF. The computer's
operating system, the ML that controls input/output and related operations, is stored
in ROM from $E000 to $FFFF, called the Kernal ROM. It contains a large number of
routines, but generally Kernal routines are taken to be only those which are called
through the Kernal jump table.
Commodore recommends that ML programmers use only Kernal routines. That,
however, rules out most of BASIC. Moreover, transportability between machines is
likely to be very difficult even with the Kernal. Generally, you should use any of
these routines where they are likely to make better programs.
There is a potential problem between machines of the same type. For example,
several 64 ROM versions exist, with Kernal ROM variations. In practice this is rarely
a problem. But if you want to be certain, relocate your routines into RAM as much as
possible.
A number of ROM routines are vectored through RAM; Chapter 8 explains how
to take advantage of this.
Notation
Labels have been included as reference points, and where possible they refer back to
well-known labels.
BASIC number handling is a bit complex. FAC1 and FAC2 refer to Floating Point
Accumulators 1 and 2. They hold two numbers during addition, multiplication, etc.,
which is done in a six-byte format (EMMMMS, consisting of exponent/mantissa or
data/sign), called FLPT for short. MFLPT refers to the way numbers are stored in
memory after BASIC, in a five-byte format with one bit of data less than FLPT.
MFLPT format is explained in Chapter 6. BASIC of course has routines to convert
these. INT or FIX format is the simpler format with bytes in sequence.
A, X, and Y are the 6510/6502's registers. A/Y means the two-byte value with
A holding the low byte and Y the high byte. String descriptors are three bytes of
data, the first holding the string's length, the second and third the low and high
bytes of the pointer to the start of the string.
The following listings consist of three columns. The first column gives the label.
The second column lists the 64 and VIC addresses; where one address is given, it
applies to both computers unless otherwise noted, but where two are given, the 64
address comes first. Finally, a description of the use of the location or of the routine
that begins at the specified address is given.
333
64 ROM Guide
Page 0: RAM$l
Label 64/VIC Descriptions
D6510 $00 6510 on-chip data direction register (Commodore 64 only).
R6510 $01 6510 on-chip input/output register (Commodore 64 only).
$02 Unused byte (Commodore 64 only).
FACINT $03-$04 Vector to routine to convert FAC to integer in A/Y (usually
$B1AA).
INTFAC $05-$06 Vector to routine to convert integer in A/Y to floating point in
FAC (usually $B391).
CHARAC $07 Delimiting character used when scanning. Also temporary integer
(0-255) used during INT.
INTEGR $07-$08 Intermediate integer used during OR/AND.
ENDCHR $08 Delimiter used when scanning strings.
TRMPOS $09 Temporary location used for calculating TAB and SPC column.
VERCHK $0A Flag to indicate LOAD (0) or VERIFY (1).
COUNT $0B Temporary pointer used with BASIC input buffer.
DIMFLG $0C Flag: default array dimension.
VALTYP $0D Flag: current variable data type; 0 means numeric, $FF means
string.
INTFLG $0E Flag: current variable data type; 0 means floating point; $80 means
integer.
GARBLF $0F Flag used in garbage collection, LIST, DATA, error messages.
SUBFLG $10 Flag to indicate integers or array elements, which are forbidden as
indexes of FOR/NEXT loops and in function definitions.
INPFLG $11 Flag used by READ routine; $00 means INPUT, $40 means GET,
$98 means READ.
TANSGN $12 Sign byte used by TAN, SIN. Also set according to any comparison
being performed: > sets this location to $01, = sets $02, and <
sets $04.
CHANNL $13 Current I/O device number; prompts suppressed if not 0.
LINNUM $14-$15 Line number integer (0-63999) or standard two-byte address used
by GOTO, GOSUB, POKE, PEEK, WAIT, and SYS.
TEMPPT $16 Index to next entry on string descriptor stack (may be $19, $1C,
$1F, or $22).
LASTPT $17-$18 Pointer to current entry on string descriptor stack.
TEMPST $19-$21 Stack for three temporary string descriptors.
INDEX1 $22-$23 General-purpose pointer, for example, for memory moves.
INDEX2 $24-$25 General-purpose pointer, for example, for number movements.
RESHO $26-$2A Floating point workspace used by multiply and divide.
TXTTAB $2B-$2C Pointer to first byte of BASIC program (2049 for the 64).
VARTAB $2D-$2E Pointer to start of program variables; first byte beyond end of
program.
ARYTAB $2F-$30 Pointer to start of arrays; first byte beyond end of variables.
STREND $31-$32 Pointer to start of free RAM available for strings; first byte beyond
end of arrays.
FRETOP $33-$34 Pointer to current lower boundary of string area. (Set to the con
tents of MEMSIZ on CLR or RUN.)
FRESPC $35-$36 Utility pointer used when new string is being added to string area.
MEMSIZ $37-$38 Pointer to one byte beyond the top of RAM available to BASIC.
CURLIN $39-$3A BASIC line number being interpreted ($FF in $003A indicates
immediate mode).
334
64 ROM Guide
OLDLIN $3B-$3C If STOP, END, or BREAK occurs, this holds the last BASIC line
number executed for CONT.
OLDTXT $3D-$3E Pointer to beginning of current BASIC line for CONT.
DATLIN $3F-$40 Line number of current DATA statement. Initialized to $0000 on
RUN.
DATPTR $41-$42 Pointer to one byte beyond the DATA item read by the last READ
statement. Initialized to contents of TXTTAB on RUN.
INPTR $43-$44 Temporary storage of DATPTR during READ statement; also
pointer within input buffer during INPUT (points to last character
entered).
VARNAM $45-$46 Current BASIC variable; two-character name with most significant
bit (bit 7) of each byte used to indicate variable type: bit 7 clear in
both bytes means floating point, bit 7 set in both means integer,
bit 7 set in $46 means string, bit 7 set in $45 means function.
VARPNT $47-$48 Pointer to current variable's address in RAM. Points one byte
beyond variable name.
FORPNT $49-$4A Temporary pointer to variables in memory for INPUT, assign
ments, etc., and for loop variable in FOR/NEXT loops. Also holds
the two parameters for WAIT statements.
OPPTR $4B Pointer within operator table during expression evaluation in rou
tine FRMEVL.
OPMASK $4D Comparison mask used in FRMEVL: > sets this location to $01, =
sets $02, and < sets $04.
DEFPNT $4E-$4F Pointer to variable in function definition, within variable table in
RAM. Also used by garbage collection routine GARBAG.
TEMPF3 $4E-$52 Temporary storage for a MFLPT item.
DSCPNT $50-$51 Pointer to descriptor in variable list or to string in dynamic string
area; used during string operations.
SIZE $52 Length of the current BASIC string.
FOUR6 $53 Length of string variable during garbage collection.
JMPER $54-$56 Jump vector for function evaluations, JMP ($4C) followed by func
tion address from function vector table.
TEM^tl $57-$5B Temporary pointers (for example, in memory move); also tem
porary floating point accumulator.
HIGHDS $58-$59 Pointer used by block transfer routine BLTU.
ARYPNT $58-$59 Pointer used when initializing arrays (when DIM is encountered).
HIGHTR $5A-$5B Pointer used by block transfer routine BLTU.
TEMPF2 $5C-$60 Temporary floating point accumulator.
DECCNT $5D Number of digits after/before decimal point in ASCII-to-FLPT and
FLPT-to-ASCII conversion for the FIN and FOUT routines.
TENEXP $5E Exponent used in ASCII-to-FLPT and FLPT-to-ASCII conversion in
the FIN and FOUTroutines.
DPTFLG $5F Flag use4 by the FIN routine ($BCF3) when inputting numbers; set
to $80 [i s'lring contains decimal point.
LINPTR $5F-$60 Pointer used when searching for line numbers, searching for vari
ables in variable list, doing block transfers.
EXPSGN $60 Sign of exponent of number being input by FIN routine; a value of
$80 signifies negative.
FACl $61-$66 Floating Point Accumulator 1. Consists of exponent byte, four
mantissa bytes, and a sign byte. (The results of most arithmetic op
erations are placed here.) Integer results are stored in two bytes
FACl+3andFACl+4.
335
64 ROM Guide
SGNFLG $67 Flag used by FIN when inputting numbers; set to $FF if the num
ber is negative. Also stores count of terms in polynomial series
when evaluating trig functions.
BITS $68 Bit overflow area on normalizing FAC1.
FAC2 $69-$6E Floating Point Accumulator 2; used with FAC1 in evaluation of
products, sums, differences, etc.
ARISGN $6F Sign comparison between FAC1 and FAC2; $00 means same sign,
$FF means opposite.
FACOV $70 Rounding/overflow byte for FAC1.
TEMPTX $71-$72 General pointer used in CRUNCH, VAL, series evaluation, with
tape buffer, etc.
CHRGET Subroutine to fetch next BASIC character into A (spaces are
skipped) and set flags; C cleared if ASCII numeral 0-9; Z set if
end-of-line or colon (:).
CHRGOT $79 Entry point within CHRGET to re-get current BASIC character and
set flags as CHRGET does. Does not increment TXTPTR first.
TXTPTR $7A-$7B Pointer into BASIC text used by CHRGET and CHRGOT routines.
RNDX $8B-$8F Floating point random number seed and subsequent pseudo
random values.
STATUS $90 Status ST for serial devices and cassette.
STOPFL $91 Flag: contains $7F (127) if RUN/STOP key pressed.
TSERVO $92 Tape timing constant.
VERCK $93 Flag to indicate LOAD (0) or VERIFY (1).
ICHRFL $94 Serial flag: a value of $FF indicates a character is awaiting output.
IDATO $95 Serial character to be output; a value of $FF indicates no character.
TEOB $96 Hag: end of data block from tape.
TEMPXY $97 Temporary X,Y storage during cassette read/RS-232 input.
NFILES $98 Number of files open (maximum of ten); index to file table.
DFLTI $99 Current input device number; default value is 0 (keyboard).
DFLTO $9A Current output device number; default value is 3 (screen).
TPARIT $9B Parity of byte written to tape.
TBYTFL $9C Flag: byte read from tape is complete.
MSGFLG $9D Flag: $00 means program mode; $80 means direct mode.
HDRTYP $9E Tape buffer header ID.
PTR1 $9E Cassette pass 1 read errors.
PTR2 $9F Cassette pass 2 read errors.
TIME $A0-$A2 Three-byte jiffy clock for TI, updated 60 times per second. Bytes
arranged in order of decreasing significance.
TSFCNT $A3 Tape read/write bit counter.
TBTCNT $A4 Tape read/write pulse counter.
CNTDN $A5 Tape synchronization write countdown.
BUFPNT $A6 Count of bytes in tape I/O buffer.
INBIT $A7 RS-232 temporary storage for received bits.
PASNUM $A7 General temporary store for cassette read/write.
BITCI $A8 RS-232 received bit count. Also temporary store for cassette read/
write.
RINONE $A9 RS-232 receive: check for start bit.
TBITER $A9 Write start bit/read bit sequence error.
RIDATA $AA Tape read mode; 0 means scan, 1-15 means count, $40 means
LOAD, $80 means end-of-tape marker.
RIDATA $AA RS-232 received byte buffer.
336
64 ROM Guide
337
64 ROM Guide
LDTBl $D9-$F2 Table of 25 high bytes of pointers to the start of screen lines in
RAM. (The low bytes are held in ROM from $ECF0.) lines with
wraparound have bit 7 set to 0; otherwise, bit 7 is 1.
USER $F3-$F4 Pointer to byte in color RAM corresponding to beginning of cur
rent line on the screen.
KEYTAB $F5-$F6 Address of current keyboard decoding table.
RIBUF $F7-$F8 RS-232: pointer to start of receive buffer.
ROBUF $F9-$FA RS-232: pointer to start of transmit buffer.
$FB-$FE Unused; available for user programs.
BASZPT $FF Temporary storage area for FLPT-to-ASCII conversion.
338
64 ROM Guide
KEYLOG $28F-$290 Vector to routine to check SHIFT pattern; used by SCNKEY Kernal
routine.
MODE $291 Flag: $00 means enable upper/lowercase toggle using SHIFT and
Commodore key; $80 means disable the toggle.
AUTODN $292 Flag: autoscroll down during input; $00 means disable.
M51CTR $293 RS-232: control register.
M51CDR $294 RS-232: command register.
M51AJB $295-$296 RS-232: nonstandard transmission rate value (not used).
RSSTAT $297 RS-232: status register ST.
BITNUM $298 RS-232: number of bits to send/receive.
BAUDOF $299-$29A RS-232: baud rate timing constant.
RIDBE $29B RS-232: input buffer pointer; points to latest character input (end
of buffer).
RIDBS $29C RS-232: input buffer pointer; points to first available character
(start of buffer).
RODBS $29D RS-232: output buffer pointer: start of buffer.
RODBE $29E RS-232: output buffer pointer: end of buffer.
IRQTMP $29F-$2A0 Temporary storage for IRQ vector during tape operations.
$2A1-$2A5 Temporary storage during tape operations.
$2A6 PAL/NTSC Flag (0 means NTSC, 1 means PAL)
$2A7-$2FF Free RAM available to user
339
64 ROM Guide
IOPEN $31A-$31B Vector to Kernal OPEN routine (normally $F34A). Called from
$FFC0.
ICLOSE $31C-$31D Vector to Kernal CLOSE routine (normally $F291). Called from
$FFC3.
ICHKIN $31E-$31F Vector to Kernal CHKIN routine (normally $F20E). Called from
$FFC6.
ICKOUT $320-$321 Vector to Kernal CHKOUT routine (normally $F250). Called from
$FFC9. y
ICLRCH $322-$323 Vector to Kernal CLRCHN routine (normally $F333). Called from
$FFCC.
IBASIN $324-$325 Vector to Kernal CHRIN routine (normally $P157). Called from
$FFCF.
IBSOUT $326-$327 Vector to Kernal CHROUT routine (normally $F1CA). Called from
$FFD2.
ISTOP $328-$329 Vector to Kernal STOP routine (normally $F6ED). Called from
$FFE1.
IGETIN $32A-$32B Vector to Kernal GETIN routine (normally $F13E). Called from
$FFE4.
ICLALL $32C-$32D Vector to Kernal CLALL routine (normally $F32F). Called from
$FFE7.
USRCMD $32E-$32F Unused vector: May be defined by user; initialized to BRK vector
($FE66).
ILOAD $330-$331 Vector to Kernal LOAD routine (normally $F4A5).
ISAVE $332-$333 Vector to Kernal SAVE routine (normally $F5ED).
$334-$33B Eight unused bytes.
TBUFFR $33C-$3FB Tape I/O buffer (192 bytes long). Can be used for ML programs
but tape use will overwrite.
$3FC-$3FF Four unused bytes.
Note: The following summary of the memory map applies to the 64 only, since there are consid
erable hardware differences between the 64 and other CBM machines.
In the Commodore 6£, locations $8000 and above are subject to memory management by both
hardware (EXROM and GAME lines) and software (locations 0 and 1), and therefore can contain
different things at different times. All this is explained in Chapter 5. Note that a plug-in cartridge
is assumed to be ROM in what follows.
The region $D000-$DFFF is configured as follows (note that I/O chips have repeat images):
$D000-$D02E 53248-53294 VIC chip (see Chapter 12).
$D400-$D41C 54272-54300 SID chip (see Chapter 13).
340
64 ROM Guide
$D800-$DBE7 55296-56295 Color RAM (low nybbles store character colors 0-15).
$DC00-$DC0F 56320-56335 CIA 1 (see Chapter 5).
$DD00-$DD0F 56576-56591 CIA 2 (see Chapter 5).
This region also includes the character generator ROM:
$D000-$D7FF 53248-55295 Uppercase/graphics character set.
$D800-$DFFF 55296-57343 Lower/uppercase character set.
341
64 ROM Guide
BLTUC $A3BF/$C3BF Move block starting at address pointed to by $5F-$60 and end
ing at address pointed to by $5A-$5B — 1 up to a new block
ending at the address pointed to by $58-$59 — 1.
GETSTK $A3FB/$C3FB Test to see whether stack will accommodate A*2 bytes: ?OUT
OF MEMORY if not.
REASON $A408/$C408 Check whether address pointed to by A/Y is below FRETOP
(current bottom of string area). If yes, exit; otherwise, do gar
bage collection and check again. If still not, then print ?OUT
OF MEMORY.
ERROR $A437/$C437 Print error message; X holds error number (half of offset within
error message address table). Vectored via ($0300) to $E38B.
Then set keyboard input and screen output, reset stack, and
print IN with line number if in program mode.
READY $A474/$C474 Restart BASIC; print READY, set direct mode. .
MAIN $A480/$C480 Receive a line into input buffer and add a terminating zero
byte. Check for program line or immediate mode command; if
immediate mode command, execute it. MAIN is vectored via
($0302) to $A483.
MAIN1 $A49C/$C49C If program line, tokenize it.
INSLIN $A4A4/$C4A4 If the line number already exists, replace it. If it's new, insert it.
Line number is in $14-$15 on entry, length 4- 4 is in Y. If the
first byte in buffer is 0, the line is null; delete it.
FINI $A52A/$C52A Having inserted a new line, do RUNC (thus, variables are lost
on editing, and you cannot CONT after editing) and LNKPRG;
then jump to MAIN.
LNKPRG $A533/$C533 Chain link pointers in BASIC program using end-of-line zero
markers.
INLIN $A560/$C560 Input a screen line into the BASIC text buffer at $200, and add
a zero terminating byte.
CRUNCH $A579/$C579 Tokenize keywords in input buffer. Vectored via ($0304) to
$A57C.
FNDLIN $A613/$C613 Search BASIC text from beginning for line number in $14-$ 15.
Carry bit set if line found. Locations $5F-$60 point to link
address.
FNDLNC $A617/$C617 Search BASIC text from address in A (low byte) and X (high
byte) for line number in $14-$15.
NEW $A642/$C642 NEW routine enters here; check syntax, and continue with
SCRTCH.
SCRTCH $A644/$C644 Reset first two bytes of text (first link pointer) to 0; load start-
of-variables pointer $2D-$2E with start-of-BASIC + 2, and
continue with RUNC.
RUNC $A659/$C659 Set pointer within CHRGET to start of BASIC text, using
STXPT, then continue with CLEAR.
CLEAR $A65E/$C65E BASIC CLR routine; erase variables by resetting end-of-
variables pointers to coincide with end-of-program pointer;
appropriate string variable pointers are also reset. Abort I/O
activity and reset stack.
STXPT $A68E/$C68E Reset pointer within CHRGET routine to beginning of BASIC
text ($2B-$2C - 1 is loaded into $7A-$7B).
LIST $A69C/$C69C Entry point of routine to process LIST command.
LIST1 $A6C9/$C6C9 List one line of BASIC; line number, then text.
342
64 ROM Guide
343
64 ROM Guide
344
64 ROM Guide
GET $AB7B/$CB7B Entry point for routine to handle GET and GET# statements;
test for direct mode (illegal) and fetch one character from key
board or file.
INPUTN $ABA5/$CBA5 Entry point for routine to handle INPUT# statement; fetch file
number, turn the device on, call INPUT, and then turn the de
vice off.
INPUT $ABBF/$CBBF Entry point for routine to handle INPUT statement; output
user's prompt string if present, then continue with QINLIN
routine.
QINLIN $ABF9/$CBF9 Print 7 prompt and receive line of text (terminated by
RETURN) into input buffer.
READ $AC06/$CC06 Entry point for routine to handle the READ statement. GET
and INPUT also share this routine, but are distinguished by a
flag in location $11.
INPCON $AC0D/$CC0D Entry point into READ routine for INPUT; set flag and call
READ, with buffer at the address specified in X (low byte) and
Y (high byte).
INPCO1 $AC0F/$CC0F Entry point into READ routine for GET; set flag and call
READ, with buffer at the address specified in X (low byte) and
Y (high byte).
DATLOP $ACB8/$CCB8 Scan text and read DATA statements.
VAREND $ACDF/$CCDF Tests for 0 at end of input buffer; if not found, print 7EXTRA
IGNORED.
EXINT $ACFC/$CCFC Messages 7EXTRA IGNORED and 7REDO FROM START.
NEXT $AD1E/$CD1E Entry point for routine to handle NEXT; check for FOR token
and matching variable on stack, and print 7NEXT WITHOUT
FOR if not found; calculate next value. If the loop increment is
still valid, reset current line number and the pointer in
CHRGET and continue.
FRMNUM $AD8A/$CD8A Evaluate a numeric expression for BASIC by calling FRMEVL,
then CHKNUM.
CHKNUM $AD8D/$CD8D Check that FRMEVL has returned a number by testing flag at
location $0D. If a number was not returned, issue a 7TYPE
MISMATCH ERROR message.
CHKSTR $AD8F/$CD8F Check that FRMEVL has returned a string by testing flag at
location $0D. If a string was not returned, issue a 7TYPE MIS
MATCH ERROR message.
FRMEVL $AD9E/$CD9E Evaluate any BASIC expression in text and report any syntax
errors; set $0D (VALTYP) to $00 if the expression is numeric
and $FF if it is a string. For numeric expressions, location $0E
(INTFLG) is set to $00 if the expression is floating point, and
the value is placed in FAC1. If the variable type is integer, set
INTFLG to $80, but leave the result in floating point format in
FAC1. Complicated expressions may need simplifying to retain
stack space and prevent 7OUT OF MEMORY.
EVAL $AE83/$CE83 Evaluate a single term in an expression; look for ASCII
numeral strings, variables, pi, NOT, arithmetic functions, etc.
PIVAL $AEA8/$CEA8 Value of pi in five-byte floating point format.
PARCHK $AEF1/$CEF1 Evaluate expression within parentheses.
CHKCLS $AEF7/$CEF7 Check whether CHRGET points to a ) character; issue a 7SYN-
TAX ERROR message if not.
345
64 ROM Guide
346
64 ROM Guide
347
64 ROM Guide
STRINI $B475/$D475 Make room in string space for a string to be inserted: A con
tains length and FAC1+3-FAC1+4 points to the string. On
exit, $61-$63 contains descriptor for new string. CHR$, LEFT$,
and so on all use this routine.
STRLIT $B487/$D487 Copy a string into string space at top of memory; A/Y points
to the start of the string. Scans for quotation mark ("), colon (:),
or zero byte as terminator to determine length. Exit with
descriptor in $61-$63.
GETSPA $B4F4/$D4F4 Allocate space for string, length in A, in dynamic string space
at top of memory; do garbage collection if space exhausted.
Called by STRINI.
GARBA2 $B526/$D526 Do garbage collection; eliminate unwanted strings in string
area and collect together valid strings. The garbage collection
routine is slow for large numbers of strings.
DVARS $B606/$D606 Search variables and arrays for next string to be saved by gar
bage collection.
CAT $B63D/$D63D Concatenate two strings.
MOVINS $B67A/$D67A Move string to string area high in RAM; entered with $6F-$70
pointing at the descriptor of the string to be stored.
FRESTR $B6A3/$D6A3 Discard string; entered with pointer to string descriptor in
FAC1+3-FAC1+4, exits with new string length and pointer in
INDEX1.
FRETMS $B6DB/$D6DB Clean the descriptor stack.
CHRD $B6EC/$D6EC Entry point for routine to handle CHR$ function; sets up a
one-byte string.
LEFTD $B700/$D700 Entry point for routine to handle LEFT$.
RIGHTD $B72C/$D72C Entry point for routine to handle RIGHTS.
MIDD $B737/$D737 Entry point for routine to handle MID$.
PREAM $B761/$D761 Pull string descriptor pointer to $50-$51, length to A (also
inX).
LEN $B77C/$D77C Entry point for routine to handle LEN function; floating point
value of string length parameter placed in FAC1.
LEN1 $B782/$D782 Extract length of string, put in Y, leave string mode, and enter
numeric mode. Called by LEN, VAL.
ASC $B78B/$D78B ASC function; get first character of string and convert to float
ing point in FAC1. String of length 0 gives 7SYNTAX ERROR.
GTBYTC $B79B/$D79B Read and evaluate an expression from BASIC text; must eval
uate to a one-byte value; value left in X and FAC1+4.
VAL $B7AD/$D7AD Entry point for routine to handle VAL function; convert value
to floating point value in FAC1.
GETNUM $B7EB/$D7EB Read parameters for WAIT and POKE from BASIC text; put
first (two-byte integer) in $14-$15, second in X.
GETADR $B7F7/$D7F7 Convert FAC1 to two-byte integer (range 0-65535) in $14-$15
and Y/A.
PEEK $B80D/$D80D Entry point for routine to handle PEEK function; on entry
FAC1 contains address to be PEEKed in FLPT form; exit with
PEEKed value in Y.
POKE $B824/$D824 Entry point for routine to handle POKE statement; fetch two
parameters from BASIC text; do POKE.
WAIT $B82D/$D82D Entry point for routine to handle WAIT statement; fetch two
parameters from text, plus optional third, which is 0 if none
found; do WAIT loop.
348
64 ROM Guide
349
64 ROM Guide
350
64 ROM Guide
RND $E097/$E094 Entry point for routine to handle RND function; set FAC1 to a
number according to sign of FAC1 by branching to either
RNDO, QSETNR, or RND1.
RNDO $E09E/$E09B If FAC1=O, Joad FAC1 from VIA timer registers; a simple way
of reseeding it with a random number.
QSETNR $EOBE/$EO66 If FACl>0, load FACl with the result of multiplying the stored
random number (in $88-$8C) generated by previous calls, by
RMULC, and adding RADDC.
RND1 $E0D3/$E0D0 If FACl<0, load FACl with mixed digits from FACl itself, so
RND with a negative argument is constant and therefore
repeatable. After any of these three conditions, FACl is stored
in $88-$8C.
RNDRNG $E0E3/$E0E0 Force the value in FACl into the range 0-1 excluding 0 and 1.
BIOERR $E0F9/$E0F6 I/O error message routine if any of the following calls return
error flags:.
BGHOUT $E10C/$E109 Output character; uses CHROUT.
BCHIN $E112/$E10F Input character; uses CHRIN.
BCKOUT $E118/$E115 Set up for output; uses CHKOUT.
BCKIN $E11E/$E11B Set up for input; uses CHKIN.
BGETIN $E124/$E121 Get one character; uses GETIN.
SYS $E12A/$E127 Entry point for routine to handle SYS statement; load A, X, Y,
and SR from locations $30C-$30F, call machine language rou
tine at address specified by the argument, then reload the reg
ister contents into $30C-$30F on return from the routine.
SAVET $E156/$E153 Entry point for routine to handle SAVE; save a BASIC pro
gram. Set A to point to address in zero page pointing to start
address, set X/Y to the value in $2D-$2E (end-of-program
pointer). Then Kernal routine SAVE is called via vector at
$FFD8.
VERFYT $E165/$E162 Entry point for routine to handle VERIFY; set flag in A to in
dicate VERIFY operation, enter LOADT and check for errors.
LOADT $E168/$E165 Entry point for routine to handle LOAD; fetch parameters from
BASIC text and set them up, call Kernal routine LOAD via vec
tor at $FFD5 .
LOADR $E16F/$E177 Load from device already set, into RAM starting at start-of-
BASIC address pointed to by $2B-$2C.
LDFIN $E195/$E195 Finish LOAD; if LOAD was called in direct mode, set top-of-
BASIC pointer ($2D-$2E) to address of last byte loaded. This
step is omitted if the routine is called from within a program,
so variable list is preserved. Finally, reset pointer in CHRGET
and warm start BASIC to run the new program.
OPENT $E1BE/$E1BB Entry point for routine to handle OPEN; read parameters from
text and set them up via appropriate Kernal calls; call Kernal
OPEN routine via vector at $FFC0.
CLOSET $E1C7/$E1C4 Entry point for routine to handle CLOSE; read parameters from
text and set them up; call Kernal CLOSE routine via vector at
$FFC3.
SLPARA $E1D4/$E1D1 Fetch parameters for LOAD, SAVE, and VERIFY from BASIC
text; set defaults if not supplied. Set up file by a call to SETLFS
via vector at $FFBA.
351
64 ROM Guide
COMBYT $E2OO/$E1FD Check for comma and evaluate the following one-byte param
eter, which is put in X.
CMMERR $E20E/$E20B Check for comma followed by anything other than end of
statement; otherwise, issue a 7SYNTAX ERROR message.
OCPARA $E219/$E216 Get parameters from BASIC text for OPEN or CLOSE calls; set
defaults if not supplied.
COS $E264/$E261 Entry point for routine to handle the COS function; the value
in FAC1 is replaced by the cosine of that value.
SIN $E26B/$E268 Entry point for routine to handle the SIN function; the value in
FAC1 is replaced by the sine of that value.
TAN $E2B4/$E2B1 Entry point for routine to handle the TAN function; the value
in FAC1 is replaced by the tangent of that value.
$E2E0/$E2DD Table of constants in MFLPT format: tt/2, tt*2, and 0.25. Then
comes a counter value (5) and six MFLPT constants used in
evaluating SIN, COS, and TAN.
ATN $E30E/$E30B Entry point for routine to handle ATN; the value in FAC1 is
replaced by the arctangent of that value.
$E33E/$E33B A counter value (11) and table of 12 constants in MFLPT for
mat for ATN evaluation.
BASSFT $E37B/$E467 BASIC warm start routine, entered on JMP ($A002); part of the
break sequence performed if BRK instruction encountered or
RUN/STOP-RESTORE keys are pressed. Close all I/O I/O
channels, initialize stack, output 7BREAK ERROR, and jump to
READY.
INIT $E394/$E378 BASIC cold start routine, entered on JMP ($A000); part of the
reset sequence. Performs INITV, INITCZ, INITMS; sets stack
and jumps to READY.
CHRCPY $E3A2/$E387 CHRGET routine and RND seed in ROM for relocation into
RAM.
INITCZ $E3BF/$E3A4 Initialize USR jump instruction and default vector, vectors from
$003 to $006; transfer CHRGET and RND seed to RAM; call
Kernal routines MEIvfBOT and MEMTOP to set start-of-BASIC
and top-of-memory pointers ($2B-$2C and $37-$38) from the
pointers at $282-$285 initialized on power-up. Set end-of-
program zero byte at 2048.
INITMS $E422/$E404 Output start-up message: **** CBM BASIC V2 ****, then num
ber of free bytes, then BYTES FREE.
INITV $E453/$E45B Initialize vectors for ERROR, MAIN, etc., at $0300-$030B.
CPATCH $E4DA Patch to diminish screen sparkle; called from $EA0B (used by
CLR). Commodore 64 only.
IOBASK $E500/$E500 Returns base address of CIA in X/Y (used by SCNKEY).
SCRENK $E505/$E505 Returns screen columns (40) in X, lines (25) in Y.
PLOTK $E50A/$E50A Set/Read cursor row (X), column (Y).
CINT $E518/$E518 General screen and VIC chip initialization; set up screen
editing tables at $D9-$F2, initialize VIC chip, set character
color to light blue, do CLR and HOME, reset default I/O de
vice numbers at $99 and $9A.
HOME $E566/$E581 Home the cursor.
INITVC $E5A0/$E5C3 Initialize the VIC chip from table of values at $ECB9-$ECE6
(international variations).
352
64 ROM Guide
GETKBC $E5B4/$E5CF Get character from keyboard queue and move remaining
characters along; queue must contain at least one character on
entry (number of characters in queue is stored in $C6). On exit,
the character is in A.
INPPRO $E5CA/$E5E5 Input and process SHIFT-RUN/STOP, RETURN, etc.
QTSWC $E684/$E6B8 Flip quotes flag ($D4) if A contains quotes on entry.
PRT $E716/$E742 Print character in A to screen, like PRINT CHR$; handles such
characters as home cursor, clear screen, delete, etc.
CHKCOL $E8CB/$E912 Test A for character color code; change color in $286 if one is
found.
COLTAB $E8DA/$E921 Table of color-change codes, arranged Black, White, Red, Cyan,
etc.
SCROL $E8EA/$E975 Scroll screen up. If the top line is more than 40 characters long,
the routine scrolls up appropriate number of lines to com
pletely remove it. The CTRL key is tested for by directly
interrogating the CIA chip, and a slight delay is performed if it
is held down.
DSPP $EA13/$EAA1 Put the character in A onto the screen at the current cursor po
sition; no checking for control characters, etc., is performed.
The color for the character is held in X.
KEY $EA31/$EABF Interrupt servicing routine: All IRQ interrupts are processed by
this routine unless the vector in $0314-$0315 has been altered.
The functions of KEY are to update the clock and location $91
using Kernal routine UDTIM, maintain flashing cursor if cursor
is enabled (see $CC-$CF), set the cassette motor on or off
according to the flags at $C0, and test the keyboard for new
character using Kernal routine SCNKEY. Finally, the interrupt
register at $DC0D in the CIA is cleared; the A, X, and Y reg
isters are pulled from the stack and restored; and a return from
interrupt instruction (RTI) continues processing the main
program.
353
64 ROM Guide
354
64 ROM Guide
For example, if you use JMP $FFD2, the jump table entry for the CHROUT rou
tine, you could have some assurance that your program would still work on future
64s; moreover, that jump table entry would also work on the Commodore VIC-20
and PET/CBM computers. On the 64, JMP $FFD2 arrives at $F1CA via an indirect
jump through the RAM vector in locations $326-$327.
The 64's Kernal jump table begins at location $FF81. Note that the table entries
have their own labels, which may be different from the labels of the routines they
point to.
Jump Table
Label 64/VIC Entry Descriptions
PCINT $FF5B/$E518 $FF81 CINT Initialize screen editor and video chip, set
interrupt frequency.
IOINIT $FDA3/$FDF9 $FF84 IOINIT Initialize input/output chips.
RAMTAS $FD50/$FD8D $FF87 RAMTAS Test and initialize RAM.
RESTOR $FD15/$FD52 $FF8A RESTOR Restore standard input/output vectors.
VECTOR $FD1A/$FD57 SFF8D VECTOR Store/set input/output vectors.
SETMSG $FE18/$FE66 Enable/disable Kernal control message out
put to screen.
SECNDK $EDB9/$EEC0 $FF93 SECOND Send secondary address for LISTEN com
mand on serial bus; LISTEN must be called
before using this routine.
TKSAK $EDC7/$EECE $FF96 TKSA Send secondary address for TALK com
mand on serial bus; TALK must be called
before using this routine.
MEMTOP $FE25/$FE73 $FF99 MEMTOP Read/set BASIC top-of-memory limit.
MEMBOT $FE34/$FE82 $FF9C MEMBOT Read/set BASIC bottom-of-memory limit.
SCNKK $EA87/$EB1E $FF9F SCNKEY Scan keyboard.
SETTMO $FE21/$FE6F $FFA2 SETTMO Set serial bus time-out.
ACPTRK $EE13/$EF19 $FFA5 ACPTR Get a byte from a serial device (usually
disk).
CIOUTK $EDDD/$EEE4 $FFA8 CIOUT Output a byte to a serial device (usually a
printer or disk).
UNTLKK $EDEF/$EEF6 $FFAB UNTALK Send an UNTALK command to devices on
the serial bus.
UNLSNK $EDFE/$EF04 $FFAE UNLSN Send an UNLISTEN command to devices
on the serial bus.
LISTNK $ED0C/$EE17 $FFB1 LISTN Cause a device on the serial bus (usually a
printer or disk) to listen.
TALKK $ED09/$EE14 $FFB4 TALK Cause a device on the serial bus (usually a
disk drive) to talk.
JIEADSS $FE07/$FE57 $FFB7 READST Read status byte into A.
SETLFS $FE00/$FE50 $FFBA SETLFS Set file number, device number, and
secondary address.
SETNAM $FDF9/$FE49 SFFBD SETNAM Set filename.
NOPEN $F34A/$F40A $FFC0 OPEN Open a file for reading or writing. Uses
RAM vector at $031A.
NCLOSE $F291/$F34A $FFC3 CLOSE Close a file. Uses RAM vector at $031C.
NCHKIN $F20E/$F2C7 $FFC6 CHKIN Prepare a file for input. Uses RAM vector at
$031E.
355
64 ROM Guide
NCKOUT $F250/$F309 $FFC9 CHKOUT Prepare a file for output. Uses RAM vector
at $0320.
NCLRCH $F333/$F3F3 $FFCC CLRCHN Restore default I/O devices. Uses RAM vec
tor at $0322.
NBASIN $F157/$F20E $FFCF CHRIN Get a character from the designated input
device. Uses RAM vector at $0324.
NBSOUT $F1CA/$F27A $FFD2 CHROUT Send a character to the designated output
device. Uses RAM vector at $0326.
LOADSP $F49E/$F542 $FFD5 LOAD Load data into memory from disk or tape.
SAVESP $F5DD/$F675 SFFD8 SAVE Save memory block to disk or tape.
SETTMK $F6E4/$F767 $FFDB SETTIM Set TI clock.
RDTIMK $F6DD/$F760 $FFDE RDTIM Read TI clock.
NSTOP $F6ED/$F770 $FFE1 STOP Test whether RUN/STOP key is pressed.
Uses RAM vector at $0328.
NGETIN $F13E/$F1F5 $FFE4 GETIN Get a character, usually from the keyboard.
Uses RAM vector at $032A.
NCLALL $F32F/$F3EF $FFE7 CLALL Abort all I/O and close all files. Uses RAM
vector at $032C.
UDTIMK $F69B/$F734 $FFEA UDTIM Add 1 to TI clock; reset to 0 if the count
reaches 240000.
SCRENK $E505/$E505 $FFED SCREEN Return the maximum number of screen col
umns and rows in X and Y (40 and 25,
respectively, for the Commodore 64).
PLOTK $E50A/$E50A $FFF0 PLOT Move the cursor to a specified row and col
umn, or read the current row and column
position of the cursor.
IOBASK $E500/$E500 $FFF3 IOBASE Find the starting address of the keyboard
CIA chip registers.
356
Chapter 12
Graphics
Graphics with BASIC
Graphics with Machine Language
The VIC-II Chip
User-Defined Characters
Bitmapped Graphics
Sprites
Chapter 12
Graphics
This chapter starts with the simplest types of graphics using only ordinary BASIC
and progresses to full-screen graphics and motion. All the special graphics effects of
the 64 are covered.
359
Graphics
Tables of these characters are available for reference in the Appendices. Apart
from space and SHIFT-space, which PEEK as 32 and 96, there is no duplication of
character definitions. There is a rather confusing distinction between characters as
they are POKEd into the screen (Appendix I) and character codes that are printed
(Appendix H). PRINT translates many characters in special ways—changing color,
clearing the screen, moving the cursor up and down or to the top of the screen, and
so on. Some, like RETURN, are fairly standard, while others are peculiar to the 64.
Appendix G lists the control functions associated with certain ASCII codes. True
ASCII reserves the first 32 character codes for control information, and Commodore
has borrowed this idea. The displayed characters corresponding to PRINTed codes
are in fact closer to true ASCII than is the case in earlier CBM machines, so conver
sion to true ASCII is easier. However, the upper- and lowercase alphabets are inter
changed in relation to true ASCII.
Only some of the 256 screen characters can be displayed by using statements of
the form PRINT CHR$(N). Since some CHR$ codes are for control purposes, like
cursor-move commands, there are only 128 ordinary printing characters; all are
obtainable by typing key combinations on the keyboard. The reverse feature allows
any of the 256 screen characters to be displayed using PRINT; the ordinary character
is preceded by a {RVS} character.
Within both blocks of 256 characters, reverse characters are arranged in step
with the unreverse characters, but displaced by 128. An easy way to reverse charac
ters in the screen RAM is to set bit 7, or in BASIC terms, add 128 (or, OR 128). Try
POKE 55296,1 (to set the color RAM location for the top left of the screen) then
POKE 1024,128. The fact that this flag or {RVS} is necessary to print a complete
graphics set can be irritating if you have laboriously designed a large graphic display
on the screen. It is impossible to save reverse characters in strings by inserting a line
number and quotation marks before the characters, and then pressing RETURN In
stead, the strings need embedded {RVS} and {OFF} characters to flip between
modes. Block saving of the relevant part of memory may be best. See Chapter 6.
There is no simple translation between unSHIFTed and SHIFTed keys but
usually setting bit 6 of the screen code to 1 displays the SHIFTed version. In BASIC
360
Graphics
terms, add 64 (or, OR with 64). Try POKE 55296,1: POKE 55297,1 to set the color
RAM, then POKE 1024,1, and POKE 1025,65 in lowercase mode.
Note that the pairs of characters on the front right of most keys apply only in
uppercase/graphics mode, the mode selected when the machine is switched on.
After SHIFT-Commodore key puts the machine into lowercase, only the left-hand
graphics symbol can be displayed on the screen, and a SHIFTed key gives the upper
case version, except for a few keys with no SHIFTed version, like @ and *. So the
right-hand set of graphics is unobtainable in lowercase mode. Fortunately, some very
useful graphics are retained; for example, boxes can be ruled on the screen, in either
mode, using Commodore key-A, Commodore key-S, Commodore key-Z, Com
modore key-X, SHIFT-* and SHIFT-.
Toggling between the two modes with SHIFT-Commodore key can be disabled
by PRINT CHR$(8) and reenabled with PRINT CHR$(9), or with POKE 657,128 and
POKE 657,0 which set the relevant flags. If programs with user-defined characters
fail to disable this toggle, SHIFT-Commodore key can produce odd results as charac
ter definitions are looked for in a region $800 bytes away from that intended by the
programmer.
Some graphics symbols are missing from the keys. Thirty-one keys have a pair
of symbols, making 62. Adding pi and SHIFT-space gives 64 graphics characters. But
four characters, only accessible in lowercase mode, also exist and are listed in the
cross-reference table of graphics: they are Commodore key-up arrow (checkerboard
characters), Commodore key-* and SHIFT-E (sloping diagonal lines), and SHIFT-@
(a square root or check mark).
361
Graphics
Try, for example, Commodore key-* and SHIFT-E, or Commodore key-A, Com
modore key-S, Commodore key-Z, and Commodore key-X, or other combinations
of characters, generally in uppercase/graphics mode.
If the string is preceded by quotes, {RVS} and color-change characters may be
included as in this example:
?"Sn(BLK} {SP£CE} {WHT} {SPACE> (RED) {SPACE} {CYN} {SPACE} {PUR} {SPACE}
Program 12-2 fills the screen with a repetitive design based on three shapes
which are designed to match, like tiles, when put next to each other, at least as far as
the character set allows. Try also SHIFT-E, Commodore key-*, and Commodore
key-+ characters. (We'll see more impressive examples when we deal with user-
defined characters.)
Program 12-3 shows how strings can be overprinted to produce the effect of
movement. The colored message is continuously scrolled to the left:
In Program 12-4, four substrings are extracted from the graphics string B$ and
are printed to give four rows of characters, alternate rows moving in opposite direc
tions across the screen:
362
Graphics
Lines 110-140 extract the substrings from a different position in B$ with each
pass through the loop; each substring is like a window moving along the string. Line
150 prints them, first positioning the cursor at the top-left position of the screen and
spacing them out vertically using {DOWN} characters. B$ contains a repeating cycle
of 40 characters (graphics characters separated by spaces) to produce continuity in
the movement of the graphics characters.
In Program 12-5, colors are randomly selected and mirrored, using string arrays,
to give an attractive symmetry when used to color reverse spaces (see line 60). Each
display takes about 15 seconds to generate. Because of the large number of strings
and despite the fact that most are very short, there is a potential problem with gar
bage collection. The CLR in line 70 discards all the strings that have just been used,
allowing the next display to be constructed with a completely uncluttered string
memory, and avoids the problem.
363
Graphics
Finding the offset from the start of the screen for any given line and column is
simple if you take some care in numbering: it is easiest to start at 0, so the horizontal
position is 0-39, and the vertical position is 0-24, with 0 being the top of the screen.
The offset is then 40*vertical position + horizontal position. The subroutine below,
Program 12-7, POKEs the character X with color C into the screen at position H
across and V down.
364
Graphics
The next example program draws a maze. This example (based on the work of
C. Bond in COMPUTED First Book of Commodore 64 Games—which includes an ML
translation) draws a simply connected maze (a maze that is basically a contorted tube
with no isolated islands within it). The algorithm uses space characters to mark
boundaries, so there's an unused border of space characters. This version selects a
random start point, and on finishing, POKEs A and B into the two points furthest re
moved from each other in the maze. Line 114 records the current longest path and
can be deleted. Conversion to ML is needed to make it run faster; white-on-white
plotting, for example, followed by color RAM POKEs is necessary if the plotting pro
cess is to be invisible.
365
Graphics
memory. Type in the following short BASIC program, which loads the machine lan
guage routine. The last line of Program 12-9 holds the ASCII values for the letters
HELLO preceded by a space and followed by a zero byte which signals the end of
the string.
This is the ML
RPT LDX #$00 ; X TO 0. USED AS POINTER
LOOP LDA TABLE,X ; GET NEXT CHARACTER
BEQ EXIT ; 0 SIGNALS END OF STRING
JSR $FFD2 ; OUTPUT CHARACTER
INX ; INCREMENT POINTER
BNE LOOP ; CONTINUE
EXIT RTS
TABLE .BYT "HELLO",0 ; STRING TERMINATED BY 0: MAX LENGTH 255
Run Program 12-9 to load the ML, then type SYS 49152 to print HELLO. Al
though this example uses only straightforward lettering, the technique can easily be
extended to include color change or cursor-move characters so that a 3 X 3 colored
block of characters, say, can be printed at the current cursor position with a SYS call.
366
Graphics
Other related effects include reversing all characters, with ORA #$80, and
unreversing all characters with AND #$7F. Flashing the whole screen is more easily
done by altering the background color rather than the characters—a couple of
POKEs to 53281 ($D021) are all you need.
1 DATA 32,178,177,165,100,133,252,165,101,133,251,
32,155,183,134,143 :rem 9
2 DATA 32,155,183,138,160,0,201,8,144,38,233,8,72,
169,160,145,251,165 :rem 68
3 DATA 251,133,253,165,252,41,3,9,216,133,254,165,
143,145,253,165,251 :rem 67
4 DATA 233,40,133,251,165,252,233,0,133,252,104,17
6,214,170,240,21,189 :rem 99
5 DATA 88,192,145,251,165,251,133,253,165,252,41,3
,9,216,133,254,165 :rem 31
6 DATA 143,145,253,96,100,111,121,98,248,247,227,1
60 :rem 5
10 FOR J=49152 TO 49248;READ X:POKE J,X:NEXT
:rem 224
100 FOR J=0 TO 39 :rem 62
110 SYS 49152,1024+24*40+J,J,J+64:NEXT :rem 23
150 FOR D=l TO 1000:NEXT :rem 12
200 FOR J=0 TO 39 :rem 63
210 SYS 49152,1024+20*40+J,2,SIN(J*t/20)*80+81:NEX
T :rem 130
367
Graphics
Line 10 loads the ML routine into RAM. To make the routine easy to use, three
parameters are input as part of the SYS call. The syntax is:
SYS 49152,bottom of column, color, height
Lines 100-120 draw columns with increasing heights across the screen; lines
200-210 draw a histogram derived from a sine wave. Finding the offset from the
start of the screen is simple with some care in numbering: it is easiest to start at 0, so
the horizontal position is 0-39 and the vertical position is 0-24. Now the offset is
40*vertical position + horizontal position. The parameters to the SYS call in line 110
illustrate this.
u y m m *
KEY: REVERSE, CHR$(18), THEN-
KEY: C-G C-H C-J C-K C-L C-N C-M SH-space
CHR$: 229 244 245 225 182 170 167 160
POKE: 101 116 117 97 246 234 231 224
□ □ i
KEY: SH-O SH-P SH-@ SH-L SH-V SH-+ SH-M SH-N
CHR$: 207 208 186 204 214 219 205 206
POKE: 79 80 122 76 86 91 77 78
□ □ D
KEY: C-X C-Z C-A C-S C-E C-R C-W C-Q
CHR$: 189 173 176 174 177 178 179 17f
POKE: 125 109 112 110 113 114 115 107
368
Graphics
H H
KEY: SH-K SH-J SH-U SH-I SH-W SH-Q
CHR$: 203 202 213 201 215 209
POKE: 75 74 85 73 87 81
J
r o •
KEY: SH-£ C-* C- + C-£
CHR$: 169 223 166 168 220
POKE: 105 95 102 104 92
* ♦ 4
Notes:
1. C- means press the Commodore key and the indicated character; SH- means press the SHIFT key and the
indicated character.
2. There are ambiguities in many of the CHR$ figures—CHR$(227) or CHR$(163) for example might equally
well be chosen. I've preferred the values with a constant difference of 64 or 128 from the screen
POKE/PEEK value.
3. As the characters are made of 8 x 8 dots, a line cannot appear exactly in the center of any character; some
characters, when positioned as neighbors, will not exactly line up together.
4. In lowercase mode, some characters aren't available; those with POKE values 65-90 appear as A-Z. The full
128 graphics characters are obtained by reversing all those in the table, whether by PRINTing the reverse
character first or by POKEing the values listed here + 128.
5. Four extra graphics, obtainable only in lowercase mode, are:
Double-Density Graphics
This program exploits the fact that all 16 possible graphics with internal quadrants
exist on the 64.
10 DATA 32,155,183,134,5,32,155,183,134,6,32,155
:rem 50
369
Graphics
11 DATA 183,134,2,165,5,-48,89,201,80,176,85,165,6
:rem 120
12 DATA 48,81,201,50,176,77,169,0,133,4,169,50,229
:rem 163
13 DATA 6,70,5,38,4,106,38,4,133,6,166,4,169,0,133
:rem 146
14 DATA 4,133,210,56,38,4,202,16,251,165,6,10,10
:rem 30
15 DATA 101,6,10,38,210,10,38,210,10,38,210,133,20
9 :rem 167
16 DATA 169,4,101,210,133,210,164,5,177,209,162,15
:rem 145
17 DATA 221,117,192,240,4,202,16,248,96,138,5,4,17
0 :rem 203
18 DATA 189,117,192,145,209,32,36,234,165,2,145,24
3 srem 219
19 DATA 96,32,126,123,97,124,226,255,236,108,127
:rem 74
20 DATA 98,252,225,251,254,160 :rem 197
100 FOR J=49152 TO 49284:READ X:POKE J,X:NEXT
:rem 16
0 12 3 4 5 6 7 8 9 10 11 12 13 14 15
The ML routine replaces one of these graphics symbols with another, depending
on the position of the "dot" to be plotted, so the whole screen in effect has a resolu
tion of 80 X 50 small squares. The color can be set in any pair of squares. Graphics
of this type don't compare to full hi-res pictures, buf tfiey have the advantage of be
ing completely compatible with BASIC, needing no special POKEs or bitmap calcula
tions. Note that the algorithm is designed to ignore text and other characters which
aren't among the 16 quadrants, so BASIC text can be intermingled with dot
diagrams.
The syntax is SYS 49152,X,Y,COLO2? where X=0-79, Y=0-49, COLOR=0-15.
All parameters are evaluated, so using variables (SYS P,X,Y,Q is accepted. The point
X=0, Y=0 starts at the bottom left of the screen.
370
Graphics
When the screen is cleared, screen RAM is filled with space characters. The fore
ground color is now irrelevant; only the background color shows. The original ROM
version of the 64 fills color RAM with white after clearing the screen, so that charac
ters POKEd to screen RAM show up in white. Some newer 64s fill color RAM with
the background color, stored in 53281, so that POKEs to a cleared screen are in
visible. The most recent version of the 64 fills color RAM with the current character
color (stored in 646), so POKEs will be visible.
Because of these ROM changes, the effect of a simple screen POKE depends on
when your 64 was built; the POKEd character may appear white, invisible, or the
same color as the cursor. To make screen POKEs work correctly on all 64s, POKE
color RAM with the desired value whenever you POKE a character into screen
memory.
ML modifications of color RAM can give many effects. The following program
gives the effect of motion by drawing colored bars, then cycling through RAM
colors:
The ML adds 1 to each color location. Line 90 delays, waits for the raster-line to
be offscreen, then updates color RAM. Raster scanning is discussed in detail later.
371
Graphics
372
Graphics
10 FOR J=49152 TO
49214:READ X:POKE J,X:NEXT
:rem 217
20 DATA 162,22,189,235,3,157,234,3,189,235 :rem 25
30 DATA 215,157,234,215,232,208,241,189,234:rem 70
40 DATA 4,157,233,4,189,234,216,157,233,216:rem 73
50 DATA 232,208,241,189,233,5,157,232,5,189:rem 77
60 DATA 233,217,157,232,217,232,208,241,189:rem 74
70 DATA 232,6,157,231,6,189,232,218,157 :rem 136
80 DATA 231,218,232,208,241,96 :rem 198
100 FOR J=1063 TO 2024 STEP 40:POKE J,160:NEXT
:rem 195
110 R=RND(1) :rem 135
120 IF R<.4 THEN X=X+1 :rem 138
130 IF R>.6 THEN X=X-1 :rem 145
140 IF X>15 THEN X=15 :rem 74
150 IF X<0 THEN X=0 :rem 221
160 H=X*40 :rem 2
170 FOR J=39 TO 39+H STEP 40:POKE 55296+J,5:NEXT
:rem 87
180 FOR J=79+H TO 1000 STEP 40:POKE 55296+J,6:NEXT
:rem 178
190 SYS 49152:GOTO 100 :rem 162
373
Graphics
Screen
A B C D ? ? ? ?
1 2 3 4 Scroll A B C D
E F G H 1 2 3 4
5 6 7 8 Down E F G H
B
1C D 1 2 |3|4|E| F | G
1" I5 6 | 7 8
Screen
A B C D B C D ?
1 2 3 4 Scroll 2 3 4 ?
E F G H F G H ?
5 6 7 8 Left 6 7 8 ?
|a|b|c D 1 2 3 | 4 | E | F G | H 5 6 7 8
It is easy to deduce that for the 64, scrolling up or down requires that the entire
screenful of characters, excluding a set of 40 at the end, be moved 40 places along.
Scrolling right or left, in the simplest case, only requires every screen character but
one to be shifted one place along. This is what the left-scrolling program does.
Simply enter the line 0 SYS 49152: GOTO 0 and run it to see the screen roll sideways.
With scroll down, the characters at the bottom-right must be moved first; other
wise, because there is no temporary storage and characters are moved directly into
the screen, characters will be overwritten before they have been moved.
374
Graphics
Speedy ML routines help. If screen RAM and color RAM are both moved, the
fastest methods use LDA $0401,X:STA $0400,X:LDA $D801,X:STA $D800,X or simi
lar commands to move each character; this takes about 1/50 second on the 64.
Color RAM
The border and screen background colors are straightforward. Color RAM is a more
difficult concept, but seems natural enough after a time. It is a block of RAM
paralleling the screen RAM; each screen location has a corresponding color RAM
location, whose lower nybble (four bits) determines the character's foreground color;
thus, each screen character may have its own foreground color. (All characters have
a common background color determined by the register at 53281.)
The colors that correspond to numbers in the foreground and background reg
isters, and in the color RAM locations, follow:
375
Graphics
Multicolor Mode
Understanding user-definable characters is essential to getting the most from mu
color mode. However, the general idea is fairly easy to grasp. It is another Com
modore compromise: In order to get more color into the screen, resolution is cut
half. Below is a discussion of how this works with ordinary graphics; the principl
the same in high-resolution mode.
Normally, a one in a character definition shows up in the foreground color, a
a zero shows up in the background color; so only two colors are obtainable withii
each 8X8 dot character area. Multicolor mode allows four colors to be selected ]
character, at the cost of halving the horizontal resolution. Instead of 8 X 8 dots, i
offers 4X8 "wider" dots, each of which can take one of four colors.
Multicolor mode is enabled by setting bit 4 of VIC-II register $16 to 1. This is
done by using POKE 53270,PEEK(53270)OR16 (normally, POKE 53270,216). The
following command switches back to normal mode: POKE 53270,PEEK(53270)
AND239 (normally, POKE 53270,200).
The above POKEs enable and disable multicolor mode globally, over the who
text area; but it must also be enabled on a character-by-character basis to have an)
effect. This is done by the value in the corresponding color RAM location: If it is '
from 0 to 7, then the character appears in ordinary mode, and if it is from 8 to 15,
then the character will be in multicolor mode. In other words, bit 3 in a color RAM
location determines whether the corresponding character is in ordinary or multicolor
mode. Thus, the screen may simultaneously display multicolored and ordinary
characters.
To get the feel of this, type some lettering in several colors, including the less
saturated Commodore-key colors. Enable multicolor mode with the POKE given
above. You'll see that characters in black through yellow are unchanged, while those
376
Graphics
in orange through light gray alter dramatically, because bit 3 of their color RAM has
this dual function.
The source of the color in each two-dot unit is shown by the following table:
Bit Pattern: Color Specified By: Address of Register:
0 0 Background 0 color register 53281 $D021
(screen background color)
0 1 Background 1 color register 53282 $D022
1 0 Background 2 color register 53283 $D023
1 1 Lower three bits of color RAM
(character color)
The three registers can take values from 0 to 15; the three bits specified by color
RAM select values 0-7. Notice that units containing 00 appear as the background
color whether the display is in standard or multicolor mode. Note that the border
color in 53280 is independent of the background colors, unlike VIC-20's multicolor
mode.
It follows from this table that an orange (Commodore key-BLK) character will
be displayed as black when multicolor mode is enabled—try it with reverse-space
block in orange. Similarly, a light green character switches to green.
Consider how the character A is defined in ROM:
Normal: Multicolor: Displays As:
0 0 0 110 0 0 00 01 10 00 BG0 BGl BG2 BG0
0 0 11110 0 00 11 11 00 BG0 CR CR BG0
0 110 0 110 01 10 01 10 BGl BG2 BGl BG2
0 1111110 01 11 11 10 BGl CR CR BG2
0 110 0 110 01 10 01 10 BGl BG2 BGl BG2
0 110 0 110 01 10 01 10 BGl BG2 BGl BG2
0 110 0 110 01 10 01 10 BGl BG2 BGl BG2
0 0 0 0 0 0 0 0 00 00 00 00 BG0 BG0 BG0 BG0
The first illustration shows how the definition is interpreted in normal mode: ze
ros display in the background color and ones display in the foreground color, speci
fied by the character's color RAM.
The second and third illustrations show how the bits are interpreted as grouped
in pairs by the 64 in multicolor mode. The abbreviations BG0, BGl, and BG2 repre
sent the three background color registers, which are set to 6 (dark blue), 1 (white),
and 2 (red), respectively, on power-up. (The SX-64 sets BG0 to white, however.) CR
is color RAM, which is 14 (pale blue) on powering-up the 64. Note that the three
background colors apply over the whole screen area; only the character color can
vary from character to character. Therefore, when designing multicolor graphics, se
lect the three colors you wish to spread most widely on the screen, and let the
character color vary locally.
Assuming the 64 has its power-up values, enter POKE 53270,216 to enable
multicolor mode. All characters will be displayed in multicolor mode, since their
color RAM value is greater than 7. Assuming the relevant registers have their power-
up values, BG0 will show up as dark blue, BGl as white, BG2 as red, CR as a dark
blue (produced by the pale blue value with bit 3 stripped off). This is what the col
ors should be, but they may not show up particularly clearly on your TV or monitor.
377
Graphics
The cursor disappears because the reverse space character is a block of bit pairs in
the pattern 11; the color is given by color RAM and thus shows up in multicolor
mode as dark blue. Type Commodore key-GRN to make it reappear: printing will
continue in multicolor mode; CTRL-GRN will also make it reappear, but causes
printing to continue in standard mode because of the different color RAM settings.
Enter POKE 53283,1 to make BG2, as well as BG1, white; the multicolored charac
ters now contain large areas of white. Type Commodore key-WHT followed by a
few more characters: even larger areas now show as white, as BG1 and BG2 and CR
are all now white. Usually, of course, contrasting colors will be used. CTRL-WHT
will select a foreground color value less than 8; type this and then further characters:
these display in standard mode, because of the color RAM value, and are unaffected
by BG1 and BG2 settings.
These multicolor characters have a chunky appearance, since they have half the
horizontal resolution of standard characters. They can be used for decorative borders
and designs, and for graphics. You may need to experiment to find the best combina
tions of colors for this effect. They are easier to use than user-defined characters and
take up no extra space in RAM. Finding characters which look right may be difficult,
though.
With some work characters in multicolor mode can produce impressive results.
For example, BGO may be set to 12, and BG1 and BG2 to 8 and 14, giving orange
and light blue and the local colors on a medium gray background, allowing, say, a
gray sky, orange ground, and light blue middle-distance, with small objects in any of
the eight main colors.
The following BASIC program lets you experiment with all combinations of
BGO, BG1, BG2, and CR. It displays almost the entire character set twice, once at the
top of the screen in standard mode and again below it in multicolor mode. The func
tion keys fl, f3, f5,17 advance the values in the three register values and the color
RAM of the multicolor mode characters.
You may prefer to experiment with character sets in two colors only; if so, mod
ify the program to POKE the background registers with 0, and make the function
keys toggle, with POKE 53281,1 -(PEEK(53281) AND 15) or a similar statement.
The AND 15 is necessary to remove the high nybble, which varies. Also try replac
ing line 250 with 250 NC = 1-NC.
378
Graphics
Multicolor mode is probably the 64's most popular graphics mode. Although in
theory resolution is halved, in practice TV limitations mean that 320 individual col
ored dots (that is, 40 sets of 8) aren't really distinguishable across a TV screen. The
64's Commodore key-+ character, for example, is not made of alternate 0's and l's.
It's composed of alternate 00 and 11 pairs. This is why multicolor characters often
look similar to their normal equivalents, and why normal characters—Commodore
key-Z, for instance—often appear thicker than you'd expect.
Even with multicolor mode enabled, characters don't have to be displayed in
multicolor mode, which adds to the mode's versatility. Programs can be developed
using PRINT and/or POKE to move characters around; such programs will work just
as well if the graphics are redefined in multicolor form. This requires more work,
since character definitions must be loaded into RAM and the VIC chip made to ac
cess them. However, this is still easier than full bitmapping.
The displayable characters are the first 64 in the character definition area. The
foreground color is set by the color RAM nybble. In summary, each of the 1000
characters' foregrounds can be set to colors 0-15; each background can be set to one
of four colors, each of which may be 0-15; and only 64 differently shaped characters
can be displayed, each in two colors at most.
379
Graphics
Extended background color mode can now be selected by typing E, and multi
color mode by typing M. Pressing f8 advances the value in register BG3, while the
other keys function as before.
You'll see the reduced character set and extra background colors clearly. The
small available range of character shapes makes this mode unsuitable for most pur
poses. But if you're content with numerals, uppercase letters, and punctuation sym
bols, extended background color mode allows colored highlighting which is
otherwise much harder to program. The unSHIFTed, SHIFTed, reverse, and reverse-
SHIFTed characters (as ordinarily entered) will be displayed on background colors as
stored in 53281-53284. Note that unSHIFTed space, conveniently, appears as the de
fault background color.
380
Graphics
shifts whole characters and is rather jerky. We can improve on this with the VIC-II
chip's facility to move the screen. It positions the screen with single-dot resolution,
allowing a maximum of eight dots of movement, so the whole picture can be shifted
slightly. This allows screen scrolling, but since sprites have to be handled separately,
the technique can be difficult. Bits 0-2 of $D011 (53265) set the vertical position,
and bits 0-2 of $D016 (53270) set the horizontal position.
Here's the method for upward scrolling: the screen is moved up by one row of
pixels (dots) seven times; on every eighth movement the screen moves back to the
lowest position, and the screen characters are scrolled up by one whole character.
Normally, this gives a jiggling motion at the top and bottom borders, which can be
reduced by matching the border and background colors, or, better, by using the VIC
chip to cut out the picture edges.
The screen scroll must be fast; otherwise, the TV scan will display part of the
old picture, giving an uneven effect. The first demonstration, Program 12-21, is in
BASIC.
Program 12-21 repeatedly moves the screen up, then flips the register back
down when the register changes from 0 to 7. Line 20 helps insure that the VIC chip
is altered only when the TV is scanning outside the picture; the timing can be fine-
tuned by putting in extra colons.
Black bands may appear at the top or bottom of the picture. When the Y value
is 3, as it is on power-up, the screen fits perfectly, but not otherwise. The VIC chip
allows the edges of the picture to be suppressed. Bit 3 in $D016 (53270) selects 38
columns when set to 0, and bit 3 in $D011 (53265) selects 24 rows when set to 0.
This affects only the appearance onscreen. The 64 still internally assumes a 40 X 25
format.
Add the line 5 POKE 53265, PEEK(53265) AND (255-8) to Program 12-21 and
run it. The black bars are no longer displayed and the screen has one row less. In or
der to demonstrate scrolling, we need to scroll the screen up at regular intervals. A
simple PRINT statement, or SYS 59626, will scroll up a whole line. BASIC has no
sideways scroll, so a simple demonstration has to scroll up.
Add 25 IF V=7 THEN READ X$: PRINT "{HOME}{25 DOWN}" X$; to the
program (don't forget the semicolon), plus some DATA statements with strings.
You'll see the strings scroll up, after being written invisibly to the bottom line of the
picture. However, it isn't possible to get a completely smooth scroll by this method,
unless the text or graphics doesn't change. This is because screen scrolling typically
takes about 1/25 second, so a mixture of the new and the old screen is displayed.
This doesn't matter if the text is identical at each line of the screen, as you can see
by altering line 25 to print some fixed X$ string.
Improving this requires the use of ML. The problem is the time spent scrolling
the screen. (The situation is worse for bitmapped screens, which have more data to
381
Graphics
move.) One scan of the TV picture takes 1/60 (U.S.) or 1/50 (U.K.) second. A full
screen scroll must take less than about 1/50 second to be invisible. A full screen,
and color RAM, cannot be scrolled in this time using BASIC; the example program
above moves characters, not color RAM, because of this.
Load and run Program 12-22, then wait for the scrolling to begin. Each SYS call
in the program scrolls the screen up one dot; it does this by checking the value in
the Y register and decrementing it if it exceeds 0. When it becomes 0, Y is replaced
by 7 and a scroll routine is called. The ML routine is located at 49152, so SYS 49152
can be used as a versatile scroll (note that it's not relocatable). Line 70 insures that
the first PRINT statement in line 110 is in the invisible, bottom screen line, so the
scroll is completely smooth. It's easy enough to modify the register checking and
character shifting to scroll left.
382
Graphics
User-Defined Characters
This section explains how new character sets can be created for the 64, by exploiting
the VIC-II chip and controlling BASIC'S memory map. Some of the earlier explana
tory part also applies to the creation of bitmapped graphics, dealt with in the next
section, and will not be repeated there.
383
Graphics
graphics-drawing programs which allow any part of the screen to be drawn on, must
use bitmapping, where 8000 bytes are needed—nearly four times as many as user-
defined character sets require. (Note that reduced-sized screens in either mode per
mit less memory to be used.)
A character editor is a program that helps you design your own user-defined
characters, allowing up to 256 individual 8X8 dot characters to be designed. A full
screen graphics editor is appropriate to bitmapping. Examples of each are included in
this book. Sprites, if they're used, must also be defined within the VIC-II's 16K bank.
Bit 0 of $D018 is unused; it is always set to 1. Bits 2-7 of $DD00 are irrelevant to
graphics.
The Screen start position is controlled by bits VM13-VM10 (the four high bits of
53272), and by Bl and B0 (the two low bits of 56576). We can find the actual full 16-
bit address by listing bits 0-15 and inserting the register values, like this:
Bit Number: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 10
Screen Start: Bl B0 VM13 VM12 VM11 VM10 0000000000
Note that bits Bl and B0 are inverted (that is, a 1 value is treated as 0), and that all
bits not controlled by the registers are set to 0.
Usually, $D018 and $DD00 contain 12 and 191 (=%0001 0101 and %xxxx
xxll). Thus, bits VM13 to VM10 are 0001, and bits Bl and B0 are both 1. The screen
therefore starts at %0000 0100 0000 00000 = $0400. Because bit VM10 is the least
significant programmable bit controlling the screen position, and 210=1024, screen
positions can be selected only to the nearest IK. Where multiple screens are used,
they can be stored next to each other, but not overlapping, in RAM.
The start of the character definitions is controlled by bits CB13-CB11, and by Bl
and B0, in exactly the same way:
Bit Number: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
Screen Start: Bl B0 CB13 CB12 CB11 VS10 0 0 0 0 0 0 0 0 0 0
384
Graphics
Again, Bl and BO are inverted, and all lower bits are 0. The reset 64 has $15 in
$D018, so, taking the right nybble 0101 and dropping bit 0, we see that CB13-CB11
are 010. The characters, therefore, are taken from %0001 0000 0000 0000 = $1000,
the RAM image of the character-generator ROM. SHIFT-Commodore key toggles
$D018 between $15 and $17, and we can see now that the second value sets the
right nybble of $D018 to %0111, so CB13-CB11 are now 0111. The characters now
come from %0001 1000 0000 0000 = $1800.
Bit CB11 has the least effect on assigning the memory region from which VIC
draws its character definitions, controlling it to the nearest $800, or 2K, bytes. In
other words, character memory is treated as though it's divided into 2K chunks by
the 64's VIC chip. This allows multiple character sets to be placed in memory ad
jacent to each other. One example is the built-in graphics and lowercase sets, which
are adjacent in memory.
385
00 Figure 12-4. VIC-II Screen and Character Management o
ON
3
Character Base Address
40 cells
/
/ A B C
/
/
/
/
/
/
/
/
/
' Display
VIC-II *
w
w
\\
\X
\ \
\ \
\ \.
\ \.
\ ^-^
i Start Address
Screen RAM:
Bytes: 999
Chr. Chr.
Image Imagei
0 400 800 4000 8000 9000 A000 C000 D000 E000 FFFF
I
1 Free RAM RAM
j
RAM
43/44 = Prog. Start 45/46 = Prog. End 5 i/56 = End of BASIC Area.
I/O ROM
VIC-II's current bank at any one time must contain screen, character, and sprite
data. But BASIC normally starts at $0800 and ends where there's hardware, either at
$9FFF or, if an autorun cartridge is present which returns to BASIC, at $7FFF. To
gether, these facts mean that a long BASIC program with user-defined characters
must either store characters at the low end of BASIC, followed by BASIC itself, start
ing higher in RAM than usual; store them after the BASIC program, but before BA-
SIC's area for variables; or store them higher in memory, in banks 1, 2, or 3. In any
of these cases, it's necessary to alter some of the pointers in (43-44), (45-46), and
(55-56) to alter the boundaries allocated to BASIC, unless the characters are stored
under ROM, with the screen RAM at or near $C000. (This is quite simple, though
it's best to avoid the I/O area. Note that there's a possible problem if you use the
area to edit characters, because, being under ROM, they're difficult to PEEK in the
usual way, and are therefore difficult to save; VIC-II, of course, is designed to PEEK
them without any problem.)
387
Graphics
Entering 0 selects the normal default, bank 0, so there's no change. Note the use of
3—X which converts 0 to 3; this allows for the fact that the two controlling bits are
inverted.
Entering 2 gives garbage, because bank 2 now treats RAM at $8400 as the start
of screen. The characters are displayed as normal 64 characters, however, because
the ROM character set is active in this bank. Entering 1 or 3 displays a screenful of
characters which aren't properly defined—they're from RAM starting at $5000 or
$D000, respectively. Try exiting the program and executing FOR J=33792 TO 34047:
POKE J,X: X=X+1: NEXT which puts 0-255 into $8400 on. You'll find that bank 2
now displays all 256 normal characters at the top of the screen, color RAM
permitting.
Moving the screen. The following line will move the screen in sixteen IK steps
within its current bank (insert a value in the range 0-15 for X):
POKE 53272/(PEEK(53272) AND 15) OR 16*X
The two lines below will allow you to instruct VIC-II to treat $0, $0400, $0800,
and so on, as the screen start. Enter 2, for example, to select $0800. You'll see the
BASIC program at the top of the screen. (SHIFT-Commodore key may make it
clearer.)
0 INPUT S: POKE 53281,247: REM MAKE CHRS VISIBLE
1 POKE 53272,(PEEK(53272) AND 15) OR 16*S: GOTO 0
Enter 0 to make the screen from the zero page on, so the region including the stack
and the keyboard buffer is displayed. Entering 1 returns the screen to the normal
position.
Moving the character definitions. Enter and run the following line:
0 INPUT C: POKE 53272,(PEEK(53272) AND 240) OR 2*C: GOTO 0
As C varies from 0 through 7, the character definitions are taken by VIC-II from $0,
$0800, $1000, and so on, in 2K steps. Two interesting examples are C=0 and C=3:
the former draws character definitions from the zero page, which means that some
of them continually fluctuate with background BASIC activity. When C is 3, charac
ters start from $1800, so the screen goes into lowercase mode. Toggling with
SHIFT-Commodore key alternates with uppercase.
Bank 0 characters can't occupy $1000-$lFFF, since VIC-II fetches data in this
area from ROM. They could be positioned anywhere else, subject to some restric
tions. RAM from $0800 on can be used, but the start of BASIC would have to be
moved up; $00 is usable, too, but an entire set of 256 characters couldn't be defined
Bank 1 characters can be placed in any of the eight available areas. Bank 2 is accept
able except for $9000-$9FFF, where VIC-II uses the ROM sets. Bank 3 is all avail
able, though $D000-$DFFF RAM can be POKEd only after switching out I/O.
388
Graphics
Moving the character ROM into RAM. This requires care to avoid I/O and
ROM selection clashes by VIC-II. Bit 2 of location 1 controls whether ROM or I/O
appears from $D000 on. As an example, delete GOTO 0 from line 0 directly above,
and add the following:
1 POKE 56333,127: POKE 1,51
2 FOR J=0 TO 2047: POKE 12288+X,PEEK(53248+X): X=X+1: NEXT
3 POKE 1,55: POKE 56333,129
4 POKE 56,48: CLR
Line 2 moves 2K of character ROM from $D000-$D7FF down to $3000-$37FF.
Lines 1 and 3 control the hardware line which turns on the ROM. Note that one of
the POKEs turns off IRQ interrupts, which is necessary since they make use of the
I/O chips. NMI interrupts aren't turned off; when using this, don't press
RUN/STOP-RESTORE, or you'll crash the program. Line 4 lowers the top of BASIC
to $3000, protecting the character set in RAM, and incidentally dramatically reducing
the free bytes available to BASIC.
Run the program, entering 6 to point the VIC-II to fetch its characters from
$3000. When READY comes back, toggling with SHIFT-Commodore key produces
garbage characters—since the lowercase set is not copied.
Toggle back to the readable characters and enter the following direct mode line:
FOR J=0 TO 255: FOR K=0 TO 7: POKE 14336+8*J+K,PEEK(12288+8*J+7-K): NEXT:
NEXT
This copies our RAM characters into $3800 on, but inverts them. Now
SHIFT-Commodore key toggles between ordinary and upside-down characters.
Backward and other modified characters are also feasible. It's possible to have 80-
column alphanumerics on the 64. Unfortunately, they're truly readable only with a
monitor, not a TV.
Saving and reloading character definitions. Character definitions (and screens)
can be saved as DATA statements or as sequential files (written by PRINT#, read
back by INPUT#) or, most efficiently, as program files, that is, as a block of data to
be loaded back, typically with a forced (nonrelocatable) LOAD like this at the start of
a program:
389
Graphics
The principle is exactly the same as saving BASIC followed by ML. All that's
needed is to alter the address in (45-46) to point after the characters. This will save
satisfactorily, and will also run properly when reloaded, assuming it loads back into
the same RAM area.
Figure 12-7 shows characters at the end of BASIC. This is a typical situation
when bank 0 is used, and character definitions are stored from $3000 (if there are
two sets) or $3800 (if there's just one).
It's easy to save such a program: just POKE 45,0: POKE 46,64 to force the 64 to
save from BASIC start, right up to $4000. On LOAD, this program will store its vari
ables after BASIC and will work perfectly well, becoming converted into the first
type of program/character definitions. In fact, it may well have more RAM than it
would have if its variables were forced to exist below the characters. But it makes
sense, particularly with tape, to keep the characters after BASIC without too great a
gap, as in the first example; saving right up to $4000 means saving about 14K bytes.
The only problem where redefined characters are saved above BASIC and vari
ables is that editing BASIC will probably disrupt the display. Changing a line or two
moves the whole character set in RAM, so the characters alter with the program
length. Obviously, this doesn't matter with a finished program, but where it's im
portant to be able to edit, include this line:
0 POKE 45,000: POKE 46,000: POKE 55,0: POKE 56,48: CLR
This will allow you to put different values into 45 and 46. (They must be put in just
before saving the program. The three zeros allow any figure to be input without
changing the program's length, 013 for 13, for example.)
391
Graphics
Character Editor
The following character editor, Program 12-24, processes 2K bytes, the definitions of
the first 256 characters in ROM, which are stored at $3000-$37FF (12888-14335).
The finished definitions can be stored and reloaded from tape or disk, giving a
permanent record of your new characters.
392
Graphics
393
Graphics
394
Graphics
When you run the character editor, you're asked FETCH CHARACTERS FROM
ROM? Enter Y to call an ML routine to copy character ROM into RAM. Otherwise,
characters already present at $3000 are retained. The screen displays all 256 of the
RAM characters in the bottom half of the screen. The top displays an enlarged ver
sion of the current character plus its screen code—for example, @ is shown as 0—
and a list of the four current color settings. (All character colors are the same.)
There's also a central scratch-pad area where several graphics can be placed so that
their joint effect can be checked. This is useful where several graphics characters to
gether build a larger composite character.
Editing is done by moving the inverted cursor with the usual cursor control keys
on the enlarged 8X8 diagram, and typing the space bar to invert the dot beneath
the cursor. The result of the modifications is instantly visible in the display at the
screen bottom and on the scratch area.
New characters are selected for editing by moving the cursor to the lower part of
the screen and typing an equal sign (=) over the chosen character. Then home the
cursor and edit.
Other commands are:
C Clears a character, setting all bits 0.
D Draws the current character in the scratch area.
I Inverts the current character's bits.
T With the cursor over a character in the bottom half of the screen, transfers
the current character's definition there—now there are two of them.
f2 Advances the screen background color.
f8 Advances the text color.
M Toggles between ordinary and multicolor modes. The text color must be
from orange to gray 3 for multicolor mode to show. When it does, keys fl,
f3, f5, and 17 set pairs of points to 00, 01, 10, and 11, corresponding to the
four colors. Keys f4 and f6 also advance background colors 1 and 2.
L and S Load and save (respectively) the character set, allowing your choice of file
name and device.
Applications could include the creation of chess and other game pieces, special
alphabet sets, musical and other notations, monsters, bombs and so on, for arcade
games If you're going to PRINT your graphics, remember that the second batch of
128 characters on the screen is generated with {RVS}, even though characters need
The early part of the program handles initialization. Lines 200-250 handle the
keypresses and call appropriate subroutines.
395
Graphics
Bitmapped Graphics
Bitmap mode gives the best graphics available on the 64. However, 64 BASIC has no
specific commands to handle this mode: in fact, it would be possible to program a 64
for years and never discover that bitmap mode existed.
Bitmapping allows each dot on the screen to be controlled. Since VIC-II maps
the screen into individual characters of 8 X 8 pixels, and displays 40 X 25 of these
characters, there are 320 X 200 = 64000 addressable dots. (In practice, resolution
isn't this good, because, for example, ordinary TVs cannot display alternate on/off
dots without color interference, and some color combinations don't have sufficient
contrast to be properly distinguishable, even on color monitors.) These figures still
apply if the screen is narrowed or shortened—see the earlier section on smooth
scrolling—or mixed with ordinary text by the use of split-screen techniques ex
plained below, but offscreen graphics are obviously less important.
Bitmapping is a more accurate expression than high resolution, with which it's
often confused. Bitmapping resolution is in fact identical to that of normal characters.
The distinction should be between high resolution and multicolor mode.
A bitmap is 8000 consecutive bytes (not quite 8K, which is 8192 bytes), enough
to map the whole screen as 64,000 bits. The display is treated by VIC-II just like
1000 consecutive user-definable characters. Bitmap mode is selected by bit 5 in
53265 ($D011).
In bitmap mode, since each bit in the bitmap can only be on or off, just two
alternatives exist for each point on the screen, which means a choice of two colors.
VIC-II allows each of the 1000 characters 2 independent colors, selectable from the
full range of 16 colors. In each bitmapped character, the 2 colors are not set in color
RAM, which has only one usable nybble. Instead, they're controlled by screen RAM,
the area, usually starting at $400, treated by BASIC as the screen, which of course
has two nybbles available for each character. This new usage can be confusing.
Unlike all other modes, the screen's normal background color setup is no longer
operative.
You don't control the color of every dot on the screen, though. One reason is the
memory requirement: a choice of 16 colors per bit would require 32K of RAM to
store the full bitmap.
Multicolor mode is selected by bit 4 in 53270 ($D016). Where multicolor mode
is used with bitmap mode, there's the usual trade-off—pairs of bits together allow a
choice of 4 colors. Each of the 1000 characters has a choice of 3 colors, plus a com
mon background color. All the colors are selected from the full palette of 16 colors.
Any 64 graphics design program, and generally flight-simulators and games
where the entire screen is filled without repetition, must be bitmapped. (Sprites can
sometimes give a similar impression, though.) The high-resolution bitmap mode has
finer resolution than the multicolor version, but is less colorful—except in the sense
of being more prone to unwanted color fringing. For example, you may find an
adventure game including black line drawings on white, which are colored by a fill-
in color (unless they're on a boundary), since three colors can't coexist in one 8 X 8
area. Multicolor mode builds the picture from 160 short horizontal bit pairs by 200
down. This is more versatile than regular 64 multicolor graphics; the background
color is in common, but all the other three colors are independently variable within
396
Graphics
every 8X8 block. This allows the character boundaries, where colors can change, to
be made imperceptible, at the cost of some loss in resolution.
397
Graphics
VIC Bank 2
0 1 3
Calculations
One way to visualize bitmapping is to imagine that all 8000 bytes are divided into
25 sets of 320 bytes. Each 320-byte block corresponds to a horizontal line, eight dots
high, on the screen. Another way to visualize bitmapping is to consider the infor
mation as 1000 eight-byte chunks of memory-defined characters 0-999 in the famil
iar 40 X 25 layout.
To control a screen dot with given X and Y coordinates, we have to determine
which bit of which byte to process. Let's consider X and Y relative to the top left of
the screen, with X=0-319 and Y=0-199. The object is to calculate where a particu
lar point, say, X=100, Y=50, will be. Points with Y from 0 to 7 lie in the top row of
398
Graphics
characters; Y from 8 to 15 must be in the next row, and generally Y has INT(Y/8)
complete pixel rows above it, each of 320 bytes. Now, points with X from 0 to 7 fall
within the first character in any row; X from 8 to 15 corresponds to the next character,
and so on. Generally, the number of characters along a row is INT(X/8). The point
can be in one of eight bytes in the character, determined by the remainder after divid
ing Y by 8; in BASIC, this is Y AND 7 for our range of Y values. If MAP is the vari
able storing the start of the bitmap, this is the address of the byte containing X,Y:
MAP + 320*INT(Y/8) + 8*INT(X/8) + (Y AND 7)
This expression can be improved to the following line, which BASIC evaluates faster:
MAP + 40*(Y AND 248) + (X AND 504) + (Y AND 7)
Finally, the actual bit within the target byte is 7 - (X AND 7), because X AND
7 gives 0-7, increasing with X, but the bits are arranged in the sequence 7-0. Bit 0-7
has to be set or cleared to set or clear the screen pixel, with:
POKE AD, PEEK (AD) OR 2t(7-(X AND 7)): REM SETS PIXEL
The 64 displays the first 8000 bytes, from $0, in bitmap mode. Character definitions,
seen by the VIC-II at 4096 and following, are displayed in the bottom half of the
screen. The zero page and stack are displayed at the top of the screen, so some of
these locations continually change. The screen RAM, at $400-$7E7, is displayed in
the top eighth to quarter of the screen; you'll see changes in the display if you cursor
around the screen and type keys. Note that the colors are mainly red and black be
cause spaces PEEK as 32 (=%0010 0000), so the high nybble is red, the low nybble
black. Nonspace characters appear in colors depending on the characters' PEEK
values.
Multicolor mode is more complex. If you select it, you'll see the common blue
background, the mode extending over the whole screen, and the light blue of the or
dinary color RAM.
Bitmapping at $6000. Add these lines to the above example to alter the bitmap
and color locations:
20 POKE 56576,150: POKE 648,92: POKE 53272,121: REM BITMAP PARAMETERS
30 FOR J=6*4096 TO 6*4096+7999: POKE J,l: NEXT: REM POKE BITMAP
40 FOR J=23553 TO 24551: POKE J,l: NEXT:REM POKE COLOR
Line 20 starts the bitmap at $6000, in bank 1, and starts its color just below at
$5C00. Line 30 fills the bitmap with 1, giving 40 fine vertical lines on the screen.
Line 40 sets the colors to black and white. This part is faster, by eight times, than
filling the screen. (Note that line 30 could clear the screen by POKEing in 0 or 255.
Random numbers would fill the screen with random dots.)
After adding the following line, BASIC strings will move down to overwrite the
bitmap, then the color, giving textilelike patterns.
399
Graphics
Inserting a line like 45 POKE 55, 0: POKE 56, 92: CLR prevents this. This sets the
top of BASIC memory at $5C00, protecting the color and bitmap information.
Bitmapping at $2000. Program 12-25 POKEs 5 into the color area, setting colors
to green (since the low nybble is 5, which determines the color of bits cleared to 0)
and black (since the high nybble is 0, for bits set to 1). Lines 50 to 70 scan across the
screen, plotting dots. Note how the Y value is forced into the range 0-200. This can
be made automatic by picking out the maximum and minimum within a loop.
10 POKE 53265,PEEK(53265) OR 32
20 POKE 53272,25:MAP=8192
30 FOR J=MAP TO MAP+7999:POKE J,0:NEXT
40 FOR J=1024 TO 2023:POKE J,5:NEXT
50 FOR X=0 TO 319:Y=SIN(X*t/80)*50+100
60 AD=MAP+40*(Y AND 248) + (X AND 504) + (Y AND 7)
70 POKE AD,PEEK(AD) OR 2t(7-(X AND 7)):NEXT
80 GET R$:IF R$="" THEN80
Program 12-25 puts the bitmap at $2000, but keeps the BASIC screen RAM so
READY prints as colored blocks. Try LIST when the program has finished running
(drawing a figure on the screen); the bitmapped dots remain, like a sprite, as the
screen scrolls. This happens whenever BASIC'S screen shares the color area. The key
E which has a PEEK value of 5 gives the same black on green color effect.
Memory map examples. Bank 0 isn't very suitable for bitmapping. The range
$2000-$4000 has to be used for the bitmap, and $1000-$lFFF is filled with character
ROM. Therefore, if the color isn't to coincide with BASIC'S screen, it must start at
$0C00, leaving only IK if BASIC starts in its usual place.
To set this configuration, POKE 53272,57: POKE 55,0: POKE 56,12: CLR,
assuming bank 0 is on.
However, BASIC'S start can be moved up to $4000, with POKE 43,1: POKE
44,64: POKE 16384,0:NEW. To move the end of BASIC to $A000, POKE 55,0: POKE
56,160: CLR. This provides 24K of RAM available for BASIC, the maximum possible
with the bitmap and color both in free RAM. If you're writing BASIC and don't want
a loader to first reconfigure BASIC, the setup with 21K for BASIC is better.
Drawing lines. Program 12-26 draws black lines on a white bitmapped screen;
an ML routine (by B. Grainger) plots individual points, and this is called by a BASIC
routine which calculates optimum points to generate straight lines.
400
Graphics
402
Graphics
The multicolor mode editor, Program 12-28, is keyboard controlled, using cursor
keys rather than a joystick. Four colors are set when the program is run, and the
fourth, with bit pattern 11, chosen when plotting starts. Keys 1-4 select current
background, high nybble, low nybble, and color RAM colors. Keys fl, f3, f5, and il
advance the background, high nybble, low nybble, and color RAM, so plotting in
different colors on the screen is simple. Note that the background color affects the
entire screen—press fl to see this. Typing the space bar toggles between plot and
move modes.
403
Graphics
404
Graphics
Sprites
Sprites (or Movable Object Blocks) are large user-definable graphics which can be
put anywhere on the screen. The VIC-II chip handles them automatically—a consid
erable technical achievement to Commodore's credit. Since many people feel intimi
dated by sprites, this section begins with simple demonstrations and leaves the
technical details for later.
First, turn on the 64, type POKE 53269,1 and press RETURN. You now have a
sprite. However, you can't actually see it since it is not in the screen display area.
Type POKE 53248,100 and POKE 53249,100 and a sprite will appear. Vary the val
ues in these locations and watch the sprite move. The sprite is, or is supposed to be,
white. This color was set on power-up.
At this point, the sprite's shape is not very satisfactory; it is defined by the first
63 bytes of RAM. We can alter it by POKEing different values into location 2040, the
location which tells the VIC-II where to find this sprite's shape data. Some values
yield a sprite which continuously changes; this means that the RAM which defines
the sprite's shape is being used for BASIC workspace and isn't a flaw in the 64.
POKE 2040,16 causes the top one-and-a-half screen lines (strictly, the first 63
characters) to define the sprite; try homing the cursor and redefining the sprite with
{RVS}-Commodore key-B, {RVS}-*, SHIFT-U, and @, setting bit patterns
11111111, 10101010, 01010101, and 00000000.
Now, POKE 53287 with different values. These change the color of the sprite.
Color changes in the sprite correspond to bits set to 1 in the sprite's definition. (You
may get color effects because of the spacing of the defining bits on the screen.) Bits
set to 0 don't represent a color, but are treated as transparent by the VIC chip, so the
background shows through.
POKE 53276,1 sets multicolor mode for the sprite. (Poke 53276,0 to return to
high-resolution.) Multicolor mode increases the available colors from 1 to 3; the extra
two are stored in 53285 and 53286, so POKEs into these locations will alter multi
color sprites (if bit patterns 10 or 01 are present), but will leave high-resolution
sprites unchanged. All multicolor sprites share these extra two colors.
POKE 53277,1 makes the sprite expand horizontally, and POKE 53271,1 ex
pands it vertically to twice the unexpanded dimensions. Without special techniques,
only eight sprites are available at one time, so this can be useful where you'd like
reasonable coverage of the screen.
405
Graphics
POKE 53275,1 changes the priority of the sprite with regard to text; check to see
that characters are now displayed in front of it.
PRINT PEEK(53279) is set to 1 if the sprite overlaps text. This is called a col
lision. As you'll find, this location is reset only when it's read from, so two PEEKs
are actually necessary to give the current status.
Finally, POKE 53265,187 sets bitmap mode. You'll see that a sprite is still dis
played—the graphics mode doesn't affect it.
406
Graphics
Byte Number
21 bits
24 Bits
Sprite high-resolution and multicolor modes. Bits 0-7 of $D01C (53276) con
trol the modes of each sprite independently. High-resolution sprites interpret each
bit set to 1 in their definitions as the sprite color, stored in one of the color registers
407
Graphics
408
Graphics
some data under both. If sprite 0 is set to appear below data, then data will show
through, even where sprite 1 is set with priority over data.
Collision detection. This is an essential aspect of sprite programming. The idea
is to signal whenever a nontransparent part of a sprite contacts screen data or an
other sprite. VIC-II has two registers for this purpose, plus two interrupt registers.
Without all these, detecting collisions would be enormously difficult. They can be
PEEKed or used with ML to generate interrupts. Here's a simple example:
The seven bits of $D01E (53278) register sprite-sprite contact, so at least two bits
are set on any sprite overlap. The register must be read to determine which sprites
were involved. Reading resets all the bits to 0. If the register is not read, the collision
bits simply stay 1 indefinitely, which is why reading twice is essential to find the
current status. Note that offscreen collisions set these flags, too.
Bits 0-7 of $D01F (53279) register sprite-data collisions in the same way. Typi
cally, just one bit will be set. In high-resolution this is straightforward. With multi
color sprites there's more flexibility. The sprite color and the transparent "color" are
each treated as transparent for collision flagging; only bit-pairs 10 or 11, that is, the
colors in multicolor registers $D025 and $D026, cause collisions to be registered.
409
Graphics
256 32
Screen 10K BASIC User Def'd Sprite
Chrs Defns
8 Sprite Pointers
Program 12-30 uses the memory map in Figure 12-11. The program is long, but
is as short as a reasonable demonstration can be:
410
Graphics
s C
c 16 O
R Sprite L [ROM Image] Bitmap BASIC
E Defn's O
n
t
8 Sprite Pointers
411
Graphics
to mix them with user-defined graphics. In either case they must be used carefully.
For example, to liven up a word processor or calculation program, you could use
sprites to define arrows, pointing fingers, rectangular frames, or other prompts.
Games might use several moving sprites to simulate cars or motorcycles on a con
ventional graphics background.
Another example is a frog trying to cross a road and a stream without being hit.
A screenful of moving trucks, cars, and logs isn't possible with simple sprite tech
niques: user-defined graphics are better, allowing duplication of the moving objects.
If the frog is defined as a sprite, with priority over character data, it will always be
visible upon its road, log, or wherever. Several sprite definitions cover the cases
where it moves left or right and extends its legs. You can choose either a high-
resolution frog in one color or a chunkier multicolor frog.
Sprites can be superimposed if extra color detail is needed, but this isn't often
done, partly because it reduces the number of available sprites, partly because mo
tion now requires two sprites to be moved, and partly because TVs may not display
the result satisfactorily anyway.
Movement with sprites. To animate sprites, you need to replace a sprite with a
similar sprite, possibly repeating the process many times. We could change the sprite
definitions themselves, change the sprite pointers to point to different definitions, or
cycle through the sprites, say, from 0 through 3.
Generally, changing the definition pointers is best, since one POKE is all that's
needed to update a sprite, and it's easy to store plenty of sprites in RAM. Some
times, though, changing the actual definition is better (for example, where an object
is fired at, and you want to make parts of it disappear). In this case, it may be easier
to set bits in the sprite definition to 0. The third option isn't usually good; it uses up
valuable sprites.
Receding and approaching motion can be simulated to a certain extent by using
the expansion feature along with changing definitions; a 2 X 2 and a 3 X 3 un-
expanded sprite will give a sequence of four sprites in about the right ratio to sug
gest constant speed. Note, however, that expansion stretches the sprite downward
and to the right (rather than equally in all dimensions), which makes this feature less
than ideal for three-dimensional effects. Lighter and bluer colors suggest distance, as
opposed to deeper and redder colors.
Sprite Editor
Rather than drawing sprites on 24 X 21 areas of squared paper and converting the
result into bytes, try the following sprite editor, Program 12-31, which automates the
process. The program processes one sprite at a time, which is displayed in four
ways, standard and enlarged, and both high-resolution and multicolor modes.
There's also an enlarged diagram of the sprite, on which individual points can be set
and cleared with the space bar, while the cursor keys allow movement. Function
keys control colors and allow plotting in pairs for multicolor mode; instructions on
the screen explain which function keys to use. Press C to erase a sprite. (You may
prefer to omit the C option to remove the risk of accidental deletion.)
The program asks which block is to be used; if a sprite already exists in mem
ory, it's not cleared and can be examined and altered.
412
Graphics
413
Graphics
When sprites have been defined, press RUN/STOP to exit the program. The
sprite definition values can then be saved as DATA or written to a file or block
saved, using the techniques discussed earlier in this book.
This process can be extended, with user-defined characters, to simulate flying birds,
crawling insects, and so on.
This style of interrupt is a poll At regular intervals, the 6510 goes off to perform
what may be a long series of operations, deciding to do some and not do others. CIA
chip 1 allows the frequency of interrupts to be changed, but the process is always
similar. However, the VIC-II chip provides a more active control over interrupts.
415
Graphics
Bit 7 of the interrupt flag register is set to 1 when the VIC chip generates an
interrupt; otherwise it's 0. This allows you to determine the source of an interrupt,
where there are several possibilities, since whenever bit 7 is on, one of the register's
other four used bits will reveal this. Note, though, that bits 4-6 are unused and that
bits 0-3 are set even when interrupts aren't enabled.
PRINT PEEK(53273) AND 143 prints the value of this register; ANDing the
value with 143 masks the bits which are fixed at 1. Bits 0-3 act as the flags. When a
bit is low (contains 0) the flag is clear; if a certain event occurs, the bit becomes high
(contains 1) and the flag is set. Bit 0 is set by a raster compare; sprite-data collisions
show in bit 1; sprite-sprite collisions show in bit 2; and connecting and using a light
pen sets bit 3.
Once a flag bit is set, it can be cleared only by POKEing it high (unlike many
other flags, which are cleared by POKEing a bit low). This is called latching, and the
idea of this dual function in the register is to keep a record of interrupts within the
flags themselves, saving programming effort. As an example, a sprite-data collision
sets bit 1; if no other triggering events have occurred, the PEEKed value will be 2.
POKE 53273,2 is required to turn off the collision flag (assuming the sprite no longer
overlaps data) and return the bit to 0. The interrupt flag register, therefore, is de
signed to store the past results of four possible events until they're cleared, and also
shows whether the VIC-II chip caused an interrupt currently in force.
The use of this register requires considerable care. Confusion will result if you
forget to clear a flag, try to clear it by POKEing its bit low, or try to clear it while the
condition that triggered it still exists. Other anomalies might arise because the key
board and light pen share common wiring.
Using the interrupt enable register to enable interrupts is simple, but the IRQ
routine which they're wired to needs modifications to handle them properly. Try
PRINT PEEK(53274) AND 15. The result is 0, showing that the VIC chip has no
interrupts enabled. Enter POKE 53266,0: POKE 53274,1. This enables raster-scan
interrupts: each time the TV picture is scanned, an interrupt occurs. But, in addition,
the flag register isn't cleared, so immediately when an interrupt finishes, a new one
begins. This stops processing, although (try SHIFT-Commodore key) the keyboard is
processed normally. Similarly, POKE 53274,2 has no effect until there's a sprite-data
collision, whereupon interrupts continuously come into effect. Because of this, user-
written interrupt routines based on the VIC-II chip always clear the interrupt flag
register before exiting.
416
Graphics
After Program 12-33 is run, there are two sources of interrupts, the result of
redirecting the IRQ vector in locations 788-789. The sprite's direction reverses when
it collides with data, so it moves left and right across the screen. This is the ML
loaded by Program 12-33:
LDA $D01F
BEQ EXIT
LDA $D01F
LDA #$02
STA $D019
EXIT STA $02
JMP $EA31
This reads the sprite-data collision register and resets the interrupt flag register
on a collision, as well as clearing $D01F a second time. The result is passed to BASIC
in location 2. JMP $EA31 continues in the ordinary key-scan interrupt. In fact, the
extra interrupt isn't necessary here; reading the relevant register works. But in more
complex situations, when several interrupts may be enabled, processing them prop
erly requires a routine like LDA $D019:AND #1:BNE RASTER:AND #2:BNE
SPRDATA, and so forth, with RASTER STA $D019, and so forth, where the first act
is to turn off the flag.
Light-pen registers can be read during interrupts, but can also generate inter
rupts of their own, by setting bit 3 in the IRQ Mask Register ($D01A), and setting bit
3 of the Interrupt Flag Register ($D019) high to clear the flag once the light pen
causes an interrupt. But since the light pen cannot trigger more than 60 interrupts
per second (50 in the U.K.), it's easier not to bother, but simply to add a test for bit 3
of $D019 to the normal IRQ, reading from $D013 and $D014 when the bit becomes
high.
417
Graphics
and even numbered lines; in other words, a single screen scan displays half the pic
ture. Sixty top-to-bottom scans (50 in the U.K.), are displayed every second, so the
entire picture is refreshed every 1/30 second (1/25 in the U.K.). The VIC-II chip
generates each raster line, keeping track of the current line as it does so.
NTSC (U.S.) TVs have 262 lines per frame, PAL (U.K.) TVs have 312. The 64
displays lines 51-251, making 200 lines—enough for 25 sets of eight dots. Raster
lines 0-50 and 252 up aren't visible. TVs in the U.K., with more lines; give a more
compressed picture.
Bit 7 of $D011 (53265) with register $D012 (53266) together make a nine-bit
register for use with raster scanning. Nine bits are necessary to include all the raster
lines, though the highest bit is often not used. The register has two different func
tions. It allows you to PEEK the current raster line. Writing to one or both registers
latches the new value, setting the VIC-II chip so an interrupt flag, bit 0 in $D019,
goes high whenever the current raster line matches all nine bits. If the interrupt is
enabled, an interrupt will be caused. POKEs which set bit 7 of $D011 and also put a
high value in $D012, prevent raster interrupts from occurring. This fact allows the 64
to deduce which type of VIC chip is fitted and set its PAL/NTSC flag in $02A6. On
power-up, VIC's raster register is POKEd with 311, a value too high for U.S. (NTSC)
signals, but within the range of U.K. (PAL) TVs.
So, when we use raster lines in programs, we have a choice of methods: reading
the raster line from its registers or, more ambitiously, generating precisely timed
interrupts synchronizing with the screen.
Routines like this use the processor full time to handle the screen. Alter #$80 to
watch the dividing-line move; the loops WAIT and START occupy almost all the
6510's time. Interrupts are trickier but allow other processing. Effects include the use
418
Graphics
419
Graphics
At each interrupt, all the X and Y positions are reset, so the sprite positions are
all independent. The sprite-enable registers are also reset, so any number of sprites
from 0 to 32 can be chosen. The colors, priority, expansion, and so on, are shared
between them here to save space. Note the color bands, to show where interrupts
occur: Their positions can be changed in lines 40 and 50. The bands are removable.
420
Graphics
The ML disassembly is too long for inclusion here. It consists of loops which
load new X,Y values, the sprite-enable register, and the timing for the next interrupt.
Program 12-36 makes use of the fact that the checked block can be imitated by
another pair of graphics. Resolution is to four dots. This method is unwieldy for a lot
of movement in many directions, because any single character needs eight more
characters to allow half-character motion in the main directions.
If the movement is one-dimensional, as in games, 80 or so mobile characters can
be used. Smooth motion like this requires 15 characters just to move one single
character, so only 17 different objects would use the entire set. This is usually less
practical than using sprites.
Dynamic redefinition of characters. An advanced method to simulate motion
and other effects is to alter the character definitions or bitmaps themselves, so partial
characters are generated when needed, rather than being stored. ML is necessary
since 16 POKEs to alter the bytes defining two characters would be slow.
Planning is important. First, the original definitions should usually be kept in
memory, away from the area that will be redefined, because characters might be irre
coverably changed by dynamic redefinition. Second, characters should be numbered
conveniently—generally consecutively down or across the screen—to simplify ML.
Vertical smooth motion. The following BASIC routine, Program 12-36, dem
onstrates up-and-down motion in this way. ROM graphics are copied into $3000 up,
and screen codes 128-137 (normally reverse-@ through reverse-I) are redefined as
numerals 0-9. These numerals, starting at $3400, are processed by ML routines, giv
ing a realistic odometer effect, while retaining the normal numeral definitions. Up-
and-down motion is simple to program; as Figure 12-13 shows, all that's necessary is
to move over the bytes making up the characters.
421
Graphics
After Move
SYS 49152 uses the loop below, which moves only the required portion of the
character definitions. (You may need to add a zero byte at the end points.)
LDX #$4F
LOOP LDA $33FE,X
STA $33FF,X
DEX
BNE LOOP
RTS
Horizontal smooth motion. Because of the way screen and character memory
are allocated, this is trickier. It is necessary to number the relevant screen locations
consecutively. As Figure 12-14 shows, you must store a bit from each byte and ro
tate it into another byte eight bytes away.
422
Graphics
Screen Characters
I 1 I I I 1 | I I 1 I I I. !■ After
Character Definitions
Replace the following lines in Program 12-36 and run it. Program 12-37 will
produce a similar effect, as the special numerals 0-9 scroll smoothly sideways.
423
Chapter 13
Sound
Sound Waves: Analysis and Synthesis
The SID Chip
Music Theory
Sound Demonstrations
Chapter 13
Sound
This chapter explains sound and music on the 64 and shows how the SID chip
handles both in practice. Although the theoretical side is important for full under
standing, it is also quite difficult, so you may prefer to try the programs first to get
the feel of SID.
Sine Waves
Sine waves are important for two reasons. First, any note in any timbre can be an
alyzed by breaking it into its component sine waves, and this analysis gives a com
mon basis of comparison between all notes and timbres. This makes the results of
electronic sound processing predictable. The second reason is psychological, in that a
similar process of analysis happens in the brain. As a result, we can think of the
timbre of the sine wave as the simplest and purest sounding of any waveform. Fig
ure 13-1 outlines a sine wave.
427
Sound
Circular motion generates sine curves; the SIN function describes this motion.
As it happens, the prongs of a sounding tuning fork give a good approximation of si
nusoidal motion. In fact, simple harmonic motion derives its name from the sound
analogy.
A tuning fork is a simple musical instrument. It works the way it does because a
struck prong is pushed toward its still position by a force that varies with (among
other things) the distance from its still position, and it can also be shown that this
produces a sine curve. This is a typical application of physics to sound.
Consider a tuning fork with a smaller tuning fork attached to one prong, so the
movement of both forks contributes to the movement of the small fork. The com
posite motion will be two sine waves added together.
If the smaller fork's frequency is exactly 2, 3, 4, or any integer times the fre
quency of the larger, then the ear-brain mechanism fuses them into a harmonious
tone, of richer timbre than a pure sine wave. As stated above, sine waves which are
multiples of a sine wave are known as harmonics of that sine wave. The fun
damental frequency is known as the first harmonic, the wave at twice this frequency
is known as the second harmonic, that at three times its frequency is the third har
monic, and so on. Most musical instruments are designed to generate harmonics, and
the relative importance of harmonics determines the instrument's timbre. For ex
ample, a closed air column can vibrate stably along its full length, or half its length,
or one-third, and so on, and therefore has a full range of harmonics; a string,
plucked in the center, cannot easily vibrate for half its length and has only odd
harmonics.
Harmonic analysis is the process of determining the sine wave components that
make up a waveform. Fourier analysis, a popular technique, is based on the principle
that any repeating function f(x) is of form bl sin (x) + b2 sin (2x) + . . ., where the
integral from minus pi to pi of f(x) sin (kx) is bk. The idea isn't new, and ancient
astronomers built up ellipses from many circular movements. The harmonics and
their relative amplitudes make up the harmonic spectrum of the waveform, which
can be drawn on a graph as amplitude against frequency.
428
Sound
Conversely, any repeating waveform, like that from the two tuning forks, can be
built up by adding harmonics with the right amplitudes: this is additive synthesis.
The process of starting with timbres which are rich in harmonics and deriving a
sound with a desired spectrum by filtering out the unwanted ones is known as
subtractive synthesis.
The SID chip isn't designed to generate sine waves, but as we'll see, it does gen
erate quite complex waves with rich spectra and also allows limited subtractive syn
thesis, since it has a filter.
Triangle
The triangle wave (see Figure 13-2) is SIN (X) - SIN (3*X)/9 + SIN (5*X)/25 -
SIN (7*X)/49 + . . . where X is the harmonic number. A triangular wave with 100
Hz as its fundamental contains frequencies of 300, 500, 700, 900 Hz, and so on,
which rapidly decrease in importance.
This is SID's closest approximation to a sine wave, and it sounds like a flute or
xylophone. Four sinusoidal harmonics—the first, third, fifth, and seventh—add to
form an approximately triangular waveshape.
429
Sound
Sawtooth
The sawtooth wave is SIN (X) + SIN (2*X)/2 + SIN (3*X)/3 + SIN (4*X)/4 +
It contains the complete harmonic series, with amplitude in inverse proportion to the
harmonic number, so the second harmonic has half the amplitude of the fun
damental, and so on. The harmonics have more importance than the triangular
wave's: Figure 13-3 adds six harmonics to provide an approximation.
The large numbers of significant harmonics enable this wave to simulate such
instruments as the trumpet, oboe, clarinet, and accordion, especially when filtered to
change the harmonic balance.
This wave's asymmetry causes a few oddities. For example, every other term of
its analysis makes up a square wave: if this is subtracted, what's left is SIN (2*X)/2
+ SIN (4*X)/4 + . . ., another sawtooth wave of twice the frequency and half the
amplitude. Add the square wave to the sawtooth on the diagram to see this. The
sawtooth also tends to sound higher than its fundamental would suggest.
430
Sound
boosted. Very asymmetrical pulse waves have very irregular harmonic spectra, with
some large amplitudes in the harmonics. The square wave, with N=2, lacks all even
harmonics, as we've seen.
A wave of duty cycle 1:5 lacks fifth and tenth harmonics, and so on, but does
have second, third, fourth, sixth, seventh, eighth, and ninth harmonics, with the
seventh and eighth harmonics of higher amplitude than the sixth and ninth. A duty
cycle of 1:5.1 will cause the fifth, tenth, etc., harmonics to be much reduced in am
plitude, but not totally negated.
Altering the duty cycle while a note is playing causes most harmonics to change
their levels, and some to vanish altogether or reappear. Thus, we have a way of
producing a dynamically changing spectrum with richer, more interesting sounds
than the triangle or sawtooth waves.
If the pulse width is altered from square toward a narrow pulse, loudness
diminishes, and the timbre becomes more nasal or buzzy compared with the well-
rounded sound of the square wave, as some high harmonics are boosted. As the
width of the pulse becomes very narrow, the decreasing amount of energy present is
spread out among very many harmonics, and the sound fades to inaudibility.
The square wave is louder than any other pulse wave of equal amplitude,
mainly because the total energy in a square wave is greater: A very short pulse is
simply quieter. A secondary factor is the energy distribution amongst the harmonics:
a square wave's harmonics taper off smoothly, but a pulse wave with duty cycle of
1:2 has a very irregular distribution, and some boosted harmonics may be inaudible.
Typical sound simulations are a piano (square wave), organ (1:4 wave), and
banjo (1:10 wave). Very narrow pulses at low frequency give a car engine sound.
Noise
Noise is sound with no fundamental frequencies and is typically generated by an un
musical source which has no dominant modes of vibration. Rumbles and hisses,
crashes and explosions, scrapes and rattles, jet engines and gas burners, wind and
water flow, illustrate this sort of sound.
431
Sound
SID generates noise by loading new random values into the oscillator output, at
regular intervals controlled by the frequency setting. While the result cannot be an
alyzed into repeating sine waves, any single interval can be analyzed, so the ear gets
a definite overall feeling of low, medium, or high pitch. White noise contains all fre
quencies in equal proportion. The SID's noise generation method sets a minimum
frequency and plays all available notes above this minimum with equal probability,
so most SID noise is biased to high frequencies; this is called blue noise.
Noise (diagrammed in Figure 13-5) is useful in simulating the sorts of sounds
mentioned above, and also certain percussion sounds like snare drums, brushed
drums, and cymbals. Dynamic changes in noise frequency can simulate ripping, tear
ing sounds, fireworks in motion, and moving vehicles.
432
Sound
slow frequencies satisfactorily; you'll get clicks and pops rather than pulsations, so
tremolo is better achieved by controlling volume.
When one or both inputs contain harmonics, each harmonic gives rise to a sum
and difference signal with each harmonic of the other input. SID's ring modulator
can be used only when the triangular wave is selected for the ring modulated voice
(even though this waveform is less rich in harmonics than others). Ring modulation
is controlled by setting the frequency of the modulating voice; other parameters of
the modulating voice are irrelevant to the effect.
Consider two frequencies, a and b. The triangular waves have harmonics a, 3a,
5a, ... (and b, 3b, 5b, . . .). Combining only the first three harmonics of each (to
save space) gives:
Adding a+b a+3b a+5b
3a+b 3a+3b 3a+5b
5a+b 5a + 3b 5a+5b
Subtracting a—b a —3b a—5b
3a—b 3a—3b 3a—5b
5a-b 5a-3b 5a-5b
This shows there are two triangular waveforms based on (a+b) and (a—b); look at
the main diagonals to see this. There are also many extra waves, with such fre
quencies as 3a+b and 5a+3b.
Consider the case where b is very small. The result will be many sine waves,
each of frequency approximately a, many of frequency about 3a, and so on. The re
sult is a modified triangular wave, where all the harmonics beat.
In the special case where a is a multiple of b, the output is a harmonic series.
For example, if 100 Hz is input with 300 Hz, all the sum and difference signals must
be multiples of 100 Hz. The result is a distinctly pitched note, typically something
like a square wave. If the frequencies aren't quite exact multiples, the output is a pair
of enhanced triangular waves which will beat, since their fundamentals aren't quite a
ratio. This gives realistic banjo twangs or rubber-band "boings."
Where one input changes continuously relative to the other, some frequencies
(those produced by addition of harmonics) slide up, and the others (produced by
subtraction) slide down, and the output passes through points where it has a single
pitch.
Bell-like timbres are produced with such inputs as 110 and 152 Hz, which give
two audible notes (42 Hz and 262 Hz) of unrelated frequency.
Synchronization also forms a joint output from two inputs, but in a different
way. The frequency and waveform remain virtually unchanged. However, at inter
vals decided by the inputs, the waveform restarts. The effect is to add high harmon
ics to the wave, while keeping frequency fairly constant.
Envelopes
So far we've discussed steady tones, produced by stable waveforms. A note's en
velope describes the rise and decay of the note's loudness against time. The time
scale of SID envelopes is much longer than for waves.
Figure 13-6 shows the envelope of a triangular wave over a tenth of a second.
The envelope has a short attack phase, and a much longer decay phase. The wave
form is independent of the envelope, but it's easy to forget the distinction.
433
Sound
434
Sound
decay is interrupted and the release phase terminates the note. A square wave
approximates the piano's timbre.
The organ has a noticeable attack and decay, as the note is established or fades.
While a key is pressed the sustain level is held, after which there's a short fade-out.
A pulse wave gives about the right timbre.
A flute has a longer attack and release, and has roughly a triangular waveform.
435
Sound
Band-pass High-pass
%of
I
Sound
Passed, %
J_
50 100 200 400 800 1500 3000 6000 12000 Hz
Synthesis Notes
Any waveform can be synthesized by sampling (by measuring the sound's amplitude
at a series of points) and simply duplicating the sequence. This is not how most syn
thesizers work, though they generate sawtooth, noise, and variable pulse waves, and
have controls for such things as vibrato and chorus (where all voices are turned on
in approximate unison). ADSR envelopes are often used, the release phase being en
tered when a key is no longer pressed. The Commodore 64 SID chip, designed by a
specialist in music synthesis, has many of these features.
Sounds produced by acoustic instruments tend to have extremely complex
changing harmonic structures, especially in the attack phase. And harmonics aren't
absolutely perfect. Their frequencies aren't quite exact multiples of the fundamental
frequency. For these reasons, it's difficult to make electronic instruments sound just
like acoustic ones.
436
Sound
Speech Synthesis
The vocal cords generate a roughly triangular wave of fundamental 70 (men) or 100
(women), which resonates in the throat, mouth, and nose and is shaped by the
tongue, palate, and lips. Speech is typically synthesized from three main frequencies
(formants), plus low-frequency noise (nasals) and high-frequency noise (fricatives). For
example, "oo" is roughly a sine wave, and "ee" a sine wave plus a 3000 Hz tone.
The frequencies of formants typically change somewhat in the course of vowel
sound. Consonants are often bursts of noise which are abruptly cut off. Since SID
has only three voices, no sine wave capability, and a single filter, speech is not easy
to synthesize with the 64, although it is possible.
Plug-in speech cartridges usually include their own speech chip, bypassing SID
and providing a known quality of output. Such chips typically store waveforms of 64
component parts of speech sound (phonemes) and allow control of such parameters
as duration. Their software converts some form of phonetic spelling into phonemes;
for example, A$="H/E/LL/OO/P2" may define a string which sounds like "hello"
followed by a pause. Standard words and sounds and the alphabet may be supplied
in ROM. Commands are either wedged into BASIC or accessed by SYS calls.
437
Sound
The SID chip (officially, the 6581 Sound Interface Device) has 29 eight-bit reg
isters, numbered 0-28 in its specification. The SID chip starts at $D400 (54272) in
the 64. Address decoding is incomplete, and 31 extra images of the SID chip reg
isters appear at $D420, $D440, and so on. The SID images are a side effect of the
way the SID chip is wired into the 64; they are not designed to be used at all. While
it is possible to use image registers in place of the actual SID registers, there is no
real advantage in doing so, and the practice is likely to confuse anyone else who
looks at such a program.
The SID chip has three electronic voices. They may be used independently, or
linked, giving ring modulation from two voices, for instance. Each voice has an os
cillator, able to generate sawtooth, triangular, variable-width pulse, noise, and other
combination waveforms, of frequency ranging from about 1/20 Hz to 4000 Hz, in
extremely small steps. Much higher frequencies than this relatively low top limit are
generated by harmonics. Each voice has its own envelope shaper, with pro
grammable attack, decay, sustain, and release phases. Any of the three voices may
be routed together via the SID chip's single filter, which has programmable low-
pass, high-pass, and band-pass modes, resonance, and cut-off or center frequency.
The often published block diagram of the SID chip charts all these features.
Most SID registers are write-only; PEEKing returns 0. Consequently, monitor-
style programs need an array to keep track of values which have been POKEd in.
However, the last four registers are read-only. Two of them return potentiom
eter readings, used with game paddles, drawing tablets and other devices. These are
dealt with in Chapter 16. Although sound could be controlled by them, perhaps as
an alternative to the keyboard, they're not directly relevant and we'll say little more
about them here.
The two other read-only registers return, respectively, the oscillator and the en
velope, as output by voice 3. We can use these to inspect the SID chip's waveforms
and envelopes, and for such control purposes as modifying oscillator frequencies for
vibrato or siren effects, or changing filter frequency to get a wah-wah effect.
The SID chip produces a reasonably high quality electrical signal, obtainable on
the audio-video socket at the rear of the 64 (pin 3 is the signal, and pin 2 is the
ground). Play this signal through an amplifier and speaker, preferably with some tre
ble removed, for a significantly higher quality sound than is available via your TV.
External input can also be processed by the SID chip: the signal, to pin 5, must be in
the range 5.7 to 6.3 volts DC, matched to the SID chip's 100K ohm impedance. The
signal can be filtered and mixed with output from the SID chip's voices, but not pro
cessed in any other way. If you're not sure how to make such external connections,
get help from someone who understands electronics. The SID chip can be damaged
by an external input that exceeds its design levels.
438
Sound
Frequency control registers (offsets of 0 and 1, 7 and 8,14 and 15). The first
two registers of each group hold a two-byte value which controls the frequency of
the note produced by that voice. The frequency is directly proportional to the value,
which means that doubling the value doubles the frequency. The frequency resolu
tion is extremely good. There are about 250 values between middle C and the next
semitone, permitting glissandos (pitch changes between notes with no perceptible
steps), though resolution is not as good at extremely low frequencies.
The registers' values increment every 256 clock cycles, setting a new output
value whenever 0 is reached; this explains how the timing works out.
The 16-bit register value is related to the true frequency like this:
U.S. True frequency = 0.0609597 * Register value
U.K. True frequency = 0.058717 * Register value
Or the other way around:
U.S. Register value = 16.40426 * True frequency
U.K. Register value = 17.0309 * True frequency
To produce a tuning standard note A (frequency 440 Hz) in the U.S., the value is
440*16.404261, or about 7218. Note that U.S. software plays at a lower pitch in the
U.K.
The highest obtainable pitch is about 0.0609597*65535 = 3995 Hz, about four
octaves above middle C, and the lowest about 0.06 Hz, far below audibility. Two or
three octaves below middle C are perceptible as a pitched sound, so the SID chip's
total range of pitched sounds is about six or seven octaves.
If the variable F contains a value to be placed into these registers, then
H%=F/256 then L%=F-H%*256 will assign the high and low bytes to H% and
L%
Pulse width registers (offsets of 2 and 3, 9 and 10,16 and 17). The pulse width
is set by a 12-bit value. The lower 8 bits are set by the first of these register pairs,
and the upper 4 bits by the least significant nybble of the second register, leaving
the upper nybble unused. So the maximum value it may take is 4095. The duty cycle
is given by the ratio ?6f'pulse width: 4095. A duty cycle of 1:2 (a square wave) results
from 2048 (8 in the second register, 0 in the first). The value 0 or 4095 gives a con
stant DC voltage. Consequently, if a pulse waveform is to be audible, a pulse width
must be set in addition to a frequency. The pulse width registers have no effect on
other waveforms.
Note that a 1:4 duty cycle sounds identical to a 3:4 duty cycle. It's the same
waveform upside down, so the value 1024 will produce the same harmonic content
as 3072.
However, if other tones are sounding simultaneously, they'll interact with the
pulse wave, and there may be a noticeable difference between 1:4 and 3:4 ratio
pulse waves. In particular, if a pulse wave and another waveform are enabled to
gether in a control register (see next section), a low ratio gives an inaudible result,
while a high ratio creates a much louder signal.
To load the contents of the variable PW into the SID chip, POKE the first reg
ister of the pulse-width register pair with PW/256, and the second with PW AND
255.
439
Sound
Control registers (offsets of 4,11, and 18). The fourth register in each group
has eight more-or-less independent bits; as usual, each is off when 0, on when 1. In
ordinary use, only one waveform will be enabled in a voice at any given time. En
abling several waves (but not noise) in the same voice logically ANDs the outputs
from point to point. A square wave cuts out half the other wave, giving variations on
the sawtooth, but ANDed triangular and sawtooth waves mostly cancel out. A very
wide pulse adds high frequencies without cutting volume too much. The following
list describes the function of each bit of the control registers:
Bit 7; Noise.
Bit 6: Pulse waveform. Pulse width must be set.
Bit 5: Sawtooth waveform.
Bit 4: Triangular waveform.
Bit 3: Test bit. This suspends the voice; when cleared to 0, the output is con
tinued. This can be useful where the exact realtime phase of a waveform is im
portant. But the most common use is probably to restart a voice, locked up after
noise has been enabled with another waveform.
If noise is selected with another waveform, the noise waveform is silenced and
will remain silent until the test bit is set to 1 and then reset to 0.
Bit 2: Ring modulation. When this bit is set to 1, its voice's output (which must
be a triangular wave) is replaced by the ring modulated signal obtained from its own
signal and the previous voice, which is automatically treated by the ring modulation
process as a triangular wave. Only the frequency setting of the previous voice has
any effect. It isn't necessary to set the waveform or even turn on the voice. Both voices
can still be used: voice 3 might play noise, while voice 1 could play a ring modu
lated signal based on the triangular wave of voice 3's frequency setting.
A common combination is to set the ring modulation bit of voice 1 and use
voice 3 for the other input signal. The voice before voice 1 is treated as voice 3. If
two or even three ring modulation bits are on, and appropriate triangular waveforms
have been selected, still more elaborate ring modulation occurs.
Bell timbre notes can be tuned by multiplying both input frequencies by
1.059463, or using tables, to find semitone values which retain the same pattern of
harmonics. Glockenspiel and vibraphone effects can be obtained in this way.
Bit 1: Synchronization bit. This links two voices just as the ring mod bit does.
As with ring modulation, only the frequency of the previous voice need be set for
sync to operate. There are two small differences: the voice with sync bit set can have
any waveform, and the synchronized output frequency is determined by the earlier
register, the one with sync bit not set.
Synchronization can be used in several ways. It provides an easy way to vary
timbre. Suppose voice 1 plays a tune on its own. If voice 2's sync bit is set, voice 1
still plays the tune, but you can get new timbres on replaying the tune by simply
POKEing a new value into voice 2's frequency.
With ring modulation, sync adds more high frequencies and may improve the
sound. It's probably always worth trying. However, voice 3 alone, not voices 1 and 3
together, now controls the pitch.
Bit 0: Gate bit. This controls the playing of the voice's note. It has two functions.
Transition from 0 to 1 immediately starts the ADSR envelope. Transition from 1 to 0
immediately starts the release phase of the ADSR envelope.
440
Sound
Both events take place irrespective of the stage reached by the current ADSR se
quence in progress. For example, if the gate bit is turned off during a slow attack, re
lease starts without decay or sustain phases.
Envelope shape registers (offsets of 5 and 6,12 and 13,19 and 20). These reg
isters control the attack, decay, sustain, and release phases of the sound envelope.
Each of the envelope register pairs is arranged as a four-nybble group in A, D, S, R
order. Thus, the high nybble of the first register controls the attack, while the low
nybble controls the decay. In like fashion, the high nybble of the second register is
for sustain, and the low nybble determines release.
Program 13-1 allows you to watch the SID chip generating envelopes. It reads
the envelope generated by voice 3 and draws the result on the screen in a distinctive
color, so several envelopes can be overlapped and compared. The envelope is timed
so a BASIC loop can read it. Try entering ADSR values such as 3, 3, 3, 3 and 7, 5, 5,
6 to get the idea.
Note that A, D, and R set rates of increase or decrease, but sustain is a level, so
a high S value raises the level portion of the envelope. Release is timed by the pro
gram to occur about 2/3 the length of the axis.
The attack/decay register (5,12,19) consists of two nybbles which hold the at
tack value (0-15) and decay value (0-15). POKE 16*A+D.
441
Sound
The sustain/release register (6,13,20) consists of two nybbles which hold the
sustain level (0-15) and release value (0-15). POKE 16*S+D.
Table 13-1 is a list of approximate attack times and maximum rates for decay
and release.
Attack times are 1/3 the other times, because they are shorter in practice. The
ratios between steps are erratic: incrementing from 5 to 6 makes little difference,
while raising the value from 8 to 9 is more significant. The idea is to give more
choice in the useful 1/20 to 1/3 second range. Attack rises to maximum output,
which declines in the decay phase to the sustain level. Sustain therefore helps deter
mine volume of a note. To increase a note's relative importance, increase its sustain
level. Release, if and when it happens, drops from sustain to zero. The phases aren't
quite linear.
To understand sustain levels, consider the three similar notes shown in Figure
13-9 with different si^tain levels.
The notes diagrammed above are identical except for the sustain level. Note
how the decay increases in importance and release decreases in importance as the
sustain level drops.
With a sustain level of 0, these envelopes decay to 0 without needing release to
be gated. POKE SID+4,16: POKE SID+4,17 plays notes of this type. If release is
gated, the envelope shape depends on the release value; the note is lengthened, un
changed, or shortened, respectively, if the release value is greater than, equal to, or
less than the decay value.
442
Sound
443
Sound
Notes on filters. The SID chip has only one filter. When it's on, not all voices
need pass through it. Bass accompaniment could be low-pass filtered to remove high
harmonics and have resonance added. For example, if you put 17 into offset register
21, 241 (or 15*16+1) into register 23, and 31 into register 24 to low-pass filter voice
1 with cut-off starting at 128 Hz (an octave below middle C). High-passing imitation
cymbal noises might need 21 in register 22, 1 in 23, and 79 in 24 to high-pass from
1000 Hz without resonance.
Bandpass filtering, notches, and resonance act on quite small regions of the fre
quency spectrum, so it's important to get the register values correct.
Read-Only Registers
Potentiometer readings (offsets of 25 and 26). See Chapter 16.
Waveform reading of voice 3 (offset of 27). Voice 3's frequency, and a wave
form, must be set, but the gate needn't be on. For example, set register 14 (fre
quency, low byte) to 4 and register 18 (waveform) to 32. This sets up a very low
frequency sawtooth wave, which takes about eight seconds to climb from 0 through
255. A sequence of PEEKs will show these changing values. With much higher fre
quencies, BASIC is too slow to PEEK consecutive values, but instead returns values
taken from different waves, so the pattern appears to bear no relation to the wave
shape. This is a standard result of wave sampling. It makes the effects of ML pro
grams like the one below difficult to guess at.
For random numbers, particularly when using ML, POKE register 15 with 255,
and register 18 with 129. This generates a fast changing noise output. Simply PEEK
(or load in ML) the location to obtain a value.
Envelope reading of voice 3 (offset of 28). Voice 3's ADSR must be set, but a
waveform needn't be selected. This is similar to the previous register; the major dif
ference is that the envelope starts only when gated, and release starts only when the
gate is set to 0.
444
Sound
445
Sound
Type in the program and run it. A few registers are preset by the program.
Sawtooth: Cursor over the gate bit of register 4 and tap the space bar a few
times. A sawtooth tone sounds, with frequency about 500 Hz. Its envelope has zero
attack and decay, but a sustain level of 8 (midvolume setting) and decay of 8, giving
about a 1/3 second decay.
Triangle: Turn the triangle bit on and the sawtooth off. Gating again turns on
the note, this time with the mellower triangle waveform. Altering bits in registers 1
and 2 changes the pitch. Also experiment with different attack and decay settings.
Pulse: Select the pulse-wave bit in register 4. You'll get no pulse-wave sound
until registers 2 and/or 3 are POKEd.
Noise: Set the noise bit and experiment. A long attack, for example, helps simu
late a steam train.
Ring modulation: Set the ring bit in register 4; set bit 2 in register 15, which sets
voice 3's frequency. This has no effect; however, selecting triangle wave in register 4
gives ring modulation. Bell-like sounds are best obtained with 00001111 in register 5
and 00000000 in register 6, so the note has only a decay phase.
The array RV stores the current register values. The program initializes volume,
as well as voice l's frequency, sustain phase, waveform, and gate. This is the mini
mum required for the steady tone produced on RUN.
The cursor is controlled by POKEing color RAM, so the new X,Y position
changes color, and the old reverts to white.
Experiments and examples. These straightforward examples are typical SID
approximations of musical sounds.
446
Sound
447
Sound
Voice 3's frequency determines oscillator register 27's output. Register 27 is loca
tion 54299 ($D41B). A very low frequency is inaudible, but when the register is
controlling an audible frequency, it causes a slow, distinct pulsation. Higher fre
quencies are audible in themselves and also generate much faster changing output,
giving frequency-modulated output (where the waveforms are rapidly condensed,
then rarefied).
Voice 3's ADSR settings and the gate bit control register 28's output. Register 28
(location 54300, $D41C) therefore needs more work than 27, often more than is jus
tified in view of the similarity between the outputs of 27 and 28.
Program 13-3 shifts the triangular oscillator output of voice 3 into register 0, so
the low byte of voice l's frequency pulsates, giving vibrato. POKE 49156,1 uses reg
ister 1 and, of course, usually sounds more erratic. Register 2 or register 3, with
pulse wave selected, causes fluctuations in readiness. POKE 49156,4 causes clicks
and buzzes as the interrupt POKEs in assorted waveform and gate configurations.
Register 22 (54294, $D416) alters the filter setting; provided voice 1 is filtered, you'll
get a repetitive wah-wah effect. Register 24 (54296, $D418) alters master volume and
a few other things, and gives vibrato or such effects as random changes in loudness
when the noise voice is chosen. Whenever a register is partly used for different pur
poses, obviously LDA $D41B:AND #$0F:STA $D418 or similar constructions can
help.
The envelope can modulate frequency or filtering, producing somewhat similar
results: A note might start deep and faint, rise to a louder, more treble sound, then
lose frequency as it decays. Voice 3 is easiest to modify in this way, since it can
modulate itself. Run Program 13-3 and do these POKEs: POKE SID+4,0 (turns voice
1 off). POKE SID + 19,176 (sets AD of voice 3). POKE 49162,15 (causes envelope
reading to alter frequency of voice 3). Now type POKE SID+18,0: POKE SID+18,17
to gate a triangular wave, and you'll get a whistling sound. There are innumerable
ways to combine voices, waveforms, ADSR shape, and duration.
Music Theory
Figure 13-10 shows a section of a piano keyboard, starting at C.
u
It has 12 keys, tuned with frequencies in constant ratio to each other, so melodies
can be played in any key, starting on any note. Adjacent notes are all separated by
semitones. Octaves differ in frequency by a ratio of exactly 2, and similarly fre
quencies of semitones differ by the twelfth root of 2, which is 1.059463. Repeated di
vision of a high frequency by this value generates regular subdivisions and provides
an efficient way to generate tuned frequency settings for the SID chip. American
standard pitch sets A at 440, while international standard pitch is 435. Whichever
scale is used, any notes' frequencies can be calculated from any one note. (The sub-
448
Sound
routine starting at line 4000 in Program 13-5 evaluates 95 semitones from a single
value.)
A conventional scale consists of 7 notes taken from these 12, plus the eighth
(octave higher) note. So all scales have 12 semitones between the starting keynote
and its octave. Of the 800 or so possible scales, in practice only a few are used. C
major is the best known scale, starting with C and using only the white notes C, D,
E, F, G, A, B, C.
The notes of this scale are separated by semitones like this: C tone D tone E
semitone F tone G tone A tone B semitone C. All major scales have this pattern, and
the arrangement of musical intervals in this type of scale has the same quality what
ever the keynote. # #
Any major scale other than C uses some black keys. For example, G major is G,
A, B, C, D, E, F-sharp, and G. Black keys are sharps moving up, and flats moving
down, so C-sharp is the same key as D-flat. White keys are called naturals.
Natural minor scales have this pattern: A tone B semitone C tone D tone E semi
tone F tone G tone A. Note the total of 12 semitones.
A chord is a simultaneous combination of notes. Here is a look at two types of
three-note chords. A major chord has four semitones between its first and second
notes, and three between its second and third notes. The first, third, and fifth notes
of a major scale identify this, so C major is C, E, and G. The fourth, sixth, and
eighth notes of any major scale also produce a major chord (F, A, and C make F ma
jor), as do the fifth, seventh, and ninth notes (G, B, and D in the next octave make G
major). Check the intervals between their notes to verify that they are major chords.
These three chords include all the notes in the scale; consequently, any tune in a
major scale may be harmonized by using one of these three chords. This is easy to
implement on the 64.
A chord using the second, fourth, and sixth notes of the major scale is a minor
chord, with three and four semitones separation between the notes. A minor chord is
also produced if we build a chord on the third or the sixth notes of the scale. In the
scale of C major, we get D minor, E minor, and A minor. Again, any note can be
harmonized by one of these chords.
As we can see, it's possible to automate harmony to some extent, although the
results are unexciting if prolonged. In general, minor chords are considered dull or
sad and major chords are thought of as bright or triumphant.
It's also possible to generate tunes, by deciding on features like rhythms of mea
sures or bars, the next note to be played, perhaps with probability depending on
previous notes, and ascending or descending sequences, and then varying them ran
domly. Again, the results are generally unexciting.
Musical notation uses a staff of five lines, or two staves, with symbols to repre
sent notes. Pitch is specified by their vertical position; the clef determines the actual
pitch, and the treble clef is usually set with E on the lowest line, G on the next, and
so on. Duration, and thus the rhythm of the music, is signaled by the type of sym
bol. Durations are relative, not absolute. A dot after a note symbol multiplies its
duration by 1.5. Sharp or flat symbols precede the symbols for notes which are to be
sharpened or flatted. Consult your dictionary for a list and explanation of basic
musical notation.
449
Sound
Sound Demonstrations
Bach to the BASICS
The BASIC listing below, Program 13-4, plays a sequence of notes stored as three
bytes of data each, two bytes controlling frequency and one, duration. It gates each
note twice, allowing the full ADSR sequence, and consequently needs two delay
loops, during sustain, then during release.
450
Sound
Music Program
The music routine below, Program 13-5, is much more complex; it plays three-voice
music from data written in approximately conventional notation, so music is fairly
easy to write, compared with programs like the last, with their unnaturally formatted
music data. The drawback is the time taken by BASIC to convert it into ML-readable
bytes; however, once calculated, these can be stored for later use.
452
Sound
453
Sound
454
Sound
To use Program 13-5, after entering the program and saving, type RUN and
press RETURN. Answer N to the question IS MUSIC DATA IN RAM? and wait for
the frequency table to be set up (about a minute and a half). To begin with, enter a
tempo of 160 to hear the music. Press RUN/STOP to leave the routine, and enter
RUN to restart, answering Y to IS MUSIC DATA IN RAM? this time. Enter a new
tempo to hear the music again.
This program uses an interrupt. The music can play during BASIC program run
ning without much slowing effect. The ML sets frequency values, gates, and so on.
Different rhythms between voices are handled correctly, which BASIC cannot do ex
cept at a very slow tempo.
Music encoding system. Notes can be specified by data items of this form:
[Octave 1-7 or + or -] Note A-G [# or F] [duration 1-255]
No spaces are allowed between parameters. The items in square brackets are op
tional: When omitted, current values are used, or defaults, where no value has yet
been input. Octave defaults to 4, the octave starting on middle C; 1 to 7 sets the oc
tave; + or — can indicate a change to the next octave up or down. Note letters are,
of course, A-G, with optional # (sharp) or F (flat) indicators. Duration is in terms of
interrupts. A smaller duration means faster tempo; the default is 16. Some examples
of valid data are:
455
Sound
This sets the on/off ratio to 1 so that the gate is permanently held on, selects the
pulse wave, and plays four C notes, changing the pulse width between each note. A
note with a continuously changing timbre will be heard.
456
Sound
end-of-data marker. The BASIC lines from 1000 on convert music data into this
format.
The ML subroutine at $C0E2 processes this data. It's called three times each
interrupt, once for each voice, and uses indirect addresses ($A5), ($A7), and ($A9).
On and off values for the three notes are stored at $C0C5-$C0CA, to time down at
each interrupt. Location 254 is set to 7 when the end of data has been reached in all
three voices.
On RUN, if data hasn't yet been read, parsed, and POKEd into RAM, lines 1000
on and 2000 on do this. The tempo is POKEd into the CIA timer controlling inter
rupts: Use 160-180 for the Bach piece. (Any tempo can be obtained by choosing an
appropriate combination of tempo value and note duration values in the music data.)
Line 1025 sets the default waveform. The subroutine at 1500-1550 initializes the en
velope of each voice. The Bach piece has A, D, and R set at 1, and sustain at 11.
Note that ADSR values should not be zero with this program.
Organ Keyboard
Program 13-6 below turns the 64 into a simple two-voice organ. It also handles three
notes as far as the keyboard permits—some groups of three keys cannot be decoded
unambiguously by the 64. The keyboard design makes it impossible. The QWERTY
row sounds white notes from G to the E, almost two octaves above. Keys 1, 2, 3, 5,
6, and so on, play F-sharp, G-sharp, A-sharp, and so on. Keys R, Y, and I together
play C major; T, U, and O give D minor; T, *, and O give D major.
5 DATA 120,160,0,132,252,132,253,132,254,162,3,142
,74,192,169,254,141 :rem 52
6 DATA 0,220,162,8,72,173,1,220,205,1,220,208,248,
74,176,21,72,142,75 :rem 50
7 DATA 192,174,74,192,185,129,235,149,251,206,74,1
92,240,20,104,174,75 :rem 142
8 DATA 192,200,192,65,176,12,202,208,224,56,104,42
,141,0,220,208,205 :rem 255
9 DATA 104,104,88,96 :rem 224
20 FOR J=49152 TO 49225:READ X:POKE J,X:NEXT
:rem 220
40 GOSUB 3000 *rem 167
45 PW=400:GOSUB 2000 :rem 93
50 GOSUB 4000 srem 169
60 GOSUB 5000 :rem 171
70 POKE 53281,8 :rem 253
74 BL$="{3 SPACES}{BLK}{RVS}{RIGHT}1{RIGHT}2
{RIGHT}3{3 RIGHT}5{RIGHT}6{3 RIGHT}8{RIGHT}9
{RIGHT}0{3 RIGHT}-{RIGHT}£" : rem 59
75 PRINT M{CLR}" BL$:PRINT BL$ :rem 251
79 WH$=M{4 SPACES}{WHT}{RVS}{RIGHT}Q{RIGHT}W
{RIGHT}E{RIGHT}R{RIGHT}T{RIGHT}Y{RIGHT}U{RIGHT}
I{RIGHT}O{RIGHT}P{RIGHT}@{RIGHT}*{RIGHT}t"
:rem 53
457
Sound
458
Sound
Running the program sets the waveform to sawtooth. Pressing f3 selects the
triangular wave, and SHIFT selects the pulse waveform and, if held, alters its duty
cycle; it can operate when a note or chord is held, making audible the resulting
change in timbre. Pressing fl reselects sawtooth.
An ML routine is essential to determine which keys are pressed, as the keyscan
routine returns just one value. This program's ML puts ASCII values in 254, or 254
and 253, or 254, 253, and 252, depending on whether one, two, or three keypresses
were detected. BASIC PEEKs these locations and converts them to pitches.
460
Sound
The program displays a grid of 3 rows and as many as 32 columns; the rows
represent the SID voices and the columns stand for beats in the bar. An X character
moves horizontally over the grid showing which beat is being played.
461
Sound
The program first asks the user to specify the number of beats to be used, the
tempo, whether the rhythms currently in RAM should be cleared, and whether the
rhythms defined in lines 7000-7026 should be loaded into memory. Press RETURN
in response to the first three prompts to select the defaults, and Y to the last prompt
to load the built-in rhythms.
Typing 1-6 plays the designated instrument at that point in the bar and adds it
to the display and to the rhythm stored in RAM so that it will be heard at that point
in the bar from now on. Pressing fl, f3, or f5 as the program plays will delete voices
1 and 2, 3 and 4, or 5 and 6, respectively, from the display and from memory at the
position in the bar where they are typed. Thus, instruments can be added to and re
moved from the rhythm. If you press RUN/STOP, then run the program again, you
can keep the new values by being careful not to clear them or overwrite them.
DATA in lines 7000-7026 holds the start-up rhythms. There are three groups of
DATA statements, each of 32 values, one for each beat of the bar. A value of 63 in
dicates that no note is present at that beat. Values of 0 and 8 in the first group mean
that instrument 1 or 2 has a note on that beat; values of 16 and 24 in the second
group mean that instrument 3 or 4 has a note on that beat; and values of 32 and 40
in the third group mean that instrument 5 or 6 has a note on that beat. The data
may be edited to provide accurately programmed rhythms, and rhythms can be
edited as the system plays.
462
Chapter 14
Tape Storage
• Loading and Saving BASIC Programs
with Tape
• Handling Tape Data Files
• Loading and Saving Machine
Language
• Tape Hardware Notes
• Advanced Tape Programming
• Copy Protection for Tape
Chapter 14
Tape Storage
Tape is a popular storage medium for the Commodore 64. This chapter discusses
tape operations completely; it will give you the information you need to handle
practically any tape operation.
IK 1/2 min 4 8 16 25
4K 1-1/2 min 1 3 6 9
8K 2-3/4 min — 1 3 5
Before considering the full syntax of LOAD and SAVE, it is helpful to look at a
few aspects of BASIC storage. These commands have the functions of loading RAM .
from tape and of dumping RAM to tape, respectively. In fact, they use the start- and
end-of-BASIC pointers, in locations 43 and 44 (start) and 45 and 46 (end). This is
why variables can't normally be stored along with BASIC. The zero byte at the very
start of all BASIC programs is not used; neither is the byte at the end-of-BASIC
position.
The cassette recorder (sometimes called the Datassette) is not under full com
puter control, which is why screen prompts are necessary. In particular, there's only
one line to test for a keypress on the cassette, so the 64 cannot distinguish PLAY
from RECORD. Even the fast forward and rewind keys are detected as though PLAY
or RECORD were being pressed. Thus, if you want to rewind a tape and record from
the start, rewind before pressing RETURN after SAVE.
In addition, be sure to press both RECORD and PLAY to save to tape. PLAY
looks the same on the screen but of course doesn't work. If PLAY and RECORD are
465
Tape Storage
accidentally pressed for the LOAD command, the program on tape will be erased,
unless the write-protect tabs at the back of the cassette are missing.
Tape operations use the IRQ interrupt, locking out the keyboard. However, the
RUN/STOP key subroutine is called at intervals, so RUN/STOP and RUN/STOP-
RESTORE still work. Without this, if tape reading failed in some way, the 64 would
have to be switched off. Note that the TI clock is turned off during tape operations.
Several programs can be stored consecutively on each side of a tape; however,
the simple LOAD syntax can't distinguish between them. So the system allows
BASIC programs to be named. The complete syntax for SAVE is SAVE "filename"
which saves the program along with a name. The corresponding LOAD "filename"
searches for the named program and also (so you know where you are) lists any
other programs it may find. For example, following the command LOAD "CHECK
ERS" the screen may show something like this:
LOAD "CHECKERS"
PRESS PLAY ON TAPE
OK
SEARCHING FOR CHECKERS
FOUND CHESS
FOUND CHECKERS
LOADING CHECKERS
READY.
The maximum length of a name, as it appears after FOUND, is 16 characters.
Provided the found program name matches, the program is loaded. LOAD "CH"
loads CHESS if it finds that program on the tape before CHECKERS. LOAD
"CHEC" loads CHECKERS. This is why LOAD alone always loads the first program
it finds.
466
Tape Storage
her. The type-of-save parameter uses two bits; 0 means SAVE allowing relocation,
while 1 means SAVE with a forced-LOAD address. This, too, is seldom used in
BASIC. If the parameter is 2, it means SAVE with an end-of-tape marker; a value of
3 means SAVE with both forced-LOAD address and end-of-tape marker.
Some examples will make this clearer. SAVE "TEST PROGRAM",1,2 stores
TEST PROGRAM on tape, followed by an end-of-tape marker. SAVE CHR$(18) +
CHR$(28) + "PROGRAM" adds an RVS/ON and a RED character to the program's
name. When it's found, this will generate FOUND PROGRAM and the name will be
reversed and in red characters.
SAVE "EXCEPTIONALLY LONG NAME" stores the program in memory onto
tape with the full name as it is given. Although LOAD checks only the first 16
characters, the others are in fact saved; as you'll see, they can be put to use in pro
gram protection.
467
Tape Storage
Because of this slow speed, data file handling may be absurdly slow. It may be
worthwhile to save data along with programs, although this is a tricky technique,
requiring the end-of-program pointer to be moved to include variables and the first
line of the program to POKE in the correct value. In addition, strings are hard to
save. The whole of BASIC memory needs to be saved, plus the pointers which
handle strings.
Files need a buffer. Unlike a program, which has a place allocated in memory at
one time, files need to be written piecemeal, with data accumulating until the buffer
468
Tape Storage
is full. It's not possible to write directly to tape, because the motor needs to pick up
speed, so there's a stop/start option with files.
Commodore 64 tape files are inevitably sequential. Since data has to be written
(and read back) in order, the only way to access data that is part of the way into a
file is to read the whole file from the start. Moreover, there's no way to alter file
information, without reading everything into memory, altering it, and writing it
back. Thus, the system has severe limitations, although to be fair these are largely
constraints of the system and are unavoidable without advanced methods like those
described later in this chapter.
There are three stages in file use. First, the file must be opened, meaning that
preparations are made in memory to write data to tape. Second, write the file to
tape. Finally, close the file, meaning that the file is correctly terminated.
When the file is to be read back, perhaps by a different program, three other
steps are necessary. First, open the file for reading, which prepares the 64 for input
from tape. Second, read the file from tape; it may be read in parts. Finally, close the
file. The final step is often not really necessary; since nothing is being written to
tape, the file will be left unchanged.
469
Tape Storage
To read this back, use the INPUT# command. This can only be used from within
a program. Therefore, most file reading is done in program mode. Type in Program
14-1, rewind the tape, and run it. After PRESS PLAY ON TAPE there'll be a delay
while the header is found and read; then the word HELLO should be printed on the
screen. The word was recovered from tape, showing in miniature how files work.
470
Tape Storage
locations. All these examples occupy continuous chunks of RAM, so LOAD and
SAVE can be used. Data files aren't necessary. BLOCK LOAD and BLOCK SAVE,
discussed in Chapter 6, provide methods (with examples) for doing this reliably.
Forced-LOAD addresses. If memory is saved with the forced-LOAD parameter,
for example by SAVE "GRAPHICS",!,!, then LOAD will always position GRAPH
ICS back in the area it was saved from. SAVE "GRAPHICS" allows repositioning;
LOAD will now load starting at the BASIC pointer area. But LOAD "GRAPH
ICS", 1,1 forces a LOAD back into the original area. Thus, it is SAVE which deter
mines whether LOAD always puts data back where it came from.
Therefore, when saving ML, it is usual to insure a forced LOAD by using the
syntax SAVE "ML",1,1. Loading into an ML area changes BASIC pointers; use the
BASIC command (not the disk command) NEW to set them back to normal.
A more easily understood method to save blocks of data is simply to PEEK in
dividual bytes and write them as files. Reconstructing the data requires the reverse
process of reading back the file and POKEing individual bytes to RAM. This methoc}
is slower than a BLOCK SAVE, though. Programs 14-2 and 14-3 illustrate the
method, applied to bytes from $8000 to $9FFF. Other address ranges can of course
be used instead:
471
Tape Storage
The C2N takes its power from the 64 through the same connector that handles
data transfer, although it could be powered separately with a small modification, The
connector can be plugged in only one way; most recorders cannot be connected in
correctly. Cable lengths vary between models but are generally adequate.
C2Ns use an ordinary 1-7/8-inch-per-second tape transport mechanism, plus
additional circuitry to control the 64 specific features like keypress detection. All tape
recorders use similar principles: The C2N has an erase head, to remove signals (if
any) from tape, followed by the record head, which records vertical magnetic stripes
on the tape. On playback, the same head acts in reverse to play back the signals on
the tape, generating induced voltage when the tape is drawn past the head.
The 64 uses a square-wave system, alternately changing the direction of mag
netization. Square waves are relatively difficult to copy with ordinary audio equip
ment, which tends to round them off, and this provides some protection against
unauthorized tape copying. However, commercial tape duplication is done by
recording on tape from an original with equipment designed to preserve the original
signal shapes.
The C2N's record/playback head is mounted so that its angle to the tape is vari
able, although it's not usually advisable to alter it. However, if the angle isn't
reasonably perpendicular, read errors are possible with tapes made on other record
ers. The newest C2Ns have a small hole through which the relevant screw can be
turned with a plastic screwdriver.
A more common source of problems is a magnetized head. Demagnetizers are
simple coils which use alternating house current to magnetize the heads alternately
in opposite directions; as the demagnetizer is moved away, the inverse square law
insures that the remaining magnetism is minimal. Tapes played with magnetized
heads may be partly erased. If your recorder doesn't read tapes which it should be
able to read, demagnetize it immediately.
The capstan is the metal spike which drives the tape at a fairly constant speed.
Tape is trapped between it and a hard rubber pinch wheel when reading or writing.
It's best not to leave the PLAY key pressed with the recorder off, or the tape may
become dented by the capstan and give irregular playback.
When rewinding or running fast forward, the pinch wheel is disengaged and
one of the spools is driven directly. When playing at normal speed, the right-hand
spool is kept under tension so the tape is wound tightly.
The tape counter is connected by a belt to the right-hand spool. One turn of the
counter therefore indicates more tape when the right-hand spool is full than it does
when the spool is nearly empty. Actual tape length is a quadratic expression of the
counter reading, so the counter readings corresponding to programs of equal length
on the same tape show progressively smaller differences.
Routine recommended maintenance involves cleaning the heads, typically with
a cleaning kit consisting of cotton swabs and cleaner. The cleaner is a liquid like iso-
propyl alcohol, never a plastic solvent like trichloroethane.
The best type of tape is ordinary ferric oxide (not chromium) tape of reasonable
quality. A screw-type cassette casing is preferable, since it can be taken apart if the
tape gets tangled. Very long tapes are good for storage, but shorter tapes, perhaps
with only one program each, save search time. It's not really possible to test tapes;
this is far too time-consuming.
472
Tape Storage
All cassettes have write-protect tabs at the back left of the cassette case, one tab
for each side of the tape. If these tabs are removed, the recorder won't save to that
tape, so much commercial software is packaged in cassettes like this. Put a piece of
masking tape over the gap if you wish to record over a protected tape.
Since both sides of the tape are usable, only half the width (1/16 inch) is used
at one time. Thin tape is prone to problems with print-through: in fact, a tightly
wound spool left for some time may degrade as magnetism is transferred between
adjacent turns of tape. However, even short tapes may be thin, and thus prone to
this problem; there's no easy way to be sure which tapes may have trouble and
which will not.
Most tapes start with a nonmagnetic leader to take the strain at the end of fast
winding. The tape operating system allows for this, with seven or eight seconds of
tone before actual recording proper starts, but you may prefer to manually wind for
ward so all recording begins on the magnetic part of tape. Another tip: Rewind
brand-new tapes before recording on them. High-speed manufacturing equipment
stretches tape to some extent; by rewinding the tape first, you relieve the stretch and
make the tape more stable.
473
Tape Storage
Pin A B C D E F
Function Ground +5 Volts Motor On/Off Read Write Cassette Key On/Off
474
Tape Storage
then two seconds of tone, then the program. The header and the program are each
written twice; you'll hear a short pause midway in each. Any program records or
loads in about 15 seconds, plus 15 seconds per K (kilobyte).
Error Correction
As data is read, errors in the first copy of the recording are noted (and corrected, if
possible, by reading the second copy). Only 30 errors are allowed; these are logged
in RAM at $0100-$013D (at the bottom of the stack area). PEEK(159) gives a count
of the errors after a full read; this should be zero. Small ML routines to be put in the
stack area are best started after $013D if tape is to be used.
Headers in Detail
Tape storage relies on headers; if you understand them, you understand most of
what you need to program tape.
There are five types of headers, as diagrammed in Figure 14-2. Only the first 21
bytes are normally used, unless you wish to add ML or program protection.
The tape buffer, which holds headers and file data, normally extends from
$033C to $03FB (828-1019), a total of 192 bytes. You can change the location of the
buffer by POKEing into $B2 and $B3 (178 and 179). As it happens, OPEN 1 accepts
any of these header types, not just files, and provides a simple way to look at
buffers.
Put a 64 program tape in the recorder, type OPEN 1, and press RETURN. Then,
when the header is found, type FOR J=828 TO 850: PRINT PEEK(J);: NEXT. Now,
the first byte is 1-5, the second and third bytes are the start address, the fourth and
fifth are the end address, and the following bytes are the name.
Using SYS 63553 in place of OPEN 1 loads the first 192 bytes of any tape data
into the buffer, so use this if you want a tape directory which allows you to examine
the start of ML or BASIC programs, or read the whole of data files.
Tape Directory
The BASIC program below, Program 14-4, will identify and list programs and files
on tape. It's easily modified if you wish. Note its use of SYS 63553. This is part of
OPEN, but doesn't skip blocks starting with bytes other than 1, 3, 4, or 5. The pro
gram uses PRINT statements to display data on the screen; if the data contains con
trol characters like 147 (clear screen) or 13 (carriage return), the display will be
altered accordingly.
475
Tape Storage
Start End
1 Name
Address Address
Program Header—Relocatable
Start End
3 Name
Address Address
Start End
4 Name
Address Address
DATA
Data Buffer
Start End
5 Name
Address Address
End of Tape
Another way to inspect header storage is POKE 178,0: POKE 179,4 to move the
start-of-buffer to the start-of-screen. Then, OPEN 1 (for data and programs, SYS
63553) will load 192 bytes directly into the screen. FORJ=55296 TO 55296+192:
POKEJ,1: NEXT will make all the bytes visible in white. Remember to POKE 178,60:
POKE 179,3 or reset with SYS 64738 to return the buffer to normal.
To summarize, executing either a LOAD or an OPEN will cause 192 bytes to be
read from whatever block is next on tape. If the first byte is 1 or 3, a program header
is assumed found; if 2 or 4, a data file; and if 5, an end-of-tape marker. If BASIC or
ML programs happen to start with a byte in the range 1-5, they'll give a spurious
appearance as a header, data file, or end-of-tape marker. This can happen if LOAD
or OPEN misses the header. In fact, this isn't likely to occur, since it's impossible for
normal BASIC'S first byte, which is part of the link address of the first line, to be less
than 7, and the only ML commands in the range 1-5 are uncommon ORAs. Note
that 0 isn't used as a marker because the earliest PETs saved the very first null byte
as part of BASIC, so the headers, to avoid confusion, started with 1.
477
Tape Storage
of 64 files. (Programs have a single short screen enable just after the first copy is
written or read.)
Several tricks to make programs harder to copy are explained at the end of this
chapter.
This loads whatever program it finds next on tape with a forced LOAD, then
jumps to address $1000.
SAVE's ROM entry point is $E156; its Kernal routine is $FFD8, which jumps to
$F5DD. Tape saving is handled from $F65F; $F76A writes the header and $F867 the
program.
All conventional LOAD and SAVE routines use both a header and its sub
sequent program. Before seeing how to operate these separately, however, note the
useful RAM locations in Table 14-3.
478
Tape Storage
479
Tape Storage
480
Tape Storage
The ML routine contains five parameters which control the header tone. Any
user-defined header can be written to tape, for example, to cause a forced LOAD
into a normally difficult area of memory. And any consecutive block of bytes can be
written to tape, allowing great flexibility in program construction.
481
Tape Storage
BASIC warm start vector in ($0302). Normally $A483, this can be directed
either into the header or into the loaded program, perhaps spanning $02A7-$0303.
Then ML LOAD and RUN will automatically run the BASIC program that comes
afterward.
Input vector in ($0324). Again, a loader might span $02A7-$0325, so the al
tered input vector might jump to $02A7.
Tape interrupt vector at ($029F). This vector is difficult to use on the 64, since
it is followed by operating system variables.
A BASIC Autoloader
The autoload routine in Program 14-7 will run the BASIC program immediately
following it on tape. All that's required is to enter LOAD. It disables RUN/STOP and
RUN/STOP-RESTORE, and scrambles LIST, to give some program protection.
First, add these few program lines to the program which writes any data to tape.
Run the program, with a rewound tape in the recorder, and prepare to write a
header: the parameters to put in are 3, 167, 2, 4, and 3. (Forced LOAD into
$02A7-$0304. The very last address is unused, so the ML will straddle $02A7
through $0303.)
482
Tape Storage
483
Tape Storage
When the header is written, prepare to write data. Use a starting address of 828
and an ending address of 921. A short ML program is put at the start of the buffer,
which will be loaded and run at $02A7. It disables RUN/STOP, restores ($0302) to
normal, puts #$83 (SHIFT-RUN/STOP) into the keyboard buffer, and executes a
JMP ($A000). This is enough to force the next program to load, then run. The final
bytes of the buffer hold the modified address, $02A7, which will be force-loaded
into ($0302) and hence start the whole process.
Many tapes using this sort of copy protection load into the entire area of RAM
from, for example, $0300 up. This makes loading times rather long, as the area from
$0400 to $0FFF is usually wasted.
There is considerable scope for ingenuity in using encoding routines, non-
standard 6510 instructions, programs without headers, and overlays. If the
RUN/STOP and RESTORE keys are disabled, a reset switch leaves most memory in
tact but erases all of RAM from $0000 to $0400 except the stack ($0100-$01FF).
Thus, if key parts of a program are left in this area, the result can be all but
impenetrable.
484
Chapter 15
Using Disk
Storage
Introduction to Disk Storage
Basic Disk Commands
Handling Disk Files with BASIC
Summary of Disk Commands and
Messages
Commodore Utility Programs
Hardware Notes
Disk Data Storage
Machine Language Disk
Programming
Chapter 15
Disk storage is more expensive than tape, but it is also more versatile. It can be used
to store a selection of programs for rapid loading, but it also gives you access to
large amounts of data.
This chapter begins with a discussion of straightforward disk commands and
progresses through more advanced material. By the end of the chapter you'll be able
to handle most disk programming tasks.
487
Using Disk Storage
The keyboard is affected by disk operations. For example, while data is being
read from disk, the interrupt is mostly off. This means the keyboard cannot be pro
cessed normally, and keys pressed when the drive is active may not show up when
it stops. If you're using a disk system to store data, bear this in mind. Clear the key
board queue (POKE 198,0) before INPUT or GET to insure that incomplete data isn't
written to disk.
Disk commands are more explicit than tape commands. Unless otherwise in
structed, the 64 assumes that LOAD (or whatever) applies to tape. Thus, disk com
mands always include the device number, which is normally 8. Also, because disks
can store many programs, the bare command LOAD is disallowed. Instead, quotation
marks and names are used. For example, to simply read the disk directory you must
type in LOAD "$",8 then LIST. The DOS 5.1 wedge (on the demo disk) offers lim
ited help with this.
Serious disk drive users, who are using disks to store valuable data and writing
their own programs, too, should take note of a few points to keep from losing data.
First, duplicating disks for security purposes isn't easy with only one disk drive. Sec
ond, there are potential problems when the process of writing to a disk is interrupted
(for example, by a SYNTAX ERROR) because incomplete information is left on the
disk and may corrupt other files. Subsequent parts of this chapter will discuss these
areas in more detail.
Formatting a Disk
Switch on the disk drive and the 64, insert a new, blank disk (or one that contains
information you no longer wish to keep) in the drive, and close the drive door.
You're now ready to format the diskette. Formatting gives the diskette a name and a
two-character identifier; it also writes data on the disk to identify it as a 1541-format
disk.
Every time you format a disk, all programs and any data that it contains will be
wiped out. Don't format a disk more than once unless you no longer need its con
tents and prefer an empty disk.
To format a disk, type in the following command and press RETURN:
OPEN 15A15/'NEW0:M4ME/ID":CLOSE 15
The red light on the disk remains on for about a minute and a half; the drive
should first move to the outer track (with some noise), then click gently as it writes
to the disk. After 35 clicks, the drive will stop and the red light will go off.
The disk's name can have up to 16 characters (for example, DISK TESTS 1) and
the identifier up to 2 characters (for example, 00 through 99). Avoid using the sym
bols ?#*,:" or @ in names sent to the disk, since they may be interpreted as
separators or special operators.
The identifier is written to the disk nearly 700 times. It helps to check that data
is in its expected position and that the disk hasn't been inadvertently changed. It's
488
Using Disk Storage
thus advisable to give your disks individual IDs; otherwise, swapping disks to load
from one and save to the other may scramble data if the IDs happen to match.
If you want to change the disk's name, you can use a shorter formatting com
mand. Omit ,ID from the formatting command; the name will be changed and the
data apparently will all be deleted, exactly like full formatting, but the ID isn't
changed. This takes about ten seconds.
Saving a Program
To see how to save a program to disk, first type NEW, press RETURN, and then
type in any short program. Then type in SAVE "PROGRAM",8 and press RETURN
to save the program to disk with the name (up to 16 alphanumeric characters) you
gave it. Don't include ? # * , : or @ in the name. A null name (SAVE "",8) is re
jected with 7MISSING FILE NAME ERROR.
If you wish, you can VERIFY, with either VERIFY "PROGRAM",8 or VERIFY
"*",8. In either case, you should see the following display as the program is com
pared with the version in memory.
SEARCHING FOR PROGRAM
VERIFYING
OK
Disks are generally reliable enough to make this unnecessary. The version with *
uses Commodore's pattern-matching technique, explained below. The same idea al
lows LOAD "*",8 to load the first program it finds, when the drive is turned on.
When using disks, SAVE won't work if a program with the same name already
exists on a given disk. This is a security measure. An error is generated by the disk
drive, and the red light flashes, but no error message is displayed on the screen.
You'll soon see how to read the disk drive's message.
SAVE's syntax has an optional form causing SAVE with replace. It allows a pro
gram to overwrite another program with the same name. The command is SAVE
"@:PROGRAM",8 where the added @: is interpreted by the disk as a command to
overwrite. So, if you modify your program and then enter SAVE "@:PROGRAM",8,
you'll find the newer version present on loading later. It's only fair to note that disk
489
Using Disk Storage
errors of the kind caused by unclosed files (corrupted programs and/or data) have
been associated with this command, so if you're the cautious sort, it's better to
scratch the old file before saving.
After saving a program, LOAD and LIST the disk directory. The diskette's name
and ID remain the same, but a program (PRG on the line after the name shows it's a
program) is present. If it's a short program it will probably occupy only one block,
leaving 663 blocks free.
Loading a Program
To load a program, type in LOAD "PROGRAM",8 and press RETURN. The disk
drive will run for a few seconds, and the READY prompt will appear. Then LIST or
RUN your program. LOAD "PR*",8 or LOAD "*",8 will have the same effect, if
PROGRAM is the first name in the directory.
LOAD "filename",8,1 is necessary for a nonrelocatable LOAD. Machine lan
guage, graphics definitions, VIC-II chip registers, and any data which needs to be re
placed at the point from which it was saved uses this syntax.
You can also use LOAD in program mode. LOAD from within a program pro
duces the same chaining effect that you get with tape; however, it is much faster.
The new program, presumed to be BASIC, runs from the start. It retains all the old
variables if the new program is no longer than the old one and if strings and func
tions held within BASIC are redefined. See CHAIN and OLD in Chapter 6 for fur
ther discussion.
ML and memory dumps can also be loaded successfully. Use 10 X=X+1:IF
X=l THEN LOAD "GRAPHICS",8,1:REM ONLY LOADS FIRST TIME.
Scratching a Program
Scratch is a strange computerese word meaning to remove or erase a program. With
tape, it's simple to rewind and obliterate a program by recording over it. Disks need
a specific command, however, because the disk drive can't know which program to
scratch unless it's told.
SCRATCH has this syntax:
OPEN 15A15/'SCRATCH:///£?«am^':CLOSE 15
490
Using Disk Storage
to guarantee that the disk to be copied to is correctly set up. INITIALIZE has this
syntax:
OPEN 15,8,15,"INITIALIZE":CLOSE 15
or
OPEN 15,8,15,"I":CLOSE 15
or
OPEN 15,8,15:PRINT#15,"I":CLOSE 15
To actually copy a BASIC program, first acquire two disks. Call them source and
destination. Then follow these steps:
1. LOAD "filename"$ from the source diskette.
2. Remove the source disk, replace it with the destination disk, and close the drive
door.
3. Enter OPEN 15,8,15/T' to initialize the destination disk.
4. SAVE "filename",% to save the program onto the destination diskette.
5. Replace the source diskette, enter PRINT#15/T' to initialize it, and return to step 1.
Repeat the process until you've moved as many programs as you want.
491
Using Disk Storage
Quite a number of copy utilities are on the market. Single-drive copiers need
only one drive. There are two basic types: Some copy an entire disk, reading as
much as possible into RAM, then copying an exact image onto a new disk. More
sophisticated versions copy only those parts of the disk on which data is stored. One
disk typically takes 15 minutes and six disk changes. The other type copies individ
ual programs or files, allowing the user to select only those worth copying. This sec
ond type (the program above is like this) is fine when small numbers of programs
are to be copied, but tedious with large numbers of programs, because the disks
typically are changed between each read/write operation.
Two-drive copiers use two daisychained drives. The technique is to switch on
one drive, load and run a disk-device number-change program to reassign it as drive
9 (unless the drive's been changed in hardware—see below), turn the other drive on,
and load the copy program. "COPY/ALL" on the demo disk is this type of program.
Again, disks can be copied in entirety or copied one file at a time by utilities.
492
Using Disk Storage
If the disk drive has no current error stored in it, the result will be 0 OK 0 0,
where the first zero is the error number, OK is the message from disk, and the track
and sector of the error (both zero) mean there's no problem. This is a long-winded
way to discover the disk status. It can be tedious to enter and run it just to discover
the reason for a disk error or problem. Note that direct mode can't be used; the line
must be entered as part of a program. Therefore, when developing disk programs, it
makes sense to include this as, say, line 40000 so that RUN 40000 is ready and wait
ing if needed.
Note that reading the channel clears it, so a subsequent read will say OK even if
there's a major problem (like an open disk drive door). The message remains until
either the channel is read or disk activity forces in another message.
You'll see later the circumstances in which the flashing error light, which is apt
to alarm newcomers, can be ignored. First, though, deliberately generate some errors
and watch the effect of running line 10 above:
1. Enter LOAD //o/o",8. This program doesn't exist on disk. RUN gives:
62 FILE NOT FOUND 0 0
which, translated, means that message 1 (which always deals with scratched files)
reports that just one file was scratched by the command. More than one file may
be scratched if pattern matching (with "S:*") is used.
5. Turn the disk drive off. Open the disk drive door if there's a disk present; this in
sures that no magnetic glitch can occur on the disk. Turn on the disk drive and
immediately enter RUN. Your message is something like this:
73 CBM DOS V2.6 1541 0 0
which tells you what type of ROM your disk unit has.
If you want to experiment more, try the DOS 5.1 wedge from the demo disk,
which modifies BASIC so that just pressing @ prints the message.
493
Using Disk Storage
or
Program Files
These are simply programs or memory dumps which can be loaded and run (if
they're programs) in the usual way. However, the disk system also allows them to be
read from and written to, and that makes several nice programming techniques
possible.
494
Using Disk Storage
Sequential Files
Next to program files, sequential files are the easiest to understand. Data is written
to them from a buffer, in sequence, without restriction on the type of data or its
length. Thus, the file can be of any length, regardless of the computer's RAM. Be
cause sequential file data isn't ordered, it is usually read back in sequence starting at
the beginning. As a result, long sequential files can be slow to handle.
In practice, 64 sequential files—whether on tape or disk—aren't usually quite so
free from structure. This is because it's easiest to use PRINT# to write data to a file
and INPUT# to read it back, and both of those commands have certain restrictions
on length and type of character that they can handle.
Relative Files
Relative files do not have to be read from the beginning. Any record in the file can
be read by number; thus, random access is a name sometimes given to such files.
With the 64, this is made possible by defining a record length when the file is ini
tially opened, and diskette space is assigned as it's needed. For example, if record
number 200 is to be written to a new relative file, the disk's operating system allo
cates space on the diskette for 200 records of the desired length before writing the
data of record number 200.
Relative filing is more ordered than sequential filing; later, you'll see exactly
how that is accomplished. For the moment, note that the records are the same
length. This wastes disk space if some records are far longer than others. Obviously,
a shorter maximum record length allows more records to be filed.
Note also that accessing records by number may not be what you really want.
For instance, you may find yourself using extra files, or arrays, to convert JONES
into number 93. Nevertheless, this is the most advanced form of filing offered by
most microcomputers.
Direct access files may also be used. Commodore's manuals refer to the system
of storing data at certain sectors on the diskette as random access, which is explained
in the section on data storage. More usually, direct access filing refers to a system
allowing access to records by a single key. This is a fascinating system of file
organization, easily implemented on the 64.
To take an actual example: You want to be able to read from disk, as fast as pos
sible, information on any one person out of a group of 400 by entering the person's
name. The 64's relative file system requires a number between 1 and 400, and the
idea of direct access is to convert the name into a number within that range. This
could be done by converting some of the name's characters into ASCII, then generat
ing a key from 0 to 1 and using RND(— key)H00+l to generate a repeatable value
in the required range. A good algorithm will, of course, evenly spread the coded val
ues of the keys. With this kind of organization, records are held in the file in a jumbled
sequence, but can be recovered by applying the coding algorithm to the key.
Direct access has several drawbacks, however. First, there's no easy way to print
a sequential list of the records. In other words, it's difficult to check what's on file.
Second, many keys will inevitably generate the same record number, so it's nec
essary when writing to the file to check that the record number isn't used (if it is, try
the next one). It's also necessary, when reading, to read until the correct record is
495
Using Disk Storage
found. For that reason, the file has to be longer than the number of records by at
least 30 percent. In this case, about 35 percent of the records are synonyms, but this
drops to 25 percent if all keys are tested first and all synonyms are stored in the file
in a second pass. If the most frequently used records are entered first, efficiency im
proves again.
Inverted files are used in data base programming, where there are huge
amounts of data in a main file, and where you want a list of items conforming to
several stringent criteria.
Instead of reading the entire file, a large number of smaller files are established,
with each holding keys to a subset of the original data. As a result, fewer files have
to be read, but only at the expense of extra file space being taken up and extra work
being required to add new records to a number of files. For example, 26 subsidiary
files for initials A-Z plus a full-length relative file works well in some applications.
For example, OPEN 2,8,2,"0:ORDINARY FILE,S,W" opens file 2 to disk drive 8, and
uses channel 2 in the disk drive. (This is relevant with random access storage and
with relative files.) The command string begins with 0:, which is a construction from
Commodore drives which have two disk drives, instead of only one. The other drive
used the prefix 1:. This chapter ignores 0:, but it is often suggested that the prefix
should be used. Regardless, readers with access to PET/CBM machines should keep
this syntax in mind.
The other part of the command string uses commas as separators and causes a
sequential (S) file, called ORDINARY FILE, to be set up for writing (W). PRINT#2
will now write to this file, and CLOSE 2 safely completes all the housekeeping.
You'll see further examples shortly in the demonstration programs on file handling.
Note that the file number cannot be 0. Ordinarily, use any number from 1
through 127. File numbers 128 through 255 should usually be avoided, because
PRINT# to them sends linefeed (CHR$(10)) with carriage return. This is useful with
some printers, but not generally helpful with disk files.
The device number is 8 unless changed by hardware or software. It's possible to
connect two drives at once, one with device number 8 and the other with device
number 9, and open several files to each (OPEN 3,9,3,"ORDINARY FILE,S,R"),
allowing reading from 9 and writing back to 8.
The channel number should generally not be 0, 1, or 15. This is because 0 and 1
are related to the directory, and 15 is the command channel. The command string
syntax varies with the type of file. See the demonstration programs.
PRINT* is one of three BASIC commands (the others are INPUT# and GET#)
that let you send output to a file and read it back, either as a batch of characters (IN-
PUT#) or as individual characters (GET#). PRINT# outputs string and number ex
pressions to the file in just the same way that output is sent to the screen. The
organization of relative files is identical to that of sequential files, as far as the stored
data is concerned. PRINT# treats a colon or end-of-BASIC line as requiring a
carriage-return character. The semicolon causes PRINT# to print no extra characters.
496
Using Disk Storage
The comma outputs ten spaces (in effect, tabulating across). Numbers appear in the
file with a leading minus or space, and with a trailing space, too.
The effect of OPEN 2,8,2,"TEST,S,W" followed by PRINT#2,"HELLO"; 12345;
"HELLO","HI" is shown in Figure 15-1.
497
Using Disk Storage
CLOSE operates on one file only; you need CLOSE 2: CLOSE 15 if files 2 and 15 are
open. Unclosed files can cause problems. See the section "When to Ignore the Red
Warning Light" below for a full discussion.
498
Using Disk Storage
You can ignore the flashing red light if you are reading from disk. Suppose
you've typed LOAD "PROGARM",8 in error; the red light flashes, and the message
is ?FILE NOT FOUND. Type the correct version, LOAD "PROGRAM",8, and loading
will proceed normally with no problems. The same sort of thing applies in a pro
gram: 10 OPEN 2/8,2//FIEL,S,R" might generate an error, but if the line is edited
and the program rerun, no harm will result.
Take the red light seriously if you are writing to disk and a write file is still
open. An unclosed file can cause problems with storage to disk, because the normal
system of chaining between sectors is disturbed; other programs and files can become
corrupted. This isn't likely to be a major problem. However, if you are using a file
system for a serious purpose, you should be aware of this possibility, since there will
almost inevitably be program crashes during testing. When programs are finally com
pleted, it is good practice to transfer them to new disks to avoid any chance of error.
The steps to take and danger signs to watch for are listed in the next section's notes.
Line 2 must include the name of the program to be examined. Alternative forms
of the command string such as OPEN 2,8/2//NAME,PROGRAM/READ// or OPEN
2,8,2^$+",P,R" are perfectly acceptable.
499
Using Disk Storage
Run this with a BASIC program as its PRG file, and you'll get BASIC in its
tokenized form. For instance, if you save a one-line program (10 PRINT'HELLO")
and then look at it using this program, you'll get something like Figure 15-2.
^.Analyzing ML programs. You've seen how to read the two LOAD address
bytes. If you wish to load ML into a different area, you can change the LOAD ad
dress by rewriting the two leading bytes using the routine in Program 15-4.
500
Using Disk Storage
Lines 50 through 70 transfer the entire file, except for the first two bytes. The
old LOAD address is thrown away; the new is set at $C000. Note in line 50 how a
character which GET# regards as null must be converted into CHR$(0); otherwise,
zero bytes will be lost. Line 60 preserves ST, which is reset by the PRINT# com
mand, for the end-of-file test in line 70.
If you change disks, you may need to initialize the new disk (or add line 0
OPEN 15,8,15/T': CLOSE 15 to the program).
PET/CBM programs load at $0401, so 64 programs can be made to load into
these machines with this program. Occasionally, you may even want to use the pro
gram to restore LOAD addresses to programs which have lost them through incorrect
copying.
Writing machine code or graphics definitions directly onto disk. As with
BASIC, there's no problem in opening a program file, writing a two-byte LOAD ad
dress, and following this with bytes. For example, where RAM is already occupied
by machine language or BASIC, or in tricky areas like zero page or the screen, this
technique allows any area of RAM to be saved to disk. An autorun routine, analo
gous to those used for tape, provides an illustration.
Autorunning program. This is trickier with disk than with tape. If the start of
BASIC is fixed, extra ML can be added to the start of the program to cause it to run
automatically after loading. Alternatively, and for greater versatility, you can use a
loader which calls the program by name, and so allows for variations in starting
address.
Program 15-5 autoruns ML programs, which therefore need no SYS call. The
forced LOAD address is $02A7. The autorun feature is caused by changing the vector
at $0302-$0303 to $02A7, where the ML program is loaded by name (in effect, with
LOAD "ML",8,1) and then jumped to.
To use this program, you need a BASIC program on disk, Program 15-5 (below)
in memory, a new name for the program, plus a two-line message. When run, the
program adds ML from $02A7 to the start of BASIC. In other words, LOAD
"NEWNAME",8,1 puts BASIC into its normal position, but prefaced by about five
blocks of ML which autoruns. It also straddles the screen, so the message which is
input appears before the program runs (it's timed to stay for about five seconds and
appears black-on-white). RUN/STOP and RUN/STOP-RESTORE are automatically
disabled. Some of the ML simulates plug-in ROM at $8000, which means that even
a hardware reset cannot break into the program as it runs.
Autorun assumes BASIC starts at $0801, as it normally does; VIC-20 is more
awkward to autorun than the 64 is, because its starting position varies with memory
expansion. Test the new program, then delete the pure BASIC version from the disk;
now you have an autorunning BASIC program.
PRG files occupy 254-byte sectors on the disk; the first two bytes are the LOAD
address. Thus, 252 bytes go into the first sector, while 254 bytes go into the remain
ing sectors. An 8K program (8192 bytes) therefore occupies approximately 32 lA sec
tors, which appear as 33 sectors on the directory. The number of sectors taken up by
any program or memory dump can be similarly calculated.
502
Using Disk Storage
stored in similar sets—for instance, name followed by four address lines and a
phone number—so there are no problems in interpreting data when it is read back
from the file.
Sequential files, once written, aren't readily changed, but new records can easily
be added onto the end. The disk operating system (DOS) has a built-in Append
command. Files can be updated only by reading, correcting records as they are read,
then writing back the edited version (with old records removed and new ones in
serted) as a new file with a different name. This process, which is impossible on 64
tape, is easy with disks.
The DOS has another command, Copy, which copies a sequential file onto the
same disk and optionally concatenates another file on the end. This is more useful
with CBM's double-disk units, but still has a few uses with the 64.
Use OPEN 2,8,2,'7i7enflme,SEQ,WRITE" to open a sequential file for write op
erations. OPEN 2/8,2,/y*7ename,SEQ,READ// opens the same file for read; OPEN
2,8,2,7*7ename,SEQ,APPEND" opens an existing file for Append.
The file and channel numbers need not be 2, and the device number does not
have to be 8; there are alternative, similar forms. Sequential files are assumed by de
fault, so if SEQ, or the shorter S, is omitted from any of these commands, it makes
no difference. READ is a further default, so OPEN 2,8,2,"filename" assumes a
sequential file will be read and reports an error if the file isn't found.
A sequential file can be opened for write only once. Thereafter, data can be ap
pended, but an attempt to open it again for write using the same file number will
cause a FILE EXISTS error message within the disk drive. However, using a different
file number erases the file and starts over.
Copy has the following syntax:
OPEN 15,S,lS,"CO?\:new name=old name"
or
PRINT#15,"COPY:/H?a; name=old name" (after OPEN 15,8,15 has been carried out)
This command writes another copy of the file, under a different name (or you'll get
FILE EXISTS), to the same disk.
OPEN 15,8,15,"COPY:j!ac; name=first file,second file"
The above combines two or more named files into another; again, the combined file
must have a new name.
Program 15-6 is a simple example of a program that reads a sequential file and
displays its contents onscreen. Note that ST in line 60 tests for the end-of-file con
dition. If that line is omitted, nothing very terrible happens; however, line 40 will
then repeatedly fetch a meaningless character.
503
Using Disk Storage
You may find that the disk warning light flashes if you misspell the file's name
or try to read a program rather than a sequential file. However, incorporating a test
for these messages is straightforward. First, add the following line:
5 OPEN 15,8,15
It is good practice to close file 15 last; if it is closed during a program, other disk
files will close, too. Then add this subroutine:
10000 INPUT#15,E,E$,T,S: IF E<20 THEN RETURN
10010 PRINT "***DISK WARNING": PRINT E;E$;T;S: CLOSE 15
Whenever the disk is accessed, a call to this subroutine will test that all's well.
Add 35 GOSUB 10000 to check that the file was opened properly; add 45 GOSUB
10000 to check each read from the file. This slows processing, of course.
The program is now almost ready, but one further subtlety is possible. ST is
changed by the new line 45 to reflect the status of input from the command file
rather than the sequential file, so you can add another line (42 S=ST) and change
ST in line 60 to S; the program then tests for all error conditions.
After making the above changes, the program will look as follows:
5 OPEN 15,8,15
10 PRINT "NAME OF SEQ FILE TO BE DISPLAYED"
20 INPUT N$
30 OPEN 1,8,2,N$:REM OPEN SEQ FILE (DEFAULT= READ)
35 GOSUB 10000
40 GET#1,X$
42 S=ST
45 GOSUB 10000
50 PRINT X$;
60 IF S>0 THEN CLOSE 1:END:REM STOP AT END OF FILE
70 GET X$:IF X$="" THEN 70
80 GOTO 40
10000 INPUT#15,E,E$,T,S:IF E<20 THEN RETURN
10010 PRINT "***DISK WARNING":PRINT E;E$;T;S:CLOSE
1
If the warning light flashes, it can be ignored with this program since no files
are being written.
If you prefer to see every file character (for example, RETURN showing as 13),
replace X$ in line 50 by ASC(X$+CHR$(0)).
504
Using Disk Storage
the red light remains on because a file is open. Enter PRINT#1,"HELLO" and note
the absence of activity. CLOSE 2 writes this data to disk and closes the file. The pre
vious program will read back the five letters of HELLO plus a final RETURN
character.
Repeating the same command causes a FILE EXISTS disk error. If the file isn't
important, OPEN 2,8,2/"@:SEQ TEST,W" will open a new file with the same name.
SEQ files occupy 254-byte sectors. Add together the lengths of all the strings of
data, including RETURNS, divide by 254, and round up to estimate the storage
requirement of any SEQ file.
Line 40 opens a relative file, named by the user, and assigns a record length one
greater than the value entered (to allow tor a RETURNjinhe end). Line 60 allows ^
record number 99999 to act as an indicator that no more data is to be entered. Lines
80 and 90 take in the material to be written to disk and check that it isn't too long.
Line 100 sets the record number parameters from the channel number and record
number. Line 110 finally puts the record onto disk. This is the simplest case, where
the record starts at the beginning of its allotted space.
Channel 15 can be read as usual. A subroutine call in a new line 45 could check
the OPEN, and lines 105 and 115 can also be added to test the command and the
print.
Remember that message 50 signals that the file is being extended, and therefore
it should be expected when the file is being set up.
Reading the file. The easiest way to read the file you've just created is to mod
ify the write program, delete lines 80 and 90, and alter 110 to INPUT#1,R$: PRINT
R$. Line 30 can be removed, and 40 OPEN 1,8,2,N$ is sufficient.
The same file is usable for either reading or writing. Typically, a program will
have a menu allowing either mode to be selected.
Copying. Use OPEN 15,8,15,"C:new name=old name" to copy this type of file
onto the same disk with a new name. This provides some security. Apart from direct
disk copying, or putting data into RAM (where there may not be enough available
room), copying records to tape by reading them in sequence, then writing them back
to a different diskette, is the easiest method.
507
Using Disk Storage
Storage. The total file length is the L parameter multiplied by the largest record
number, plus one (since record 0 exists). One thousand records of length 21 occupy
21,000 bytes; these are stored in 254-byte sectors, so the data occupies 83 sectors.
(Commodore 64 disks have 664 free sectors.) Additionally, the side sectors contain
ing the pointers occupy from 1 to 6 sectors, depending on the file length. The actual
number is the file length in sectors divided by 120; so our example file needs 1 side
sector only, and the entire file takes 84 sectors.
A relative file which fills the entire diskette takes about five minutes to set up
when first written.
Note that some versions of CBM DOS aren't reliable in validating disks with rel
ative files; however, 1540/1541 DOS apparently does not have this fault.
Record#. For relative files only, this sets the record number and position within
the record from which write or read will take place. Typical syntax is
508
Using Disk Storage
Pattern Matching
Disk commands involving loading or opening for reading generally can be abbre
viated using * and ? as pattern-matching symbols. For example, LOAD "A*",8 loads
the first PRG-type file in the directory which begins with A. However, LOAD"*",8
and VERIFY "*",8 assume the last loaded program applies, unless no program has
been loaded, in which case the first program loads. Similarly, OPEN 2,8,2,"S:X*":
CLOSE 2 scratches all files beginning with X.
The question mark (?) allows wild-card matching, but the exact positions have to
match; LOAD "????BON*",8 loads TROMBONE, but not BONZO.
Because of the possibility of sending spurious disk commands, you should not
include symbols like * ? # : , in filenames.
509
CJ1
Table 15-1. Summary of Disk Drive Messages
o
To avoid this sort of problem, simply make a point of closing write files if a pro
gram crashes before they are closed properly. Enter CLOSE 2 in direct mode, for ex
ample, and include a CLOSE statement for channel 15.
OPEN 15,8,15:CLOSE 15 will generally close all disk files successfully.
Program problems. Sometimes the final sector of programs becomes corrupted;
on LOAD the program loads, but READY never appears. The best solution is to press
RUN/STOP-RESTORE, then POKE zeros into memory after the end of the program,
using BASIC'S pointers at locations 45 and 46 to locate the end.
CHECK DISK
Tests a diskette by writing to and reading from every sector.
COPY/ALL
A BASIC program, written by Jim Butterfield, to copy an entire disk from one drive
to another. Two drives are necessary. One drive can be reassigned device number 9
with DISK ADDR CHANGE.
DIR
Reads the directory of device 8 from BASIC. No advantage over ordinary directories.
DISPLAY T&S
Displays any track and sector on the diskette. Very useful for examining the disk's
entire storage system or (in extreme cases) for reading programs or files directly.
DOS 5.1
The 64 wedge. Use this to make direct mode disk commands simpler. The wedge
will not coexist with some other utilities. It adds these direct mode commands:
@ alone reads the disk status and prints it,
@$ reads and displays the directory, without affecting BASIC,
/PROGRAM loads PROGRAM.
Some versions allow a LOAD and RUN option, and an abbreviated SAVE command.
All versions allow > (a wedge) as an alternative to (5).
PERFORMANCE TEST
Formats a disk, writes, and reads, but doesn't exhaustively test either diskette or
drive.
511
Using Disk Storage
PRINTER TEST
For CBM printers only.
VIC-20 WEDGE
This program simplifies disk commands for the VIC-20, as DOS 5.1 does for the 64.
VIEW BAM
Prints a diagram of the Block Availability Map.
Hardware Notes
1540/1541 Disk Drive Units
These drives contain a transformer to supply power, a printed circuit board contain
ing the ROM, RAM, and interface chips which hold the disk operating system, and a
drive unit, which is positioned away from the heat-generating components. Some
models have metal shielding over the printed circuit board to reduce radio frequency
emission. Both the shielding and the top half of the outer casing are easily removed
(for example, to exchange a 1540 ROM for a 1541 ROM or to change the device
number from 8). The design is similar to earlier Commodore disk drives, the 2031
single disk and 4040 double disk.
The device number can be set to any number from 8 to 15. At least four drives
can be daisychained together, so in principle, a four-drive system would be feasible.
Given the right hardware, a single 64 can also share a disk drive with other 64s.
The read/write head faces up, so the underside of the diskette is the active side.
Closing the door brings a pressure pad down on the head, keeping it in close contact
with the diskette. During read/write operations, the diskette is rotated by the spindle
motor at about 300 revolutions per minute, and centrifugal force gives the diskette
some rigidity. The head itself is mounted on rails and can move, along with the
pressure pad, a maximum of about one inch. Movement is handled by a stepper
motor. Each step moves the head about 1/30 inch.
These drives use 35 tracks. The actual magnetized zones are about 1/60 inch
wide; the clutch mechanism which grips the diskette has to position it within that
tolerance.
Head alignment problems sometimes occur, in which diskettes work on one disk
drive but not on another, because the heads aren't quite in the same place relative to
the disk center. Special alignment diskettes, having very slightly elliptical tracks,
allow a disk drive head to be accurately repositioned. Realigning disk drive heads is
specialized work.
Diskettes
1540/1541 disk drives use 5-1/4-inch floppy disks. Any good-quality, single-sided,
single-density diskettes are fine. Soft-sectored diskettes are generally used, but hard-
sectored disks will also work well, as their index hole isn't used by the drives.
Write-protection is readily implemented with 1540/1541 drives. An adhesive tab
over the notch prevents writing to the disk. Attempting to write to such a disk re
turns 26 WRITE PROTECT ON.
512
Using Disk Storage
Stress-Reducing Notches
4, *,
-Track 1
Read/Write -Directory Track
Slot —> -Track 35
Index
Hole
Write
Label.
r-Protect
Notch
Diskettes are inserted label up, read/write slot foremost. Diskette labels are
deliberately positioned away from the slot, to reduce the chance of fingerprint dam
age and to allow the label to be read when the diskette is in its dust cover. Writing
on the label with a sharp implement—for instance, a ballpoint pen—may damage
the diskette surface below. Always write on the label before putting it on the disk.
It is good practice to open the drive door when drives are turned on or off.
There's some small chance of magnetic "glitch" damage to a diskette that's left in a
drive with the door closed when power is turned on.
It's easy to modify diskettes so that both sides are usable. The index hole isn't a
factor; all that's needed is to cut a notch in the diskette opposite the write-protect
notch. The diskette then works on either side. However, that may not be desirable.
The standard argument against this practice is that small particles of dust, smoke,
and other debris, which become trapped by the self-cleaning wiper which lines the
diskette, may be dislodged when the direction of rotation is reversed. In addition,
some single-sided diskettes have defects on the back side. Nonetheless, quite a num
ber of people do this successfully.
Diskette life is typically quoted as several million passes per track. At 300 rpm
this represents about a week's continuous running.
513
Using Disk Storage
happens whenever there is a read error. This occurs because the head counts in until
it arrives at its correct track, then tries reading again in case its position was wrong
before.
A track is not a solid block of data. Instead, it is broken into 256-byte blocks
called sectors. Any program or file is stored in sectors, and the first two bytes are al
ways pointers to the next sector.
Sector storage tolerates some, but not much, variation in disk rotation speed. If
the disk spins too fast, sectors will overlap and data will be lost. Typically, there's at
least a one-second delay between starting the disk motor and writing or reading
data. For this reason, the motor is left on for some time after an access, so if another
access follows shortly, no time is lost waiting for the speed to build up.
Commodore's system uses more sectors on the outer tracks than the inner. This
takes advantage of the fact that the outer circumference is greater than the inner, in
the same way that other recording media usually give better resolution at the edge
than near the middle. However, because the angular speed is constant, outer tracks
must be written and read more rapidly than inner tracks, so hard sectoring is
impossible.
Sectors are not written in sequence around the disk. If an entire track is filled
with data from a single file or program, it's more efficient to chain sectors which are
far apart on the disk, so that only half a revolution (rather than a whole revolution)
is lost between reads or writes. A typical sequence on the outer tracks is 8, 18, 6, 16,
4, 14, 2, 12, 0, 10, 20, 9, 19, 7, 17, 5, 15, 3, 13, 1, and finally 11.
Sectors are stored with a short header, followed by data. Each part begins with a
so-called sync field and ends with a checksum. The header contains 08, a two-byte
ID, and the track and sector number. The data is preceded by 07. Messages 20-29
from the disk may indicate that some aspect of this elaborate error-checking system
has failed. For example, if a magnet is held near the edge of a diskette, the outer sec
tors become unreadable. This technique can be used to protect disks from being
copied.
The conversion of bytes into magnetic patterns on disk, and vice versa, is an an
alog hardware function, relying on cross-over detectors, amplifiers, and pulse
shapers.
514
Using Disk Storage
When two drives are used, they must be turned on separately. Typically, one
drive is turned on, then the 64 is turned on, then the live disk's number is changed,
and the second drive is turned on. In that way the system isn't confused. LOAD
"$",8 and LOAD "$",9 load directories from the respective drives.
Hardware conversion involves cutting jumpers. These jumpers are not wires, but
round spots of solder on the circuit board, separated into halves, with a thin strand
of solder connecting each half. You cut the jumpers by scraping away, or breaking,
the connecting strand with a sharp knife.
The actual board layouts vary. The jumpers in the 1540 and early 1541 disk
drives are located on the left side of the circuit board as you face the front of the
disk drive. On the newer 1541 drives, the jumpers are in the center of the board.
The early 1541 drives can be identified by their white cases, while the newer 1541
drives have brown cases. In both versions, jumper number 1 is nearest the front, and
just behind it is jumper 2. Figure 15-4 shows the layout.
Jumper 2
Jumper 1
Front of drive
I
Cutting only jumper 1 changes the device number to 9. Cutting only jumper 2
changes it to 10. Cutting both jumpers changes it to 11. Note that opening the drive
case to do this will probably void your warranty. To avoid severe electrical shock, do
not attempt any such operation until you have turned the drive off and unplugged
every connector. If you're not sure what's involved, get help from someone who
understands electronics.
Disk ROM
Commodore disk drives have internal ROM from $C000 to $FFFF and RAM from $0
to $07FF. It's easy to disassemble disk ROM, because disk memory can be read with
the following command:
PRINT#15/'M-R//CHR$(/ow;)CHR$^W:GET#15/X$:X=ASC(X$+CHR$(0))
That assumes, of course, that OPEN 15,8,15 has been performed. The value X is the
result of using GET#, which in this case is equivalent to PEEKing the disk's memory.
The low and high bytes of the location should be used in place of low and high. You
can disassemble the ROM by replacing PEEK in a BASIC disassembler with this
routine.
515
Using Disk Storage
Disk ROM has the conventional 6502 features, including NMI, Reset, and IRQ
vectors at the top of memory. It also has tables of error messages and tables of com
mands, some of which are undocumented.
Minimizing Errors
To minimize errors, the general rules are simple: Keep the disk drive free of dust,
smoke, and other contaminants; store and treat the diskettes properly; keep copies of
programs and data; and so on. It's worth having a standby system if your 64 is used
for any serious purpose.
Hardware errors are rare; one bad bit in 1011 is typical of quoted figures. Errors
caused by unclosed files are far more likely. With some systems, programs to vali
date data may be used. Such systems can be written to minimize disk use, favoring
RAM where possible to minimize the probability of a mistake.
1-17 0-20
18-24 0-18
25-30 0-17
31-35 0-16
The directory track, track 18, is diagrammed in Figure 15-5 and has 19 sectors.
Sector 0 holds the disk name, as well as a bitmap of every sector on the disk, show
ing whether the sector is used or not. Sectors 1-18 store file type, filename, and
pointers to the actual data. Each of these sectors can store eight filenames, giving a
maximum of 144 directory entries.
516
Using Disk Storage
Each time a file is written, the BAM (Block Availability Map) is updated, so the
system knows which sectors are free for subsequent recording. VIEW BAM on the
demo disk prints a diagram of this map. To see how this works, load DISPLAY T&S
and inspect track 18, sector 0. Its layout is described in Table 15-3.
(Omitted bytes are SHIFT-spaces, $A0, or spaces, $20. Remember, DISPLAY T&S prints values in hex.)
BAM
Each of the 35 tracks is represented by four bytes in the BAM, as shown in Table 15-4.
Number of sectors Bits for sectors Bits for sectors Bits for sectors
free in this track 7,6,5,4,3, 2, 1, 0 15,14,13,12,11,10,9, 8 X, X, X, 20,19,18,17,16
[From 0 to 21] [0=used, 1=free] [0=used, 1=free] [0=used or unavailable, 1=free]
Directory Entries
Directory entries are fairly straightforward. Use DISPLAY T&S on track 18, sector 1;
you'll find it split into eight sets of 32 bytes each. Except for the first 2 bytes of the
sector, which serve as a link to the next directory entry, the interpretation is shown
in Table 15-5.
517
Using Disk Storage
Relative files have slightly more detail than other file types because of their in
dex system. A track and sector pointer points to the first side sector (of a possible
six), which is linked like any other file and treated as a separate file by the operating
system. The record length parameter is also stored here. If you've forgotten it this is
the place to look.
The side sectors have the structure shown in Table 15-6.
518
Using Disk Storage
Up to 120 sectors can be stored in one of these blocks. The system calculates the
effect of the record it is asked to read or write, by multiplying record length by
record number, then calculates which sector the start of the record must appear in.
In the worst case, a new side sector has to be loaded, a track and sector looked up in
it, then finally the correct track and sector read. (If a record straddles two blocks, a
fourth disk movement occurs.)
Six side sectors can cover 720 blocks; this, of course, is enough for a file cover
ing the whole diskette. However, in this case an extra channel (for a total of three)
needs to be kept open within the disk: one for a side sector, one for a data sector,
and a third for the data itself. A sequential file needs only two, one for the current
sector and one for the correct data. Since 1541 disk drives allow five channels, two
sequential files or one relative file or one of each type can be open at the same time.
519
Using Disk Storage
Sorted Directory
Program 15-9 prints a directory in the usual format, except that the names are sorted
alphabetically. That makes it particularly useful if you have lots of programs. It can
be modified for use with a printer and can process any number of disks, one after
another.
0 DATA 32,115,0,133,97,169,128,133,98,32,115,0,240
,7,9,128,133,98,32,115 :rem 213
1 DATA 0,165,47,133,99,165,48,133,100,160,0,165,97
,209,99,208,7,200,165,98 •rem 79
2 DATA 209,99,240,20,24,160,2,177,99,101,99,72,200
,177,99,101,100,133 :rem 71
3 DATA 100,104,133,99,144,221,160,5,177,99,133,102
,200,177,99,133,101,208 :rem 3
4 DATA 2,198,102,198,101,24,165,99,105,7,133,99,16
5,100,105,0,133,100,165,101 :rem 192
5 DATA 208,2,198,102,198,101,208,4,165,102,240,18,
133,105,162,0,134,103,134 :rem 82
6 DATA 104,165,99,133,106,165,100,133,107,240,224,
240,114,24,165,106,105 :rem 198
520
Using Disk Storage
7 DATA 3,133,106,165,107,105,0,133,107,230,103,208
,2,230,104,160,2,177,106 :rem 17
8 DATA 153,109,0,136,16,248,160,5,177,106,153,109,
0,136,192,2,208,246,170 :rem 7
9 DATA 56,229,109,144,2,166,109,160,(2 SPACES}5,23
2,200,202,208,8,165,112,197,109 :rem 169
10 DATA 144,10,176,34,177,113,209,110,240,238,16,2
6,160,2,185,112,0,145 :rem 142
11 DATA 106,136,16,248,160,5,185,106,0,145,106,136
,192,2,208,246,169,0,133 :rem 49
12 DATA 105,165,101,197,103,208,152,165,102,197,10
4,208,146,165,105,240,138,96 :rem 1
15 REM *** SORT DIRECTORY *** (SEE LINE 40000 FOR
{SPACE}OUTPUT)(2 SPACES}*** :rem 227
20 POKE 56, PEEK(56)-1: CLR :rem 130
30 T = PEEK(55) + 256*PEEK(56) :rem 167
100 FOR J=T TO T+242: READ X: POKE J,X: NEXT
:rem 107
1000 PRINT "INSERT DISK; PRESS(5 SPACES}RETURN"
:rem 58
1002 GET X$: IF ASC(X$+CHR$(0))<>13 GOTO 1002
srem 19
1004 OPEN 15,8,15,"10": OPEN 1,8,0,"$0" :rem 93
1006 PRINT "OK" srem 50
1008 N=2: GOSUB 10000 :rem 49
1010 N=32: GOSUB 10000: IF ST=0 THEN D=D+l: GOTO 1
010 . srem 191
1012 CLOSE 1: DIM D$(D) :rem 124
1014 T = PEEK(55) + 256*PEEK(56) :rem 10
1100 OPEN 1,8,0,"$0" srem 169
1110 N=6: GOSUB 10000 srem 47
1120 FOR J=l TO 25s GET#l,X$s D$(0)=D$(0)+X$s NEXT
srem 236
2000 N=3s GOSUB 10000: K=K+1s GET#l,Nl$s GET#1,N2$
s IF ST>0 GOTO 20000 srem 24
2010 D$(K) = STR$(ASC(N1$+CHR$(0)) + 256*ASC((N2$)
+CHR$(0))) + " " srem 21
2020 FOR J=l TO 27s GET#1,X$ srem 133
2030 D$(K)=D$(K)+X$s NEXT srem 42
2040 GOTO 2000 srem 193
10000 FOR J=l TO Ns GET#l,X$s NEXTs RETURN srem 42
20000 CLOSE Is CLOSE 15 srem 175
30000 SYSTsD srem 196
40000 OPEN 4,3s REM OR OPEN 4,4 TO DISPLAY TO PRIN
TER srem 190
40005 PRINT#4,CHR$(147) srem 247
40010 FOR J=0 TO K-ls PRINT#4,"{10 SPACES}" D$(J)s
NEXT srem 233
40020 FOR J=l TO 10s PRINT#4s NEXT srem 48
40030 CLOSE 4 srem 161
40040 CLRs GOTO 1000 srem 13
521
Using Disk Storage
Program 15-11 prints all 35 tracks of BAM information, arranged in sets of four,
preceded by the track number. For example, 35 17 255 255 1 means that track 35
has 17 free sectors, and all bits 0-16 are on. The number of free blocks can be cal
culated from BAM; this number is usually the same as the directory's blocks-free
figure.
Knowing that, you can write a directory to use information from the directory
entries, for example, the first track and sector. Program 15-12 reads the directory
track and reports the LOAD address of every PRG type file; this is often helpful if
you're trying to remember whether a program is BASIC or ML, or where a memory
dump belongs in RAM.
522
Using Disk Storage
Line 50 skips the BAM sector, and line 110 loops through each sector in track
18. That is necessary because, although most entries have 32 bytes, the first in each
block has only 30. Line 120 tests for PRG type. If this is found, its name is printed
(line 130) and its track and sector pointers are used to read the block holding the
start of the program. The command Ul is explained after the next section. Line 150
rejects the track and sector links but reads the low and high bytes of the start
address.
The U Commands
These commands, summarized in Table 15-7, work via channel 15. For example,
OPEN 15,8,15"UJ" resets the drive by turning off the light, setting the device num
ber to 8, and generally behaving as though the disk were just turned on. Ul and U2
are versions of block read (B-R) and block write (B-W); they operate correctly on en
tire sectors, including track and sector numbers of links at the start. Thus, you
should generally use Ul and U2 instead of B-R and B-W.
523
Using Disk Storage
Command Function
Ul or UA Block Read
U2 or UB Block Write
U3 or UC Jump to $0500
U4 or UD Jump to $0503
U5orUE Jump to $0506
U6 or UF Jump to $0509
U7 or UG Jump to $050C
U8 or UH Jump to $050F
U9 or UI Jump to ($FFFA)
ui- Set 1541 for VIC
UI+ Set 1541 for 64
U: orUJ Jump to ($FFFC)
Block Commands
Block read and block write (unlike all other commands) need an extra channel in
which to store their data. OPEN 1,8,2,"#" opens a buffer, which BASIC refers to by
its channel number (2) and file number (1). An alternative system is typically OPEN
1,8,2,"#3" where, if the channel isn't available, error 70 (NO CHANNEL) is returned.
You can use this to experiment with channels.
For this discussion, assume OPEN 15,8,15 has been entered. Remember: If you
are writing data, be sure to close these files so that the final buffer is written. The
syntax for block read is PRINT#15,'Vl'';channel;0;track;sector.
Program 15-13 is an example of how block read works. It follows a chain of sec
tors. Try inputting track 18, sector 0 at the start. Note the use of two files, the com
mand channel to load sectors in line 40, and the file to input characters in line 50.
The program ends when a sector has a link set to track 0.
Program 15-14 demonstrates block write. It reads, alters, and writes back the
first directory entry block, on track 18, sector 1. Note the use of block pointer, or B-
P, in line 30, which is analogous to the P parameter used with relative files.
524
Using Disk Storage
30 PRINT#15,MB-P";2;2
40 PRINT#1,CHR$(130+64);
50 PRINT#15,"U2";2;0;18;1
60 CLOSE 1:CLOSE 15:END
This program assumes the directory has a PRG file first; by setting bit 6 to 1, the
file is locked and cannot be scratched. It appears as file type PRG<. Making line 30
PRINT#15/'B-P";2;34 selects the second file in the directory, and so on, adding 32
to the second parameter for each subsequent file. If line 20 is omitted, the directory
will never be read into the buffer; as a result, garbage in the buffer gets written to
the directory and corrupts it.
Another example is a diskette test program. DATA statements hold the highest
sector numbers (from 20 to 16) for all 35 tracks; a loop (FOR T=l TO 35:READ MS:
FOR S=0 TO MS: write 255-character string and return: NEXT S: NEXT T) writes
the same data to every sector. A similar loop reads each sector back to check.
Block Execute
Block execute, or B-E, has syntax OPEN 15,8,15/'B-E";channel;0;track;sector, exactly
like the two previous commands. It loads the requested sector into disk memory,
then jumps to the start of the same buffer, thus executing the ML program. RTS or
the equivalent returns to BASIC. This could be used as the basis of a diskette copy
protection device. Obviously, ML knowledge is necessary.
Memory Commands
Like the U commands, each of the following commands acts on disk memory rather
than on sectors.
B-A (Block Allocate). Block allocate sets a bit in the BAM low, to show that a
sector is in use. A bit value of 1 means it's free. Use the following form:
1000 PRINT#15,"B-A";0;T;S
1010 INPUT#15,E,E$,ET,ES
1020 IF E<>65 THEN END :REM T,S OK
1030 T=ET:S=ES:IF T=18 THEN T=19
1040 GOTO 1000
If block allocate fails (that is, if T and S in line 1000 are already used), error 65,
NO BLOCK, causes the program to calculate the next block, which is returned in
channel 15. In this way, the BAM can accurately reflect blocks written to disk by
Block Write.
B-F (Block Free). The block free command sets a bit in the BAM high,
corresponding to one sector. The syntax is identical to that for B-A. Obviously, the
input message isn't needed.
B-P (Block Pointer). Block pointer, as you've seen on U2, sets the point within a
sector where read or write will start. Its syntax is PRINT#15,"B-P"; channel; position
1-255. For example, PRINT#15/'B-P; 2; 32*F-31, where F is 1-8 with the directory
entries in track 18, can be used to read from or write to any of the eight file entries
in any of the sectors.
M-E (Memory Execute). The memory execute command jumps to ML in disk,
exactly like B-E, except that no sector is loaded and the starting address can be any-
525
Using Disk Storage
M-R (Memory Read). This command sends an address to disk, and returns the
value at that location along channel 15. Its syntax is PRINT#15,"M-R"; CHR$(/ow;
byte); CHR$(high byte): GET#15,M$.
To disassemble disk ROM, use a BASIC disassembler and add the following sub
routine, replacing X=PEEK(P) in the disassembler.
10000 PRINT#15//M-R";CHR$(P-256*INT(P/256));CHR$(P/256)
10010 GET#15,X$:X=ASC(X$+CHR$(0)):RETURN
M-W (Memory Write). Memory write puts data into disk RAM or interface
chips. Each M-W command can write 35 bytes at most. The syntax is PRINT#15,"M-
W"; CHR$(/ou; byte) CHR$(high byte) CHR$(length) X$, where X$ is a string of not
more than 35 bytes and the other parameters are the starting address in RAM and
the number of bytes.
SAVE is similar, except that ISR $FFD8 is SAVE, and the start and end addresses
must be specified. The X and Y registers hold the low and high bytes of the final ad
dress + 1. The accumulator holds the zero page address of a pointer to the start ad
dress. In addition, the setup for the Kernal routine SETLFS is slightly different. The
parameters for SETLFS are summarized in Table 15-8.
File Handling
OPEN and CLOSE can be done in ML, though it's often easier to OPEN files in
BASIC and save the hassle of setting up a name or command string in RAM.
As an example, consider the process of copying sequential or program files in
order to change a program's LOAD address. That can be done in BASIC with OPEN
1,8,2 "ORIGINAL,P,R" and OPEN 2,8,3//NEW,P,W" followed by
526
Using Disk Storage
527
Using Disk Storage
The Kernal SETNAM routine at $FFBD uses the name, or command string,
pointers, and length exactly like LOAD or SAVE. The Kernal OPEN routine is at
$FFC0.
The Kernal CLOSE routine is easier. The file number is stored in the accu
mulator, then JSR $FFC3 closes the file.
Channel 15 and ML
OPEN 15,8,15 is just a special case of OPEN. Messages from channel 15 consist of
ASCII numbers and the message separated by commas and terminated by return.
Thus, message 0 is this string:
48 48 44 32 79 75 44 48 48 44 48 48 13
00, OK,00,00 RETURN
Thus, to check channel 15 from disk, open file 15, input two bytes, and check
that each is 48. If not, the message can be printed by inputting further characters
and outputting them, using $FFD2, in a loop until RETURN is received.
The following routine performs the equivalent of OPEN 15,8,15:
INPUT#15,E,E$,T,S: PRINT E,E$,T,S: CLOSE 15:
528
Using Disk Storage
;OPEN 15,8,15
LDA #$0F
LDX #$08
LDY #$0F
JSR $FFBA ;SET 15,8,15
LDA #$00 ;SET LENGTH OF NAME^O
JSR $FFBD POINTERS IRRELEVANT
JSR $FFC0 ;OPEN 15,8,15
LDX #$0F
JSR $FFC6 ;OPEN 15 FOR INPUT
;INPUT#15
LOOP JSR $FFCF ;GET A BYTE
CMP #$0D ;EXIT IF RETURN
BEQ EXIT
JSR $FFD2 ;PRINT TO SCREEN
BNE LOOP
;CLOSE 15
EXIT LDA #$0F
JSR $FFC3 ;CLOSE 15
JSR $FF^C ;FILES NORMAL
RTS
529
Chapter 16
This chapter explains how to program controllers which fit the 64's control ports.
Fast ML routines are supplied which can be used to write more efficient programs.
Joysticks
The 64 has two control ports, commonly called joystick ports, each with the stan
dard nine-pin D-connector. Joysticks are the most popular external controller, and
the 64 can use two. The four- or eight-direction control plus fire button is fine for
games, but less suitable for complicated inputs like graphics and words. However,
the 64 keyboard can be used along with joysticks, subject to a few restrictions. Com
modore joysticks are interchangeable with those for Atari, Coleco, and several mod
els of videogame machines.
Joysticks are based on a simple principle. The central stick is connected to elec
trical ground, so moving it makes contact with the sensors positioned up, down, left,
or right. The button grounds another wire when pressed. So, the cable to the 64 conr
tains six lines, one of them ground and the other five normally high but capable of
being grounded. The 64 tests for one or more wires being grounded. Most joysticks
are designed so that intermediate positions (northeast or up and right, and so on)
ground two wires at once. Thus, the 64 may detect up to three wires of either joy
stick low simultaneously, counting the fire button. Some combinations aren't nor
mally possible, of course, like north and south at the same time.
Internally, the most common arrangement is a grounded metal ring and
pressure-sensitive, dimpled-metal switches which give way and make contact when
the stick moves. Heavy-duty models have other arrangements; some even use
microswitches. Some models have two fire buttons, and/or a button on top of the
stick, so they can be used in either hand (converting some types for left-hand opera
tion isn't hard). Joysticks tend to break down easily, often because the cable contains
the thinnest possible strands of wire, which may break just inside the casing. To test
a joystick, try it with one of my programs to verify that all eight directions can be lo
cated easily.
Programming Information
Two locations are relevant when programming the control ports for use with joy
sticks. Programming is easy if you know how to manipulate bits with AND and OR.
Bit #: 7 6 5 4 3 2 1 0
PEEKing locations 56320 and 56321 reads the joysticks. Nothing else is needed.
Bits 0-4 are normally set to 1. The joystick's grounding action sets them to 0. The
order of the registers is reversed from what might seem natural. Joystick 1 is read
533
The Control Ports
from a register with a higher address than joystick 2. It's easy to incorrectly assume
53260 is joystick 1.
Keyboard interference. The control ports are wired to the keyboard circuitry,
which is an economical arrangement. Generally, the keyboard won't be used with a
joystick. But if it is, there are a few side effects to watch for.
The keyboard scan routine sets the column (a bit in location 56320), then reads
the row (location 56321); so joystick 1 causes spurious characters to appear on the
screen occasionally because it mimics a row. In fact, the 1 key, back arrow, CTRL, 2
key, and space bar are interchangeable with S, N, W, E, and fire for joystick 1. Thus,
if you press joystick 1 in the east direction, it outputs a 2; if you press it to the west,
it slows screen scrolling, like the CTRL key does. Actually, locations 53261 and 145
($91) can be interchanged in joystick programming, because 145 holds a copy of the
default row, for testing the RUN/STOP key.
Joystick 2 doesn't generate spurious characters and is usually the better one to
use with programs requiring only one joystick. But it does alter the keyscan. Pressing
joystick 2 west while typing the X key has the same effect as SHIFT-RUN/STOP,
for example.
Hardware. Looking at the 64, pins 1-5 are on top and 6-9 underneath, in left-
to-right order. Joysticks use pins 1 (north), 2 (south), 3 (west), 4 (east), 6 (fire), and 8
(ground). It's easy to experiment with these ports, but be careful with pin 7, which
carries 5 volts.
Using WAIT. WAIT loops until a bit or bits at some location change, so this
command is useful for starting or restarting games. For example, WAIT 56320,16,16
waits until joystick 2's fire button is pressed, and WAIT 56321,16 waits until joystick
l's button is not pressed.
Program Examples
BASIC routines to handle joysticks tend to be long, which is hard to avoid since all
possible directions must be separated out for processing. Program 16-1 demonstrates
the method of combining bits into one value.
534
The Control Ports
Where all eight directions are needed, they can be combined together by a rou
tine similar to the following, which uses the ML routine above. Delete line 40 in
Program 16-2 and add the lines shown in Program 16-3. Replace the FIREBUTTON
in line 1010 and the direction indicators in line 1020 with the line numbers of the
routines in your program that process those joystick operations. Program 16-3 will not
run properly unless you replace these with valid line numbers.
Paddles are analog devices: they sweep through a continuous range of values.
Differences between devices may cause slight compatibility problems because of this,
unlike joysticks, where grounding is a simple on-or-off alternative. Pin 7 of each
control port is connected to the 5-volt power supply. Pins 5 (Y) and 9 (X) are con
nected to this, via the paddles. Rotating the paddles alters the resistance of the in
ternal potentiometers (variable resistors), each connected to the knob so that, as it
turns, the resistance changes. The SID chip's POT X and POT Y registers measure
the changing voltage levels produced by this changing resistance, performing an
analog-to-digital (A/D) conversion which relates the voltage level (0-5 volts) to a
number 0-255.
It's simple to use the same principle with other resistances. Commodore paddles
are rated at 470K ohms (the K stands for thousand), and their minimum resistance is
a few hundred ohms. They are approximately linear, changing in even steps of about
1000 ohms, so the overall range is large. To avoid damaging the SID chip while
experimenting, keep minimum resistances of several hundred ohms between the 5-
volt line and the POT inputs of the SID chip. Commodore documentation suggests
hardware smoothing with a lOOOpF capacitor between the POT inputs and ground,
but this is already built in on the 64. Commodore paddles have a fire button on each
unit, wired to pins 3 and 4 of the controller port for the X and Y paddles, respec
tively, which is connected to ground (pin 8) when pressed, just like joystick contacts
for the west and east directions.
Atari paddles can also be used with a Commodore 64, but since the maximum
resistance of the potentiometers in the Atari unit is about twice that of Commodore
paddles—1M (1 million ohms) versus 470K ohms—the Atari paddles are very sen
sitive. A half turn of an Atari paddle is approximately equal to a full turn of a Com
modore paddle.
Programming Information
Paddles can be read in BASIC or ML. The following locations are important:
Bit #: 7 6 5 4 3 2 1 0
Note that the same location reads the paddle potentiometer values irrespective
of the port, but the fire button locations vary, and there's no hardware switch be
tween them. Note also that the default is port 1, so if you want to use the simplest
approach, use this port and simply PEEK the POT X and POT Y registers.
Program 16-4 selects and reads the paddle connected to port 2. Add a line 80
PRINT X,Y,FB to see the values on the screen.
536
The Control Ports
The program selects port 2 in line 30. Interrupts have to be turned off to prevent
the keyscan routine from altering values. After reading the registers, line 60 restores
normal keyboard operation. If you don't need the keyboard, line 60 can be omitted.
Paddle buttons generate spurious characters when connected to port 1. Pressing
the button on paddle X is equivalent to pressing the CTRL key, which slows process
ing. In port 2, the keyscan is altered (try X with fire button). As noted, port 2 cannot
be read at the same time as the keyboard because of the conflict over the use of
$DC00. This means that, with paddles in port 1, PEEKing values is easy, but the fire
buttons will have to be avoided in some circumstances.
ML programming is fast enough to disable the keyboard without noticeable ef
fect. Program 16-5 allows four paddles and four buttons to be read with virtually no
problems. The fire buttons register in the status variable ST, so IF ST>0 is a simple
test for a button press.
To activate the routine, use SYS 49152. The results will be left in the following
locations:
The POT registers are usually updated about every 500 processor cycles. Extra
time is necessary when the ports are shifted, so in ML it's best to allow a loop like
LDX #$D0 : LOOP DEX : BNE LOOP before reading from the SID paddle registers.
Accuracy. The 64's A/D conversion is better than the VIC-20's. There's less
crosstalk and no need to correct for the other paddle's value. With paddles, resolu
tion is limited, and you may find intermediate values can't be read. Occasionally,
values returned are actually very different from other recent values. Generally, don't
537
The Control Ports
try to aim for accuracy beyond the eight-bit limit imposed by the SID chip. It's pos
sible to smooth values, but use a moving average method, taking a running total at
regular intervals. With devices other than paddles, smoothing isn't suitable: graphics
pads, where a stylus can jump from point to point at will, need a good estimate of
each point's coordinate, not an average value between unrelated points.
Light Pens
A light pen is a pen-shaped device, fitted with a cable, which plugs into control port
1. The line carrying the light pen's signal is tied to the keyboard, so keys B, C, M, Z,
fl, the left SHIFT key, and period won't work while a light pen is plugged in, unless
the light pen has a switch on it. Watch for this if you want to input from the key
board and use a light pen simultaneously.
The internal electronics include a light-sensitive component, usually a photo-
transistor, which allows current to pass only when exposed to light. Light pens use
the 5-volt and ground lines, plus a line into the VIC-II chip. When fairly intense
light—such as the electron beam that creates the video display in a television or
monitor—is detected, this line is grounded and two VIC chip registers are frozen, or
latched, and remain unaltered until the next exposure to light. The two registers hold
the horizontal and vertical positions of the pen inferred from the distance of the
electron beam from its starting position.
Whenever a range of alternatives is to be selected from a screen display, a light
pen may be useful. Selecting alternative answers to multiple-choice questions and
selecting options from a menu are examples. Also, games like chess can make good
use of light pens. Numbers can be input with a numeric 0-9 pad on the screen, and
a light pen can help sketch on a screen.
The drawbacks to using light pen input are the limited accuracy of the pens,
computer limitations, and the fact that more people own joysticks than light pens.
The glass in front of the TV tube and general lack of precision make accuracy and
repeatability not as good as with analog devices like graphics tablets. Another diffi
culty is that some colors, such as black, won't trigger the pen.
The light pen programming registers are read-only (they cannot be POKEd):
$D013 (53267) is the horizontal position register and $D014 (53268) is the vertical
position register. The following one-line program displays both registers:
10 PRINT PEEK(53267): PRINT PEEK(53268): PRINT "{CLR}": GOTO 10
As you move the pen across the screen, the first value varies, increasing as you
move the pen from left to right. As you move it down, the second reading increases.
The readings are taken from the VIC chip as it monitors and controls the TV. Values
538
The Control Ports
across vary from about 30 to 190, ignoring the border area, and values down range
from about 50 to 250 (on U.S. televisions). The monitor scans each picture twice,
interlacing the pictures, which is why there are only 200 separately distinguished
raster lines. To convert these ranges into 0-39 and 0-24 for column and row equiva
lents is easy. Just subract 30, then divide by 4, to get the horizontal position, and
subtract 50 and divide by 8 for the vertical. Resolution is in principle two dots hori
zontally and one dot vertically.
This simple program will draw a small ball on the screen when it detects a light
pen reading. You'll probably find your pen's readings show quite a bit of jitter, even
when held still, removing any chance of serious high-resolution work.
Program 16-7 uses an ML subroutine to read the pen and convert its readings to
a screen position. It POKEs a character into the screen, then loops back for more. As
you'll see, this is much faster than BASIC. Color change and character change
demonstrations are included, and you can modify these to suit your requirements.
539
The Control Ports
The ML checks to see that the light pen values are in range. If so, it calculates
the position of the screen character, and plots. You may prefer to read the screen
and detect a character as the pen points to it. Modify Program 16-7 as follows. Re
place each of the first six numbers in line 500 with 234, then replace line 505 with
505 DATA 169,81,177,2,96, and delete line 506. RUN and PRINT J to find the cor
rect upper limit to use after TO in the FOR-NEXT loop in line 10, and replace line
100 with 100 SYS 49152: PRINT "{HOME}" PEEK(780): GOTO 100. Now the
screen PEEK value (32 for space, 1 for A, 2 for B, etc.) of the character selected by
the light pen appears in the top left. The program's calculations are unaltered, but
the screen is no longer POKEd. Values 32, 160, 48, and 200 in the DATA check the
limits, subtracting 32 and checking to see that the result doesn't exceed 160 hori
zontally; these can be fine-tuned.
Light pens and other devices which ground the appropriate line can be pro
grammed to generate interrupts (the end of Chapter 12 explains how). This has the
advantage of signaling every activation of the device, but otherwise isn't very useful.
Joy W JoyE
or or
Paddle Y Paddle X
Joy N JoyS Button Button Paddle Y
V V
o O O
2 3 4
6 7 8 9
O O O O
Joy Button
540
Chapter 17
Peripherals
• Printers
• Plotters
• Modems
• The RS-232 Interface
• The Serial Port
Chapter 17
Major Peripherals
This chapter covers printers, plotters, modems, and the 64's interfaces. Simple pro
gram examples are included for quick reference.
Printers
Simple Commands
Commodore printers designed for the 64 plug into the serial port, the round port at
the back of the 64 next to the video output, or at the back of the disk unit when
daisychaining. At the simplest level, printers are controlled with OPEN 4,4 (which
opens file number 4 to the printer), PRINT#4,"HELLO" (which prints a message to
file 4), and CLOSE 4 (which closes the file). Any number of PRINT#4 statements
can be issued. PRINT statements can still be used to send output to the screen.
The 64 has no LIST#4 statement. To LIST programs to the printer, use OPEN
4,4: CMD 4: LIST (which opens file 4, directs output to that file instead of to the
screen, and lists). Follow this with PRINT#4: CLOSE 4 (which disengages CMD and
closes the file). The PRINT#4 is needed to close the file properly, so get in the habit
of using it before CLOSE whenever you use CMD.
With machine language monitors, all output can be sent to a printer with OPEN
4,4: CMD 4: followed by a SYS to the entry point of the monitor. Then enter M
1000 1200, for example, and output for the desired memory dump will be made to
the printer. The commands may have to be typed in blind, since they may not echo
to the screen, but this isn't a big problem. Enter X or E to exit the monitor, then
PRINT#4: CLOSE 4 to redirect output to the screen.
Non-Commodore Printers
For most applications, many non-Commodore printers use commands identical to
those for Commodore printers. Also, many interfaces are available which emulate
Commodore printer features in addition to allowing you to use the special features
of the non-Commodore printer. Printers which use the RS-232 port at the back left
of the 64, usually with an RS-232 converter cartridge, use device number 2, instead
of 4. To use such a device, the following sequence is typical:
OPEN 2,2,0,CHR$(6):PRINT#2,"HELLO"
This opens file 2 with baud rate 300, then sends HELLO to the device. PRINT#2:
CLOSE 2 closes the file. OPEN 2,2,0: CMD 2: LIST will list to such a printer. See
the notes later in this chapter for more on RS-232 printers, which may not always
work with the 64.
Easy Printing
PRINT# statements are similar to ordinary PRINT statements, but there is a distinc
tion between the carriage-return character, CHR$(13), and the linefeed character,
CHR$(10), which advances the paper in the printer. Commodore printers are de
signed to treat PRINT# followed by a semicolon as an instruction to remain on the
same line. PRINT# followed by a colon or end-of-line marker is treated as a com
bined carriage return and linefeed, so PRINT# behaves just like PRINT to the screen.
543
Major Peripherals
Not all printers have an automatic linefeed. If your non-Commodore printer over
prints lines on top of each other, use a file number of 128 or greater (OPEN 128,4,
for example, with PRINT#128,//HELLO//) to cause the 64 to output the linefeed.
Control characters to the printer (to print reversed text, lowercase, and so on)
are sent as special characters which the printer recognizes, typically as PRINT#4,
CHR$(27) or as PRINT#4,A$ (where A$ is a string of CHR$ values). Printer pro
grams are likely to contain a number of PRINT# statements which are meaningful
only with reference to the printer in use.
Dot Resolution
7 up X 6 across 7X6 8X8
of Characters
Characters per Inch 12 10 11
Approx. Speed,
30 30 60
Characters per Sec.
Separation Between
2 dots 2 dots Programmable
Lines
Programmable
No No Yes
Formatting of Output?
Programmable Top-of-
No No Yes
Form Feed?
544
Major Peripherals
Each of these printers has the complete range of ROM graphics, although none
prints characters that are identical to the 64's characters. The 1515's reverse charac
ters, for example, lack the solid underline of the screen characters. In addition,
lowercase letters like g lack true descenders, so they seem to float up on the line.
The printers have built-in ROM to process incoming commands and store graphics
patterns, as well as RAM to act as a buffer, storing data while the printer deals
with it.
ROMs may be changed by Commodore without warning, so there's no guar
antee that one model won't differ from others. Commodore's printers haven't consis
tently used identical commands in the past, either. These commands are discussed
further in the next section.
Each printer allows 80 normal-width characters to the line (except that the 1515
uses a smaller typeface and nonstandard 8-inch-wide paper). It is possible to use 8-
1/2-inch paper on the 1515 by loosening the paper guide, removing the lid and the
guide, and taking out the bar so that only the paper holders touch the paper. How
ever, the result is a very noisy printer.
The number of lines per page has to be counted with the earlier printers.
Usually, a total of 66 lines, including linefeeds, has to be arranged per page if neat
output is desired. Six lines per inch is standard.
In addition to the standard Commodore 64 graphics characters, the 1515, 1525,
and MPS-801 printers have a graphic mode in which individual columns of dots can
be programmed. This is demonstrated in the manuals by reproducing the Com
modore symbol. A page of graphics can be printed continually redefining the dot
pattern. This makes it easy to print high-resolution graphics. The 1526 lacks a
graphic mode, but a similar effect can be achieved using this printer's single user-
defined character. However, only one redefined character is allowed per line printed,
so multiple overprints must be made to reproduce a complete line of graphics. Thus,
the 1526 is less suitable for high-resolution graphics printing than the other models.
Most software assumes device 4 for a printer. However, that can be switched to
device 5, so two printers can be used simultaneously, with PRINT#4 selecting one
printer and PRINT#5 selecting the other.
The Commodore printers have a self-testing facility, a loop in internal ROM
which outputs the character set (except for reverse characters, which may cause
overheating if used excessively). They also have a power-on sequence. The older
1515 can jam and appear completely dead when turned on, because the cam driving
the ribbon stuck. If this happens, lightly flick the pivoting part of the cam to loosen it.
Other Commodore printers include a series of printers for the earlier PET/CBM
machines. All PET/CBM printers require an IEEE interface connected to the 64's
normal printer port to operate. The 4022 is the main PET/CBM printer; it has a
considerable number of features, including ten secondary addresses. A heavy-duty
German printer and a very slow modified Olympia daisywheel are sometimes en
countered, too.
Other Printers
Most printers have a Centronics interface, which is a parallel interface using
multiwire flat-ribbon cable. RS-232 serial interfaces are also common. IEEE interfaces
545
Major Peripherals
are rarer, and current loop interfaces are another relatively uncommon type. All of
these can be connected to the 64, with the proper interfacing.
It is always advisable to test the compatibility of equipment before purchasing,
particularly if packaged software is to be used. Most good word processors make
allowances for printer type, but other programs may not work correctly with all
printers, particularly with features like margin and tab.
Printer Types
Several different kinds of printers are now available for the 64. They are described
below.
Teletypes. These are old-fashioned terminals, uppercase only, which commu
nicate with computers via RS-232. Since they have been replaced by video terminals
in industry, they can sometimes be found very cheaply. Of course, they may cost
you more in the long run, and are severely limited in their capability.
Modified typewriters. Many typewriter manufacturers are now including inter
face sockets on their machines, so daisywheel machines with this dual function are
likely to become popular. Ball typewriters with interfaces are slower, though the im
pression is often slightly better.
Thermal and spark printers. These printers make up characters from columns
of dots, like dot-matrix printers, but use methods that are less demanding mechani
cally. Thermal printers use short bursts of high temperature, while spark printers use
short bursts of high voltage. These printers are inexpensive, but the paper they use is
relatively costly and generally not the best quality.
Dot-matrix printers. These are by far the most widely used computer printers.
The print head typically has seven to nine wires arranged vertically, and each wire is
separately controlled by its own solenoid which drives the wire briefly into contact
with ribbon and paper. Higher quality machines have more dots, so the image qual
ity is better, although the delicacy of serifs and other features of typefaces are lost.
An advantage of this method is that any characters within the limits imposed by the
dot resolution can be generated, so dot-matrix printers often have internal switches
for assorted international alphabets, as well as the ability to print graphics. Some
dot-matrix printers have double strike, emphasized, and correspondence-quality
modes, and are able to print in several type widths.
Daisywheel printers. A daisywheel has approximately 100 radial spokes, each
of which holds a character at the tip. The wheels have low rotational inertia so they
can be spun rapidly, and common letters are clustered together to reduce search
time. A solenoid drives the letter against ribbon and paper. Commonly used spokes
will eventually wear and the wheel will need replacing. Wheels and ribbons aren't
standardized to any extent. These printers are usually more expensive than dot-
matrix units and are often slower, but the print quality is very good. Some
daisywheel printers offer double strike, proportional, and shadow printing.
General Remarks
Printers normally use continuous fanfold paper. Letterhead stationery is available in
continuous fanfold paper. Pin feed or sprocket feed usually implies that the printer
feed mechanism has fixed sprockets. Tractor feed often implies that variable-width
paper is usable. Friction feed indicates that rolls or sheets of unperforated paper are
accepted.
546
Major Peripherals
Most printers use endless-loop cartridge ribbons or, for higher quality, fixed-
length carbon film ribbons. Ribbon cartridges are not standardized, so be sure that
you have access to a reliable supplier. Some of these printers use standard typewriter
ribbons (like the popular Gemini printers), which makes the ribbon less costly to
replace.
External switches can range from simple paper control (linefeed, formfeed, and
online buttons) up to complete control over baud rate, parity, horizontal and vertical
spacing, and so on. Some printers, like Epson's RX-80, have an automatic linefeed
switch inside the machine. The switch is inaccessible without removing the lid and
can be a liability if a printer is shared between computers.
Maintenance generally requires return of the machine to the manufacturer, often
via a dealer. Fortunately, most printers are quite reliable. But it is still a good idea to
be sure that some maintenance is possible and that it is not too costly.
The speed of a printer is usually quoted in characters per second or lines per
minute, neither of which is a very satisfactory description. A lot depends on the den
sity of the text to be output. Moreover, the figures quoted are often inaccurate. As
usual it is best to test the printer in the conditions you plan to use it before you buy.
Commodore 64 compatibility is difficult with regard to graphics and upper/
lowercase switching. Few printers offer the entire 64 character set, and interfaces
may not handle the 64 upper/lowercase switch. However, in some cases, the inter
faces themselves are programmable to allow for this or contain their own ROM
character definitions for the Commodore graphics and reverse-field characters.
547
Major Peripherals
All other controls have varied among different models of Commodore printers,
and it is risky to assume they will remain the same as they are on your model. For
example, the user-definable single character is CHR$(8), but in the past has been
CHR$(254). Secondary addresses have varied as well: the 1515 uses OPEN 4,4,7 to
set lowercase mode; earlier models used this for uppercase.
To avoid such problems, you should let the 64 do the work of formatting and so
on as much as possible. Otherwise, if you give your program to another user or
change printers for some reason, you may be faced with the irritating job of rewrit
ing PRINT# statements.
The same method can select device 5 rather than device 4, if appropriate, and
OPEN 128+D,D with PRINT#128+D can add an extra linefeed which some print
ers may require.
As noted above, you should use PRINT#4 after CMD4 to "unlisten" the printer,
and return everything to normal, followed by CLOSE4. Note that CMD4; and
PRINT#4; each output nothing and can be used if it is important not to linefeed
when these commands are executed.
Formatting
PRINT USING in Chapter 6 can format numbers, inserting leading spaces and trail
ing zeros (as in 100.00). Alternatively, in BASIC, it's best to use something like
SP$ = "{10 SPACES}": PRINT#4,RIGHT$(SP$+X$,10) instead of TAB. This right
548
Major Peripherals
justifies a string (or numeral held as a string) by padding with spaces, then selecting
a fixed length.
The simplest way to truncate numerals is to use an expression like PRINT#4,
INT(X*100 + .5)/100 which rounds to the nearest hundredth. Some CBM printers
have formatting, typically allowing one format at a time to be defined (for instance,
OPEN 2,4,2: PRINT#2,"S$$$$$9.99" and OPEN 1,4,1). PRINT#1 then prints in a
format defined by secondary address 2, so that 123.456 prints as +$123.45.
User-Defined Graphics/Screen Dump
The 1525 and MPS-801 use CHR$(8) to enter graphic mode, in which the dot pat
tern of the printed character can be defined. Since these printers form characters in a
6X7 matrix, six columns of seven dots have to be defined. It's also necessary to
add 128 to the value for each column. All that's needed is PRINT#4, CHR$(8) fol
lowed by the bytes which define the columns. You can use CHR$ to define the bytes
for the column values—for example, PRINT#4,CHR$(8) CHR$(150) CHR$(182)
CHR$(224) CHR$(224) CHR$(182) CHR$(150). You can also use PRINT#4,X$ where
X$ is built from values in a DATA statement, starting with 8. Remember to PRINT#4,
CHR$(15) after graphic printing to return the printer to normal text mode.
The 1526 has a single definable character, CHR$(254), specified as eight col
umns of eight dots by opening a file to the printer with a secondary address of 5.
Thus the 1526 equivalent for the example above would be OPEN 5,4,5: PRINT#5,
CHR$(0) CHR$(22) CHR$(54) CHR$(96) CHR$(96) CHR$(54) CHR$(22) CHR$(0):
CLOSE 5 to define the character, followed by OPEN 4,4:PRINT#4,CHR$(254):
CLOSE 4 to print it.
An interesting use for this definable character capability is to dump a high-
resolution screen to the printer. Multicolor mode is not as easy, since the printer
can't distinguish the four colors. Program 17-1 slowly dumps a bitmap screen start
ing at 8192 ($2000). It will not work with all printers, due to differences in the
printer commands and features. (It cannot be used with the 1526 printer.)
549
Major Peripherals
The above routine substitutes the dummy character # for any graphics found in
the screen display. Alter the 35 in line 15 to change the dummy character to some
other symbol.
Repeat
Some printers allow repetition of characters, notably of a single column of dots to
build up a horizontal bar. A command like PRINT#4, CHR$(8) CHR$(26) CHR$(X)
CHR$(255) CHR$(15); turns graphics on, turns repeat mode on, specifies the number
of repetitions (X, which must be in the range 1-255), specifies character definition
(255 gives a solid column of dots), and returns to normal graphics.
Printer Presence
Some programmers find this useful as a reminder to users to switch on the printer.
In its simplest form, the command is OPEN 4,4: POKE 154,4: SYS 65490: POKE
154,3: CLOSE 4: S=ST. When the printer is on, ST should be 0; when off, ST is
-128, corresponding to 7DEVICE NOT PRESENT. SYS 65490 is the output routine
550
Major Peripherals
at $FFD2, and the above routine in effect tries to output to file 4, but avoids crashing
in the way that PRINT#4 does.
Spooling
The idea of spooling is that a file can be read from disk and printed while the 64 is
left free to run programs normally, except that accessing the serial bus is prohibited.
In principle this seems easy, since the disk can talk and the printer can listen, but
there is no simple way to accomplish it. Some printers and some interfaces include a
printer buffer, which accepts data from the computer and holds it until the printer
can process it. Once all the data has been sent to the buffer, the computer returns to
other operations.
Plotters
Plotters are most commonly used commercially for technical drawings. Plotters have
two stepper motors controlling pen or paper movement (or both) vertically and hori
zontally, with a mechanism to lift the pen off the paper. Typically, eight directions of
motion can be selected. Small step sizes make for finer drawings, if the pen itself is
fine enough, but tend to be slow. The fastest rate of plotting with inexpensive plot
ters is roughly three inches per second, so be prepared for long delays, particularly if
the interface is slow and if commands are sent with BASIC.
Commodore's 1520 plotter uses 4-1/2-inch-wide unsprocketed paper and has
four pens (typically black, red, blue, and green). It has built-in alphanumerics which
can be scaled to four sizes; the smallest draws 22 characters per inch. The pens
move across the paper, and vertical motion is provided by a roller that moves the
paper itself. It connects to the serial port as device 6.
These plotters can be used to draw perspective pictures, including color-
separation pairs in red and green, and can also draw geometrical patterns. Yellow,
magenta, and cyan pens could give an imitation of color-separation printing.
Lines
Program 17-3 is a plotter drawing subroutine that assumes a line, having a slope be
tween zero and one, is to be drawn from left to right. (Other slopes, including verti
cal lines, are treated by analogous routines.) XD and YD are the distances to be
plotted in the X and Y directions, M is the slope, and XP and YP keep track of the
current X and Y positions relative to the start of the plot. Line 120 plots northeast
whenever that gives a better approximation than east.
551
Major Peripherals
Circles
There are several methods to plot circles. One useful circle plotting routine is eiven
in Program 17-4. a
Modems
Most 64 modem users have either the model 1600 VICModem or the 1650
Automodem. Both are designed for the U.S. phone system. The 1650 plugs into the
base of the phone, while VICModem requires that you use a phone with a modular
plug handset in order to connect correctly.
When the 64 is used to communicate with another computer, the users must de
cide which computer will originate the communication and which will answer. For
example, when a bulletin board system like CompuServe is to be accessed, the 64 is
always set to originate while talking to the system.
To use a modem, first connect the modem to the computer with the 64 turned
off. Plug the Commodore modem into the user port (and connect it to the phone, if
it is a direct-connect model like the 1650), then load and run the terminal software.
Terminal software is the program that facilitates computer-to-computer talking via
the modems. It may be on cartridge, tape, or disk. For the VICModem, you may use
64 Term and for the Automodem, use Term 64, provided with the modem package.
You may want to use your own terminal software. BASIC, although slow, is
about as fast as the modem, so this is often a useful thing to do, notably when talk
ing to computers with slightly unusual characteristics or when trying out unusual
maneuvers like transferring files of data.
Once the software has been loaded and run, dial the number (either by dialing
yourself or by inputting the number into the 64 and allowing the 1650 to dial for
you). Wait for the carrier signal (a high-pitched tone). You may of course get a
wrong number, outdated number, no reply, or a reply from a system operator
(sysop). With some modems, you'll need to set the voice/data switch.
Now, wait for the carrier-detected indicator (red light) to come on. Your soft
ware may print something like 64 CONNECTED. Either signifies that your modem
has recognized the incoming frequency. Again, the actual procedure varies between
modems; it's automatic with the Automodem, but acoustic modems require you to
put the handset into the cups of the modem and switch to data (online) mode.
Wait for the system's first welcome frame to appear. Public systems often ask
for a password. This allows them to charge for access to the system. Use the menu
to select an item from what's available. CompuServe and other systems provide a
large directory to help you with this: GO CBM 310 is a shortcut command with
CompuServe.
Notes on Modems
It's helpful to know something about how modems work before looking at program
ming. Modems and their software are designed around phone systems. This has sev
eral consequences. Data must be transmitted serially, as bits rather than as bytes, so
each end of the line needs a way to convert between parallel and serial transmission.
In addition, the system needs some means of identifying the start of a byte; it also
needs timing conventions so that it can reliably detect individual bits.
Certain technical parameters are also important. Phone companies maintain con
trol over certain technical details of their lines. They do not permit excessive voltages
to get to exchanges, and some tones and signals are reserved for diagnostic use.
Such standards vary internationally. As a result, modems in different countries are
553
Major Peripherals
554
Major Peripherals
This is a security measure. Any received byte that doesn't conform to this pat
tern must be wrong and is retransmitted. Note that some seven-bit codes always
send the same parity bit, either a space or mark, ignoring the security aspect. The
start and stop bits are both signaled by transmitting a space (rather than a mark), so
synchronization is always okay. OPENing an RS-232 file allows these variables to be
controlled by the programmer, within limits.
Note that since Commodore 64 characters use eight bits, standard ASCII isn't
enough. In fact, much software simply ignores parity bits, using other error-checking
methods instead. Getting the baud rate, the number of bits per word, and the num
ber of start and stop bits right is necessary to successful modem communication.
Converting bytes into bits and sending them, and the converse process of
assembling bits into bytes, can be performed in software (as the 64's RS-232
handling does) or by chips like the UART (Universal Asynchronous Receiver-
Transmitter—asynchronous means it can process data by watching for a start bit).
Error checking is a complex process, which basically uses hash totals sent after
data as a check. With any system there must be some chance of completely random
data conforming to the check, and such events constitute undetected errors. Gen
erally, note that data is sent in batches (called records) of 256 bytes each. Records
with errors are retransmitted, and the overhead spent on this process can be as much
as 50 percent of the ideal error-free transmission time, depending on the quality of
the phone link. Error correction may be automatic, or software may use a recall fea
ture if a frame is unacceptable.
Bell 103 modems use full duplex, which means that either terminal can commu
nicate at any time. Half duplex is analogous to radio communication, where either
direction is available, but normally only one at a time. The half-duplex switch turns
off the echo-plexing feature, a verification system which returns characters when
they're received. Thus, if characters appear double, use this switch or software which
verifies the echoed characters. True half duplex requires a line like RS-232's second
ary channel to be able to interrupt unwanted messages.
Smart terminal software can download programs (load them and either run
them or store them on disk or tape). Data files are more difficult to handle, because
they don't transfer as simply as programs, having RETURN characters and so on
embedded in them. They are also liable to exceed RAM storage. Downloading files,
therefore, generally refers to programs and frames from data bases.
The two other common modem standards are the Bell 202 and 212A, which are
faster than the 103 standard. The 212A can work with the 103, but the 202 and
212A are currently less popular than the 103, mainly because the faster modems are
significantly more expensive. Incidentally, the 103 system can operate at 600 bits per
second, which may be worth trying.
One problem with acoustic modems may be getting the two cups which are sup
posed to fit the handset into place. A few modems forgo the rigid body in favor of a
pair of cups on leads, so they can fit many phone styles. Incidentally, over short dis
tances it's not necessary to use a modem—two VICs or 64s can be connected by
three lines between their user ports, or with RS-232 adapters. As always, leave this
work to someone with sufficient technical experience.
555
Major Peripherals
Programming Modems
Programs for use with modems must allow for two things. First, the RS-232 file must
be opened properly. Second, transmissions both to and from the 64 must be allowed
for. Both are fairly straightforward, though they may appear difficult.
Opening an RS-232 file. Only one RS-232 file can be opened at any one time,
and the syntax is typically OPEN 2,2,0,CHR$(6). The device number must be 2, so
file number 2 is simplest, allowing PRINT#2 and GET#2 for output and input via
the modem. The secondary address is irrelevant.
The filename consists of one or two characters in a string; the example is
equivalent to CHR$(6)+CHR$(0). These parameters are explained fully in the next
section. The value shown assigns eight bits of data per word, with one stop bit (and
a start bit, implicit in the whole process), 300 baud transmission, no parity, and full
duplex. Three-line handshaking is assumed.
This is the most common combination. Use OPEN 2,2,0,CHR$(38)CHR$(96) to
assign a seven-bit word and even parity instead.
Transmitting and receiving characters. All that's needed is a loop to get
characters from the keyboard and send them through the modem using PRINT#2,
and to get characters from the modem (using GET#2). BASIC may need delay loops
in its output to avoid sending characters too fast. For most purposes, some characters
have to be converted, and BASIC provides an adaptable and quite easy means to do
this. One reason for the conversion is that 64 ASCII is slightly different from true
ASCII, so unless you're happy with strange-looking lettering, conversion is nec
essary. The other reason is that it's useful to define some keys so that they perform
modem-specific functions.
Program 17-6 is a good example of a program for use with a modem.
Line 100 opens the file, while line 101 shows an alternative OPEN statement.
Lines 200-260 allow for conversion between input and output characters. An alter
native way to do this is to use several IF-THEN range comparisons; however, arrays
are faster, since the correct value can simply be looked up. Integer arrays save space.
Lines 300-320 convert the from array into the inverse of the to array. Line 500 veri
fies that output data has actually been sent.
The status byte, ST, can also be tested. The variable IN$ comes from the
modem; OUT$ is actually fetched from the keyboard but is called OUT$ because it is
to be sent to the other computer. Remember that the 64 keyboard allows control
characters to be typed without special programming.
ML conversion to true ASCII. ML programmers may need this routine, which
converts a 64 ASCII character into true ASCII. It interchanges upper- with lowercase:
CMP #$41
BCC END
CMP #$5B
BCS LABEL
ORA #$20
BCC END
LABEL CMP #$61
BCC END
CMP #$7B
BCS END
AND #$DF
END continue...
557
Major Peripherals
In the 64's RS-232 system, an OPEN to the RS-232 device initializes a number
of RAM locations and prepares for NMI interrupts, which are used with RS-232.
These interrupts disturb disk and tape timing, which is one reason neither the disk
drive nor the Datassette can be used during transmission.
RS-232's OPEN routine (at $F409 in the 64) sets the parameters indicated in
Table 17-4. If you OPEN and then PEEK, you'll see some of them. Most are reason
ably straightforward. Two points are worth noting: OPEN to RS-232 lowers the top
of memory by 512 bytes, making room for two 256-byte FIFO (first-in, first-out)
buffers. BASIC pointers are altered to clear variables (equivalent to a CLR state
ment), so it's best to OPEN the RS-232 file early in the program to avoid losing vari
able values. The baud rate is controlled by reference to tables in ROM, which in 64
has ten usable values.
ML programmers may want to alter the NMI vector into RAM so that the tables
can be changed. Alter the first tabled value to generate the new baud rate. After
OPEN, remember to POKE the vector at ($299) with twice that value, plus 200. To
convert ROM values into equivalent baud rates, use 50*EXP(9.23308 —
LOG(VALUE + 100)).
558
Major Peripherals
Location Explanation
559
ON Figure 17-1. The Control Register
o Q
128 64 32
O"
128 64 32 16
1 1 |
Finally, six bits of location 663 ($297) report conditions resulting from RS-232
use. If bit 0 is set, there is a parity-bit error. Bit 1 being set indicates an error in the
structure of received bits, perhaps due to noise. Bit 2 is set when the receiver buffer
is full (that is, when data is coming in too fast). Bit 3 is set to indicate when the re
ceiver buffer is empty. Bit 4 is set when the clear-to-send signal is off (when the re
mote terminal is not ready to receive). Bit 5 is unused, and bit 6 is set when the
data-set-ready signal is missing (the remote terminal is not ready to send). When bit
7 is set, a break has been detected.
Pin 6 is connected to the 64's reset line, which is why the disk drive and printer
reset when the 64 is switched on. Data is transferred in and out through pin 5. Pin
1, the service request line, allows devices to request service from the 64. CLK is a
clock signal and ATN (attention) is described below.
Both CIAs are used in processing. The part of ROM handling this can be in
spected in detail by looking at the places where bit 7 of $DD00 (CIA 2's Port A) is
used; this line inputs data bits. Data is transmitted from bit 5 of CIA 2 Port B, so
$DD00 also controls data output. Other important functions of that location are bits
4 and 6, which provide input and output clock signals.
Briefly, the serial bus is controlled by the 64. Devices on the bus are talkers, lis
teners, or both. For example, printers listen and disk drives both talk and listen. The
Kernal has routines to make devices talk, listen, untalk, and unlisten, meaning in ef
fect that they're on or off. BASIC handles all this itself, apart from a few special
effects.
Commands are sent to devices when ATN is low (the bit value is 0). When ATN
is set high again, all the bytes sent are interpreted as data. When ATN is low, typi
cally a single byte is sent as a command; that byte is interpreted by the device as fol
lows: If it is in the range $20-$3E, it means listen; if it's $3F, it means unlisten; $40-
$5E mean talk; $5F means untalk; and $60-$7F indicate secondary addresses. This is
561
Major Peripherals
why secondary addresses are stored in the 64 with 96 decimal added, and why the
Kernal LISTEN and TALK routines begin with ORA #$20 and ORA #$40.
A printer can be made to print, without opening a file, by setting the device
number to 4, calling Kernal LISTEN, setting ATN out high, sending characters with
CHROUT, and finally unlistening with CLRCHN. Whenever files are open to a de
vice, the device is first made a talker or a listener. Then the secondary address is
sent (the Kernal has two routines for this purpose), so the device knows which file to
address.
562
Appendices
Appendix A
What Is a Program?
A computer cannot perform any task by itself. Like a car without gas, a computer
has potential. But without a program, it isn't going anywhere. Most of the programs
published in this book are written in a computer language called BASIC. BASIC is
easy to learn and is built into all Commodore 64s.
BASIC Programs
Computers can be picky. Unlike the English language, which is full of ambiguities,
BASIC usually has only one right way of stating something. Every letter, character,
or number is significant. Common mistakes are substituting the letter O for the nu
meral 0, a lowercase 1 for the numeral 1, or an uppercase B for the numeral 8. Also,
be sure to enter all punctuation, such as colons and commas, just as they appear in
the book. Spacing can be important. To be safe, type in the listings exactly as they
appear.
565
Appendix A
every time you want to use it. Learn to use your machine's editing functions. How
do you change a line if you make a mistake? You can always retype the line, but at
least you need to know how to backspace. Do you know how to enter reverse video,
lowercase, and control characters? It's all explained in your 64's manual.
A Quick Review
1. Type in the program, a line at a time, in order. Press RETURN at the end of each
line. Use the INST/DEL and cursor keys to correct mistakes.
2. Check the line you've typed against the line in the book. You can check the entire
program again if you get an error when you run the program.
3. Make sure you've entered statements in braces using the appropriate control key
(see Appendix B, "How to Type In Programs").
566
Appendix B
Many of the programs in this book contain special control characters (cursor control,
color keys, reverse characters, and so on). To make it easy to know exactly what to
type when entering one of these programs into your computer, we have established
the following listing conventions.
Generally, Commodore 64 program listings contain words within braces which
spell out any special characters: {DOWN} means to press the cursor-down key, while
{5 SPACES} tells you to press the space bar five times.
To indicate that a key should be shifted (hold down the SHIFT key while press
ing the other key), the key would be underlined in our listings. For example, S
would mean to type the S key while holding down the SHIFT key. This would ap
pear on your screen as a heart symbol. If you find an underlined key enclosed in
braces (for example, {10 N}), you should type the key as many times as indicated. In
this case, you would enter ten SHIFTed NTs. One exception to this is that {SHIFT-
SPACE} means to hold down the SHIFT key and type the space bar.
If a key is enclosed in special brackets, f< >\, you should hold down the Com
modore key while pressing the key inside the special brackets. (The Commodore key
is the key in the lower-left corner of the keyboard.) Again, if the key is preceded by a
number, you should press the key as many times as necessary.
Occasionally, you will see a single character within braces. These characters are
entered by pressing CTRL while typing the letter indicated. For example, {A} is en
tered by pressing CTRL-A.
About the quote mode: You know that you can move the cursor around the
screen with the CRSR keys. Sometimes a programmer will want to move the cursor
under program control. That's why you see all the {LEFT}'s, {HOME}'s, and
{BLU}'s in our programs. The only way the computer can tell the difference between
direct and programmed cursor control is the quote mode.
Once you press the quote (the double quote, SHIFT-2), you are in the quote
mode. For instance, if you type quote followed by a few characters, then try to
change it by moving the cursor left, you'll only get a bunch of reverse-video charac
ters. These are the symbols for cursor left. The only editing key that isn't pro
grammable is the INST/DEL key, so you can still use INST/DEL to back up and edit
the line. Once you type another quote, you are out of quote mode.
You also go into quote mode when you use INST/DEL to insert spaces into a
line. In any case, the easiest way to get out of quote mode is simply to press RE
TURN. You'll then be out of quote mode and can cursor up to the mistyped line and
fix it.
Use the following table when entering cursor and color control keys:
567
Appendix B
{HOME} CLR/HOME i 2 g
{DOWN} | CRSR i
{RVS} | CTRL | [ 9 |
{OFF} | CTRL | | 0 |
{BLK} | CTRL | | 1 | {Fl }
{WHT} | CTRL] 1 2 ] { R}
{CYN} | CTRL | [ 4 | ! W }.
{PUR} | CTRL | | 5 | {F5}
{GRN} | CTRL | | 6 | ! F6 }
{YEL} | CTRL || 8 | { F8 }
568
Appendix C
"The Automatic Proofreader" will help you type in program listings without typing
mistakes. It is a short error-checking program that hides itself in memory. When ac
tivated, it lets you know if you have made a mistake immediately after typing a line
from a program listing.
569
Appendix C
Here's another thing to watch out for: If you enter the line by using abbrevi
ations for commands, the checksum will not match up. But there is a way to make
the Proofreader check it. After entering the line, LIST it. This eliminates the abbrevi
ations. Then move the cursor up to the line and press RETURN. It should now
match the checksum. You can check whole groups of lines this way.
Hidden Perils
The Proofreader's home in the 64 is not a very safe haven. Since the cassette buffer
is wiped out during tape operations, you need to disable the Proofreader with RUN/
STOP-RESTORE before you save your program. This applies only to tape use. Disk
users have nothing to worry about.
Not so for 64 owners with tape drives. What if you type in a program in several
sittings? The next day, you come to your computer, load and run the Proofreader,
then try to load the partially completed program so you can add to it. But since the
Proofreader is trying to hide in the cassette buffer, it is wiped out.
What you need is a way to load the Proofreader after you've loaded the partial
program. The problem is that a tape LOAD to the buffer destroys what it's supposed
to load.
After you've typed in and run the Proofreader, enter the following three lines in
direct mode (without line numbers) exactly as shown:
A$="PROOFREADER•T":B$="{10 SPACES}":FOR X = 1 TO 4
: A$=A$+B$: NEXT X
OPEN 1,1,1,A$:CLOSE 1
After you enter the last line, you will be asked to press RECORD and PLAY on
your cassette recorder. Put this program at the beginning of a new tape; this gives
you a new way to load the Proofreader. Anytime you want to bring the Proofreader
into memory without disturbing anything else, put the cassette in the tape drive, re
wind, enter OPEN1:CLOSE1, and press PLAY on the recorder. You'll get the mes
sage FOUND PROOFREADER, but not the familiar LOADING. Don't worry; the
Proofreader is now in memory. You can then start the Proofreader by typing SYS
886. To test this, type in PRINT PEEK (886). It should return the number 173. If it
570
Appendix C
does not, repeat the steps above, making sure that A$ contains 13 characters
(PROOFREADER.!) and that B$ contains ten spaces.
You can now reload the Proofreader into memory whenever LOAD or SAVE de
stroys it, restoring your personal typing helper.
571
Appendix D
Row
0 1024
1064
-
1104
-
1144
1184
D 1224
1264
1304
1344
10 1424
1464
1504 • •
1544
15 1624
1664
1704
1744
20 S • •
1864
1904
_ . 1944
24 1984
10 15 20 25 30 35 39
Column
572
Appendix E
Row
0 55296
55336
55376
55416
55456
5 55496
55536
55576
55616
^ 55656
10 55696
55736
55776
55816
15 55896
55936
55976
56016
56056
2U 56096
56136
56176
o/t 56256
24 56216
10 15 20 25 30 35 39
Column
573
Appendix F
Code: 0 1 2 3 4 5 6 7
Code: 8 9 10 11 12 13 14 15
574
Appendix G
ASCII Codes
Dec Hex Meaning Dec Hex Meaning Dec Hex Meaning Dec Hex Meaning
15 OF SI -Shift in 47 2F / 79 4F O 111 ££ o
575
Appendix H
05 5 WHITE 37 55 7
08 8 DISABLE 38 56 8
SHIFT-COMMODORE 39 57 9
09 9 ENABLE 3A 58
SHIFT-COMMODORE 3B 59 ;
0D 13 RETURN 3C 60 <
0E 14 LOWERCASE 3D 61 =
>
11 17 CURSOR DOWN 3E 62
7
12 18 REVERSE VIDEO ON 3F 63
13 19 HOME 40 64 @
14 20 DELETE 41 65 A
1C 28 RED 42 66 B
ID 29 CURSOR RIGHT 43 67 C
IE 30 GREEN 44 68 D
IF 31 BLUE 45 69 E
20 32 SPACE 46 70 F
21 33 i 47 71 G
22 34 48 72 H
23 35 # 49 73 I
24 36 $ ■ 4A 74 J
25 37 % 4B 75 K
26 38 & 4C 76 L
27 39 4D 77 M
28 40 ( 4E 78 N
29 41 ) 4F 79 O
2A 42
*
50 80 P
2B 43 + 51 81 Q
2C 44 52 82 R
2D 45 —
53 83 S
2E 46 54 84 T
2F 47 / 55 85 U
30 48 0 56 86 V
31 49 1 57 87 W
32 50 2 58 88 X
33 51 3 59 89 Y
34 52 4 5A 90 Z
35 53 5 5B 91 [
36 54 6 5C 92 f
576
Appendix H
5D 93 ] 85 133 fl
5E 94 I 86 134 f3
5F 95 87 135 f5
60 96 B 88 136 i7
61 97 89 137 (2
62 98 8A 138 f4
63 99 B 8B 139 f6
64 100 8C 140 f8
SHIFT-RETURN
65 101 □ 8D 141
66 102 D 8E 142 UPPERCASE
67 103 D 90 143 BLACK
68 104 a 91 145 CURSOR UP
69 105 □ 92 146 REVERSE VIDEO OFF
6A 106 93 UZ. CLEAR SCREEN
6B 107 □ 94 148 INSERT
6C 108 D 95 149 BROWN
6D 109 S 96 150 LIGHT RED
6E 110 0 97 151 GRAY1
6F 111 98 152 GRAY 2
70 112 n 99 153 LIGHT GREEN
71 113 H 9A 154 LIGHT BLUE
72 114 P 9B 155 GRAY 3
73 115 9C 156 PURPLE
74 116 9D 157 CURSOR LEFT
75 117 9E 158 YELLOW
76 118 9F 159 CYAN
77 119 A0 160 SHIFT-SPACE
78 120 Al 161 1
79 121 a A2 162 u
7A 122 A3 163 □
7B 123 A4 164 □
7C 124 A5 165 □
7D 125 m A6 166
7E 126 A7 167 a
7F 127 H A8 168
81 129 ORANGE A9 169 E
577
Appendix H
AA 170 □ CF 207 □
AB 171 m DO 208 □
AC 172 a Dl 209
AD 173 E D2 210 Q
AE 174 B D3 211
AF 175 y D4 212 D
BO 176 H D5 213 □
Bl 177 H D6 214
B2 178 D7 215
B3 179 ffl D8 216
B4 180 □ D9 217
B5 181 DA 218
B6 182 DB 219
B7 183 □ DC 220
B8 184 a DD 221 m
B9 185 u DE 222
BA 186 a DF 223 H
BB 187 ■ EO 224 SPACE
BC 188 H El 225 E
BD 189 H E2 226
BE 190 E3 227 n
BF 191 E4 228 D
CO 192 B E5 229
D
Cl 193 H E6 230
C2 194 m E7 231 n
C3 195 B E8 232
C4 196 B E9 233 B
C5 197 □ EA 234 □
C6 198 B EB 235 m
C7 199 D EC 236 a
C8 200 ED 237
a
C9 201 EE 238
CA 202 □ EF 239
u
CB 203 FO 240
B
CC 204 Fl 241
D H
CD 205 F2 242
S H
CE 206 F3 243
0 ffl
578
Appendix H
F4 244 D
F5 245 C
F6 246 []
F7 247
F8 248 n
F9 249
FA 250 D
FB 251 El
FC 252 H
FD 253
FE 254 5]
FF 255
1. 0-4, 6-7, 10-12, 15-16, 21-27, 128, 130-132, and 143 have no effect.
2. 192-223 same as 96-127, 224-254 same as 160-190, 255 same as 126.
579
Appendix I
Hex Dec Uppercase and Lower- and Hex Dec Uppercase and Lower- and
Full Graphics Set Uppercase Full Graphics Set Uppercase
00 0 @ IF 31 —
01 1 A a 20 32 -space-
02 2 B b 21 33 i !
03 3 C c 22 34 »
04 4 D d 23 35 # #
05 5 E e 24 36 $ $
06 6 F f 25 37 °/c
07 7 G g 26 38 & &
08 8 H h 27 39 '
09 9 I 28 40 (
0A 10 J 29 41 )
OB 11 K 2A 42
•
OC 12 L 2B 43 +
OD 13 M m 2C 44
OE 14 N n 2D 45 _
OF 15 O o 2E 46
10 16 P P 2F 47 /
11 17 Q q 30 48 0 0
12 18 R r 31 49 1 1
13 19 S s 32 50 2 2
14 20 T t 33 51 3 3
15 21 U u 34 52 4 4
16 22 V V 35 53 5 5
17 23 w w 36 54 6 6
18 24 X X 37 55 7 7
19 25 Y y 38 56 8 8
1A 26 z z 39 57 9 9
IB 27 [ 3A 58
1C 28 £ 3B 59
ID 29 ] 3C 60 <
IE 30 t 3D 61 _
580
Appendix
Hex Dec Uppercase and Lower- and Hex Dec Uppercase and Lower- and
Full Graphics Set Uppercase Full Graphics Set Uppercase
3E 62 5F 95
3F 63 60 96 - -space- -
40 B I II
64 B 61 97
41 65 a A 62 98 U H
42 66 B 63 99 □ □
43 67
B C 64 100 D D
44 68 B D 65 101 □ O
45 69
□ E 66 102
46 70 □ F 67 103 a a
47 71
D G 68 104 s
48 72
O H 69 105 B
49 73 □ I 6A 106 a a
4A 74 ] 6B 107 E E
4B 75 □ K 6C 108 Q a
4C 76
D L 6D 109 B
4D 77
S M 6E 110 H
4E 78
0 N 6F 111 a a
4F 79
□ O 70 112 B B
50 80
□ P 71 113 H H
51 81 Q 72 114 H H
52 82
□ R 73 115 ffl
53 83 S 74 116 D □
54 84
D T 75 117 C C
55 85
Q U 76 118 a a
56 86 V 77 119 □ n
57 87 w 78 120 n n
58 88 X 79 121 □ a
59 89
a Y 7A 122 a
5A 90 0 z 7B 123 D
5B 91 7C 124 H
5C 92 c 7D 125 H
5D 93 m 7E 126 E
5E 94 7F 127 B H
581
Hex Decimal
00
Address Address Offset Function
D012 53266 18 Raster Scan line and Write Registe r for Raster Interrupts
1 = Interrupts
0 = No 1 1 1 light Pen Sprite-Sprite Sprite-Data Raster
Interrupts Collision Collision Scan
DO1F 53279 31 Sprite-Data Col lision Register (c leared only v/he n read)
/^D400 54272 0 (0
( D407 54279 7 Frequency Control (low byte)
1 D40E 54286 14 a
x"
I D401 54273 1
1 D408 54280 8 Frequency Control (high byte)
\ D40F 54287 15
I D402 54274 2
D
I D409 54281 9 Pulse Width (bits 7-0)
/ D410 54288 16
Voice 1 Jf D403 54275 3
O
15ulse Width (bits 11-8
Voice 2 \ D40A 54282 10
Voice 3 D411 54289 17 not used bit 11 bit 10 | bit 9 bit 8
D406 54278 6
D40D 54285 13 Sustain Level (0-15) Release (0-15)
D414 54292 20
Cutout
D418 54296 24 Voice 3 Filter Type Master Volume (0-15)
Device Numbers
Table of second parameter in OPEN. Example: OPEN 5,4 opens file 5 to printer.
0 Keyboard
1 Tape (not used in SX-64 models)
2 RS-232, usually modem
3 Screen
4 Printer
5 Printer—alternative setting
6 Plotter
8 Disk Drive
9 Disk Drive—alternative
10 Disk Drive—alternative
11 Disk Drive—alternative
586
73
I
a
x
en ui in ui i
oo ui uiooomux
K) «*j -- en « C« W CD MO-» L _
comoioj 00 M WOtOONOOfii
-(AHAHAHAHAHAHAHAHAHAHAHAHAHAHAHA HAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHA
HA HA HA HA HA HA HA VH UUIUIVIUIUIVIUIlnVIVIUIUIUI
Zj Zj Zj vJ >vj «>J *-J •*J-
Tim OOCD >«> eo •n m oocd :
o r
_*-»oooooooooo(Oto<o<o (0(0<OVO(0(OOOOOOOOOC000090000<
-»o»oo«jo>ui*wN-'oioeoNjffi UI*rWh-»-'OVOOO'^OUl4SWKJ-*< Si
w w www w oo *J *«j "^J n| on en en
N M ^ ^ .• ©
co vj sjs4Njc»a)ata)inuitnut* j:4:WWWWNJION)N)
sj (o *^i ^ nj (O en ^ ^ (O en jr >■■* oo en
->(O O) W -' 00 O) U) -'COO WOC0UI UOCOUIIOOvlUINJO' NCn-1UIOt0BW>JNWOUllOC
Ul KJ ' > *J * N> (O
2 S o * » w-5 0)OJ;i0W00N)0tlUIOC00W>l - — rocno*oohjcnojrooioeno
ff» o -cr oo ro <r>
HAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAH
00 00 00 0000 00 00 00 0000
$88888888 monCD>*ooo^Jcnui4rujNj-
<noi*ww-o
o r
n o
(0(00000000000 p 5
cncnencnuiuiuiui.tS'&j; «WWWUI
> O) at at vi m » ui C«««UIWUUIOMNJ- OMU1MO<
uiwoauiwo uiioo^iyiioo-J^K);
m w- ■ H *: ^ *£ S o ui (O j
S CO KJ <OW00K)0)
N0>O*C?
AHAHAHAHA HA HA HA HA ■(
*nrnrnrnrnmrTimmrnrnmmnirnm m oc OOPO
N>NJ|OK)K»K>KIKirOhJrOK>N-.-
K> K> rs> K> K> NJ NJKI-*—•—»-J---»-»-»—»—' O O 0010(0(0(0(0(
CjJ ijj OU O00JWU1*WW«OV000 J-'OtOCOMtnVl
SSSS2S (DOOM
I UI UI UI UI UI UI
iCOOlWOCOUIWOMWWOvJOIW
(OUOOtom-'UiOCOSU^MOlOVI
rOOtOOICP wmOtC0M0)O4:00KJfflOJr05'-
CJ1
00
Appendix N
Opcodes in Detail
N V B D I Z C
588
Appendix N
8D 4 9D 5 99 5 85 3 95 4 81 6 91 6 STA
96 4 STX
86 3
94 4 STY
84 3
AA 2 TAX
A8 2 TAY
BA 2 TSX
* .
rt if i ndex c:rosses page 8A 2 TXA
2
+1 if b ranch is tak en, 9A 2 TXS
hI mor e if p age cr ossed 98 2 TYA
589
CJl
v£>
O Opcode Low Nybble
0 1 2 4 5 6 8 9 A C D E (D
XT g.
0 BRK ORA (Ind,X) ORA Zer ASL Zer PHP ORA Imm ASL A ORA Abs ASL Abs
x"
1 BPL ORA (Ind),Y ORA Zer,X ASL Zer,X CLC ORA Abs, Y ORA Abs.X ASL Abs, X
5" o
2 JSR AND (Ind,X) BIT Zer AND Zer ROL Zer PLP AND Imm ROL A BIT Abs AND Abs ROL Abs
o
3 BMI AND (ind),Y AND Zer,X ROL Zer,X SEC AND Abs, Y AND Abs.X ROL Abs X
4 RTI EOR (Ind.X) EOR Zer LSR Zer PHA EOR Imm LSR A JMP Abs EOR Abs LSR Abs
o*
5 BVC EOR (Ind),Y EOR Zer.X LSR Zer,X CLI EOR Abs, Y EOR Abs.X LSR Abs X
6 RTS ADC (Ind.X) ADC Zer ROR Zer PLA ADC Imm ROR A JMP Ind ADC Abs ROR Abs o
7 BVS ADC (Ind),Y ADC Zer.X ROR Zer,X SEI ADC Abs, Y ADC Abs,X ROR Abs X
8 STA (Ind.X) STY Zer STA Zer STX Zer DEY TXA STY Abs STA Abs STX Abs
9 BCC STA (Ind),Y STY Zer,X STA Zer,X STX Zer.Y TYA STA Abs, Y TXS STA Abs,X
A LDY Inun LDA (Ind,X) LDX Imm LDY Zer LDA Zer LDX Zer TAY LDA Imm TAX LDY Abs LDA Abs LDX Abs
B BCS LDA (Ind)pY LDY Zer.X LDA Zer,X LDX Zer.Y CLV LDA Abs, Y TSX LDY Abs.X LDA Abs,X LDX Abs ,Y
C CPY Imm CMP (Ind.X) CPY Zer CMP Zer DEC Zer INY CMP Imm DEX CPY Abs CMP Abs DEC Abs
o
D BNE CMP (Ind),Y CMP Zer,X DEC Zer.X CLD CMP Abs, Y CMP Abs,X DEC Abs ,x
o
E CPX Inun SBC (Ind.X) CPX Zer SBC Zer INC Zer INX SBC Imm NOP CPX Abs SBC Abs INC Abs
■o
p
BEQ SBC (Ind),Y SBC Zer,X INC Zer.X SED SBC Abs Y SBC Abs,X INC Abs ,x
o
a
(ft
Appendix P
6502/6510 Quasi-Opcodes
Instruction Abs Abs,X Abs,Y Zer Zer,X Zer,Y (Ind,X) (Ind),Y Imm
ASO (ASL,ORA) OF IF IB 07 17 03 13 0B
RLA (ROL,AND) 2F 3F 3B 27 37 23 33 2B
LSE (LSR,EOR) 4F 5F 5B 47 57 43 53
RRA (ROR,ADC) 6F 7F 7B 67 77 63 73
AXS (STX,STA) 8F 87 97 83
LAX (LDX,LDA) AF BF A7 B7 A3 B3
DCM (DECCMP) CF DF DB C7 D7 C3 D3
INS (INQSBC) EF FF FB E7 F7 E3 F3
ALR (LSR,EOR) 4B
ARR (ROR,ADC) 6B
OAL (TAX,LDA) AB
SAX (DEX,CMP) CB
A number of bit patterns which do not appear in Appendices N and O will still
be interpreted by the 6502/6510 as opcodes. These commands are not part of the
6502/6510's specification. Types X3, X7, XB, and XF (and most of X2) aren't defined.
Generally, these quasi-opcodes arise from the processor attempting to execute two
instructions simultaneously.
591
Appendix P
There are many regularities in these results. Codes ending in bits 11 execute two
standard instructions ending with bits 01 and 10, simultaneously; if the addressing
modes of the instructions don't match, the higher may be executed first. Those
quasi-opcodes shown in the table in boldface seem likely to be more reliable than
the others.
While there are no guarantees that these opcodes will continue to work with all
revisions of the 6502/6510, it is a fact that some published software containing these
codes has given no problems. All 6502/6510s seem to be produced from the same
masks, as is shown by the well-known bug in indirect JMP, where JMP ($01FF) takes
its two-byte address from $01FF and $0100.
Besides providing some programming shortcuts, quasi-opcodes allow some mea
sure of concealment from disassembly, as no standard disassembler program will be
able to interpret them. For example:
033C ASO $0342 ;Shift Left contents of $0324
033F DCM $0345 ;Decrement contents of $0345
0342 ML program
033C OF 42 03 CF 45
0341 03 XX ?? ?? YY
where XX, ??, and YY are parts of the ML program. Disassembly starting at 033C
will produce at least ten bytes of garbage. However, the program will run properly,
but only once. You must compensate for the first two instructions, which halve the
contents of $0345 and decrement the contents of $0345. If you set up a loop to
change some other portion of the ML—for example, by EORing it with some set val
ues—the whole of a large section of RAM ML can be made hard to decipher.
592
Appendix Q
Conversion is a deceptively simple word, hiding the reality that one machine's pro
grams must often be rewritten for use on another. First, you'll see how to transfer
programs between machines. Then you'll see how to convert them to run in their
new environments. Generally, these remarks apply only to BASIC; ML programs
usually have to be rewritten.
593
Appendix Q
Simulate CBM/PET
10 SIMPLE PET/CBM SIMULATOR
20 POKE 792,193:POKE 646,5:POKE 53281,0:POKE 53280
,0
30 POKE 56576,5:POKE 53272,4:POKE 648,128
40 POKE 1024,0:POKE 43,1:POKE 44,4:POKE 55,0:POKE
{SPACE}56,128:PRINT"{CLR}":NEW
Program Conversion
Programs which are pure BASIC, even for non-CBM computers, can often be con
verted to run on the 64. Difficulties are likely, though, particularly if disk or tape ac
cess is needed. The 64 can perform any Commodore disk operation, although CBM
BASIC 4.0 requires translation into the lower level version, since it includes disk
commands not available on the VIC or 64. Other computers' disk operations may
well be rewritten to operate with the 64.
There are often other subtle differences between computers, too. For instance,
some interpret logical true as 1, rather than —1 as with CBM, so logical operations
may work incorrectly. And some commands (like PRINT USING) are simply missing
from CBM BASIC.
CBM BASICs are all more or less transportable between machines. However, the
earliest PETs and latest CBMs are a bit different from the 64 in several small ways—
GO TO isn't allowed as one word in the oldest PETs, for example, and DS is a re
served variable in the most recent models. Pure BASIC (without SYS, PEEK, POKE,
WAIT, or USR) is compatible to a very large extent; screen problems can occur, with
related features like the bug in INPUT "LONG PROMPT";X$, differences with POS,
SPC, and TAB, and cursor movements which may scroll the screen.
You can expect that calculation programs and programs which print out results
will work with little change; so will programs written without PEEKs or POKEs.
With luck, programs which use the built-in graphics set may convert easily. A check
ers program, with complicated logic and a simple board display, may need work on
the display but can be expected to run properly if the graphics are right.
POKE, PEEK, SYS, WAIT, and USR. These are the problem areas when
converting programs; very little ML is transportable between machines. Some ML
has an exact equivalent in each CBM machine, for example, screen POKEs and
POKEs into the keyboard buffer. But other ML is machine-specific. For example,
sprites in the 64 have no equivalent in other CBM machines.
If you're lucky and the BASIC program has many REMarks, conversion can be a
simple matter of looking up the location in one memory map and finding the
equivalent in another. Disabling the RUN/STOP key and manipulating the keyboard
buffer are examples. You may be able to delete some commands; disabling
RUN/STOP isn't very important. In addition, you may be able to replace some
PEEKs amd POKEs. For instance, the 64's POKE 198,0 has a BASIC equivalent, FOR
J=l TO 10: GET X$: NEXT, which clears a ten-character keyboard buffer. CBM's
POKE 59468,14 to switch to lowercase is replaceable by PRINT CHR$(14) on the
VIC and 64.
Generally, POKEs, PEEKs, and WAITs involving locations 140-250 are likely to
apply to the screen or keyboard. Low memory values often alter BASIC pointers.
594
Appendix Q
Most low RAM locations have the same sort of effect with VIC and the 64. CBM is
rather different, though as a rule BASIC 2.0's usage of locations up to 120 or so are
just three addresses less than VIC/64 values (a POKE to location 41 in a PET/CBM
has the same effect as a POKE to location 44 on a VIC or 64).
SYS commands can be converted only if you have ML knowledge. A routine
may call some Kernal addresses and be usable unchanged; more likely, disassembly
will show up a few addresses which have to be changed. Without ML knowledge
you can't be sure what ML POKEd to RAM does.
POKE commands are usually the most difficult to convert, because they can
change the whole program configuration. Screen POKEs and the color RAM, graph
ics definitions and sound, interface chip manipulations, and uses of multicolor mode
illustrate this sort of thing. PEEKs (to read joysticks, for example) can be tricky as
well, but they can be routinized more easily in view of the narrower purposes they
serve.
The following table gives relevant POKE and PEEK locations for a variety of
functions. It should help you identify the purpose of a few of those mysterious
POKEs in other people's programs.
7680-8185 32768-33767
(unexpanded) (40-column)
Screen Memory 1024-2023
4096-4591 32768-34767
(with 8K or more
expansion) (80-column)
37888-38393
Color Memory (unexpanded) 55296-56295 —
38400-38905
(with 8K or more
expansion)
PIA1 59408-59411
Interface Chip VIA1 37136-37151 CIA1 56320-56335 PIA2 59424-59427
Registers VIA2 37152-37167 CIA2 56576-56591 VIA 59456-59471
595
Appendix R
Supermon 64
Supermon is a relatively short monitor for the 64. It is a public domain program, so it
is free. It can be loaded like BASIC and run, and this puts it into the top of BASIC
memory, leaving RAM from $C000 free for ML programs. Chapter 8 explains its op
eration. This version prints your input in white, and the monitor's output in cyan, for
good readability.
SYS 38910 reenters Supermon after .X has been used to exit to BASIC, assuming
Supermon is in its usual position in RAM and hasn't been disconnected by
RUN/STOP-RESTORE. SYS to an address containing zero (SYS 13, for example)
will reenable the monitor as well.
The following instructions tell you how to enter and save Supermon. The first
step is to switch on the 64 and type in the entire program (not necessarily all at
once) and save "Supermon Data" to tape or disk. Note that complete accuracy is re
quired in entering the data. For security, a simple checksum is included. It's often
most efficient to have a friend call out the numbers as you type in programs.
Next, turn the computer off, then on again, and type in:
POKE 43,1: POKE 44,18: POKE 18*256,0: NEW
to move BASIC up out of the way. After this, LOAD "SUPERMON DATA",8 (or ,1
for tape). Run the Supermon Data program (which takes about 25 seconds), then
SAVE "SUPERMON 64",8 (or ,1 for tape). Now, "Supermon 64" becomes the pri
mary version of Supermon; just load and run it.
Supermon Data
For mistake-proof program entry, be sure to use the "Automatic Proofreader," Appendix C.
596
Appendix R
600
Appendix R
601
Appendix R
602
Index
603
CLV instruction 301 diet calculator 990-91
CMD statement 24, 543, 548 DIM statement 27-28, 101, 147
CMP instruction 301-2 DIR Commodore disk utility 511
COLOR BASIC extension 171 direct access commands, disk 523-26
color RAM 93, 375-79, 396 direct access files, disk 495-96
changing with ML 370-71 direct mode 11, 151
"Color Ram Motion7' program 371 disk 3, 487-529
"Combine lines" program 198-99 command summary 508-9
commas, INPUT and 155-56 copying 490-92
commercial software 131-34 device number, changing 514-15
Commodore 64, different models of 113 directory 489, 517-18, 51-23
Commodore ASCII codes 576-79 error channel 493-94
Commodore 64 105-36 errors, 498
"Compare ROM" program 258-59 file handling, ML 526-29
comparisons, ML 207-8 formatting 488-89
COMPILE BASIC extension 171-72 hardware notes 512
CompuServe 553 ID 488-89
COMPUTE's Machine Language Routines for loading program 490
the Commodore 64 167 message summary 510
COMPUTE'S Second Book of Machine Language scratching file 490
237 saving program 489-90
"Computed GOTO and GOSUB" program troubleshooting 509-11
173 ROM 515-16
CONT command 25 DISK ADDR CHANGE Commodore disk
control port 9-10, 120, 533-40 utility 511
controllers 137 "Disk Merge" program 181
copy protection, tape 481-82 disk utility programs, Commodore 511-12
COPY/ALL Commodore disk utility 511 diskette storage 513-14
COS function 25 diskettes, physical characteristics of 512-13
CPX instruction 303 disk, commercial software and 133
CPY instruction 304 DISPLAY T&S Commodore disk utility 511
CRUNCH BASIC extension 173 DOKE BASIC extension 174
cursor 248 DOS 5.1 Commodore disk utility 511
D (Disassemble) Monitor command 203, 230 dot-matrix printers 546
daisywheel printers 546 "Double Density" program 369-70
data files, tape 468-70 "Drawing Lines" program 400-02
"Data Maker" program 278 DUMP BASIC extension 174-76
DATA statement 26, 97, 278, 565 editing BASIC 11-12
data structures 97-99 editor/assemblers 238
Datassette 465-66, 471-73 END statement 2-29
troubleshooting 472 end-of-tape marker 466
"Date Validator" program 99-100 envelope, SID 433-35, 437
"Day of the Week Calculator" program 100 EOR instruction 221-22, 307
"Days Between Two Dates" program 100 EPROM (erasable programmable read only
debugging BASIC programs 87 memory) 109, 135,36
DEC instruction 304-5 "Equation Solver" program 91-92
decay (sound) 433-34 error message subroutine 85
decimal arithmetic 224 error messages, BASIC 69-73
"Decimal Input" program 88-89 errors, in ML programming 224-25
decimal notation 106-8 EXP function 17, 29
DEEK BASIC extension 174 expansion boards 135
DEF FN statement 26-27 exponential notation 15
"Delete" program 174 expressions, BASIC 17-18
DEX instruction 305-6 extended background color mode 379-80
DEY instruction 306 "Extended Background Color Mode" program
device number 12, 166, 514-15, 586 380
"Dice" program 97 "Fast Step" program 165
604
1540 model disk drive 487 interfaces 134-35
1541 model disk drive 487 interrupt register, VIC II 415-16
files 12, 468-70, 494, 495-96, 499, 500, interrupts 127, 269-73, 415-21
502-8, 509, 518-19, 526-29 "Investigating the CIA" program 126-27
filters, sound 435-36 INX instruction 206, 309
"Finding ML or Memory Dump LOAD INY instruction 309
Address" program 500 IOINIT Kernal routine 260
flags 110 IRQ interrupt 415, 466
floating point accumulator 333 "IRQ Polling" program 415
flow chart 80-81 IRQ vector 213-14, 269
FOR-NEXT structure 29-31, 87 "Jesu Joy" program 450-51
forced-load address 466-67 jiffy clock 156
"Fraction Maker" program 92 JMP instruction 310
FRE function 17, 31-32 joystick 9, 127, 402, 533-35
"Froggie Graphics" program 363 joystick port. See control port
full duplex communication 555 JSR instruction 213, 311
function keys 157-59 "Kaleidoscope" program 363
"Function Keys" program 157-58 Kernal ROM 333
functions, BASIC 17, 146 Kernal routines 241-49, 354-56
G (Go) Monitor command 203, 230 I/O errors 241-42
game paddles 535-38 new languages and 259-61
gate bit 440 keyboard 10-11, 159-65, 457, 248
"General Program Copier" 492 decoding 160-61
GET statement 32, 89 redefinition 164
GET# statement 32-33, 470, 497 reading 159-60
GETIN Kernal routine 248 repeat keys 164
GOSUB statement 34-35, 84 keyboard buffer 155, 159
GOSUB-RETURN structure 87 keys 10-11
GO dummy statement 33 intercepting 162-63
GOTO statement 35, 84 keywords, BASIC 11-12, 16, 19-73
graphics 3, 359-423 L (Load ML) Monitor command 231
cross-reference 368-69 labels 235
double-density 369-70 LADS assembler 237
"Graphics Screen Dump" program 549-50 languages, new 259-61
H (Hunt Memory) Monitor command 231 LDA instruction 312
half duplex communication 555 LDX instruction 313
"Handling Relative Files" program 507 LDY instruction 313-14
hardware schematic 112-13 LEFT$ function 17, 39, 92
hardware vectors 356 "Legible list" program 177-78
header, tape 481-82 LEN function 40
hex-to-decimal conversion subroutine 84 LET statement 40-41
hex-to-decimal conversion, ML 253-54 "LET Vector Demo" program 267
hexadecimal notation 5,107-8 light pen 10, 127, 417, 538-40
"Histogram Demo" program 367 "Line Plotter" program 551-52
"Horizontal Motion" program 423 linked lines, BASIC 140-41
hybrid programs 279-80 linking devices 135
I (Interpret Memory) Monitor command 231 LIST BASIC extension 176-80
IEEE communication 134-35 LIST statement 41
IF-THEN statement 35-36 LIST Kernal routine 262
index 205-6 listing conventions 4-5
INC instruction 308 literals 15
input buffer 154-55 "Load Anywhere" program 479
INPUT statement 36-38 LOAD command 42-43, 465-68
INPUT# statement 38, 87, 470, 497 loading 151-52
INT function 39 LOG function 17, 43-44
integer variables 16 logical expression 17
intercepting keys 162-63 logical file number 12
605
logical line 12 number storage, tape and 470
loops, ML 219-21, 206-7 numbers 15, 152-54
LSR instruction 221, 314-15 numeric expression 17
M (Memory Display) Monitor command 203, object code 233-34
231 "Oh, Zeros" program 93
machine language. See ML "OLD" program 181-82
"Machine Language Sort for String Arrays" ON statement 46-47
program 193-94 ONERR BASIC extension 182
MAE editor/assembler 238 opcode, 6510 chip 235, 287-329, 588-90
making BASIC run faster 100-101 OPEN statement 12, 47-48
"Maze Demo" program 365 control register 559-60
memory commands, disk 525-26 disk 496
memory configuration 114-18 ML 526-29
memory locations, equivalent 595 printers 543
memory map 109-11, 333-56 RS-232 channel and 559
bitmap merge BASIC extension 180 tape 469, 470
MOD BASIC extension 181 operand 235
MICROMON-64" monitor 226, 228 operators 16
"MicroScope" program 111 OR operator 48-49
MID$ function 17, 44, 92, 93 ORA instruction 221-22, 315-16
ML 3, 105, 203-38 "Organ Keyboard" program 457-49
graphics and 365-75 originate mode (modem) 553
relocating 280-83 P (Printer Disassembly) Monitor command 232
tape routines 470-71, 478-81 "Packing Numbers" program 93
techniques 216-21 paddles 10
"ML Autorun" program 501-2 pattern matching, filename 509
"ML Character Screen Dump" program 550 PAUSE BASIC extension 182-83
"ML Clock" program 128-29 "Payroll Analyser" program 91
"ML File Reader" program 528 PEEK function 18, 49
ML joystick interpreter 535 PERFORMANCE TEST Commodore disk
ML joystick routine 535 utility 511-12
"ML light Pen Draw" program 539-40 PET 64 computer 4
"ML Paddle Reader" program 537 "Pet Your 64" program 260-61
"ML Read-Only" program 447-48 PHA instruction 213, 316-17
"ML Relocator" program 283 PHP instruction 317
"ML Reverse" program 366-67 physical line 12
modems 137, 553-57 PLA (programmed logic array) 109, 113
monitors 112, 203-8, 226-33, 234, 543 PLA instruction 213, 318
command dictionary 229-33 PLOT Kernal routine 248
"Mosaic" program 391 "Plotter Demo" program 552
"Multicolor Bitmap Draw Routine" program plotters 551-52
404-5 plotting 367-69
multicolor mode 376-79 PLP instruction 318-19
"Multicolor Mode" program 378-79 pointers 110
"Music Program" 451-47 POKE statement 50
music theory 448-49 BASIC graphics and 364-65
N (Number Adjuster) Monitor command 232 "POKEing BASIC to the Screen" program 142
NEW command 44-45 POP BASIC extension 183
NEXT statement 45 "POP" program 183-84
NMI (Non-Maskable Interrupt) 124, 213-14, POS function 50-51
269-73, 415
PRINT statement 51-52, 87, 360-63
"NMI Demo" program 271 PRINT BASIC extension 184
noise 431-32
PRINT USING BASIC extension 184-87
NOP instruction 315
"PRINT USING Demo" program 186
NOT operator 45-46
"PRINT USING" program 184-85
"Number Guessing Game" program 81-82
PRINT# statement 52-53, 87, 470, 496-97,
"Number of Blocks Free" program 522 543-45, 548
606
printers 109, 543-51 volume 443
Commodore 543-45 relative files, disk 495-96, 506-8, 518-19
control characters 547-48 release (sound) 434
non-Commodore 545-7 "Relocating Program Generator", program
presence 550-51 283-84
spooling 551 REM BASIC extension 188-89
program chaining 151-52, 467 REM statement 54, 84, 101
program conversion 593-95 ML and 278
program counter 213 RENUMBER BASIC extension 19-91
program files, disk 494, 499, 500 "Renumber" program 190
program mode 11, 151 RESET BASIC extension 191
program recovery 129-31 reset vector 213-14
programming aids, music 462 resetting the computer 129-31
programming standards 83-86 reset, hardware 130-31
"Programming Sprites with User-Defined RESTORE statement 55
Characters" program 41-11 RETURN statement 55-56
programs (ROM) 110 RF modulator output jack 9
PROM (Programmable Read Only Memory) "Rhythm Box" program 459-61
109 RIGHTS function 17, 56, 92
pseudo-opcode 234, 235 ring modulation 432-33, 440
pulse wave 430-31 RND function 56-57
quasi-opcodes 591-92 ROL instruction 221, 319
"Queens" program 96-97 ROM 108-9
R (Register Display) Monitor command 232 ML manipulation 257-59
Rabbit tape operating system 473 upgrading 258-59
RAM 108, 109 ROM cartridge 109
free areas 165-66 "ROM RAM" program 257
in disk drive 487-88 ROM upgrade" program 259
ML manipulation 256-59 ROR instruction 221, 320
RAM data storage 99 "Rounding" program 89-90
BASIC and 277 RS-232 interface 557-61
RAMTAS Kernal routine 260 OPEN and 559
randomizing 96-97 pin functions 558
ML and 254-55 RS-232 processing 9, 124, 134, 545-46
range of byte, testing 219 RTI instruction 213, 320-21
raster interrupt 417-19 RTS instruction 203, 321-22
"Reading and Displaying a Sequential File" RUN command 57-58
program 503-4 RUN/STOP 129-30
READ statement 53-54 RUN/STOP and RUN/STOP-RESTORE,
"Reading Bytes from Tape" program 471 disabling 156-57
"Reading Paddle 2" program 537 RUN/STOP-RESTORE 129-30
"Reading Programs Byte by Byte" program RUN/STOP-RESTORE, RAM BASIC and 260
499 S (Save ML) Monitor command 232
"Reading the Bam" program 522 "Save Anywhere" program 480-81
"Reading the Directory Track" program SAVE command. 58-59, 465-68
522-23 saving 151-52
RECONFIGURE BASIC extension 187-88 sawtooth wave 430
"Reconfigure" program 187-88 SBC instruction 322-23
register 333 screen 166-67, 247-48
registers, SID 438-41 screen character codes 580-81
control 440-^1 screen color codes 574
envelope shape 441, 442 screen color memory table 573
filter 443 "Screen Dump" program 175
frequency control 439 screen location table 572
pulse width 439 screen RAM 396
read-only 444 "Screen Save and Load" program 170-71
voice 438 "Scroll Down" program 372
607
"Scroll Left" program 373 interrupts and 416-20
"Scroll Right" program 373 mapping 409-11
"Scroll Up" program 372 modes 407-8
scrolling 372-75 positioning 406-7
smooth 380-82 priority 408-9
search algorithm, relative files 495 SQR function 17, 60-61
"Search" program 191-92 square wave. See pulse wave
searching 95 ST reserved variable 16, 61, 470, 498
SEC instruction 323 STA instruction 325
secondary address 12 stack 110-11, 213
sector 513-14, 516-17 statements 18
SED instruction 324 status register 211-13
SEI instruction 324-25 STOP statement 62
sequential files, disk 495, 496, 502-6 storage in memory, BASIC 139-51
serial port 9, 120, 487, 561-62 accuracy of numbers 152-54
series calculations 256 arrays 146-48
SET BASIC extension 192 BASIC bytes 143
SETLFS Kernal routine 527 calculating 139-40, 150-51
SGN function 59 floating-point 153
"Shell-Metzner Sort" program 192 garbage collection and 148-50
shift and rotate instructions 221 string 148-49
"Shuffler" program 95-96 variables 144-46
shuffling 95-96 STR$ function 16, 62-63
SID chip 3, 109, 112, 259, 427-35, 437-48, "String and Integer Input" program 88
535, 585 string expression 17
side sectors 518-19 string handling 92-94
"Simple Design" program 362 string variables 16, 145-46
"Simple Menu" program 86 STX instruction 326
"Simple ML Output" program 366 STY instruction 326
"Simple POKE" program 364 subroutines, ML 207-8
"Simple PRINT Demo" program 362 "SUPERMON" monitor 203, 226-28, 596-602
"Simple SIDMON" program 444-46 sustain (sound) 434
"Simpler Shuffler" program 96 SX-64 computer 4
SIN function 59-60 SYS statement 63
sine waves 427-29 systems 11, 79, 82-83
"Single-Key Keyword Entry" program 163-64 T (Transfer Memory) Monitor command 232
1650 model AUTOMODEM 553 TAB( function 64
1600 model VICMODEM 553 tables (ROM) 110
6502 chip 108 TAN function 64
6510 chip 108, 287-329 tape 3, 127, 465-84
"64 Terminal Program" 556-57 commercial software and 133-34
"Smooth Scroll" program 382 headers 475-77
"Sorted Directory" program 520-21 type to purchase 472-73
sorting 95, 192-95 tape buffer 156
sound 3, 427-62 "Tape Directory" program 476-77
source code 233 tape recorders, non-Commodore 473
SPC( output function 60 tape recorder, programming 474-76
speech synthesis 437 TAX instruction 326-27, 205
"sprite collision" program 409 TAY instruction 327
"Sprite Editor" program 412-15 Teletype printers 546
"Sprite-Data Collision" program 416-17 terminal software 553
sprites 405-15 thermal and spark printers 546
collision 409 "Thirty-Two Sprites" program 419-21
defining 406 TI reserved variable 16, 64-65
disabling 406 TI$ reserved variable 64-65
enabling 406 timers 127-28
expansion 408 timing 215-16
608
'Tournament Sort" program 192-93 "USR Demonstration" program 252-53
"Trace" program 195-96 USR function 66, 251-53
track 513-14, 516-17 V (Verify) Monitor command 232
triangle wave 429 VAL function 16, 66
TSX instruction 327-28 "Variable Dump" program 175-76
tunes 437 variables, BASIC 11, 15-16, 101, 144-46, 157
turnkey systems 118 "VARPTR" program 199-200, 250
TV 111-12 vectors 110, 262-69
two-byte operations 217-19 VERIFY command 66-67, 467
twos complement arithmetic 222-23 "Vertical Motion" program 422
TXA instruction 328 VIC II chip 3, 4, 100, 109, 112, 113-14, 259,
TXS instruction 328-29 375, 383, 384-87, 391, 397, 405, 538,
TYA instruction 329 582-84
typewriters, modified 546 VIEW BAM Commodore disk utility 512
typing in programs 565-68 voices, SID 438, 448
U commands, disk 523-24 wait statement 67-68, 534
unclosed files 509 warm start 260
UNCRUNCH BASIC extension 173 warning light, disk 498-99
UNLIST BASIC extension 196-99 wedges 263-65
user port 9, 105, 120 "Window List" program 176-77
user-definable characters 376-77, 383-95, 421 "Wordscore" program 94
"Using a Quote Before Input" program 155 "Writing Bytes to Tape" program 471
"Using Block Read" program 524 X (Exit to BASIC) Monitor command 233
"Using Block Write" program 524-25 x register 205, 206-7
"Using Files" program 470 zero flag 207
"Using the Input Buffer" program 155 zero page 209, 213,
609
To order your copy of Programming The Commodore 64
Disk, call our toll-free US order line: 1-800-334-0868 (in NC
call 919-275-9809) or send your prepaid order to:
Subtotal $_
□ Payment enclosed
Charge a Visa □ MasterCard □ American Express
Signature
Name
Address
4595073
If you've enjoyed the articles in this book, you'll find
the same style and quality in every monthly issue of
COMPUTEI's Gazette for Commodore.
COMPUTED Gazette
P.O. Box 5058
Greensboro, NC 27403
My computer is:
□ Commodore 64 □ VIC-20 □ Other.
Name
Address
City State Zip
Country
Name_
Addres