Compute's Programming the Commodore 64, The Definitive ... - Home
Compute's Programming the Commodore 64, The Definitive ... - Home
Compute's Programming the Commodore 64, The Definitive ... - Home
Create successful ePaper yourself
Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.
Contents<br />
Foreword<br />
vii<br />
1. About This Book l<br />
Introduction 3<br />
<strong>Programming</strong> Your <strong>64</strong> 3<br />
Conventional Terms 4<br />
Acknowledgments 5<br />
2. Getting to Know <strong>the</strong> <strong>64</strong> 7<br />
<strong>The</strong> <strong>64</strong>'s Connectors 9<br />
<strong>The</strong> Keyboard 10<br />
Editing BASIC on <strong>the</strong> <strong>64</strong> 11<br />
3. BASIC Reference Guide 13<br />
BASIC Syntax 15<br />
BASIC Keyword Dictionary 19<br />
BASIC Error Message Dictionary 69<br />
4. Effective <strong>Programming</strong> in BASIC 75<br />
How to Become Fluent in BASIC , 77<br />
Programs, Systems, and People 77<br />
Program Design 79<br />
System Design 82<br />
Serious and Less Serious <strong>Programming</strong> 83<br />
Debugging BASIC Programs 87<br />
Examples in BASIC 88<br />
Making BASIC Run Faster 100<br />
5. <strong>Commodore</strong> <strong>64</strong> Architecture 103<br />
Introductory Hardware Topics 105<br />
<strong>The</strong> <strong>64</strong>'s Memory Configurations 114<br />
<strong>Commodore</strong> <strong>64</strong> Ports 118<br />
<strong>Programming</strong> <strong>the</strong> CIAs 121<br />
Program Recovery and Resetting 129<br />
Commercial Software and Hardware 131<br />
6. Advanced BASIC 137<br />
How BASIC Is Stored in Memory 139<br />
Special Locations and Features of BASIC 154<br />
Dictionary of Extensions to BASIC 167<br />
7. 6510 Machine Language 201<br />
Introduction to 6510 ML <strong>Programming</strong> 203<br />
Description of <strong>the</strong> 6510 Chip 208<br />
6510 ML Techniques 216<br />
Monitors for <strong>the</strong> <strong>64</strong> 226
Monitor Command Dictionary 229<br />
Assemblers for <strong>the</strong> <strong>64</strong> 233<br />
8. ML Methods Specific to <strong>the</strong> <strong>64</strong> 239<br />
Kernal Routines 241<br />
BASIC ROM Routines 250<br />
Using RAM Under ROM 256<br />
Modifying BASIC 262<br />
Vectors 266<br />
Interrupts 269<br />
9. Mixing BASIC with Machine Language 275<br />
RAM Available for ML Routines 277<br />
Combining BASIC and ML 278<br />
Relocating ML 280<br />
10. Vocabulary of <strong>the</strong> 6510 Chip 285<br />
<strong>The</strong> 6510 Instruction Set 288<br />
11. <strong>64</strong> ROM Guide 331<br />
<strong>64</strong> Memory Map 333<br />
12. Graphics 357<br />
Graphics with BASIC 359<br />
Graphics with Machine Language 365<br />
<strong>The</strong> VIC-II Chip 375<br />
User-Defined Characters 383<br />
Bitmapped Graphics 396<br />
Sprites 405<br />
13. Sound 425<br />
Sound Waves: Analysis and Syn<strong>the</strong>sis 427<br />
<strong>The</strong> SID Chip 437<br />
Music <strong>The</strong>ory 448<br />
Sound Demonstrations 450<br />
14. Tape Storage 463<br />
Loading and Saving BASIC Programs with Tape 465<br />
Handling Tape Data Files 468<br />
Loading and Saving Machine Language 470<br />
Tape Hardware Notes 471<br />
Advanced Tape <strong>Programming</strong> 474<br />
Copy Protection for Tape 481<br />
15. Using Disk Storage 485<br />
Introduction to Disk Storage 487<br />
Basic Disk Commands 488<br />
Handling Disk Files with BASIC 494<br />
Summary of Disk Commands and Messages 508
<strong>Commodore</strong> Utility Programs 511<br />
Hardware Notes 512<br />
Disk Data Storage 516<br />
Machine Language Disk <strong>Programming</strong> 526<br />
16. <strong>The</strong> Control Ports 531<br />
Joysticks 533<br />
Paddles and O<strong>the</strong>r Analog Devices 535<br />
17. Major Peripherals 541<br />
Printers 543<br />
Plotters 551<br />
Modems 553<br />
<strong>The</strong> RS-232 Interface 557<br />
<strong>The</strong> Serial Port 561<br />
Appendices 563<br />
A. A Beginner's Guide to Typing In Programs 565<br />
B. How to Type In Programs 567<br />
C. <strong>The</strong> Automatic Proofreader<br />
Charles Brannon 569<br />
D. Screen Location Table 572<br />
E. Screen Color Memory Table 573<br />
F. Screen Color Codes 574<br />
G. ASCII Codes 575<br />
H. <strong>Commodore</strong> ASCII Codes 576<br />
I. Screen Character Codes 580<br />
J. <strong>The</strong> VIC-II Chip 582<br />
K. <strong>The</strong> SID Chip 585<br />
L. Device Numbers 586<br />
M. Decimal-Hexadecimal Conversion Table 587<br />
N, Opcodes in Detail 588<br />
O. Table of 6502/6510 Opcodes 590<br />
P, 6502/6510 Quasi-Opcodes 591<br />
Q. Converting <strong>Commodore</strong> <strong>64</strong>, VIC-20, and CBM Programs 593<br />
R. Supermon <strong>64</strong> 596<br />
Index 603<br />
Disk Coupon 61T
Foreword<br />
<strong>Programming</strong> <strong>the</strong> <strong>Commodore</strong> <strong>64</strong> is <strong>the</strong> definitive guide. It covers virtually every<br />
aspect of <strong>the</strong> <strong>Commodore</strong> <strong>64</strong>, from simple BASIC commands to complex machine<br />
language techniques. Every explanation is written in depth and with clarity. <strong>The</strong> re<br />
sult is a comprehensive, easy-to-understand reference that thoroughly explains <strong>the</strong><br />
<strong>64</strong>'s capabilities.<br />
If you program in BASIC, you'll find <strong>the</strong> detailed, annotated listings of every<br />
BASIC command a tremendous aid. And if you're writing in machine language,<br />
you'll be especially interested in <strong>the</strong> ROM maps and listings of Kernal routines. No<br />
matter what your experience with <strong>the</strong> <strong>Commodore</strong> <strong>64</strong>, you'll find <strong>the</strong> numerous pro<br />
gram examples both useful and informative.<br />
Beginning with a brief introduction to <strong>Commodore</strong> BASIC and BASIC program<br />
ming, this book goes on to discussions of more advanced programming, including<br />
sophisticated machine language techniques. Specialized sound and graphics applica<br />
tions are included as well. Complete chapters are devoted to disk and tape storage,<br />
and to <strong>the</strong> selection and use of various peripheral devices.<br />
Author Raeto Collin West, one of <strong>the</strong> world's foremost authorities on Com<br />
modore computers, has distilled years of experience into <strong>Programming</strong> <strong>the</strong> <strong>Commodore</strong><br />
<strong>64</strong>. You'll discover new information on each page.<br />
<strong>The</strong> author has included scores of practical programs to demonstrate many of<br />
<strong>the</strong> techniques discussed. To help you enter <strong>the</strong> programs correctly, we've included<br />
"<strong>The</strong> Automatic Proofreader," an error-checking program. As a convenience, COM<br />
PUTE! Publications is also making available for purchase a disk that contains most of<br />
<strong>the</strong> significant programs from this book. To order a disk, use <strong>the</strong> coupon in <strong>the</strong> back<br />
or call 800-334-0868 (in North Carolina 919-275-9809).<br />
This is <strong>the</strong> first book to thoroughly cover every aspect of <strong>64</strong> programming. It's<br />
certain to become an indispensable work for any <strong>Commodore</strong> <strong>64</strong> owner.<br />
vn
Chapter 1<br />
About This<br />
Book<br />
Introduction<br />
<strong>Programming</strong> Your <strong>64</strong><br />
Conventional Terms<br />
Acknowledgments
Chapter 1<br />
About This<br />
Book<br />
Introduction<br />
<strong>The</strong> two main objectives of this book are to teach competent programming on <strong>the</strong><br />
<strong>Commodore</strong> <strong>64</strong>, and to provide a comprehensive reference book for people wanting<br />
quick, accurate answers to questions about <strong>the</strong> <strong>64</strong>. <strong>The</strong>se two goals are difficult<br />
enough to achieve. For example, while virtually everyone begins with BASIC and<br />
progresses to machine language (ML), it is often desirable to use both ML and<br />
BASIC in examples, which means comparative newcomers to <strong>the</strong> <strong>64</strong> find <strong>the</strong>mselves<br />
skipping sections of temporarily difficult text. It is practically impossible to arrange<br />
<strong>the</strong> material so that everything falls into a natural sequence for all readers, because<br />
many of <strong>the</strong> chapter headings <strong>the</strong>mselves can't be understood properly without<br />
some knowledge of <strong>the</strong> machine's structure.<br />
This book explains BASIC and ML in sequence from simple to complex, ending<br />
with a chapter on mixing ML with BASIC, an efficient <strong>64</strong> programming method.<br />
<strong>The</strong>se chapters are interspersed with machine organization details and are followed<br />
by in-depth examinations of major topics—sound, graphics, tape, and so on.<br />
For <strong>the</strong>se reasons, <strong>the</strong> text contains two kinds of programs. First, <strong>the</strong>re are very<br />
short routines, intended to be typed in quickly (and <strong>the</strong>refore with little chance of er<br />
ror). Second, <strong>the</strong>re are longer, more practical programs, which use graphics, sound,<br />
tape, disks, and all <strong>the</strong> o<strong>the</strong>r features of <strong>the</strong> <strong>64</strong>. <strong>The</strong> shorter, example programs cover<br />
how BASIC commands are used, how special features work (notably <strong>the</strong> VIC and<br />
SID chips), and how to use ML. Many useful routines are included, and <strong>the</strong>se can be<br />
used successfully without any knowledge of <strong>the</strong>ir internal operation. Many readers will<br />
thus be able to acquire BASIC and ML experience painlessly as <strong>the</strong>y use this book.<br />
<strong>Programming</strong> <strong>the</strong> <strong>Commodore</strong> <strong>64</strong> is one of a set of three books, which also in<br />
cludes <strong>Programming</strong> <strong>the</strong> VIC and <strong>the</strong> earlier <strong>Programming</strong> <strong>the</strong> PET/CBM. <strong>The</strong> books<br />
have been written entirely independently of <strong>Commodore</strong>; <strong>the</strong>y contain criticisms,<br />
comments, hints, and a great deal of o<strong>the</strong>rwise unpublished information.<br />
<strong>Programming</strong> Your <strong>64</strong><br />
<strong>The</strong> <strong>64</strong> is one of <strong>the</strong> world's most popular microcomputers; like all big-selling<br />
computers it is both ra<strong>the</strong>r inexpensive and ra<strong>the</strong>r easy to use. But many owners<br />
have found that however easy using o<strong>the</strong>r people's programs may be, writing <strong>the</strong>ir<br />
own programs for <strong>the</strong> <strong>64</strong> is not so simple. Reliable information has been difficult to<br />
find—until now.<br />
<strong>Programming</strong> <strong>the</strong> <strong>Commodore</strong> <strong>64</strong> shows you how to plan and write BASIC pro<br />
grams, how to move programs from tape to disk to save time, and how to play mu<br />
sic while a program runs. It also explains how to program <strong>the</strong> function keys, round<br />
numbers to two places, use high-resolution graphics, design and store your own<br />
graphics characters, display a screenful of sprites, make <strong>the</strong> screen scroll smoothly,<br />
and save sections of memory to tape. This is only a small sample of problems which<br />
have puzzled many <strong>64</strong> users. All <strong>the</strong>se and more are comprehensively discussed<br />
here.
About This Book<br />
<strong>The</strong> <strong>64</strong> is at times a difficult machine to program, in spite of what you may have<br />
heard. But programming is easier if you have a good overall understanding of <strong>the</strong><br />
machine, and <strong>Programming</strong> <strong>the</strong> <strong>Commodore</strong> <strong>64</strong> attempts to generalize results ra<strong>the</strong>r<br />
than give isolated facts. For example, <strong>the</strong> way <strong>the</strong> VIC chip determines what kind of<br />
graphics to display is crucially important to understanding <strong>the</strong> system, and <strong>the</strong>re is a<br />
handy table to illustrate this.<br />
This book is just above <strong>the</strong> introductory level. <strong>The</strong>re's not enough room to cover<br />
<strong>the</strong> elementary topics (which are often better learned directly at <strong>the</strong> keyboard or<br />
from a friend who knows <strong>the</strong> machine) and still provide <strong>the</strong> information you need<br />
on advanced topics. But prior knowledge of <strong>Commodore</strong> systems is not essential, so<br />
anyone with intelligence and some microcomputer experience ought to be able to<br />
grasp <strong>the</strong> fundamentals of <strong>the</strong> <strong>64</strong> fairly easily.<br />
Several versions of <strong>the</strong> <strong>64</strong> exist: <strong>the</strong> repackaged SX-<strong>64</strong>, <strong>the</strong> PET <strong>64</strong> (which has<br />
no sound chip and only monochrome graphics), and <strong>64</strong>s with slightly different<br />
ROMs. As Chapter 8 explains, <strong>the</strong>se computers run software similarly, but not ex<br />
actly alike. Most of <strong>the</strong> book is applicable to all <strong>the</strong>se machines, but emphasis is on<br />
<strong>the</strong> more common models.<br />
<strong>The</strong> programs have been tested and will almost always work without difficulty.<br />
If <strong>the</strong>re are problems, a program may have been entered incorrectly, or <strong>the</strong> soft<br />
nature of <strong>the</strong> <strong>64</strong> may be to blame—<strong>the</strong>re are many special memory locations in <strong>the</strong><br />
<strong>64</strong>, any one of which can cause odd results if altered. <strong>The</strong> first thing to do when a<br />
program will not run properly is to check <strong>the</strong> program carefully. If that doesn't work,<br />
it's usually easiest to save <strong>the</strong> program, turn <strong>the</strong> <strong>64</strong> off, <strong>the</strong>n back on, reload <strong>the</strong> pro<br />
gram, and try again. Some of <strong>the</strong> programs in this book use <strong>the</strong> "Automatic Proof<br />
reader," Appendix C, which allows you to quickly and easily check each line you<br />
have entered for accuracy.<br />
Information with <strong>the</strong> widest appeal—BASIC, graphics, music, and full use of <strong>the</strong><br />
<strong>64</strong>'s RAM capacity—is documented fully. However, minority interests are not ex<br />
cluded. <strong>Programming</strong> <strong>the</strong> <strong>Commodore</strong> <strong>64</strong> doesn't gloss over difficulties and evade im<br />
portant topics. Many people have gone to great lengths to check <strong>the</strong> information in<br />
this book, for it is intended to be reliable. Never<strong>the</strong>less, <strong>the</strong>re are certain to be errors,<br />
and for any resulting inconvenience or bafflement, we apologize in advance.<br />
Conventional Terms<br />
<strong>The</strong> special <strong>Commodore</strong> logo key (located at <strong>the</strong> bottom left of <strong>the</strong> <strong>64</strong>'s keyboard)<br />
will be called <strong>the</strong> <strong>Commodore</strong> key. Program listings and short examples, like SYS<br />
<strong>64</strong>738, will usually be in capitals to mimic <strong>the</strong>ir appearance on <strong>the</strong> screen and print<br />
ers of <strong>the</strong> <strong>64</strong> in its uppercase mode. This is <strong>the</strong> mode <strong>the</strong> <strong>64</strong> is in when you turn <strong>the</strong><br />
power on. Text can, of course, appear in lowercase mode, usually after pressing<br />
SHIFT-<strong>Commodore</strong> key. (Don't type <strong>the</strong> hyphen; just hold down <strong>the</strong> SHIFT key and<br />
press <strong>the</strong> <strong>Commodore</strong> key.) In ei<strong>the</strong>r case, programs are mostly entered using<br />
unSHIFTed keys. Named keys (CLR/HOME, SHIFT, CTRL, fl, and so on) are gen<br />
erally referred to as <strong>the</strong>y appear on <strong>the</strong> keyboard of <strong>the</strong> <strong>64</strong>.<br />
Some BASIC listings have been made with a program which prints <strong>the</strong> screen<br />
editing commands, colors, and o<strong>the</strong>r special characters, such as {HOME}, {CYN},<br />
{Fl}, and so on, in place of <strong>the</strong> less readable <strong>64</strong> characters. With apologies to people
^ \<br />
About This<br />
Book<br />
who can spell, <strong>the</strong> book uses <strong>the</strong> spelling Kernal for <strong>the</strong> ROM routines which handle<br />
<strong>the</strong> screen, keyboard, and input/output devices (this is <strong>the</strong> spelling <strong>Commodore</strong><br />
uses). This book also uses ML as a handy abbreviation for machine language.<br />
Hexadecimal numbers are represented here with a leading dollar sign ($). If you<br />
don't understand hexadecimal notation, read Chapter 6. In accordance with 6502<br />
convention, <strong>the</strong> number symbol (#) indicates a value, not an address, so that LDA<br />
#$00 denotes <strong>the</strong> command to load <strong>the</strong> accumulator with <strong>the</strong> number 0, while LDA<br />
$00 loads <strong>the</strong> accumulator with <strong>the</strong> value in location 0.<br />
Many <strong>64</strong> BASIC programs begin with a few commands to change color from <strong>the</strong><br />
usual light blue characters on a dark blue background. <strong>The</strong> following line sets a<br />
green background, white border, and black characters:<br />
POKE 53281,5: POKE 53280,1: POKE <strong>64</strong>6,0<br />
Some of <strong>the</strong> demonstration programs include this line; o<strong>the</strong>rs assume that CTRL-<br />
WHITE or some similar command has already been used to improve clarity.<br />
Acknowledgments<br />
Several chapters, notably those on sound and graphics, are partially <strong>the</strong> work of<br />
Marcus West. Additional hardware information has been provided by Rod Eva of Y2<br />
Computers, Watford, U.K. COMPUTE! Publications, Inc. in <strong>the</strong> U.S., TPUG (Toronto<br />
PET Users Group) of Canada, and ICPUG (Independent <strong>Commodore</strong> Products Users<br />
Group) of <strong>the</strong> U.K. have provided information.
Chapter 2<br />
Getting to Know<br />
<strong>the</strong> <strong>64</strong><br />
<strong>The</strong> <strong>64</strong>'s Connectors<br />
<strong>The</strong> Keyboard<br />
Editing BASIC on <strong>the</strong> <strong>64</strong>
Chapter 2<br />
Getting to Know <strong>the</strong> <strong>64</strong><br />
<strong>Commodore</strong> has been making computers for a decade or so, and became a house<br />
hold word with <strong>the</strong> introduction of <strong>the</strong> low-priced VIC-20, followed more recently in<br />
<strong>the</strong> early 1980s by <strong>the</strong> <strong>Commodore</strong> <strong>64</strong>. Both machines proved remarkably successful,<br />
far outselling o<strong>the</strong>r <strong>Commodore</strong> Business Machine (CBM) computers in volume.<br />
CBM's earlier experience enabled a range of appropriately priced peripherals (tape<br />
drives, disk drives, printers, modems) to be produced at about <strong>the</strong> same time. All<br />
CBM machines have strong resemblances, and straightforward BASIC programs<br />
which run on one CBM machine are likely to run on o<strong>the</strong>rs, too. But programs of<br />
any complexity will generally run only on <strong>the</strong> machine for which <strong>the</strong>y were written.<br />
<strong>The</strong> <strong>64</strong>'s Connectors<br />
<strong>The</strong> back panel of <strong>the</strong> <strong>64</strong> has <strong>the</strong>se features (left to right viewed from <strong>the</strong> back):<br />
<strong>The</strong> cartridge socket is where ROM cartridges are plugged in. (Only those de<br />
signed for <strong>the</strong> <strong>64</strong> will work.) Be sure to insert cartridges correctly. Chapter 5 has fur<br />
<strong>the</strong>r information. Some o<strong>the</strong>r peripherals are designed to plug into this socket,<br />
including <strong>the</strong> CP/M cartridge and <strong>the</strong> Magic Voice speech module.<br />
<strong>The</strong> channel selector switch selects channel 3 or 4 on TVs in <strong>the</strong> U.S. (Com<br />
modore <strong>64</strong>s for PAL-type TVs in Europe and elsewhere don't have this switch.)<br />
<strong>The</strong> RF modulator output jack provides a combined video and audio signal<br />
that can be used directly by a standard television. <strong>The</strong> RF (radio frequency) modu<br />
lator inside <strong>the</strong> <strong>64</strong> performs <strong>the</strong> function of a tiny TV station, converting (modulat<br />
ing) <strong>the</strong> computer's video and audio signals into a signal that can be received via <strong>the</strong><br />
TV's antenna connectors.<br />
<strong>The</strong> audio-video socket provides high-quality output from <strong>the</strong> <strong>64</strong>'s sound and<br />
video chips, for use by video monitors or even hi-fi sound equipment. This is a DIN<br />
socket. Earlier <strong>64</strong>s have five-pin sockets; some later models may have eight-pin sock<br />
ets. A cable for <strong>the</strong> five-pin socket will work with <strong>the</strong> eight-pin connector, but not<br />
vice versa.<br />
<strong>The</strong> serial port is a modified version of <strong>the</strong> parallel IEEE input/output port of<br />
<strong>the</strong> earlier CBM computers. <strong>The</strong> signal format on <strong>the</strong> port, unique to <strong>Commodore</strong>, is<br />
designed for use with VIC and <strong>64</strong> devices (disk drives, printers) but is not directly<br />
compatible with much else. (This should not be confused with <strong>the</strong> user port, which<br />
provides RS-232 serial communication.) Chapters 15 (disk drives) and 17 (printers)<br />
have more information.<br />
<strong>The</strong> cassette port is designed to power and control CBM's Datassette tape drive.<br />
O<strong>the</strong>r devices sometimes draw power from this socket. Chapter 14 has full details<br />
about tape.<br />
<strong>The</strong> user port is compatible in size and function with VIC equipment. It is de<br />
signed to allow communication with <strong>the</strong> outside world with a <strong>Commodore</strong> modem,<br />
or for o<strong>the</strong>r purposes, including <strong>the</strong> control of electronic instruments. Chapter 5 ex<br />
plains how it's programmed.<br />
<strong>The</strong> side panel of <strong>the</strong> <strong>64</strong> has <strong>the</strong>se features, again from left to right:<br />
Control ports 1 and 2 allow one or two joysticks to be connected to <strong>the</strong> <strong>64</strong>. <strong>The</strong><br />
joystick nine-pin D-connector is standard. Port 2 is often preferred, as it's a little
Getting to Know <strong>the</strong> <strong>64</strong><br />
easier to program. Pairs of paddles (rotary controllers) can be plugged into ei<strong>the</strong>r<br />
port. A light pen can be plugged into port 1. Chapter 16 has full programming infor<br />
mation on <strong>the</strong>se and o<strong>the</strong>r controllers.<br />
<strong>The</strong> power switch, as you might have guessed, is where you turn <strong>the</strong> power<br />
on.<br />
<strong>The</strong> power input socket is where you plug in <strong>the</strong> connector from <strong>the</strong> power<br />
supply unit, which requires standard household current.<br />
<strong>The</strong> Keyboard<br />
Chapter 6 discusses <strong>the</strong> keyboard in depth. Here, we'll briefly survey <strong>the</strong> keys and<br />
<strong>the</strong>ir functions as <strong>the</strong>y act just after <strong>the</strong> computer is turned on, before <strong>the</strong>y are modi<br />
fied by a program (this is called <strong>the</strong>ir default arrangement). Alphabetic, numeric, and<br />
symbol keys appear as <strong>the</strong> keytop legend implies, subject to <strong>the</strong> changes mentioned<br />
below.<br />
SHIFT selects uppercase lettering or <strong>the</strong> right-hand graphics set, depending on<br />
<strong>the</strong> character set in use.<br />
<strong>Commodore</strong> key accesses <strong>the</strong> left-hand graphics set or, with <strong>the</strong> color keys, se<br />
lects one of eight additional colors not named on <strong>the</strong> standard <strong>64</strong> keyboard. Where<br />
<strong>the</strong>se don't apply, <strong>the</strong> <strong>Commodore</strong> key acts as an alternative SHIFT key.<br />
SHIFT-<strong>Commodore</strong> key (SHIFT and <strong>Commodore</strong> key toge<strong>the</strong>r) changes <strong>the</strong><br />
display from uppercase, with <strong>the</strong> full keytop graphics set, to upper- and lowercase<br />
lettering, plus <strong>the</strong> left-hand graphics set.<br />
CTRL (Control) acts with <strong>the</strong> color keys and reverse keys to change <strong>the</strong> color of<br />
<strong>the</strong> characters or to turn reverse video printing on and off. CTRL-A through CTRL-Z<br />
generate CHR$(1) through CHR$(26), acting as a conventional control key—see<br />
Chapter 3. CTRL also slows screen scrolling; this is useful when listing a program.<br />
Function keys (f 1 through f8) display nothing. In BASIC, GET is <strong>the</strong> easiest<br />
way to detect <strong>the</strong>m. (See Chapter 6 for program information.)<br />
RUN/STOP interrupts BASIC programs. <strong>The</strong> command CONT allows resump<br />
tion of BASIC, subject to certain conditions.<br />
SHIFT-RUN/STOP (SHIFT and RUN/STOP toge<strong>the</strong>r) loads, <strong>the</strong>n runs <strong>the</strong><br />
next BASIC program on tape. Note that almost any key followed by SHIFT-<br />
RUN/STOP runs <strong>the</strong> BASIC program in memory. A normal disk LOAD command<br />
followed by a colon, <strong>the</strong>n SHIFT-RUN/STOP will load <strong>the</strong> specified program from<br />
disk, <strong>the</strong>n run it.<br />
RUN/STOP-RESTORE acts like a panic button; <strong>the</strong> system returns to its nor<br />
mal state, retaining <strong>the</strong> BASIC program in memory. Chapter 6 explains both RE<br />
STORE and RUN/STOP in detail.<br />
CLR/HOME places <strong>the</strong> cursor at <strong>the</strong> top left of <strong>the</strong> screen; SHIFT-CLR/HOME<br />
also erases <strong>the</strong> screen, leaving it blank, with <strong>the</strong> cursor flashing in <strong>the</strong> home<br />
position.<br />
INST/DEL (insert/delete) is part of <strong>the</strong> screen editing system—<strong>the</strong> set of opera<br />
tions which allow <strong>the</strong> alteration of any part of <strong>the</strong> screen. Both keys repeat, although<br />
INST has little effect if it isn't followed by characters on its BASIC line. <strong>The</strong> screen<br />
editing is powerful and easy to use, despite having a few small quirks in quote<br />
mode. To delete characters to <strong>the</strong> left of <strong>the</strong> flashing cursor, press <strong>the</strong> key<br />
unSHIFTed; to insert characters to <strong>the</strong> right of <strong>the</strong> cursor, press SHIFT-INST/DEL.<br />
10
Getting to Know <strong>the</strong> <strong>64</strong><br />
CRSR keys (cursor keys) move <strong>the</strong> flashing cursor in <strong>the</strong> directions printed on<br />
<strong>the</strong> keytops. UnSHIFTed, <strong>the</strong> cursor will move in <strong>the</strong> direction printed below <strong>the</strong> let<br />
ters CRSR on <strong>the</strong> key. SHIFTed, <strong>the</strong> cursor will move in <strong>the</strong> direction shown above<br />
CRSR. <strong>The</strong>se keys automatically repeat to save time. Movement down <strong>the</strong> screen<br />
eventually causes <strong>the</strong> text on <strong>the</strong> screen to scroll up.<br />
RETURN is <strong>the</strong> key used by <strong>the</strong> <strong>64</strong> to signal that information on <strong>the</strong> screen is<br />
ready for processing. For example, if you type:<br />
PRINT 'HELLO"<br />
on <strong>the</strong> screen, pressing RETURN causes HELLO to appear. Similarly, RETURN sig<br />
nals that data typed at <strong>the</strong> keyboard in response to an INPUT statement is ready for<br />
<strong>the</strong> <strong>64</strong> to process.<br />
SHIFT-RETURN or <strong>Commodore</strong> key-RETURN moves <strong>the</strong> cursor to <strong>the</strong> next<br />
BASIC line (not <strong>the</strong> same as <strong>the</strong> next screen line—see below), but without causing<br />
<strong>the</strong> <strong>64</strong> to take in <strong>the</strong> information. (For example, if you begin to correct a line of<br />
BASIC, but change your mind, SHIFT-RETURN leaves <strong>the</strong> line as it was.)<br />
Quotation marks (SHIFT-2) are important in BASIC; quotation marks designate<br />
<strong>the</strong> start or end of a string (a group of characters) that you may want to print, assign<br />
to a variable, or manipulate in some o<strong>the</strong>r way. When in quote mode (more on this<br />
later), several special characters which follow double-quotes are stored as reversed<br />
characters. See SHIFT-RETURN above.<br />
<strong>The</strong> space bar repeats if held down. Chapter 6 gives full information.<br />
Editing BASIC on <strong>the</strong> <strong>64</strong><br />
Everything entered into <strong>the</strong> <strong>64</strong> is treated as BASIC, unless some special language has<br />
been loaded into memory. <strong>The</strong> <strong>64</strong> operates in several modes, which are described<br />
below.<br />
Direct mode. We've seen how PRINT "HELLO" is interpreted as an instruction<br />
to print <strong>the</strong> word HELLO. Because of <strong>the</strong> instant response to <strong>the</strong> command, this is<br />
called direct or immediate mode.<br />
Program mode. Type <strong>the</strong> following line, followed by RETURN:<br />
10 PRINT "HELLO"<br />
Apparently, nothing happens. In fact, however, <strong>the</strong> line is stored in <strong>the</strong> <strong>64</strong>'s memory<br />
as a line of a BASIC program; any line beginning with a number (up to 63999) is<br />
interpreted as a program line and stored. This is called program mode or deferred<br />
mode. LIST displays <strong>the</strong> BASIC program in memory; RUN executes it. If you run <strong>the</strong><br />
above example, you should see HELLO on <strong>the</strong> screen.<br />
Quote mode. In quote mode, <strong>the</strong> <strong>64</strong>'s special characters are stored for future<br />
use. Quote mode enables you to use <strong>the</strong> <strong>64</strong>'s powerful screen control features,<br />
including cursor moves and screen clearing, from within programs.<br />
BASIC Terms<br />
Variables are algebraic in nature; X=10: PRINT X prints <strong>the</strong> number 10 on <strong>the</strong><br />
screen, since <strong>the</strong> variable, X, has been given <strong>the</strong> value 10. <strong>The</strong> value can be altered,<br />
so X is referred to as a variable. <strong>The</strong> next chapter explains this more fully.<br />
Keywords are <strong>the</strong> commands recognized by BASIC; PRINT is one. All <strong>the</strong><br />
11
Getting to Know <strong>the</strong> <strong>64</strong><br />
keywords are listed, with examples, in <strong>the</strong> next chapter. Note that most keywords<br />
can be entered in a shortened form. For example, PRINT can be abbreviated with a<br />
question mark.<br />
10?<br />
followed by LIST reads as:<br />
10 PRINT<br />
Device numbers allow <strong>the</strong> <strong>64</strong> to communicate with external hardware devices,<br />
selectively. <strong>The</strong> tape unit, for example, is device 1. Tape is <strong>the</strong> default device, which<br />
means that if no o<strong>the</strong>r device is specifically requested by number, <strong>the</strong> tape unit will<br />
be selected. Data can also be written to or read from o<strong>the</strong>r devices, but <strong>the</strong>re are<br />
restrictions; you can read and write to tape, but only write to a printer, for example.<br />
<strong>The</strong> commands for reading and writing also require a reference number, which is<br />
called a logical file number. Sometimes <strong>the</strong>se commands will need a secondary address<br />
as well. For details, see OPEN and CLOSE in <strong>the</strong> reference section in <strong>the</strong> next<br />
chapter.<br />
<strong>The</strong> <strong>64</strong> has 25 screen lines, each with 40 characters. But BASIC can link toge<strong>the</strong>r<br />
<strong>the</strong> information on two screen lines into one program line—hence, <strong>the</strong> distinction<br />
between screen lines and BASIC lines (sometimes called physical and logical lines,<br />
respectively). Try typing PRINT, followed by quotes and several lines of asterisks (or<br />
o<strong>the</strong>r characters). You'll find that <strong>the</strong> third and subsequent lines aren't included as<br />
part of <strong>the</strong> program line.<br />
Finally, BASIC has built-in error messages, which are designed to help with<br />
debugging (removing mistakes from) your programs. <strong>The</strong> final section of Chapter 3<br />
lists <strong>the</strong> error messages alphabetically with explanations.<br />
12
Chapter 3<br />
BASIC Reference<br />
Guide<br />
• BASIC Syntax<br />
• BASIC Keyword Dictionary<br />
• BASIC Error Message Dictionary
Chapter 3<br />
BASIC Reference Guide<br />
BASIC Syntax<br />
BASIC is now <strong>the</strong> most popular language for personal computers. It's easy to write,<br />
test, and edit BASIC, and simple programs can be written by people with very little<br />
computing experience, which is exciting and encouraging. BASIC is sometimes de<br />
scribed as being like English, but <strong>the</strong> resemblance is tenuous.<br />
Almost every machine has its own version of BASIC. As a result, expressions<br />
work differently on different machines. <strong>The</strong> information in this book applies to <strong>the</strong><br />
version of BASIC contained in <strong>the</strong> <strong>64</strong>.<br />
Numbers and Literals<br />
Numbers and literal character strings are constants, as opposed to variables. Exam<br />
ples of ^umbers are 0, 2.3 E —7, 1234.75, and —744; examples of literals are<br />
"HELLO", "ABC123", and "%!£/"', where <strong>the</strong> quotation marks are delimiters (not<br />
part of <strong>the</strong> literal). <strong>The</strong> rules which determine <strong>the</strong> validity of <strong>the</strong>se forms are com<br />
plex; generally, numbers are valid if <strong>the</strong>y contain 0-9, +, —, E, or a decimal point<br />
in legal combinations. Thus, 1.2.3 is not valid (only one decimal point may be used);<br />
nor is 2EE3 (only one E is permitted). But ei<strong>the</strong>r 0E or a single decimal point is ac<br />
cepted as 0.<br />
Exponential notation (using E) may be unfamiliar to some; <strong>the</strong> number following<br />
E is <strong>the</strong> number of positions left or right that <strong>the</strong> decimal point must be moved to<br />
produce a number in ordinary notation. (1.7E3 means 1.7 X 10 to <strong>the</strong> third power,<br />
or 1.7 X 1000, which is 1700. <strong>The</strong> form 9.45E-2 is simply ano<strong>the</strong>r notation for <strong>the</strong><br />
number .0945.) Be careful when typing <strong>the</strong>se numbers in, because SHIFT-E is not ac<br />
cepted. Values outside <strong>the</strong> ranges .01 to 999,999,999 and -.01 to -999,999,999 are<br />
printed in exponential form.<br />
Strings can contain any of <strong>the</strong> <strong>Commodore</strong> <strong>64</strong>'s ASCII characters; all characters<br />
can be accessed with <strong>the</strong> CHR$ function, including quotes, CHR$(34), and RETURN,<br />
CHR$(13). <strong>The</strong> maximum length of a string is 255 characters.<br />
Variables<br />
A variable is an algebraic symbol used to represent a number or string of characters.<br />
X, X%, and X$, respectively, are numeric (values between ±2.93873588E-39 and<br />
±1.70l4ll83E38), integer (whole numbers between -32768 and +32767), and<br />
string (up to 255 characters) variables. If <strong>the</strong> variables haven't been assigned values,<br />
numeric and integer variables default to 0, strings to <strong>the</strong> null character, a string of<br />
zero length.<br />
A variable, as <strong>the</strong> name implies, can be changed at will. <strong>The</strong> direct mode line<br />
X=l: PRINT X: X=2: PRINT X<br />
illustrates this point.<br />
Names of variables are subject to <strong>the</strong>se rules:<br />
• <strong>The</strong> first character must be alphabetic.<br />
• <strong>The</strong> next character may be alphanumeric.<br />
15
BASIC Reference Guide<br />
• Any fur<strong>the</strong>r alphanumerics are allowed, but not considered part of <strong>the</strong> variable<br />
name.<br />
• <strong>The</strong> next character may be % or $, denoting integer and string variables,<br />
respectively.<br />
• <strong>The</strong> next character may be (to denote a subscripted variable.<br />
• A name cannot include reserved words, since <strong>the</strong> BASIC interpreter will treat <strong>the</strong>m<br />
as keywords. Note that reserved variables (TI, ST) can be incorporated in names<br />
(but not used by <strong>the</strong>mselves as variable names), since <strong>the</strong>y are not keywords.<br />
All <strong>the</strong>se rules remove ambiguity and make storage convenient and fast. If 1A<br />
were a valid variable name, for example, <strong>the</strong> line 100 1A=1 would require special<br />
treatment to distinguish it from 1001 A=l. And if symbols o<strong>the</strong>r than alphanumerics<br />
were permitted—so that B= were a valid name, for instance—this would<br />
cause problems.<br />
Conversion between different types of numeric variables is automatic; however,<br />
string-to-numeric and numeric-to-string conversions require special functions. For ex<br />
ample, L%=L/256 automatically truncates L/256 (removing <strong>the</strong> fractional portion,<br />
but not rounding it) and checks that <strong>the</strong> result is in <strong>the</strong> range —32768 to +32767.<br />
L$ = STR$(L) and L=VAL(L$) are for converting between numbers and strings. Two<br />
o<strong>the</strong>r conversion functions are CHR$ and ASC, which operate on single bytes and<br />
enable expressions which would o<strong>the</strong>rwise be treated as special cases to be<br />
processed.<br />
Operators<br />
Binary operators take two items of <strong>the</strong> same type and generate a single new item of<br />
<strong>the</strong> same type from <strong>the</strong>m. Unary operators modify a single item. <strong>The</strong> numeric op<br />
erators supported by BASIC on <strong>the</strong> <strong>64</strong> are standard, much like those supported by<br />
o<strong>the</strong>r computer languages, while <strong>the</strong> string and logical operators are less similar.<br />
When a string or arithmetic expression is evaluated, <strong>the</strong> result depends on <strong>the</strong><br />
priority assigned to each operator and <strong>the</strong> presence or absence of paren<strong>the</strong>ses. In<br />
both string and arithmetic calculations, paren<strong>the</strong>ses insure that <strong>the</strong> entire expression<br />
within <strong>the</strong> paren<strong>the</strong>ses is evaluated as a unit before <strong>the</strong> o<strong>the</strong>r operations are per<br />
formed. <strong>The</strong> rules for using paren<strong>the</strong>ses dictate levels of priority, so that an ex<br />
pression in paren<strong>the</strong>ses within ano<strong>the</strong>r set of paren<strong>the</strong>ses will be evaluated first. In <strong>the</strong><br />
absence of paren<strong>the</strong>ses, priority is assigned to operators in this order, starting with<br />
<strong>the</strong> highest level:<br />
t<br />
Exponents<br />
+ or — Unary plus or minus sign—positive or negative number<br />
• or / Multiply or divide<br />
+ or — Binary plus or minus—addition or subtraction<br />
< = or > Comparisons—less than, equal to, greater than<br />
NOT Logical NOT—unary operator<br />
AND Logical AND—binary operator<br />
OR Logical OR—binary operator<br />
Logical operators are also called Boolean operators. In an expression like A AND<br />
B, A and B are called operands.<br />
16
BASIC Reference Guide<br />
Arithmetic operators work in a straightforward way, but string comparisons are<br />
not as simple. Strings are compared on a character-by-character basis until <strong>the</strong> end<br />
of <strong>the</strong> shorter string is reached. If <strong>the</strong> characters in both strings are identical up to<br />
<strong>the</strong> end of <strong>the</strong> shorter string, <strong>the</strong> shorter one is considered <strong>the</strong> lesser by string<br />
comparison logic. Characters later in <strong>the</strong> ASCII sequence are considered greater than<br />
those earlier in <strong>the</strong> series. <strong>The</strong>refore, <strong>the</strong> string "1" is less than <strong>the</strong> string "10", but<br />
"5" is greater than "449".<br />
Functions<br />
Some BASIC keywords are valid only when followed by an expression in paren<br />
<strong>the</strong>ses; <strong>the</strong>y may be used on <strong>the</strong> right of assignment statements or within ex<br />
pressions. <strong>The</strong>se are functions: <strong>The</strong>y return a value dependent on <strong>the</strong> expression in<br />
paren<strong>the</strong>ses. Numeric functions return numeric values and include SQR, LOG, and<br />
EXP; string functions, which include LEFT$, MID$, RIGHTS, and CHR$, return<br />
string values. (<strong>The</strong> last character of all string functions is a $, like that of string vari<br />
able names.) PEEK, though not a function in <strong>the</strong> ma<strong>the</strong>matical sense, has <strong>the</strong> syntax<br />
of a numeric function and is considered one. Some functions (like FRE) take a socalled<br />
dummy argument: an expression required by <strong>the</strong> interpreter's syntax-checking<br />
routine, but ignored by <strong>the</strong> code which evaluates <strong>the</strong> function. Typically, <strong>the</strong> dummy<br />
parameter is a 0, for convenience. <strong>The</strong> line below is an example:<br />
PRINT FRE(O)<br />
Expressions<br />
A numeric expression is a valid arrangement of numbers, numeric functions, real and<br />
integer variables, operators and paren<strong>the</strong>ses, or logical expressions. Numeric ex<br />
pressions can replace numbers in many BASIC constructions, for example, <strong>the</strong> right<br />
side of <strong>the</strong> assignment statement:<br />
X=SQR(M)+PEEK(SCREEN + J)<br />
A string expression is an arrangement of one or more literals, string functions,<br />
string variables, <strong>the</strong> string operator +, or paren<strong>the</strong>ses. String expressions can replace<br />
literals in many BASIC constructions, like this:<br />
X$=MID$("HI" + NAME$,1,L)+ CHR$(13)<br />
A logical (or Boolean) expression evaluates as true or false (—1 or 0, respectively,<br />
in BASIC) and usually contains one or more relational operators (), logical<br />
operators, paren<strong>the</strong>ses, numeric expressions, or string expressions. <strong>The</strong>ir main use is<br />
in IF statements.<br />
IF X$="Y" OR X$="N" GOTO 100<br />
contains logical expressions.<br />
BASIC doesn't distinguish sharply between logical and arithmetic expressions;<br />
<strong>the</strong>y are evaluated toge<strong>the</strong>r and can be mixed. This allows constructions like:<br />
IF INT(YR/4)*4=YR AND MN=2 THEN PRINT "29 DAYS"<br />
which is fairly simple, but also trickier lines like:<br />
DAYS = 31 + 2*(M=2) + (M=4 OR M=6 OR M=9 OR M=11)<br />
17
BASIC Reference Guide<br />
where <strong>the</strong> value — 1, generated by a true statement, is used in <strong>the</strong> calculation of<br />
days in a month. (<strong>The</strong>se lines are examples only, not complete routines that you<br />
should type in and run.)<br />
Ano<strong>the</strong>r aspect of logical expressions is that logical operators can easily be<br />
wrongly programmed; because mistyping may be undetected by BASIC, <strong>the</strong> priority<br />
of logical expressions is low (<strong>the</strong>y're executed last), and <strong>the</strong> meaning of expressions is<br />
easily misunderstood. For example:<br />
IF PEEK(X)=0 AND PEEK(X+1)=O THEN END<br />
looks for two zero bytes, <strong>the</strong>n ends, which is <strong>the</strong> desired result, but:<br />
IF PEEK(X) AND PEEK(X+1)=O THEN END<br />
ends whenever PEEK(X) is nonzero and PEEK(X+1)=O.<br />
True and false are actually two-byte expressions like integer variables; — 1 (true)<br />
means all bits are 1; 0 (false) means all bits are 0. Chapter 5 explains in detail.<br />
Every intermediate result in an expression must be valid; numerals must be in<br />
<strong>the</strong> floating-point range, strings no longer than 255 characters, and logical ex<br />
pressions in <strong>the</strong> integer range.<br />
Statements<br />
A statement is a syntactically correct portion of BASIC separated from o<strong>the</strong>r state<br />
ments by an end-of-line marker or a colon. All statements begin with a BASIC<br />
keyword, or, where LET has been omitted, with a variable. <strong>The</strong> different types of<br />
statements are discussed below.<br />
• Assignment statements. LET variable = expression. (LET is optional; but its presence<br />
makes <strong>the</strong> intention behind arithmetically impossible statements, like X=X+1,<br />
clearer for <strong>the</strong> beginner. Languages like Pascal indicate assignments with <strong>the</strong> sym<br />
bol :=, which is read as "becomes.")<br />
• Conditional statements. IF logical expression THEN statement.<br />
• Program control statements. For example, GOTO, GOSUB, RETURN, STOP.<br />
• Input statements. Fetch data from device or from DATA statement: INPUT, GET,<br />
INPUT#, GET#, READ.<br />
• Looping statements. FOR-NEXT loops, for example.<br />
• Output statements. Send data to screen, disk, cassette, or o<strong>the</strong>r device: PRINT,<br />
PRINT#.<br />
• REM statements. <strong>The</strong>se allow <strong>the</strong> programmer to include comments for documenta<br />
tion. <strong>The</strong> interpreter detects <strong>the</strong> REM statement and ignores <strong>the</strong> remainder of <strong>the</strong><br />
line when <strong>the</strong> program runs. Program lines which are never run and lines that con<br />
tain only colons can be included in this category.<br />
• Conversion statements. <strong>The</strong>se convert between string variables and literals, real vari<br />
ables and numbers, and integers and numerals. Functions like ASiC, CHR$, STR$,<br />
and VAL are examples.<br />
BASIC programs are made up of numbered program lines; each program line is<br />
made up of statements, separated from eacft o<strong>the</strong>r by colons where two or more<br />
statements are used on <strong>the</strong> same program jihe. Spaces generajjy are ignored outside<br />
quotation marks, as are multiple colons.<br />
18
BASIC Reference Guide<br />
BASIC Keyword Dictionary<br />
This section lists every BASIC keyword, with explanations and examples, in a uni<br />
form format. Details of error messages are not included here, but collected in an<br />
alphabetic list after this section.<br />
Each command's syntax is given in a standard way. Parameters are usually nu<br />
meric expressions, string expressions, or variables, and <strong>the</strong>se are always carefully<br />
distinguished; for example, ABS (numeric expression) means that any valid numeric<br />
expression is usable with ABS, which in turn implies ABS can be used with vari<br />
ables, as in ABS(X).<br />
Square brackets denote optional parameters; where such a parameter is omitted,<br />
a default value is assumed by <strong>the</strong> system.<br />
Numeric functions probably cause most errors. First, <strong>the</strong>re's a chance of a simple<br />
SYNTAX ERROR, perhaps an arithmetically wrong construction or omitted paren<br />
<strong>the</strong>sis. Second, number parameters have a wide assortment of range restrictions: byte<br />
values must be 0-255, memory addresses must be 0-65535, integer and logical ex<br />
pressions must be within —32768 and +32767, no numbers can be outside approxi<br />
mately — 1E38 and +1E38, zero denominators are not valid, square roots cannot<br />
exist for negative numbers, and so on. <strong>The</strong>se errors are relatively easy to correct, so<br />
errors are mentioned only when, as in DATA, some noteworthy feature exists.<br />
Chapter 11 is a guide to <strong>the</strong> <strong>Commodore</strong> <strong>64</strong>'s ROMs and includes information<br />
on <strong>the</strong> keywords. In a few cases, information is provided in this chapter, where it<br />
helps clarify some aspect of BASIC, and tokens are listed for programmers interested<br />
in looking into BASIC storage in memory.<br />
ABS<br />
Type: Numeric function<br />
Syntax: ABS(numeric expression)<br />
Modes: Direct and program modes are both valid.<br />
Token: $B6 (182)<br />
Abbreviated entry: A SHIFT-B<br />
Purpose: ABS returns <strong>the</strong> absolute value of <strong>the</strong> paren<strong>the</strong>sized numeric expression. In<br />
o<strong>the</strong>r words, ABS makes a negative number or expression positive.<br />
Examples:<br />
1. 50 IF ABS(TARGET-X)
BASIC Reference Guide<br />
AND<br />
Type: Logical operator<br />
Syntax: Logical or numeric expression AND logical or numeric expression<br />
Modes: Direct and program modes are both valid.<br />
Token: $AF (175)<br />
Abbreviated entry: A SHIFT-N<br />
Purpose: AND applies <strong>the</strong> logical AND operator to two expressions. For <strong>the</strong> pur<br />
poses of <strong>the</strong> AND comparison, numeric expressions are evaluated as 16-bit signed<br />
integers, so each operand must be in <strong>the</strong> range —32768 to 32767. Values outside this<br />
range result in an 7ILLEGAL QUANTITY ERROR. Each of <strong>the</strong> 16 bits in <strong>the</strong> first op<br />
erand is ANDed with <strong>the</strong> corresponding bit in <strong>the</strong> second operand, resulting in a 16-<br />
bit, two-byte integer. <strong>The</strong> four possible combinations of corresponding individual bits<br />
are:<br />
0 AND 0 = 0<br />
0 AND 1 = 0<br />
1 AND 0 = 0<br />
1 AND 1 = 1<br />
<strong>The</strong> result becomes 1 only if both bits are 1.<br />
AND has two separate uses in BASIC. First, it allows <strong>the</strong> truth-value of several<br />
logical expressions to be calculated toge<strong>the</strong>r, as in:<br />
IF X>2 AND X84 AND YR0 AND MN
BASIC Reference Guide<br />
ASC<br />
Type: Numeric function<br />
Syntax: ASC(string expression at least one character long)<br />
Modes: Direct and program modes are both valid.<br />
Token: $C6 (198)<br />
Abbreviated entry: A SHIFTS<br />
Purpose: This function returns a number in <strong>the</strong> range 0-255 corresponding to <strong>the</strong><br />
ASCII value of <strong>the</strong> first character in <strong>the</strong> string expression. It is generally used when<br />
this number is easier to handle than <strong>the</strong> character itself. See <strong>the</strong> Appendices for a<br />
table of ASCII values.<br />
Note that <strong>the</strong> converse function to ASC is CHR$, so ASC(CHR$(N)) is <strong>the</strong> same<br />
as N, and CHR$(ASC(//P//)) is <strong>the</strong> character P. All keys except RUN/STOP, SHIFT,<br />
CTRL, <strong>the</strong> <strong>Commodore</strong> key, and RESTORE can be detected with GET and ASC.<br />
Examples:<br />
1. X = ASC (X$+CHR$(0))<br />
Calculates <strong>the</strong> ASCII value of any character X$. Adding CHR$(0) allows<br />
detection of <strong>the</strong> null character, which o<strong>the</strong>rwise gives 7ILLEGAL QUANTITY<br />
ERROR.<br />
2. X = ASC(X$)-192<br />
Converts uppercase (SHIFTed) A-Z to 1-26. Useful when computing<br />
checksums, where each letter has to be converted to a number.<br />
3. 1000 IF PEEK(L)=ASC("*") THEN PRINT "FOUND AT" L<br />
Shows how using ASC can make your programs more readable; <strong>the</strong> example<br />
is part of a routine to search memory for an asterisk.<br />
ATN<br />
Type: Numeric function<br />
Syntax: ATN(numeric expression)<br />
Modes: Direct and program modes are both valid.<br />
Token: $C1 (193)<br />
Abbreviated entry: A SHIFT-T<br />
Purpose: This is <strong>the</strong> arc tangent, or inverse tangent, function. This function returns,<br />
in radians in <strong>the</strong> range — tt/2 to + v/2, <strong>the</strong> angle whose tangent is <strong>the</strong> numeric ex<br />
pression. <strong>The</strong> expression may take any value within <strong>the</strong> valid range for floating-point<br />
numbers, approximately ±1.7E38.<br />
To convert radians to degrees, multiply by 180/tt. This changes <strong>the</strong> range of val<br />
ues of ATN from - n/2 through tt/2 to -90° through 90°.<br />
In some cases, ATN(X) is a useful transformation to apply, since it condenses al<br />
most <strong>the</strong> entire number range into a finite set from about —1.57 to +1.57.<br />
Examples:<br />
1. R=ATN((E2-E1)/(N2-N1))<br />
From a program for surveyors, this computes a bearing from distances east<br />
ing and northing.<br />
21
BASIC Reference Guide<br />
2. DEF FN AS(X)=ATN(X/SQR(1-X*X))<br />
DEF FN AC(X)=t/2-ATN(X/SQR(1-X*X))<br />
<strong>The</strong>se function definitions evaluate arc sine and arc cosine, respectively.<br />
Remember that <strong>the</strong> arc tangent can never be exactly 90 degrees; if necessary, test<br />
for this extreme value to avoid errors.<br />
CHR$<br />
Type: String function<br />
Syntax: CHR$(numeric expression)<br />
Modes: Direct and program modes are both valid.<br />
Token: $C7 (199)<br />
Abbreviated entry: C SHIFT-H (This includes <strong>the</strong> $.)<br />
Purpose: CHR$ converts a numeric expression (which must evaluate and truncate to<br />
an integer in <strong>the</strong> range 0-255) to <strong>the</strong> corresponding ASCII character. It is useful for<br />
manipulating special characters like RETURN and quotes which are CHR$(13) and<br />
CHR$(34), respectively. Check <strong>the</strong> Appendices for a table of ASCII values. Note that<br />
ASC is <strong>the</strong> converse function of CHF*<br />
Examples:<br />
1. A$=CHR$(18)+NAME$+CHR$(146)<br />
This adds {RVS} and {OFF} around NAME$, so PRINT A$ prints NAME$<br />
in reverse video.<br />
2. FOR J=833 TO 848: PRINT CHR$(PEEK(J));:NEXT<br />
This prints <strong>the</strong> name of <strong>the</strong> most recently loaded tape program, by reading<br />
<strong>the</strong> characters from <strong>the</strong> tape buffer, assuming <strong>the</strong> buffer hasn't been altered by a<br />
program.<br />
3. PRINT#4, CHR$(27)"E08"<br />
<strong>The</strong> above command sends <strong>the</strong> ASCII ESC (escape) character, plus a com<br />
mand, to a printer. Special printer features are often controlled like this, and <strong>the</strong><br />
codes will vary from one brand of printer to <strong>the</strong> next.<br />
4. OPEN 2,2/0/CHR$(38)+CHR$(60)<br />
A command which opens a file to a modem. <strong>The</strong> two CHR$ parameters are<br />
required in this format by BASIC.<br />
CHR$(0) represents <strong>the</strong> null character, but, unlike <strong>the</strong> null string, "", it has a<br />
length of one, and can be added to strings. See ASC for an application. Embedded<br />
null characters, as in Y$="12"+CHR$(0)+"34" can cause strange results.<br />
CLOSE<br />
Type: Input/output statement<br />
Syntax: CLOSE numeric expression<br />
Modes: Direct and program modes are both valid.<br />
Token: $A0 (160)<br />
Abbreviated entry: CL SHIFT-0<br />
22
BASIC Reference Guide<br />
Purpose: CLOSE completes <strong>the</strong> processing of <strong>the</strong> specified file and deletes its file<br />
number, device number, and secondary address from <strong>the</strong> file tables.<br />
A numeric expression may be used as a logical file number; it must evaluate to a<br />
number in <strong>the</strong> range 0-255. No error message is given if <strong>the</strong> file is not open. (Ac<br />
tually CLOSE shares OPEN's syntax checking, so four parameters are valid after<br />
CLOSE, but only <strong>the</strong> first is used.)<br />
Notes:<br />
1. Files opened for reading do not have to be closed, but files opened for saving to<br />
tape or disk should always be closed, or tape files will lose <strong>the</strong> last portion of data<br />
held in <strong>the</strong> buffer, while disks may be corrupted. Chapters 14 and 15 have details.<br />
(OPEN 15,8,15: CLOSE 15 is an easy way to correctly close disk files, perhaps<br />
after a program stops with 7SYNTAX ERROR while writing to disk.)<br />
2. CLOSE is a straightforward command, but it is made more complicated by <strong>the</strong><br />
behavior of CMD, which must be followed by a PRINT# command to switch out<br />
put back to <strong>the</strong> TV or monitor.<br />
Example:<br />
OPEN 4,4: PRINT#4/'HELLO": CLOSE 4<br />
<strong>The</strong> line above opens a file, sends data to a printer through <strong>the</strong> file, <strong>the</strong>n closes<br />
<strong>the</strong> file. <strong>The</strong> second number in <strong>the</strong> OPEN command is a device number, which selects<br />
<strong>the</strong> printer (device 4).<br />
CLR<br />
Type: Statement<br />
Syntax: CLR<br />
Modes: Direct and program modes are both valid.<br />
Token: $9C (156)<br />
Abbreviated entry: C SHIFT-L<br />
Purpose: CLR clears <strong>the</strong> memory area currently allocated to variables, leaving <strong>the</strong><br />
BASIC program, if <strong>the</strong>re is one, unchanged. Any machine language routines in RAM<br />
are left unaltered. Additional effects are noted below.<br />
Note: CLR is actually part of NEW, and does most of <strong>the</strong> things NEW does, while<br />
keeping <strong>the</strong> current program intact. CLR operates by resetting pointers, and doesn't<br />
actually erase variables, so in principle <strong>the</strong>se could be recovered. It has o<strong>the</strong>r func<br />
tions, too. Following is <strong>the</strong> complete list:<br />
• <strong>The</strong> string pointer is set to top-of-memqjy, and <strong>the</strong> end-of-variables and end-ofarrays<br />
pointers are set to end-of-BASIC. AH variables and arrays are thus effectively<br />
lost.<br />
• <strong>The</strong> stack pointer is reset, but <strong>the</strong> previous address is retained; <strong>the</strong>refore, all FOR-<br />
NEXT and GOSUB-RETURN references are lost, and also, if CLR executes within a<br />
program, that program continues at <strong>the</strong> same place.<br />
• <strong>The</strong> DATA pointer is set to start.<br />
• Input/output activity is aborted.<br />
• Files are aborted (but not closed), and keyboard and screen become <strong>the</strong><br />
input/output devices.<br />
23
BASIC Reference Guide<br />
Examples:<br />
1. POKE 55,0: POKE 56,48: CLR<br />
Sets <strong>the</strong> top of <strong>the</strong> BASIC program storage area to 48*256=$3000, typically<br />
to reserve space for graphics in VIC bank 0.<br />
2. 1000 CLR: GOTO 10<br />
This sort of operation is useful in some simulation programs; all existing<br />
variables are erased and <strong>the</strong> program continues. RUN 10 has a similar effect.<br />
CMD<br />
Type: Output statement<br />
Syntax: CMD numeric expression [, any expression]<br />
<strong>The</strong> numeric expression, a file number, must evaluate to a number in <strong>the</strong> range<br />
1-255. <strong>The</strong> optional expression does not include <strong>the</strong> brackets shown above, but must<br />
follow a comma; it is printed to <strong>the</strong> specified file and can be used to put a header on<br />
a printout.<br />
Modes: Direct and program modes are both valid.<br />
Token: $9D (157)<br />
Abbreviated entry: C SHIFT-M<br />
Purpose: CMD is identical to PRINT#, except that <strong>the</strong> output device is left listening.<br />
<strong>The</strong>refore, a CMD statement followed by a device number redirects printed output<br />
from TV to <strong>the</strong> specified device. <strong>The</strong> effect usually lasts until PRINT# unlistens <strong>the</strong><br />
device.<br />
Notes:<br />
1. CMD is a convenient way to cause a program with many PRINT statements to di<br />
vert its output to a printer. This is easier than changing all PRINT statements to<br />
PRINT# statements. However, CMD has bugs; GET and sometimes GOSUB will<br />
redirect output to screen. Where this is a problem, use PRINT#.<br />
2. CMD is necessary in order to list programs to printers.<br />
Examples:<br />
1. OPEN 4,4: CMD4,"TITLE":LIST<br />
This will list <strong>the</strong> current program (or disk directory file, if present in mem<br />
ory) to a printer. Follow this with:<br />
PRINT#4: CLOSE4<br />
to return output to <strong>the</strong> screen.<br />
2. 100 INPUT "DEVICE NUMBERED: OPEN D,D: CMD D<br />
Allows PRINT to direct output ei<strong>the</strong>r to device 3 (screen), device 4 (printer),<br />
or elsewhere.<br />
24
BASIC Reference Guide<br />
CONT<br />
Type: Command<br />
Syntax: CONT<br />
Modes: Only direct mode is available. (In program mode CONT enters an infinite<br />
loop.)<br />
Token: $9A (154)<br />
Abbreviated entry: C SHIFT-O<br />
Purpose: CONT resumes execution of a BASIC program stopped by a STOP or END<br />
statement, or by <strong>the</strong> RUN/STOP key. CONT cannot be used to restart a program<br />
that has stopped due to any sort of error. Also, CONT cannot be used if you edit<br />
any program lines after <strong>the</strong> program stops.<br />
For debugging purposes, STOP instructions may be inserted at strategic points in<br />
<strong>the</strong> program, and variables may be PRINTed and modified after <strong>the</strong> program has<br />
stopped. CONT will continue, provided you make no error. ?CAN'T CONTINUE ER<br />
ROR has several causes. In such cases, GOTO a line number serves a similar purpose<br />
as CONT.<br />
Note: Because STOP aborts files, CONT may be accepted, but not actually continue<br />
as before; for example, output which ought to go to a printer may be displayed on<br />
<strong>the</strong> screen after CONT.<br />
Example:<br />
10 PRINT J: J=J + 1: GOTO 10<br />
Run this, <strong>the</strong>n press <strong>the</strong> RUN/STOP key. <strong>The</strong> BASIC command CONT will<br />
cause <strong>the</strong> program to continue. You can change J, by typing J=10000 in direct mode,<br />
for example, and CONT will resume (using <strong>the</strong> new value).<br />
COS<br />
Type: Numeric function<br />
Syntax: COS(numeric expression)<br />
Modes: Direct and program modes are both valid.<br />
Token: $BE (190)<br />
Abbreviated entry: None<br />
Purpose: COS returns <strong>the</strong> cosine of <strong>the</strong> numeric expression, which is assumed to be<br />
an angle expressed in radians.<br />
Examples:<br />
1. PRINT COS(45*tt/180)<br />
<strong>The</strong> above statement prints <strong>the</strong> cosine of 45 degrees (conversion from radi<br />
ans to degrees is accomplished by multiplying <strong>the</strong> value in radians by tt and<br />
dividing by 180).<br />
2. FOR J=0 TO 1000 STEP ?r/10: PRINT COSfl): NEXT<br />
This shows <strong>the</strong> cyclical nature of COS. Large values of <strong>the</strong> argument don't<br />
introduce significant error, because COS uses only <strong>the</strong> remainder in <strong>the</strong> range 0 to<br />
2*7T.<br />
25
BASIC Reference Guide<br />
DATA<br />
Type: Statement<br />
Syntax: DATA list of data separated by commas<br />
Modes: Only program mode is available.<br />
Token: $83 (131)<br />
Abbreviated entry: D SHIFT-A<br />
Purpose: DATA enables numeric or string data to be stored in a program. <strong>The</strong> READ<br />
statement retrieves <strong>the</strong> data in DATA statements in <strong>the</strong> same order it's stored in <strong>the</strong><br />
program.<br />
Notes:<br />
1. DATA statements to store ML programs can be generated automatically: See<br />
Chapter 9.<br />
2. A 7SYNTAX ERROR in a valid DATA statement line means that <strong>the</strong> READ and<br />
DATA statements don't match properly.<br />
3. Unnoticed or omitted commas can cause baffling bugs:<br />
DATA R,O,Y,G,,B/P,<br />
contains eight data items, two of <strong>the</strong>m (between G and B, and following P) null<br />
characters.<br />
4. Because DATA statements are handled in sequence (RESTORE restarts <strong>the</strong> se<br />
quence), take care when adding more data (for example, by appending a sub<br />
routine) in case data from a wrong routine is read.<br />
Examples:<br />
1. 100 DATA "7975, LAZY RIVER ROAD"<br />
This shows that quotes enable commas, colons, leading spaces, and o<strong>the</strong>r<br />
special characters to be included in strings.<br />
2. 1000 DATA CUCOPPER^e^FEJRON^S.l<br />
This illustrates how sets of data can be stored. Typically, a loop with READ<br />
A$,M$,W inside might be used to read each set of three items.<br />
3. 10000 DATA SUB1 :REM MARKS START OF SUBl's DATA<br />
Here's a trick that might be used to insure that <strong>the</strong> correct data is being read.<br />
Use <strong>the</strong> following line to locate SUB1:<br />
1000 READX$: IF X$ "SUB1" GOTO 1000<br />
DEFFN<br />
Type: DEF FN, statement; FN, numeric function<br />
Syntax: DEF FN valid variable name (real variable)^ arithmetic expression<br />
Modes: Only program mode is available.<br />
Token: DEF: $96 (150); FN: $A5 (165)<br />
Abbreviated entry: DEF: D SHIFT-E (FN has no abbreviated form)<br />
Purpose: DEF FN sets up a numeric (not string) function, with one dependent vari<br />
able, which can be called by FN. Function definitions help save space where an ex-<br />
26
BASIC Reference Guide<br />
pression needs to be evaluated often. However, <strong>the</strong>ir main advantage is improving<br />
BASIC'S readability.<br />
Notes:<br />
1. Direct mode is forbidden, but function definitions are stored along with ordinary<br />
variables. See Chapter 6 on storage. Once defined, functions can be called by FN<br />
in direct mode.<br />
2. ?UNDEF'D FUNCTION ERROR results if DEF FN hasn't been encountered before<br />
FN is used. 7SYNTAX ERROR, when caused by a definition, refers to <strong>the</strong> line<br />
using FN, even when that line is valid.<br />
3. After loading a new program from within BASIC, redefine any functions; o<strong>the</strong>r<br />
wise, <strong>the</strong>y'll probably not work. Chapter 6 explains why.<br />
4. Function definitions work by calling a routine to evaluate expressions. <strong>The</strong>refore,<br />
each definition must fit into one line of BASIC; IF-THEN statements aren't al<br />
lowed in <strong>the</strong> function definition, so logical expressions may be necessary—see <strong>the</strong><br />
examples. Calling ano<strong>the</strong>r function definition is valid, however.<br />
5. <strong>The</strong> dependent variable need not be used in <strong>the</strong> definition; if not, it's called a<br />
dummy variable.<br />
Examples:<br />
1. 100 DEF FN DEEK(X) = PEEK(X)+256*PEEK(X+1)<br />
110 PRINT FN DEEK(50)<br />
<strong>The</strong>se lines print <strong>the</strong> decimal value of <strong>the</strong> two-byte quantity stored in mem<br />
ory locations 50 and 51. (DEEK is a double-byte PEEK.)<br />
2. 100 DEF FN MIN(X) = -(A>B)*B-(B>A)*A<br />
This will return <strong>the</strong> smaller of A and B. Note that X is a dummy variable in<br />
this case; any o<strong>the</strong>r variable could be used. <strong>The</strong> awkward form of <strong>the</strong> expression<br />
is necessary to fit it into a single statement.<br />
3. 100 DEF FN PV(I) = 100/ (1+1/100)<br />
This sets up a present value function, where I is an annual interest rate.<br />
4. 1000 DEF FN E(X) = 1 + X +X*X/2 + X*X*X/6 + FN E1(X)<br />
1010 DEF FN El(X)=X*X*X*X/24 + X*X*X*X*X/120<br />
<strong>The</strong> above lines show in outline how a very long expression can be spread<br />
over several lines of BASIC.<br />
DIM<br />
Type: Statement<br />
Syntax: DIM variable name [, variable name..]<br />
Modes: Direct and program modes are both valid.<br />
Token: $86 (134)<br />
Abbreviated entry: D SHIFT-I<br />
Purpose: DIM is short for DIMension. It sets up space above <strong>the</strong> BASIC program in<br />
memory for variables in <strong>the</strong> order <strong>the</strong> variables are listed in <strong>the</strong> DIM statement. This<br />
command is automatically carried out when a variable is given a value (for example,<br />
a line that contains <strong>the</strong> expression X=l), so <strong>the</strong>re's no need for DIM, unless arrays<br />
27
BASIC Reference Guide<br />
with dimensions greater than 10 are needed. All variables set up by DIM are set to 0<br />
(if numeric) or null (if strings).<br />
Notes:<br />
1. Arrays can use numeric expressions in <strong>the</strong>ir DIM statements, so <strong>the</strong>ir size can be<br />
determined by some input value; <strong>the</strong>y don't have to be of fixed size. Arrays start<br />
with <strong>the</strong> zeroth element, so DIM X(4) sets up a numeric array with five storage<br />
locations, X(0) through X(4). Dimensions can have a maximum of 32767 elements,<br />
and not more than 255 subscripts may be used in multidimensional arrays; in<br />
practice, an ?OUT OF MEMORY ERROR will result long before you reach <strong>the</strong>se<br />
limits.<br />
2. Arrays are stored in memory above regular variables. Chapter 6 explains <strong>the</strong> con<br />
sequences in detail, but here are a couple:<br />
• New variables introduced after an array has been set up cause a delay.<br />
• Arrays can be deleted with:<br />
POKE 49,PEEK(47): POKE 50,PEEK(48)<br />
So if intermediate results are computed with a large array, this can be deleted<br />
when you are finished.<br />
3. Integer arrays are efficient, while <strong>the</strong> efficiency of string arrays depends on <strong>the</strong><br />
lengths of <strong>the</strong> strings.<br />
PRINT FRE(O)<br />
gives a quick indication of spare RAM at any time. RAM space occupied by arrays<br />
is explained in Chapter 6.<br />
4. Large string arrays are vulnerable to garbage collection delays, also explained in<br />
Chapter 6. <strong>The</strong> total number of separate strings, not <strong>the</strong>ir lengths, is <strong>the</strong> signifi<br />
cant factor in garbage collection.<br />
Examples:<br />
1. 100 INPUT "NUMBER OF ITEMS";N: DIM IT$(N)<br />
This might be used in a sorting program, where any number of items may<br />
be sorted.<br />
2. DIM X,YJ,L,P$ :REM SET ORDER OF VARIABLES<br />
Ordering variables, with <strong>the</strong> most frequently used ones dimensioned first,<br />
will help increase <strong>the</strong> speed of BASIC programs.<br />
3. 100 DIM A(20): FOR J=l TO 20: INPUT A(J): A(0)=A(0)+A(J): NEXT<br />
<strong>The</strong> above line uses <strong>the</strong> zeroth element to keep a running total.<br />
4. DIM X%(10,10,10)<br />
This sets up an array of 1331 integers, perhaps to store <strong>the</strong> results of three<br />
11-point questionnaires.<br />
END<br />
Type: Statement<br />
Syntax: END<br />
Modes: Direct and program modes are both valid.<br />
28
BASIC Reference Guide<br />
Token: $80 (128)<br />
Abbreviated entry: E SHIFT-N<br />
Purpose: END causes a program to cease execution and exit to immediate mode.<br />
This command may be used to set breakpoints in a program; CONT causes a pro<br />
gram to continue at <strong>the</strong> instruction after END. BASIC doesn't always need END; a<br />
program can simply run out of lines, but END is needed to finish <strong>the</strong> program in <strong>the</strong><br />
middle. END leaves BASIC available for LISTing; you may prefer to prevent this<br />
with NEW or SYS <strong>64</strong>738 in place of END. (Note: Early computers always needed<br />
END, to separate each program's punch cards. Now this isn't so.)<br />
Examples:<br />
1. 10000 IF ABS(BEST-V)
BASIC Reference Guide<br />
Modes: Direct and program modes are both valid.<br />
Token: FOR: $81 (129); TO: $A4 (1<strong>64</strong>); STEP: $A9 (169)<br />
Abbreviated entry: FOR: F SHIFT-O; TO: None; STEP: ST SHIFT-E<br />
Purpose: FOR-TO [STEP] provides a method to count <strong>the</strong> number of times a portion<br />
of BASIC is executed.<br />
Notes:<br />
1. How FOR-NEXT loops work. <strong>The</strong> syntax after FOR is checked, rejecting, for ex<br />
ample, FOR X% = 1 TO 10. <strong>The</strong>n <strong>the</strong> stack is tested to see if FOR with <strong>the</strong> present<br />
variable exists; if it does, <strong>the</strong> previous loop is deleted, so<br />
FOR X=l TO 10: FOR X=l TO 10<br />
is treated as a single FOR statement. Now, 18 bytes are put on <strong>the</strong> stack, if <strong>the</strong>re's<br />
room. Once <strong>the</strong>y are placed <strong>the</strong>re, <strong>the</strong>y won't change, so <strong>the</strong> upper limit of <strong>the</strong><br />
loop FOR X=l TO N won't change after <strong>the</strong> looping starts, even if <strong>the</strong> value of N<br />
is changed within <strong>the</strong> loop.<br />
10 FOR X=489 TO 506: PRINT PEEK(X): NEXT<br />
lists 18 bytes from <strong>the</strong> stack; <strong>the</strong>se are <strong>the</strong> FOR token, <strong>the</strong> two-byte address of <strong>the</strong><br />
loop variable, <strong>the</strong> STEP size in floating-point format, <strong>the</strong> sign of <strong>the</strong> STEP, <strong>the</strong><br />
floating-point value of <strong>the</strong> upper limit of <strong>the</strong> loop, <strong>the</strong> line number of <strong>the</strong> FOR,<br />
and <strong>the</strong> address to jump to after <strong>the</strong> loop is finished. <strong>The</strong> STEP value defaults to 1.<br />
Because NEXT determines whe<strong>the</strong>r <strong>the</strong> loop will continue, every FOR-NEXT<br />
loop is executed at least once, even FOR J=l TO 0: NEXT. NEXT also checks <strong>the</strong><br />
loop variable, so NEXT X,Y, for example, helps insure correct nesting of loops—it<br />
must be preceded by FOR Y and FOR X statements. NEXT adds <strong>the</strong> STEP size to<br />
<strong>the</strong> variable value; if <strong>the</strong> result exceeds <strong>the</strong> stored limit (or is less, if a negative<br />
STEP size was used), processing continues with <strong>the</strong> statement following NEXT.<br />
<strong>The</strong>re's no way <strong>the</strong> system can detect a missing NEXT; if a set of loops is un<br />
expectedly fast, this may be <strong>the</strong> reason.<br />
When <strong>the</strong> STEP size is held exactly, <strong>the</strong>re is no loss of accuracy in using<br />
loops. So:<br />
FOR J=l to 10000 STEP .5<br />
is exact, as is <strong>the</strong> default STEP size of 1. On <strong>the</strong> o<strong>the</strong>r hand:<br />
FOR M=l TO 1000 STEP 1/3: PRINT M: NEXT<br />
will produce errors. Chapter 6 explains this in more detail.<br />
This description should enable you to pinpoint bugs in loops, which can be<br />
difficult to locate without detailed information.<br />
2. Loop execution speed. When fine-tuning a long program for speed, pay special<br />
attention to loops, because inefficiencies are magnified in proportion to <strong>the</strong> loop's<br />
size. If you dimension variables in decreasing order of importance, this can in<br />
crease <strong>the</strong> speed of execution (don't dimension any variables inside <strong>the</strong> loop,<br />
though, as this will cause an error condition).<br />
3. Exiting from loops. One of <strong>the</strong> best ways to exit from a loop is from <strong>the</strong> NEXT<br />
statement, and changing <strong>the</strong> loop variable is a simple way to accomplish this. For<br />
example:<br />
30
BASIC Reference Guide<br />
5 FOR J=l TO 9000: GET X$: IF X$="A" THEN J=9000<br />
10 NEXT<br />
finishes early if <strong>the</strong> A key is pressed.<br />
4. O<strong>the</strong>r loops. <strong>The</strong> extended command DO WHILE can be simulated with:<br />
FOR J= -1 TO 0: statements you wish to execute : J=CONDITION: NEXT<br />
Processing continues until J is false. Obviously, more intricate looping structures<br />
are possible.<br />
Examples:<br />
1. PRINT "{CLR}": FOR J=l TO 500: PRINT "*";: NEXT<br />
<strong>The</strong> above example line prints 500 asterisks.<br />
2. K=0: FOR J=1024 TO 1024+255: POKE J,K: K=K+1: NEXT<br />
This POKEs characters 0 to 255 sequentially into screen memory. K counts<br />
along with J.<br />
3. FOR J=2048 TO 9E9: IF PEEK(J)123 THEN NEXT:PRINT J<br />
<strong>The</strong>se statements search memory from 2048 upward for a byte equal to 123.<br />
When <strong>the</strong> loop ends, PRINT J gives <strong>the</strong> location.<br />
4. 5 FOR J=l TO 12<br />
10 IF M$MID$("JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC",<br />
3*J-2,3) THEN NEXT<br />
<strong>The</strong> above lines match a correctly entered month abbreviation, previously in<br />
put as M$. When <strong>the</strong> program is finished running, J will be a number from 1 to<br />
12, indicating <strong>the</strong> month (or 13 if no match was found).<br />
Type: Numeric function<br />
Syntax: FRE (numeric expression)<br />
Modes: Direct and program modes are both valid.<br />
Token: $B8 (184)<br />
Abbreviated entry: F SHIFT-R<br />
Purpose: FRE computes <strong>the</strong> number of bytes available to BASIC. If <strong>the</strong>re are more<br />
than 32767, <strong>the</strong> value returned is negative; adding 65536 converts this to <strong>the</strong> true<br />
figure. <strong>The</strong> <strong>Commodore</strong> <strong>64</strong> has <strong>64</strong>K of RAM memory, but 24K of this is normally<br />
hidden by overlying ROM and doesn't appear in <strong>the</strong> total of free bytes. See Chapter<br />
5. FRE is useful for removing unused dynamic strings, which take up variable space<br />
in RAM and are a potential source of ?OUT OF MEMORY ERRORs. FRE first per<br />
forms a garbage collection (see Chapter 6) before returning its value. FRE uses a<br />
dummy expression; usually a zero is used, so <strong>the</strong> expression is FRE(0).<br />
Examples:<br />
1. 1000 IF FRE(0)
BASIC Reference Guide<br />
2. F=FRE(O): DIM X$(50): PRINT F-FRE(O)<br />
This prints <strong>the</strong> number of bytes used to dimension X$ to 50.<br />
3. 200 F=FRE(0)-(FRE(0)
BASIC Reference Guide<br />
<strong>The</strong> numeric expression, a file number, must evaluate and truncate to a number<br />
in <strong>the</strong> range 1-255.<br />
Mode: Only program mode is available.<br />
Token: $A1 (161) <strong>the</strong>n $23 (35). This is GET <strong>the</strong>n #; GET# has no token of its own.<br />
Abbreviated entry: G SHIFT-E #<br />
Purpose: GET# reads a single character from <strong>the</strong> specified file, which must be open<br />
to an input device or a ?FILE NOT OPEN ERROR will result. Unlike <strong>the</strong> INPUT#<br />
statement, GET# can read characters like colons, quotes, and RETURNS. GET# can<br />
read files character by character in a way impossible with INPUT# and is not limited<br />
to 88 characters per string.<br />
Notes:<br />
1. GET# can read from screen or keyboard, although <strong>the</strong>re's usually no real advan<br />
tage in this.<br />
2. GET# from tape sets <strong>the</strong> status variable (ST) to a value of <strong>64</strong> when it reaches <strong>the</strong><br />
end-of-file, so programs can evaluate ST to test for <strong>the</strong> end of data if no special<br />
marker was used. ST is immediately reset, so <strong>the</strong> test is needed after each GET#.<br />
Chapter 14 has full details.<br />
3. GET# from disk also sets ST=<strong>64</strong> at end-of-file; from <strong>the</strong>n on, ST is set to 66,<br />
which indicates end-of-file plus device not responding. Chapter 15 has full details.<br />
Examples:<br />
1. 1000 IN$=""<br />
1010 GET#1,X$: IF ASC(X$)=13 GOTO 2000 :REM RETURN FOUND<br />
1020 IN$=IN$+X$: GOTO 1010 :REM BUILD STRING<br />
This program extract reads in a string, character by character, from tape or<br />
disk, building IN$ from each character, and exiting to <strong>the</strong> next part of <strong>the</strong> program<br />
when RETURN indicates <strong>the</strong> end of a string. A routine at line 2000 would handle<br />
<strong>the</strong> string after <strong>the</strong> GET# process was complete.<br />
2. 100 GET#8,X$: IF ST=<strong>64</strong> GOTO 1000: REM END OF DATA<br />
<strong>The</strong> above line shows how to use ST to detect that no more data is on file,<br />
and how to jump to ano<strong>the</strong>r part of <strong>the</strong> program based on that information.<br />
3. 100 GET#1,X$,Y$<br />
This example program line GETs a pair of consecutive characters from file 1,<br />
which has already been opened to an input device.<br />
GO<br />
Type: Dummy statement<br />
Syntax: Always part of GO TO<br />
Modes: Direct and program modes are both valid.<br />
Token: $CB (203)<br />
Abbreviated entry: None<br />
Purpose: <strong>The</strong> sole function of GO is to allow GO TO as a valid form of GOTO,<br />
which occasionally gives problems. For example, some renumbering programs ignore<br />
it, some early CBM machines don't have it. Chapter 8 shows how you can modify<br />
GO to suit your own purposes.<br />
33
BASIC Reference Guide<br />
GOSUB<br />
Type: Statement<br />
Syntax: GOSUB line number<br />
Modes: Direct and program modes are both valid.<br />
Token: $8D (141)<br />
Abbreviated entry: GO SHIFT-S<br />
Purpose: GOSUB jumps to <strong>the</strong> specified BASIC line, saving <strong>the</strong> address of <strong>the</strong> orig<br />
inal line on <strong>the</strong> stack, so that <strong>the</strong> RETURN statement in a program can transfer con<br />
trol back to <strong>the</strong> statement immediately following <strong>the</strong> GOSUB statement. This means<br />
a subroutine can be called from anywhere in BASIC while keeping normal program<br />
flow. IF or ON allows conditional calls to be made to subroutines.<br />
Notes:<br />
1. Testing subroutines in direct mode. It is often simple to test parts of a large program<br />
while in direct mode. For example:<br />
L=1234: GOSUB 500<br />
tests <strong>the</strong> decimal/hex converter in Chapter 6's "PRINT USING."<br />
2. Processing GOSUB. Line numbers following GOSUB are scanned by a routine simi<br />
lar to VAL; numbers are input until a nonnumeric character is found. (For ex<br />
ample, GOSUB and GOSUB NEW and GOSUB OXX are treated as GOSUB 0.)<br />
This allows ON-GOSUB to work, since it can <strong>the</strong>n skip commas. After this,<br />
GOSUB puts five bytes on <strong>the</strong> stack.<br />
10 GOSUB 20<br />
20 FOR J=500 TO 504: PRINT PEEK(J);: NEXT<br />
<strong>The</strong> above program prints five bytes from <strong>the</strong> stack, which are a GOSUB<br />
token (141), GOSUB's line number, and a pointer to <strong>the</strong> GOSUB statement. <strong>The</strong><br />
line number is used in <strong>the</strong> error message if <strong>the</strong> destination line doesn't exist. It's<br />
slightly faster to collect subroutines at <strong>the</strong> start of BASIC, to reduce <strong>the</strong> time spent<br />
searching for <strong>the</strong>m, and it's also slightly faster to number lines with <strong>the</strong> smallest<br />
possible numbers to cut down time spent processing line numbers.<br />
Note that GOSUBs without RETURNS can fill <strong>the</strong> stack and cause ?OUT OF<br />
MEMORY ERROR. Type and run <strong>the</strong> following one-line program to see <strong>the</strong> effect:<br />
100 GOSUB 100<br />
3. Miscellaneous. Chapter 6 has a computed GOSUB, and a POP to delete GOSUB<br />
statements that don't have matching RETURN statements. GOSUB 500: RETURN<br />
is identical to GOTO 500 in its effect, but uses more space on <strong>the</strong> stack.<br />
Structured programming makes a lot of use of subroutines; rewriting pro<br />
grams which use multiple IFs or o<strong>the</strong>r complex constructions into subroutines<br />
helps make programs clearer. See Chapter 4 for more on this.<br />
Examples:<br />
1. 100 EM$="DISK NOT IN DRIVE//:GOSUB2000<br />
110 END<br />
2000 PRINT "{HOME} {RVS}*** ERROR " EM$ " {OFF}"<br />
2010 FOR J=l TO 2000: NEXT: RETURN<br />
34
BASIC Reference Guide<br />
Part of a simplified error message routine, this excerpt prints an error (EM$<br />
must be set before GOSUB 2000) in reverse at <strong>the</strong> top of <strong>the</strong> screen.<br />
2. 500 GOSUB 510<br />
510 PRINT"*"<br />
520 RETURN<br />
This shows how a subroutine can have several entry points. Here, GOSUB<br />
510 prints an asterisk, while GOSUB 500 prints it twice.<br />
GOTO; GO TO<br />
Type: Statement<br />
Syntax: GOTO line number; GO TO line number<br />
Modes: Direct and program modes are both valid.<br />
Token: GOTO: $89 (137). Separate GO and TO tokens are also accepted.<br />
Abbreviated entry: G SHIFT-0<br />
Purpose: GOTO jumps to <strong>the</strong> specified BASIC line. IF and ON allow conditional<br />
GOTO statements.<br />
Notes:<br />
1. Using GOTO in direct mode. Direct mode GOTO executes <strong>the</strong> program in memory<br />
without executing CLR, so if <strong>the</strong> program has been previously run, <strong>the</strong> variables<br />
are retained. This is similar to CONT, except that any line can be selected as <strong>the</strong><br />
starting point. Variables can be changed, but <strong>the</strong>se will be lost if any BASIC pro<br />
gram lines are edited.<br />
2. Line numbers are read by <strong>the</strong> same routine that handles GOSUB's line numbers,<br />
and similar restrictions apply.<br />
Examples:<br />
1. TI$="235910": GOTO 1000<br />
This is a direct mode example; <strong>the</strong> clock is set just short of 24 hours, <strong>the</strong>n<br />
<strong>the</strong> program in memory is executed from line 1000 on, retaining <strong>the</strong> value of TI$.<br />
2. 100 GET A$: IF A$="" GOTO 100<br />
This simple loop awaits a keypress.<br />
IF-THEN<br />
Type: Conditional statement<br />
Syntax: IF logical expression THEN line number<br />
IF logical expression GOTO line number<br />
IF logical expression THEN statement [: statement...]<br />
Modes: Direct and program modes are both valid.<br />
Token: IF: $8B (139); THEN: $A7 (167)<br />
Abbreviated entry: IF: None; THEN: T SHIFT-H<br />
Purpose: IF-THEN statements allow conditional branching to any program line or<br />
conditional execution of statements after THEN.<br />
35
BASIC Reference Guide<br />
<strong>The</strong> expression after IF is treated as Boolean (that is, if zero it's false, if nonzero<br />
true). If <strong>the</strong> expression is true, <strong>the</strong> statement after THEN is performed; if it is false,<br />
<strong>the</strong> remainder of <strong>the</strong> line is ignored, and processing continues with <strong>the</strong> next line. (If<br />
<strong>the</strong> expression is a string, <strong>the</strong> effect depends on <strong>the</strong> last calculation to use <strong>the</strong><br />
floating-point accumulator, so IF X$ THEN may be true or false.)<br />
Examples:<br />
1. 1000 LC=LC+1: IF LC=60 THEN LC=0: GOSUB 5000<br />
This program excerpt increments <strong>the</strong> line count (LC); if LC is 60, it resets <strong>the</strong><br />
count value to 0 and calls a form advance subroutine at 5000 before continuing.<br />
2. 700 IF X=l THEN IF A=4 AND B=9 THEN PRINT "*"<br />
This is a composite IF statement, identical in effect to:<br />
IF X=l AND A=4 AND B=9 THEN PRINT "*"<br />
but probably a little faster.<br />
3. 500 IF X THEN PRINT "NONZERO"<br />
This example illustrates that IF X THEN is <strong>the</strong> same as IF X0 THEN.<br />
INPUT<br />
Type: Input statement<br />
Syntax: INPUT [string prompt in quotes;] variable name ^variable name...]<br />
Modes: Only program mode is available.<br />
Token: $85 (133)<br />
Abbreviated entry: None<br />
Purpose: INPUT is an easily programmed command which takes in data from <strong>the</strong> <strong>64</strong><br />
and assigns it to a variable. INPUT echoes <strong>the</strong> data to <strong>the</strong> screen, so editing features<br />
like DEL can be used. Pressing <strong>the</strong> RETURN key signals <strong>the</strong> end of INPUT.<br />
Notes:<br />
1. INPUTS prompts. INPUT N$ and INPUT "NAME";N$ illustrate <strong>the</strong> two forms of<br />
INPUT. Both print a question mark followed by a flashing cursor, but <strong>the</strong> second<br />
version also prints NAME, giving NAME? as <strong>the</strong> prompt. When you use an IN<br />
PUT statement with multiple variables—for example, INPUT X$,Y$—two consec<br />
utive question marks (??) will be displayed on <strong>the</strong> screen if only <strong>the</strong> first string is<br />
entered, followed by RETURN. In response to <strong>the</strong> double question marks, you<br />
simply enter <strong>the</strong> remaining data. Typing <strong>the</strong> two entries, separated by a comma<br />
(FIRST, SECOND) assigns both strings, with no fur<strong>the</strong>r prompt.<br />
<strong>The</strong>se demonstration lines show how <strong>the</strong> prompt string can be used:<br />
36<br />
100 INPUT "{CLR}{DOWN}{RIGHT}";X$<br />
This clears <strong>the</strong> screen and moves <strong>the</strong> cursor (and <strong>the</strong> question mark prompt)<br />
down and to <strong>the</strong> right of <strong>the</strong> home position.<br />
100 INPUT "{2 SPACES}-{LEFT} {LEFT} {LEFT} {LEFT}",X$<br />
This illustrates <strong>the</strong> technique of indicating <strong>the</strong> expected length of <strong>the</strong> input<br />
data. <strong>The</strong>re should be two spaces after <strong>the</strong> first quote, and two more cursor-lefts<br />
than hyphens.
BASIC Reference Guide<br />
100 INPUT "{RED}{RVS}NAME",N$<br />
This prints <strong>the</strong> prompt in red and reverse video. Long prompt strings may<br />
cause problems. If <strong>the</strong> user's response is long enough to cause <strong>the</strong> cursor to wrap<br />
around (move to <strong>the</strong> next line), <strong>the</strong> prompt may be added to <strong>the</strong> input. To avoid<br />
this, you may want to use a combination of PRINT and INPUT statements:<br />
PRINT "A LONG PROMPT MESSAGE";:INPUT X$<br />
You may not have to worry about this problem, since recent <strong>64</strong>s are free<br />
from this bug.<br />
Ano<strong>the</strong>r approach is to POKE <strong>the</strong> keyboard buffer; in this way <strong>the</strong> question<br />
mark can be eliminated. Type and run <strong>the</strong> following line:<br />
100 POKE 198,1: POKE 631,34: INPUT X$<br />
This inserts a quote in <strong>the</strong> keyboard buffer, effectively pressing quote just<br />
after INPUT is run, allowing strings like LDA $A000,X to be input despite <strong>the</strong>ir<br />
containing commas or colons. Chapter 6 has more on this.<br />
2. How input data is handled. When you press RETURN, <strong>the</strong> line of data is put into<br />
<strong>the</strong> input buffer to await processing. Chapter 6 has a more detailed discussion, but<br />
note that one effect of this is <strong>the</strong> inability to use INPUT in direct mode. Chapter 7<br />
explains how ML can solve this problem. After entry, <strong>the</strong> data in <strong>the</strong> buffer is<br />
matched with <strong>the</strong> list of variables after INPUT. <strong>The</strong> message 7EXTRA IGNORED<br />
means too many separate items were entered; <strong>the</strong> prompt ?? means too few items<br />
were entered and requests more; and 7REDO FROM START means <strong>the</strong> variable<br />
types didn't match <strong>the</strong> data entered.<br />
100 INPUT X<br />
<strong>The</strong> above line causes <strong>the</strong> computer to expect numeric input; it will accept<br />
123.4 and even 1E4, but not HELLO.<br />
100 INPUT X$,Y$<br />
In <strong>the</strong> above example, <strong>the</strong> computer expects two strings to be entered. It will<br />
accept HELLO, THERE but as two separate strings; if HELLO, THERE, <strong>64</strong> is en<br />
tered, <strong>the</strong> computer will accept HELLO as <strong>the</strong> first string, THERE as <strong>the</strong> second,<br />
and will not accept <strong>the</strong> characters <strong>64</strong>, but will instead respond with 7EXTRA<br />
IGNORED.<br />
Generally, <strong>the</strong>se aren't serious problems, unless a program is intended to be<br />
foolproof, in which case <strong>the</strong> GET statement is essential (see Chapter 4 for more<br />
information). Without some kind of error trapping, a user could type HOME,<br />
CTRL-WHT, SHIFT-RUN/STOP, or quotes and <strong>the</strong> INPUT statement would be<br />
wrecked.<br />
Note: All prompts (?, ??, 7EXTRA IGNORED) are suppressed when INPUT#<br />
is in use. When using INPUT:<br />
POKE 19,1<br />
has this effect. Similarly, <strong>the</strong> following line, which opens a file to <strong>the</strong> keyboard,<br />
suppresses prompts:<br />
OPEN 1,0: INPUT#1,X$<br />
37
BASIC Reference Guide<br />
Examples:<br />
1. INPUT "ENTER NAME";N$: PRINT "HELLO, " N$<br />
This is a straightforward string input of N$, followed by a personal greeting.<br />
2. FOR J=l TO 10: INPUT Xfl): NEXT<br />
<strong>The</strong> above line inputs ten numbers into an array.<br />
3. 100 INPUT "DEVICE{2 SPACES}3{LEFT}{LEFT}{LEFT}";D<br />
This example shows one method to allow a default value for INPUT. In this<br />
case, simply pressing RETURN assigns <strong>the</strong> value 3 to D, which saves time for <strong>the</strong><br />
user. Ano<strong>the</strong>r method, which doesn't print <strong>the</strong> default under <strong>the</strong> cursor, is this:<br />
4. 100 X$="YES": INPUT X$<br />
If <strong>the</strong> user simply presses RETURN, X$ retains <strong>the</strong> value "YES".<br />
INPUT#<br />
Type: Input statement<br />
Syntax: INPUT# numeric expression, variable name ^variable name...]<br />
<strong>The</strong> numeric expression, a file number, must evaluate and truncate to a number<br />
in <strong>the</strong> range 1-255.<br />
Mode: Only program mode is available.<br />
Token: $84 (132)<br />
Abbreviated entry: I SHIFT-N (This includes <strong>the</strong> #.)<br />
Purpose: INPUT# provides an easy method to read variables from a file, usually on<br />
tape or disk. <strong>The</strong> format is <strong>the</strong> same as with PRINT#; <strong>the</strong> data consists of ASCII<br />
characters separated by RETURN characters. Provided INPUT# matches PRINT#,<br />
this command should be trouble-free.<br />
Note: INPUT# is closely similar to INPUT. Below is a list of differences between <strong>the</strong><br />
two:<br />
• Since <strong>the</strong> device generally can't use it, no prompt is printed.<br />
• Some characters are ignored (like spaces without text, and screen editing characters)<br />
unless preceded by quotes. Similarly:<br />
PRINT#1,"HELLO:THERE"<br />
is read by INPUT# as two strings, because <strong>the</strong> colons are treated as separators.<br />
Usually, PRINT# with straightforward variables will help you avoid <strong>the</strong>se bugs.<br />
• INPUT# can't take in a string longer than 88 characters, as this results in a<br />
7STRING TOO LONG ERROR. Screen input doesn't have this problem, since part<br />
of an overlong string is simply ignored.<br />
• ST signals <strong>the</strong> end-pf-file point, as it does with <strong>the</strong> GET# statement.<br />
Example:<br />
10 OPEN 1 :REM READ TAPE FILE<br />
20 DIM D$(100): FOR J=l TO 100: INPUT#1,D$(J): NEXT<br />
This example reads 100 strings from a previously written tape file into an array.<br />
38
BASIC Reference Guide<br />
INT<br />
Type: Numeric function<br />
Syntax: INT(numeric expression)<br />
Modes: Direct and program modes are both valid.<br />
Token: $B5 (181)<br />
Abbreviated entry: None<br />
Purpose: INT, <strong>the</strong> integer function, converts <strong>the</strong> numeric expression to <strong>the</strong> nearest<br />
integer less than or equal to <strong>the</strong> expression. Two sample results of <strong>the</strong> integer func<br />
tion are INT(10.4) is 10, and INT(—2.2) is -3. <strong>The</strong> expression is assumed to be in<br />
<strong>the</strong> full range for numerals, between about -1.7 E38 and +1.7 E38. So<br />
L=INT(123456.7) is valid. But L%=INT(123456.7) gives an error, since <strong>the</strong> result is<br />
too large for an integer variable.<br />
Examples:<br />
1. 100 PRINT INT(X+.5) :REM ROUND TO NEAREST WHOLE NUMBER<br />
<strong>The</strong> above example rounds numbers—including negative numbers—to <strong>the</strong><br />
nearest whole numer.<br />
2. 100 PRICE = INT(.5 + P*(l+MARKUP/100))<br />
This calculates price to <strong>the</strong> nearest cent from percentage markup and pur<br />
chase price in cents.<br />
LEFT$<br />
Type: String function<br />
Syntax: LEFT$(string expression, numeric expression)<br />
Modes: Direct and program modes are both valid.<br />
Token: $C8 (200)<br />
Abbreviated entry: LE SHIFT-F (This includes <strong>the</strong> $.)<br />
Purpose: LEFT$ returns a substring consisting of <strong>the</strong> leftmost characters of <strong>the</strong> orig<br />
inal string expression. <strong>The</strong> numeric expression (which must evaluate to 0-255) is<br />
compared with <strong>the</strong> length of <strong>the</strong> string; <strong>the</strong> smaller of <strong>the</strong> two determines <strong>the</strong> length<br />
of <strong>the</strong> substring.<br />
Examples:<br />
1. FOR J=0 TO 20: PRINT LEFT$("HELLO THERE",J): NEXT<br />
This prints 20 strings, "", "H", "HE", "HEL", and so on. As J increases, <strong>the</strong><br />
number of characters printed does, too. However, <strong>the</strong> number of characters<br />
printed never exceeds 11, <strong>the</strong> length of <strong>the</strong> original string. Thus, <strong>the</strong> eleventh<br />
through twentieth strings printed will be identical.<br />
2. PRINT LEFT$(X$ + " ",20)<br />
This formatting trick pads X$ to exactly 20 characters with hyphens. This<br />
way <strong>the</strong> output is always 20 characters long.<br />
3. PRINT LEFT$(" ",20-LEN(X$)); X$<br />
This line right justifies X$, preceding it with hyphens. (X$ is assumed not to<br />
be longer than 20. O<strong>the</strong>r characters, notably spaces, are usable in <strong>the</strong> same way to<br />
format output.)<br />
39
BASIC Reference Guide<br />
LEN<br />
Type: Numeric function<br />
Syntax: LEN(string expression)<br />
Modes: Direct and program modes are both valid.<br />
Token: $C3 (195)<br />
Abbreviated entry: None<br />
Purpose: LEN determines <strong>the</strong> length of a string expression. <strong>The</strong> result is always be<br />
tween 0 and 255 (see Chapter 6 for more details).<br />
Examples:<br />
1. 10 PRINT SPC(20-LEN(MSG$)/2);MSG$<br />
This line will center a short message on <strong>the</strong> computer screen by adding lead<br />
ing spaces.<br />
2. 50 IF LEN(IN$)oL THEN PRINT "MUST BE" L "DIGITS": GOTO 40<br />
<strong>The</strong> above excerpt rejects an input string of <strong>the</strong> wrong length, sending <strong>the</strong><br />
program back to line 40, where ano<strong>the</strong>r attempt may be tried.<br />
3. 100 FOR J=l TO LEN(W$): IF L$=MID$(W$,J,1) GOTO 200: NEXT<br />
110 PRINT "NOT FOUND"<br />
This checks word W$ for <strong>the</strong> presence of letter L$. <strong>The</strong> use of LEN(W$) al<br />
lows <strong>the</strong> program to set <strong>the</strong> loop counter for any length of W$.<br />
LET<br />
Type: Statement<br />
Syntax: [LET] numeric variable = numeric or logical expression<br />
[LET] integer variable — numeric expression in range —32768 to +32767 or logical<br />
expression<br />
[LET] string variable = string expression<br />
Modes: Direct and program modes are both valid.<br />
Token: $88 (136)<br />
Abbreviated entry: L SHIFT-E<br />
Purpose: LET assigns a number or string to a variable. <strong>The</strong> statement LET is usually<br />
omitted, since <strong>the</strong> <strong>64</strong> assumes LET by default. Simple or array variables may be<br />
used, and if a simple or array variable doesn't already exist, LET makes room for it<br />
in <strong>the</strong> variable storage area in memory. LET will dimension an array to <strong>the</strong> default<br />
size of 11 (elements 0 through 10) if it has not been previously dimensioned with a<br />
DIM statement.<br />
Notes:<br />
1. Chapter 6 has full details on variable storage; it also has a routine, VARPTR,<br />
showing how LET can be used from ML. Since LET is rarely used, it can be<br />
modifed by <strong>the</strong> user (Chapter 7 demonstrates this).<br />
2. Variables can be reassigned freely, so be careful not to try using a variable for two<br />
purposes simultaneously. This is often a problem when using subroutines, because<br />
it is harder to keep track of variables.<br />
40
BASIC Reference Guide<br />
Examples:<br />
1. X=123456: LET X=123456<br />
Both of <strong>the</strong>se statements set X to 123456.<br />
2. Q%=Q/100<br />
<strong>The</strong> above line sets Q% equal to <strong>the</strong> integer portion of Q/100, so if<br />
Q = 1234, Q% = 12.<br />
3. LET QH%=Q/256: LET QL%=Q-QH%*256<br />
This sets QH% and QL% equal to <strong>the</strong> high and low bytes of <strong>the</strong> number Q.<br />
LIST<br />
Type: Command<br />
Syntax: LIST [line number] [-] [line number]<br />
Modes: Direct and program modes are both valid.<br />
Token: $9B (155)<br />
Abbreviated entry: L SHIFT-I<br />
Purpose: LIST displays part or all of BASIC in memory to <strong>the</strong> screen, or (with CMD)<br />
to disk, tape, or o<strong>the</strong>r output device.<br />
Notes:<br />
1. Line numbers must be ASCII characters, not variables.<br />
2. LIST uses many RAM locations; it always exits to READY mode if used within a<br />
program.<br />
3. LOAD errors and o<strong>the</strong>r errors may show up in LIST. For example, if a machine<br />
language program designed for some o<strong>the</strong>r part of memory is loaded into <strong>the</strong><br />
BASIC program area, an attempt to LIST will show only random characters.<br />
4. Chapter 6 lists BASIC tokens and has examples of BASIC storage in memory.<br />
Also, a LIST reference has programs to modify LIST in useful ways. (Chapter 8<br />
shows how it's done.) <strong>The</strong> entry for REM has notes on <strong>the</strong> way LIST interprets<br />
screen-editing and o<strong>the</strong>r characters. TRACE is a modified LIST which works while<br />
a program runs. UNLIST shows ways to protect your programs.<br />
Examples:<br />
1. LIST 2000-2999<br />
<strong>The</strong> line above displays <strong>the</strong> BASIC lines from 2000 through 2999.<br />
2. LOAD "$",8 followed by LIST<br />
This displays a disk directory, which is stored in memory as though it were a<br />
BASIC program.<br />
3. 1000 LIST -10<br />
This lists all lines up to and including line 10 of a BASIC program. As<br />
shown in this example, LIST can be included within a BASIC program line. How<br />
ever, execution of this line will stop <strong>the</strong> program, and CONT will not restart it.<br />
4. LIST 1100-<br />
This displays all lines in <strong>the</strong> current BASIC program with line numbers of<br />
1100 or greater. If <strong>the</strong>re is no line 1100 in <strong>the</strong> current program, <strong>the</strong> listing begins<br />
with <strong>the</strong> first existing line greater than 1100.<br />
41
BASIC Reference Guide<br />
LOAD<br />
Type: Command<br />
Syntax:<br />
Tape: LOAD [string expression [,numeric expression^ numeric expression]]]<br />
All parameters are optional. <strong>The</strong> first numeric expression, if used, must evaluate<br />
to 1 (device number). <strong>The</strong> second normally evaluates to 0 (BASIC LOAD) or 1<br />
(forced LOAD). Chapter 14 has full details.<br />
Disk: LOAD string expression, numeric expression [, numeric expression]<br />
A name and a numeric expression, typically 8, which is <strong>the</strong> device number, are<br />
required. <strong>The</strong> second numeric parameter has <strong>the</strong> same meaning as in tape LOAD.<br />
Chapter 15 has full details.<br />
Modem: LOAD cannot be used with a modem. Attempting to use device number<br />
2, <strong>the</strong>refore, will result in an error.<br />
Modes: Direct and program modes are both valid. (See "Notes" below.)<br />
Token: $93 (147)<br />
Abbreviated entry: L SHIFT-O<br />
Purpose: LOAD reads from an external device, filling RAM with a BASIC program,<br />
ML, graphics, or o<strong>the</strong>r data. In its simplest form, LOAD {RETURN}, <strong>the</strong>n RUN {RE<br />
TURN} loads BASIC from tape and runs it. (SHIFT-RUN/STOP does this, too.)<br />
Notes:<br />
1. LOAD is followed by a standard set of messages, like PRESS PLAY ON TAPE, OK<br />
when <strong>the</strong> cassette starts, and so on. <strong>The</strong>se are listed in <strong>the</strong> chapters on tape and<br />
disk usage. Program mode LOADs don't have <strong>the</strong>se messages (apart from PRESS<br />
PLAY ON TAPE, which can't be avoided), so <strong>the</strong> screen layout can be kept tidy.<br />
2. Tape LOAD blanks <strong>the</strong> TV screen to <strong>the</strong> border color during any tape reading.<br />
When a program or file header is found, a FOUND message is displayed on<br />
screen for about ten seconds, after which loading takes place if <strong>the</strong> program name<br />
is acceptable, and <strong>the</strong> screen temporarily blanks again. O<strong>the</strong>rwise, <strong>the</strong> process of<br />
searching goes on. Pressing <strong>the</strong> <strong>Commodore</strong> key, or one of several o<strong>the</strong>r keys, cuts<br />
<strong>the</strong> ten-second pause short.<br />
3. A BASIC program LOAD nearly always requires that LOAD'S third parameter be<br />
0. This allows LOAD to relink BASIC, so that any start-of-BASIC position is<br />
acceptable. For example:<br />
LOAD "BASIC PROG"<br />
loads that program from tape into <strong>the</strong> <strong>64</strong> with any memory configuration and pre<br />
pares it for RUN. In fact:<br />
POKE 43,LO: POKE 44,HI: POKE HI*256+LO,0: NEW<br />
followed by <strong>the</strong> correct disk or tape LOAD can put a BASIC program anywhere<br />
you choose, if <strong>the</strong>re's room for it.<br />
4. Loading ML, graphics definitions, and o<strong>the</strong>r data is generally trickier than loading<br />
BASIC programs, and needs a LOAD format like this:<br />
LOAD "CHARSET",1,1<br />
42
BASIC Reference Guide<br />
to insure that <strong>the</strong> data is put back where it came from. Supermon's .L load com<br />
mand (see Chapter 7) does this. Block LOAD in Chapter 6 explains how blocks of<br />
bytes can be loaded without disturbing BASIC.<br />
5. Program mode LOADs generally chain BASIC; see CHAIN in Chapter 6, and also<br />
OLD, which explains how to chain a long program from a shorter one.<br />
Examples:<br />
1. LOAD: LOAD "",1<br />
<strong>The</strong>se are tape LOADs and have identical effects. Ei<strong>the</strong>r loads <strong>the</strong> first<br />
BASIC program found on tape.<br />
2. LOAD "PROG"<br />
This loads <strong>the</strong> program with <strong>the</strong> filename PROG from tape. Actually, be<br />
cause of <strong>the</strong> filename checking scheme used, <strong>the</strong> first program encountered on<br />
tape having a name beginning with PROG (PROGRAM, or PROGDEMO, for ex<br />
ample) will be loaded.<br />
3. LOAD "PROG",8<br />
This line will load only PROG from disk. No o<strong>the</strong>r program with a name<br />
beginning with PROG will be loaded if PROG is not found; instead, a ?FILE NOT<br />
FOUND ERROR will be reported.<br />
4. LOAD "PAC*",8<br />
This illustrates a typical disk pattern-matching LOAD command, which will<br />
load PACMAN, PACKER, or <strong>the</strong> first program beginning with PAC.<br />
5. 10000 PRINT "PLEASE WAIT": LOAD "PART2"<br />
<strong>The</strong> above line loads and <strong>the</strong>n runs <strong>the</strong> tape program PART2 from within<br />
BASIC. If <strong>the</strong> correct key on <strong>the</strong> tape deck is pressed, no message appears on <strong>the</strong><br />
TV.<br />
6. 10 IF X=0 THEN X=l: LOAD "GRAPHICS",1,1<br />
20 REM THE PROGRAM CONTINUES HERE AFTER THE LOAD<br />
This loads <strong>the</strong> graphics into a fixed area of memory. A LOAD command<br />
from within a program causes that program to be run again from <strong>the</strong> start. <strong>The</strong><br />
variable, X, is used as a flag, which prevents GRAPHICS from being loaded<br />
repeatedly and allows <strong>the</strong> program to continue.<br />
LOG<br />
Type: Numeric function<br />
Syntax: LOG(numeric expression)<br />
Modes: Direct and program modes are both valid.<br />
Token: $BC (188)<br />
Abbreviated entry: None<br />
Purpose: LOG returns <strong>the</strong> natural logarithm (log to <strong>the</strong> base e) of a positive<br />
arithmetic expression. This function is <strong>the</strong> converse of EXP.<br />
Logarithms transform multiplication and division into addition and subtraction;<br />
for example, LOG(l) is 0 since multiplication by 1 has no effect. Logarithms are used<br />
mainly in scientific work; <strong>the</strong>ir susceptibility to rounding errors makes <strong>the</strong>m less suit<br />
able for commercial work.<br />
43
BASIC Reference Guide<br />
Examples:<br />
1. PRINT LOG(X)/LOG(10) :REM LOG TO BASE 10<br />
PRINT LOG(X)/LOG(2) :REM LOG TO BASE 2<br />
PRINT EXP(LOG(A)+LOG(B)) :REM PRINTS A*B<br />
<strong>The</strong>se are all standard uses of <strong>the</strong> LOG function.<br />
2. LF=(N+.5)*(LOG(N)-1) + 1.41894 + 1/(12*N)<br />
This defines LF, an approximation of LOG(N!), so that EXP(LF) approxi<br />
mately equals N!. This illustrates how LOG helps when using very large numbers.<br />
MID$<br />
Type: String function<br />
Syntax: MlD$(string expression, numeric expression ^numeric expression])<br />
Modes: Direct and program modes are both valid<br />
Token: $CA (202)<br />
Abbreviated entry: M SHIFT-I (This includes <strong>the</strong> $.)<br />
Purpose: MID$ extracts any required substring from a string expression. <strong>The</strong> first nu<br />
meric parameter is <strong>the</strong> starting point (1 represents <strong>the</strong> first character of <strong>the</strong> original<br />
string, 2 <strong>the</strong> second, and so on). <strong>The</strong> final parameter is <strong>the</strong> length of <strong>the</strong> substring to<br />
be extracted. If this isn't used, <strong>the</strong> substring extends to <strong>the</strong> end of <strong>the</strong> original string.<br />
Examples:<br />
1. N$=MID$(STR$(N),2) :REM REMOVE LEADING SPACE FROM N<br />
This is useful when a number's leading spaces aren't wanted. It works with<br />
any positive numbers in <strong>the</strong> correct range.<br />
2. 10 INPUT X$: L=LEN(X$)<br />
20 FOR J=l TO L: PRINT MID$(X$,L-J+1,1);: NEXT<br />
This inputs a string, <strong>the</strong>n prints it out backward, one character at a time.<br />
NEW<br />
Type: Command<br />
Syntax: NEW<br />
Modes: Direct and program modes are both valid.<br />
Token: $A2 (162)<br />
Abbreviated entry: None<br />
Purpose: NEW allows a new BASIC program to be entered, by ignoring any pre<br />
vious program. It corrects pointers after a forced (nonrelocating) LOAD like:<br />
LOAD "ML",1,1<br />
so BASIC can operate without an ?OUT OF MEMORY ERROR.<br />
Notes:<br />
1. Actually, most of BASIC and all ML routines and data are unaltered; NEW puts<br />
zero bytes at <strong>the</strong> start of BASIC, resets pointers, and performs a CLR, which<br />
44
BASIC Reference Guide<br />
aborts files, among o<strong>the</strong>r things. OLD in Chapter 6 will recover BASIC after NEW<br />
(or after resetting by <strong>the</strong> method described in Chapter 5), provided new program<br />
lines haven't been entered.<br />
2. NEW may sometimes generate 7SYNTAX ERROR. (See <strong>the</strong> error message notes.)<br />
Examples:<br />
1. NEW<br />
In direct mode, NEW readies <strong>the</strong> <strong>64</strong> for a new program. (Without NEW, <strong>the</strong><br />
program would be simply added to <strong>the</strong> one currently in BASIC as extra or replace<br />
ment lines.)<br />
2. 20000 NEW: REM PROGRAM NO LONGER WANTED<br />
This exits to READY mode. <strong>The</strong> program won't LIST and appears erased.<br />
NEXT<br />
Type: Statement<br />
Syntax: NEXT [numeric variable][,numeric variable...]<br />
Modes: Direct and program modes are both valid.<br />
Token: $82 (130)<br />
Abbreviated entry: N SHIFT-E<br />
Purpose: NEXT marks <strong>the</strong> end of a FOR-NEXT loop. See FOR, which has a detailed<br />
account of loop processing.<br />
Examples:<br />
1. FOR 1=1 TO 10: FOR J=l TO 10: PRINT I*J;:NEXT J: PRINT: NEXT<br />
This prints an unformatted multiplication table for values up to 10 X 10.<br />
Note that NEXT:PRINT:NEXT works, too. In fact, it's a little faster. NEXT J: NEXT<br />
I can be replaced with NEXT J,I. Once a program is debugged, <strong>the</strong> variables<br />
following NEXT statements can generally be removed; however, <strong>the</strong>y do improve<br />
readability.<br />
2. 80 FOR J=l TO 2000: GET X$: IF X$="" THEN NEXT<br />
81 FOR J=0 TO 0: NEXT<br />
This delays approximately ten seconds, unless a key is pressed; if it is, line<br />
81 gets rid of <strong>the</strong> still active J loop.<br />
3. 10 FOR J=l TO 3: GOTO 40<br />
20 NEXT K<br />
30 NEXT J: END<br />
40 FOR K=l TO 2: GOTO 20<br />
NEXT can appear anywhere, allowing clumsy constructions like <strong>the</strong> one<br />
above.<br />
NOT<br />
Type: Logical operator<br />
Syntax: NOT logical or numeric expression<br />
Numeric expressions must evaluate after truncating to —32768 to +32767.<br />
45
BASIC Reference Guide<br />
Modes: Direct and program modes are both valid.<br />
Token: $A8 (168)<br />
Abbreviated entry: N SHIFT-O<br />
Purpose: NOT computes <strong>the</strong> logical NOT of an expression. Logical expressions are<br />
converted from false to true, and vice versa. Numeric expressions are converted to<br />
16-bit signed binary form, and each bit is inverted. <strong>The</strong> result, like <strong>the</strong> original, is al<br />
ways in <strong>the</strong> range —32768 to +32767, and always equals —1 minus <strong>the</strong> original<br />
value. So NOT of arithmetic expressions does not necessarily convert true to false.<br />
Note: NOT has precedence over AND and OR. Thus:<br />
NOT A AND B<br />
is identical to:<br />
(NOT A) AND B<br />
<strong>The</strong> usual rules of logic apply to NOT, AND, and OR.<br />
Examples:<br />
1. 55 IF X$=CHR$(34) THEN Q=NOT Q<br />
This line flips a quote mode flag, denoting whe<strong>the</strong>r quote mode is on or off.<br />
2. IF NOT OK THEN GOSUB 20000: REM ERROR MESSAGE AT 20000<br />
<strong>The</strong> above line uses <strong>the</strong> result of variable OK, set in earlier tests, to test for<br />
errors.<br />
ON<br />
Type: Conditional statement<br />
Syntax: ON numeric expression GOTO line number [,line number...]<br />
ON numeric expression GOSUB line number [,line number...]<br />
<strong>The</strong> numeric expression must evaluate and truncate to 0-255.<br />
Modes: Direct and program modes are both valid.<br />
Token: $91 (145)<br />
Abbreviated entry: None<br />
Purpose: ON allows a conditional branch to one of <strong>the</strong> listed line numbers, depend<br />
ing on <strong>the</strong> value of <strong>the</strong> expression after ON. If it is 1, <strong>the</strong> first line number is used; if<br />
it is 2, <strong>the</strong> second is used, and so on. If <strong>the</strong> value is 0 or too large for <strong>the</strong> list, <strong>the</strong><br />
line is ignored and processing continues with <strong>the</strong> next statement. This provides a<br />
readable method of programming multiple IFs, provided a variable takes consecutive<br />
values 1, 2, 3, ...<br />
Examples:<br />
1. ON SGN(X)+2 GOTO 100,200,300<br />
<strong>The</strong> above line branches three ways, depending on X being negative, zero, or<br />
positive.<br />
2. 90 ON ASC(IN$)-<strong>64</strong> GOTO 100,200,300,400<br />
This line jumps to one of <strong>the</strong> lines listed, depending on IN$ being A, B, C,<br />
or D.<br />
46
BASIC Reference Guide<br />
3. 30 ON 6*RND(1)+1 GOSUB 100,200,300,400,500,600<br />
This selects at random one of six subroutines in a game.<br />
4. 100 ON X GOTO 400,410,420,430,440,450<br />
101 ON X-6 GOTO 460,470,480<br />
Above is an example of how options can be spread over several program<br />
lines (provided X is not 0).<br />
OPEN<br />
Type: Input/output statement<br />
Syntax:<br />
Tape: OPEN numeric expression [,numeric expression [.numeric expression [.string<br />
expression]]]<br />
<strong>The</strong> first numeric expression, <strong>the</strong> file number, must evaluate to 1-255; <strong>the</strong> sec<br />
ond is <strong>the</strong> device number, which is 1; <strong>the</strong> third sets read or write type; and <strong>the</strong> op<br />
tional string expression is <strong>the</strong> filename. Chapter 14 has full details.<br />
Disk: OPEN numeric expression, numeric expression, numeric expression [,string ex<br />
pression].<br />
Here, <strong>the</strong> file number must be 1-255, <strong>the</strong> device number is usually 8, <strong>the</strong><br />
secondary address is usually between 2 and 15, and <strong>the</strong> string expression a com<br />
mand like "SEQ FILE,W", which <strong>the</strong> disk drive itself, not BASIC, processes. Chapter<br />
15 has full details.<br />
Modems and o<strong>the</strong>r RS-232 devices: <strong>The</strong> same as for a disk drive, except that <strong>the</strong><br />
device number is 2, and <strong>the</strong> string expression is a pair of bytes which set transmit/<br />
receive features. Chapter 17 has full details.<br />
Printers Hfid o<strong>the</strong>r write-only devices: <strong>The</strong>se require file and device numbers. <strong>The</strong><br />
string expression is irrelevant with <strong>the</strong>se devices, while <strong>the</strong> third numeric parameter<br />
may or may not matter. See Chapter 17.<br />
Tape and disk filenames can't exceed 16 characters.<br />
Modes: Direct and program modes are both valid.<br />
Token: $9F (159)<br />
Abbreviated entry: O SHIFT-P<br />
Purpose: OPEN sets up a file to write or read (sometimes both) data to or from ex<br />
ternal devices like tape or disk drives. For example, <strong>the</strong> statement:<br />
OPEN 1,1,1/TAPE FILE"<br />
opens logical file 1, called "TAPE FILE", to <strong>the</strong> cassette. After this, <strong>the</strong> statement:<br />
PRINT#1<br />
followed by data will write <strong>the</strong> data to tape, and<br />
CLOSE 1<br />
leaves a complete new file called "TAPE FILE", which can be read back later, typi<br />
cally by OPEN 1 and INPUT#1,X$ or similar statements.<br />
As many as ten files (enough for almost any purpose) can be open at once; each<br />
must have a different logical file number (<strong>the</strong> first parameter of OPEN) so that <strong>the</strong>y<br />
47
BASIC Reference Guide<br />
are distinguished. In addition, <strong>the</strong> secondary addresses of disk files must all be dif<br />
ferent. Three tables in RAM store <strong>the</strong> file numbers along with <strong>the</strong>ir device numbers<br />
and o<strong>the</strong>r information.<br />
Note: Opening a file to tape blanks <strong>the</strong> screen during tape read/write activity. OPEN<br />
1 in direct mode (valuable for reading a program header's information) pauses for<br />
ten seconds before returning to READY unless <strong>the</strong> <strong>Commodore</strong> key, space bar, or<br />
one of a few o<strong>the</strong>r keys is pressed. OPEN 1 in program mode does not result in a<br />
pause.<br />
Examples:<br />
1. OPEN 2,l,0,"TAX"<br />
<strong>The</strong> above example opens a file from tape called TAX (or TAXI, TAXI<br />
DERMY, etc.) for reading (since <strong>the</strong> third parameter is 0), and assigns it logical file<br />
2, so INPUT#2 or GET#2 will fetch data from <strong>the</strong> file. This is identical to OPEN<br />
2, except that <strong>the</strong> file is asked for by name; OPEN 2 opens <strong>the</strong> first file it finds.<br />
With tape, OPEN reads tape until it finds a header.<br />
2. OPEN 1,8,3/'ORDINARY FILE,S,R"<br />
<strong>The</strong> line above opens a sequential file (specified by <strong>the</strong> ,S after <strong>the</strong> filename)<br />
on disk called ORDINARY FILE for reading (specified by <strong>the</strong> ,R after <strong>the</strong> filename)<br />
by INPUT#1 or GET#1 statements.<br />
3. OPEN 2,2,0,CHR$(6)<br />
This is an OPEN which prepares <strong>the</strong> modem (device 2) for PRINT#2 and<br />
INPUT#2. <strong>The</strong> string expression is used to set modem parameters such as parity<br />
and data transfer rate.<br />
4. OPEN 4,4: REM OPENS FILE#4 TO DEVICE#4<br />
This line opens a file to a printer, assuming that <strong>the</strong> printer (and interface, if<br />
one is used) operate like a standard <strong>Commodore</strong> printer.<br />
OR<br />
Type: Logical operator<br />
Syntax: Logical or numeric expression OR logical or numeric expression<br />
Numeric expressions must evaluate after truncating to -32768 to +32767.<br />
Modes: Direct and program modes are both valid.<br />
Token: $B0 (176)<br />
Abbreviated entry: None<br />
Purpose: OR calculates <strong>the</strong> logical OR of two expressions, by performing an OR on<br />
each of <strong>the</strong> bits in <strong>the</strong> first operand and <strong>the</strong> corresponding bits in <strong>the</strong> second. For <strong>the</strong><br />
purposes of <strong>the</strong> OR comparison, numeric expressions are evaluated as 16-bit signed<br />
binary numbers. <strong>The</strong> four possible combinations of single bits are:<br />
0 OR 0 = 0<br />
0 OR 1 = 1<br />
1 OR 0 = 1<br />
1 OR 1 = 1<br />
<strong>The</strong> result is 0 only if both bits are 0.<br />
48
BASIC Reference Guide<br />
It follows that a logical OR is true if ei<strong>the</strong>r or both of <strong>the</strong> original expressions<br />
were true. And it follows that:<br />
380 OR 75 = 383<br />
though verifying this by finding <strong>the</strong> binary arithmetic forms of 380 and 75 is some<br />
what tedious.<br />
Examples:<br />
1. 560 IF (A20) THEN PRINT "OUT OF RANGE"<br />
This is a typical validation test; A must be a value from 1 to 20.<br />
2. POKE 328,PEEK(328) OR 32<br />
<strong>The</strong> above POKE and PEEK combination sets bit 5 of location 328 to 1,<br />
whe<strong>the</strong>r or not it was 1 before, leaving <strong>the</strong> o<strong>the</strong>r bits unaltered. OR can set bits<br />
high; AND can clear <strong>the</strong>m to 0.<br />
Type: Numeric function<br />
Syntax: PEEK(numeric expression)<br />
<strong>The</strong> expression must evaluate to a number in <strong>the</strong> range 0-65535; <strong>the</strong> value re<br />
turned will be in <strong>the</strong> range 0-255.<br />
Modes: Direct and program modes are both valid.<br />
Token: $C2 (194)<br />
Abbreviated entry: P SHIFT-E<br />
Purpose: PEEK returns <strong>the</strong> decimal value of <strong>the</strong> byte in a memory location. PEEK<br />
allows BASIC programs and <strong>the</strong>ir variables and pointers to be examined, plus o<strong>the</strong>r<br />
internal memory, like ML programs, <strong>the</strong> BASIC interpreter, hardware registers, and<br />
so on.<br />
Notes:<br />
1. PEEK (like POKE) is unusual in that it is easily replaced by ML routines. Chapter<br />
17, for example, has ML routines to read joystick values, which are much faster<br />
than using PEEK in BASIC.<br />
2. A number of locations are modified by hardware and <strong>the</strong>refore have values which<br />
vary: joystick, paddle, and keyboard locations are obvious examples. Some loca<br />
tions vary as a result of software modification; much of <strong>the</strong> memory area from<br />
locations 0-255 (called <strong>the</strong> zero page) is used by BASIC as it operates, and numer<br />
ous locations in <strong>the</strong> range 256-1023 are used from time to time during BASIC<br />
execution. Most of <strong>the</strong> memory above 32768 can be reallocated for different uses<br />
and is largely controlled by <strong>the</strong> byte in memory location 1, as Chapter 5 explains.<br />
Examples:<br />
1. PRINT CHR$(34);: FOR J=2048 TO 2147: PRINT CHR$(PEEK(J));: NEXT<br />
This prints 100 characters PEEKed from <strong>the</strong> start of <strong>the</strong> <strong>64</strong>'s normal BASIC<br />
program storage area. (<strong>The</strong> quotation mark generated by CHR$(34) is an attempt<br />
to prevent spurious control characters from clearing <strong>the</strong> screen, etc.)<br />
2. 500 IF (PEEK(653) AND 1)=1 THEN PRINT "SHIFT KEY"<br />
This tests bit 0 of location 653—<strong>the</strong> byte that stores <strong>the</strong> SHIFT, <strong>Commodore</strong><br />
key, and CTRL key flags—to determine if <strong>the</strong> SHIFT key is pressed.<br />
49
BASIC Reference Guide<br />
POKE<br />
Type: Statement<br />
Syntax: POKE numeric expression, numeric expression<br />
<strong>The</strong> first numeric expression is an address, and <strong>the</strong> second is a one-byte value,<br />
so <strong>the</strong> ranges must be 0-65535 and 0-255, respectively.<br />
Modes: Direct and program modes are both valid.<br />
Token: $97 (151)<br />
Abbreviated entry: P SHIFT-O<br />
Purpose: POKE stores <strong>the</strong> byte specified by <strong>the</strong> second expression into <strong>the</strong> address<br />
given by <strong>the</strong> first. POKE can store ML routines into memory from DATA statements,<br />
alter BASIC pointers, alter hardware registers, and perform o<strong>the</strong>r useful functions.<br />
Notes:<br />
1. POKE (like PEEK) can be replaced by simple ML routines; replacing a POKE to<br />
<strong>the</strong> screen with <strong>the</strong> ML equivalent is aij ideal introduction to ML (see Chapter 7).<br />
2. A careless POKE to an uninitialized variable, like:<br />
POKE A0,0<br />
in place of:<br />
POKE A,0<br />
will gltfr <strong>the</strong> direction register at location 0; this affects tape and <strong>the</strong> <strong>64</strong>'s<br />
RAM/ROM bank switching.<br />
Examples:<br />
1. POKE 53281,9<br />
This changes <strong>the</strong> screen background color by altering a VIC chip register.<br />
2. Chapter 6 has a large number of programs which READ values from DATA state<br />
ments, <strong>the</strong>n POKE <strong>the</strong>m into RAM.<br />
3. FOR J=0 TO 499: POKE 1024+J, PEEK(2048+J): POKE 55296+J,0: NEXT<br />
This puts |0P bytes of BASIC program data onto <strong>the</strong> screen in j^lack, by<br />
POKEing values "to both screen and color memory. '*;<br />
4. FOR J=40960 TO 49151: POKE J, PEEKg): NEXT: POKE 1,54<br />
This moves <strong>the</strong> BASIC ROM ($A000-$BpFF) into RAM, <strong>the</strong>n switches that<br />
part of memory to RAM, so <strong>the</strong> BASIC interpreter is now held in RAM. Normally,<br />
POKE J, PEEK(J) has no effect, of course. <strong>The</strong> example works only because <strong>the</strong> <strong>64</strong><br />
is designed so that POKEd information goes into RAM which is underneath ROM.<br />
(This means that ei<strong>the</strong>r <strong>the</strong> usual ROM or alternative RAM may be used. This<br />
technique will not work with most o<strong>the</strong>r computers.)<br />
POS<br />
Type: Numeric function<br />
Syntax: POS(numeric expression)<br />
<strong>The</strong> numeric expression is a dummy expression, as with FRE.<br />
Modes: Direct and program modes are both valid.<br />
50
BASIC Reference Guide<br />
Token: $B9 (185)<br />
Abbreviated entry: None<br />
Purpose: POS returns <strong>the</strong> position of <strong>the</strong> cursor on its current logical line as seen by<br />
BASIC. Normally, POS(O) is 0-79, but some PRINT statements may return values up<br />
to 255. POS's usefulness is confined to <strong>the</strong> screen; it won't work with printers, for<br />
example.<br />
Examples:<br />
1. 90 FOR J=l TO 100: PRINT W$(J-1)" ";<br />
92 IF POS(0) + LEN(W$(J))>38 THEN PRINT<br />
94 NEXT<br />
<strong>The</strong> above program lines print <strong>the</strong> words in array W$ in a tidy format, with<br />
out allowing wraparound to following lines. (This assumes that no string longer<br />
than 38 characters will be allowed.)<br />
2. Chapter 9 contains a routine (to convert ML into DATA) which uses POS.<br />
PRINT<br />
Type: Output statement<br />
Syntax: PRINT [expression]<br />
<strong>The</strong> expression may be any type, separated by one or more of <strong>the</strong> following:<br />
SPC(numeric expression), TAB(numeric expression), space, comma, semicolon, or no<br />
separator (where this causes no ambiguity).<br />
Modes: Direct and program modes are both valid.<br />
Token: $99 (153)<br />
Abbreviated entry: Question mark (?)<br />
Purpose: PRINT evaluates and prints string, numeric, and logical expressions to an<br />
output device, usually <strong>the</strong> TV or monitor. <strong>The</strong> punctuation of <strong>the</strong> material after <strong>the</strong><br />
PRINT statement affects <strong>the</strong> appearance of <strong>the</strong> output, which also depends on <strong>the</strong><br />
internal character set being used. (See "PRINT USING" in Chapter 6, for infor<br />
mation on ML formatting of numbers.)<br />
Notes:<br />
1. Built-in graphics. <strong>The</strong> entire character set can be printed, but {RVS} is necessary to<br />
complete <strong>the</strong> set using PRINT statements (POKE, of course, does not require <strong>the</strong><br />
use of {RVS}). Color and o<strong>the</strong>r controls are easy to include in strings, ei<strong>the</strong>r in<br />
quote mode or with CHR$. PRINT "{RED}HELLO{BLU}" and PRINT<br />
CHR$(28)"HELLO''CHR$(31) are equivalent. Because {RVS} is necessary to print<br />
some graphics, it's not always easy to convert a picture on <strong>the</strong> TV into PRINT<br />
statements in a program. Homing <strong>the</strong> cursor, inserting spaces, and typing line<br />
numbers followed by ?" and RETURN will sometimes work. This method, how<br />
ever, won't accept reversed characters, so be careful when designing graphics di<br />
rectly on <strong>the</strong> screen. Chapter 12 has detailed information on graphics.<br />
Note that <strong>the</strong> SHIFT-<strong>Commodore</strong> key combination normally toggles between<br />
two different character sets, one with lower- and uppercase (good for text), and<br />
one with uppercase and extra graphics characters. Printing CHR$(14) selects <strong>the</strong><br />
51
BASIC Reference Guide<br />
lowercase/uppercase set, CHR$(142) selects <strong>the</strong> uppercase/graphics set, CHR$(8)<br />
locks out SHIFT-<strong>Commodore</strong> key switching, and CHR$(9) enables SHIFT-<br />
<strong>Commodore</strong> key character set switching.<br />
2. User-defined graphics. PRINT operates with ASCII characters, and <strong>the</strong>ir onscreen<br />
appearance is irrelevant, so user-defined characters can be handled by PRINT, too.<br />
In most cases, it's easiest to keep most characters as usual, so that program listings<br />
on <strong>the</strong> screen are readable. See Chapter 12 for full details.<br />
3. Punctuating PRINT:<br />
• Expressions, as stated at <strong>the</strong> start of this chapter, are fairly straightforward. Nu<br />
meric expressions can include numbers, TI, ST, <strong>the</strong> value 7r, and so on; string ex<br />
pressions may include TI$.<br />
• SPC and TAB allow <strong>the</strong> print position to be altered.<br />
• Commas tabulate output into <strong>the</strong> first, eleventh, twenty-first, or thirty-first col<br />
umns. For example, try:<br />
PRINT 1,2,3,4,5<br />
• Semicolons prevent print position from skipping to <strong>the</strong> next line, and <strong>the</strong>refore<br />
act as neutral separators. Try this line:<br />
PRINT 1;2;3;: PRINT 4<br />
Remember that numbers are output with a leading space (in case <strong>the</strong>re is a neg<br />
ative sign) and a trailing space. Often <strong>the</strong> semicolon isn't needed, as in:<br />
PRINT X$ Y$ "HELLO" N% A<br />
where <strong>the</strong> interpreter will correctly identify everything.<br />
• Colons end <strong>the</strong> statement, and in <strong>the</strong> absence of a semicolon move print position<br />
to <strong>the</strong> next line. <strong>The</strong> following line advances <strong>the</strong> print position two lines:<br />
PRINT: PRINT<br />
• Spaces (unless within quotation marks) are generally ignored, so:<br />
PRINT X Y;2 4<br />
does <strong>the</strong> same as PRINT XY;24.<br />
Examples:<br />
1. PRINT X+Y; 124; P*(l+R%/100) :REM NUMERIC EXPRESSIONS<br />
This prints three numbers on <strong>the</strong> same line. Notice, though, that if <strong>the</strong> first<br />
semicolon is left out, X + Yl is printed.<br />
2. PRINT "HI " NAMES$ ", HOW ARE YOU?" :REM STRING EXPRESSION<br />
<strong>The</strong> above line prints output on a single line (if <strong>the</strong>re's room).<br />
3. FOR J=l TO 20: PRINT J,: NEXT :REM SHOWS USE OF COMMA<br />
This illustrates <strong>the</strong> tabbing effect of <strong>the</strong> comma in PRINT statements.<br />
PRINT#<br />
Type: Output statement<br />
Syntax: PRINT# numeric expression ^expression]<br />
<strong>The</strong>re must be no space between PRINT and #; <strong>the</strong> numeric expression is a file<br />
52
BASIC Reference Guide<br />
number—<strong>the</strong> file must be open; and subsequent expressions should use format simi<br />
lar to PRINT.<br />
Modes: Direct and program modes are both valid.<br />
Token: $98 (152)<br />
Abbreviated entry: P SHIFT-R (This includes #, and ?# does not wort)<br />
Purpose: PRINT# sends data to an output device, usually a printer, tape, disk drive,<br />
or a modem.<br />
Notes:<br />
1. Punctuating PRINT*. <strong>The</strong> effect of punctuation in PRINT# statements is identical<br />
to PRINT, except for a few cases: Eleven spaces are always written after a comma,<br />
and expressions in TAB or POS will not work.<br />
PRINT#4,X$<br />
writes X$ followed by CHR$(13), but:<br />
PRINT#4,X$;:<br />
writes X$ alone.<br />
PRINT#128/X$:<br />
writes X$ followed by a carriage return and linefeed; this feature of files numbered<br />
128 or more is useful with certain non-<strong>Commodore</strong> printers.<br />
2. PRINT* and INPUT*. Remember that INPUT# cannot handle strings longer than<br />
88 characters, so this limit must be observed when setting up data files with<br />
PRINT#.<br />
3. PRINT* and CMD. PRINT#4,;: unlistens <strong>the</strong> device using file 4, while CMD4,;:<br />
leaves it listening, so <strong>the</strong>se expressions are opposites. See Chapter 17 for full de<br />
tails on printers and modems.<br />
Examples:<br />
1. OPEN 1,1,1/TAPE FILE": INPUT X$: PRINT#1,X$: CLOSE 1<br />
This opens TAPE FILE to tape and, after allowing <strong>the</strong> user to enter a string,<br />
writes <strong>the</strong> string to tape. Chapter 14 has full details on tape; Chapter 15 has infor<br />
mation on disk files.<br />
2. 100 FOR J=32768 TO 40959: PRINT #l,CHR$(PEEKg));: NEXT<br />
This prints <strong>the</strong> bytes in RAM or ROM in <strong>the</strong> plug-in area to file 1. Note <strong>the</strong><br />
semicolon to prevent characters having a RETURN character following each one<br />
(making <strong>the</strong> file twice as long). <strong>The</strong> resulting file must be read back with GET#,<br />
since it is ML and not formatted for INPUT# to handle.<br />
READ<br />
Type: Statement<br />
Syntax: READ variable [,variable...]<br />
Modes: Direct and program modes are both valid.<br />
Token: $87 (135)<br />
Abbreviated entry: R SHIFT-E<br />
53
BASIC Reference Guide<br />
Purpose: READ assigns data stored in DATA statements to a variable, or variables.<br />
If <strong>the</strong> type of variable doesn't match <strong>the</strong> data (for example DATA "ABC": READ<br />
X), ?TYPE MISMATCH ERROR is printed when <strong>the</strong> program runs. ?OUT OF DATA<br />
ERROR is given when all <strong>the</strong> data has been read and <strong>the</strong> program has encountered<br />
an extra READ statement; a RESTORE statement is used to start reading data from<br />
<strong>the</strong> beginning again.<br />
Examples:<br />
1.100 READ X$: IF X$o"ML ROUTINE" GOTO 100<br />
This shows how a piece, or group, of data can be found anywhere in <strong>the</strong><br />
DATA statements. This construction (with RESTORE) allows data to be mixed<br />
fairly freely throughout BASIC program space.<br />
2. 10 READ X: DIM N$(X): FOR J=l TO X: READ X$0): NEXT<br />
<strong>The</strong> above line shows how string variables (for example, words for a word<br />
game) can be read into an array effectively, by putting <strong>the</strong> word count at <strong>the</strong> start,<br />
like this:<br />
1000 DATA 2,RED,YELLOW.<br />
Type: Statement<br />
Syntax: REM [anything]<br />
Modes: Direct and program modes are both valid.<br />
Token: $8F (143)<br />
Abbreviated entry: None<br />
Purpose: REM allows documentation to be included in a program. Everything on <strong>the</strong><br />
BASIC line after REM is ignored by <strong>the</strong> BASIC interpreter. REM statements take<br />
space in memory, and a little time to execute, so final versions of programs will gen<br />
erally have <strong>the</strong> REM statements removed.<br />
Note: See <strong>the</strong> section on REM in Chapter 6 for some special effects. Chapter 7 ex<br />
plains how ML can be stored in REM statements.<br />
Examples:<br />
1. GOSUB 51000: REM PRINT SCREEN WITH INSTRUCTIONS<br />
Above is a sample REM comment.<br />
2. 70 FOR J=l TO 1000: REM MAIN LOOP<br />
80 Ag)=J*A: NEXT<br />
This shows poor placing of REM, because <strong>the</strong> REM executes 1000 times.<br />
Move <strong>the</strong> REM to line 69 to increase speed.<br />
3. 15998 REM<br />
15999 REM *** SUB 16000 PRINTS TITLE ***<br />
<strong>The</strong>se lines show how REM statements can be made easy to read in long<br />
programs.<br />
54
BASIC Reference Guide<br />
RESTORE<br />
Type: Statement<br />
Syntax: RESTORE<br />
Modes: Direct and program modes are both valid.<br />
Token: $8C (140)<br />
Abbreviated entry: RE SHIFTS<br />
Purpose: RESTORE resets <strong>the</strong> data pointer, so that subsequent READs retrieve data<br />
starting from <strong>the</strong> first DATA statement. NEW, RUN, and CLR all perform RESTORE<br />
as part of <strong>the</strong>ir functions.<br />
Note: This command has no connection with <strong>the</strong> RESTORE key.<br />
Examples:<br />
1. 2000 READ X$: IF X$="**" THEN RESTORE: GOTO 2000<br />
This example is <strong>the</strong> first line of a loop to read <strong>the</strong> same block of DATA<br />
continuously—perhaps <strong>the</strong> notes for a tune to be repeated. When ** is en<br />
countered as <strong>the</strong> last element of <strong>the</strong> data, <strong>the</strong> RESTORE resets <strong>the</strong> data pointer,<br />
and <strong>the</strong> line executes again.<br />
2. 130 RESTORE: FOR L=l TO 9E9: READ X$: IF X$o"MLROUTINEl"<br />
THEN NEXT<br />
140 FOR L=328 TO 336: READ V: POKE L, V: NEXT<br />
9000 DATA 27, 32, SUB, MLROUTINE1,169, 0, 141, 202, 3, 162,1, 160, 20<br />
This shows how data can be labeled to insure that <strong>the</strong> correct section is read;<br />
line 130 reads <strong>the</strong> data until it reaches <strong>the</strong> label MLROUTINE1, which is followed<br />
by <strong>the</strong> desired data.<br />
3. RESTORE: GOTO 100<br />
This is a direct mode command of <strong>the</strong> sort helpful in testing programs which<br />
contain DATA statements, since after <strong>the</strong> RESTORE statement, all <strong>the</strong> data will be<br />
reread from <strong>the</strong> start.<br />
RETURN<br />
Type: Statement<br />
Syntax: RETURN<br />
Modes: Direct and program modes are both valid.<br />
Token: $8E (142)<br />
Abbreviated entry: RE SHIFT-T<br />
Purpose: RETURN transfers program control to <strong>the</strong> statement immediately after <strong>the</strong><br />
most recent GOSUB statement. GOSUB and RETURN <strong>the</strong>refore permit subroutines<br />
to be automatically processed without <strong>the</strong> need to store return addresses in<br />
programs.<br />
Notes:<br />
1. See GOSUB for a full account of subroutine processing.<br />
2. This command has no connection with <strong>the</strong> RETURN key.<br />
55
BASIC Reference Guide<br />
Example:<br />
10 INPUT L: GOSUB 1000: GOTO 10 :REM TEST SUBROUTINE 1000<br />
1000 L=INT(L+.5)<br />
1010 PRINT L: RETURN<br />
This example repeatedly inputs a number and calls <strong>the</strong> subroutine at 1000 to<br />
process it; <strong>the</strong> RETURN causes execution to resume with <strong>the</strong> GOTO 10 statement.<br />
RIGHT$<br />
Type: String function<br />
Syntax: RlGHT$(string expression, numeric expression)<br />
Modes: Direct and program modes are both valid.<br />
Token: $C9 (201)<br />
Abbreviated entry: R SHIFT-I (This includes <strong>the</strong> $.)<br />
Purpose: RIGHTS returns a substring made up from <strong>the</strong> rightmost characters of <strong>the</strong><br />
original string expression. <strong>The</strong> numeric expression (which must evaluate to a value<br />
between 0 and 255) is compared with <strong>the</strong> original string's length, and <strong>the</strong> smaller<br />
value determines <strong>the</strong> substring's length.<br />
Examples:<br />
1. FOR J=l TO 7: PRINT SPC(8-J) RIGHT$("AMAZING",J): NEXT<br />
prints seven substrings of "AMAZING", aligned using SPC.<br />
2. 100 PRINT RIGHT$(" "+STR$(N),10)<br />
is ano<strong>the</strong>r method for right justification; each string is padded with leading spaces,<br />
making a total length of ten.<br />
RND<br />
Type: Numeric function<br />
Syntax: RND(numeric expression)<br />
Modes: Direct and program modes are both valid.<br />
Token: $BB (187)<br />
Abbreviated entry: R SHIFT-N<br />
Purpose: RND generates a pseudorandom number in <strong>the</strong> range 0-1, but excluding<br />
<strong>the</strong>se limits. RND can help generate test data, mimic random events in simulations,<br />
and introduce unpredictability in games.<br />
Notes:<br />
1. RND's argument <strong>The</strong> argument used in <strong>the</strong> paren<strong>the</strong>ses as part of <strong>the</strong> RND state<br />
ment affects <strong>the</strong> way <strong>the</strong> number will be generated:<br />
• Positive. <strong>The</strong> value of <strong>the</strong> number is irrelevant. RND(l) and RND (1234) behave<br />
identically. <strong>The</strong> sequence of numbers generated is always <strong>the</strong> same, starting with<br />
.1855<strong>64</strong>016 immediately after <strong>the</strong> computer is turned on.<br />
• Zero. This causes RND to take values from CIA timers; <strong>the</strong> result is more truly<br />
random, although short ML loops, for example, may show repetitiveness.<br />
56
BASIC Reference Guide<br />
• Negative. <strong>The</strong> random number is reseeded with a value dependent on <strong>the</strong> argu<br />
ment. A negative argument always returns <strong>the</strong> same value; RND (—1) is always<br />
2.99 E—8, for example. Chapter 8 has information on RND and explains why<br />
negative integers give very small seed values.<br />
• <strong>Programming</strong> with RND. During program development with random numbers,<br />
start with, say, X=RND(-1.23) to seed a fixed value, <strong>the</strong>n use RND(l) while<br />
testing <strong>the</strong> program, which will always follow <strong>the</strong> same sequence. <strong>The</strong> final ver<br />
sion of <strong>the</strong> program might use X=RND(0) to start seeding with a random value.<br />
2. To obtain a random number between A and B (but excluding <strong>the</strong> exact values of A<br />
and B), use:<br />
A + RND(1)*(B-A)<br />
For example:<br />
-1 + RND(1)*2<br />
generates numbers between —1 and +1.<br />
Integers are equally simple. To obtain an integer value between A and B<br />
(including <strong>the</strong> exact values A and B), use:<br />
A + INT(RND(1)*(B-A+1))<br />
For example:<br />
1 + INT(RND(l)*10)<br />
generates integers from 1 to 10 with equal probability.<br />
Examples:<br />
1. FOR J=0 TO 3000*RND(l): NEXT<br />
<strong>The</strong> above line causes a random delay of up to roughly three seconds.<br />
2. 100 RESTORE: FOR J=0 TO 100*RND(l): READ X$: NEXT<br />
This example reads a random number of items from DATA statements (be<br />
tween 1 and 100); thus, <strong>the</strong> last item read into X$ will be retained as a randomly<br />
selected string. This could be used perhaps to choose a word for a language test<br />
from a list of 100 data items.<br />
3. 1000 IF RND(1)
BASIC Reference Guide<br />
Token: $8A (138)<br />
Abbreviated entry: R SHIFT-U<br />
Purpose: RUN executes a BASIC program in memory, ei<strong>the</strong>r from its beginning or<br />
from a line number. In effect, RUN starts by executing a CLR, so variable values are<br />
lost; GOTO [line number] retains variable values.<br />
Notes:<br />
1. RUN doesn't execute a LOAD. <strong>The</strong> program must be read into memory<br />
beforehand.<br />
2. 7SYNTAX ERROR as <strong>the</strong> result of a RUN means <strong>the</strong> start-of-BASIC pointers have<br />
been altered. See <strong>the</strong> information about CLR.<br />
3. Chapter 8 shows how to run BASIC with ML.<br />
Examples:<br />
1. RUN<br />
RUN 1000<br />
<strong>The</strong>se are two straightforward direct mode examples of <strong>the</strong> RUN command.<br />
2. IF LEFT$(YN$,1)="Y" THEN RUN<br />
<strong>The</strong> above example is for use after:<br />
INPUT "ANOTHER RUN";YN$<br />
This starts <strong>the</strong> program from scratch after Y, or YES, is typed in.<br />
SAVE<br />
Type: Command<br />
Syntax: SAVE [string expression ^numeric expression^ numeric expression]]]<br />
Identical to that for LOAD. <strong>The</strong> interpretation of <strong>the</strong> final parameter is different,<br />
however, when using a tape drive: 0 allows a relocating LOAD, so a BASIC program<br />
can work whatever its starting address; 1 forces LOAD to put <strong>the</strong> program where it<br />
was saved from; 2 and 3 are like 0 and 1 but additionally write an end-of-tape<br />
marker. Chapter 14 has full details on saving to tape, and Chapter 15 discusses disk<br />
SAVEs.<br />
Modes: Direct and program modes are both valid.<br />
Token: $94 (148)<br />
Abbreviated entry: S SHIFT-A<br />
Purpose: SAVE writes <strong>the</strong> BASIC program in memory to tape or disk, so <strong>the</strong> pro<br />
gram is stored for future use. Programs must be saved to disk by name, but tape<br />
programs need not have names (although names can be useful in identifying tape<br />
contents).<br />
ML, graphics characters, and o<strong>the</strong>r continuous blocks of RAM can be saved, too.<br />
Only two pointers have to be changed (in four memory locations), effectively<br />
redefining <strong>the</strong> position of BASIC'S program area. <strong>The</strong> pointers are locations 43 and<br />
44 (start), as well as 45 and 46 (end). See Block SAVE in Chapter 6. Saving BASIC<br />
with its variables is also possible. For example, BASIC followed by integer arrays<br />
holds data in a very compact form, and both variables and BASIC can be saved to<br />
ge<strong>the</strong>r, although this is tricky (and strings are best excluded). BASIC followed by<br />
58
BASIC Reference Guide<br />
graphics definitions can be saved like this, too—see Chapter 12. (In each case only<br />
<strong>the</strong> pointer in 45 and 46 need be altered before saving.)<br />
Note: As with LOAD, standard messages prompt <strong>the</strong> user when saving to tape.<br />
PRESS PLAY AND RECORD ON TAPE is <strong>the</strong> first. <strong>The</strong> system can't distinguish<br />
<strong>the</strong>se keys from PLAY on its own, so if you're careless you may find you've recorded<br />
nothing.<br />
Examples:<br />
1. SAVE :REM SAVES BASIC TO TAPE WITH NO NAME<br />
SAVE "PROG",1,2:REM SAVE TO TAPE, WITH END-OF-TAPE MARKER<br />
Above are two BASIC program SAVE commands for use with tape. (SAVE<br />
with forced LOAD address is generally used only with ML, where <strong>the</strong> correct loca<br />
tion of <strong>the</strong> program in memory is essential.)<br />
2. SAVE "PROGRAM"+TI$,8<br />
Here is a sample disk SAVE command. This adds a clock value to keep track<br />
of several versions of a given program. <strong>The</strong> third parameter is ignored when sav<br />
ing to disk; <strong>the</strong>re is no SAVE with forced LOAD address for disk.<br />
SGN<br />
Type: Numeric function<br />
Syntax: SGN(numeric expression)<br />
Modes: Direct and program modes are both valid.<br />
Token: $B4 (180)<br />
Abbreviated entry: S SHIFT-G<br />
Purpose: SGN computes <strong>the</strong> sign of a numeric expression; it yields — 1 if <strong>the</strong> ex<br />
pression is negative, 0 if it has a value of zero, and +1 if <strong>the</strong> expression is positive.<br />
This is related to logical expressions and to ABS and <strong>the</strong> comparison operators. For<br />
example, SGN(X-Y) is 0 if X=Y, 1 if X exceeds Y, and -1 if X is less than Y.<br />
Examples:<br />
1. ON SGN(X)+2 GOTO 400,600,800<br />
This statement causes program execution to branch to 400 if X is negative, to<br />
600 if X is 0, and to 800 if X is positive.<br />
2. FOR J= -5 TO 5: PRINT J;SGNa);SGNg)*J;SGN(J)*INT(ABS(J)): NEXT<br />
This example prints several results; <strong>the</strong> last is like INT but rounds negative<br />
numbers up.<br />
SIN<br />
Type: Numeric function<br />
Syntax: SlN(numeric expression)<br />
Modes: Direct and program modes are both valid.<br />
Token: $BF (191)<br />
Abbreviated entry: S SHIFT-I<br />
59
BASIC Reference Guide<br />
Purpose: SIN returns <strong>the</strong> sine of <strong>the</strong> numeric expression, which is assumed to be an<br />
angle in radians. (Multiply degrees by V180 t0 convert to radians.)<br />
See ATN for <strong>the</strong> converse function.<br />
Examples:<br />
1. FOR J=0 TO 90: PRINT J SIN(J* tt/180): NEXT<br />
<strong>The</strong> above line prints sines of angles from 0 to 90 degrees in one degree<br />
steps.<br />
2. 120 X=A+SIN(A)/2: Y=A+SIN(A)*3/2<br />
This calculates <strong>the</strong> x and y coordinates of a geometrical shape.<br />
SPC(<br />
Type: Special output function<br />
Syntax: SPC(numeric expression)<br />
SPC appears only in PRINT and PRINT# statements. <strong>The</strong> numeric expression<br />
must evaluate to a value in <strong>the</strong> range 0-255.<br />
Modes: Direct and program modes are both valid.<br />
Token: $A6 (166)<br />
Abbreviated entry: S SHIFT-P (This includes <strong>the</strong> open paren<strong>the</strong>sis mark.)<br />
Purpose: SPC helps format screen or printer output. <strong>The</strong> name is misleading: With a<br />
TV or monitor, 0 to 255 cursor-rights can be printed, but <strong>the</strong>se aren't spaces. Try <strong>the</strong><br />
following:<br />
PRINT SPC(200)"HI!"<br />
However, with any o<strong>the</strong>r device, spaces are output, since cursor-rights are not in<br />
<strong>the</strong> ASCII system. TAB is exactly <strong>the</strong> same except that it moves from <strong>the</strong> leftmost<br />
column, ra<strong>the</strong>r than moving from <strong>the</strong> current position.<br />
Examples:<br />
1. 100 PRINT "{HOME}";: FOR J=0 TO 21: PRINT "X" SPC(38) "X";: NEXT<br />
This prints a border down each side of <strong>the</strong> screen, without disturbing <strong>the</strong><br />
screen characters between <strong>the</strong> borders.<br />
2. 90 OPEN 1,3: CMD 1<br />
Add this line to <strong>the</strong> previous; note how an open file causes spaces, not<br />
cursor-rights, to be output.<br />
SQR<br />
Type: Numeric function<br />
Syntax: SQR(numeric expression)<br />
<strong>The</strong> numeric expression must be positive, or an 7ILLEGAL QUANTITY ERROR<br />
will result.<br />
Modes: Direct and program modes are both valid.<br />
Token: $BA (186)<br />
Abbreviated entry: S SHIFT-Q<br />
60
BASIC Reference Guide<br />
Purpose: SQR calculates <strong>the</strong> square root of a positive argument. This is a special case<br />
of <strong>the</strong> power (up-arrow) function. SQR actually works faster than XT .5, though, and<br />
is also more familiar to many people.<br />
Examples:<br />
1. PRINT SQR(2) :REM PRINTS 1.41412356<br />
This prints <strong>the</strong> square root of 2.<br />
2. Xl=(-B + SQR(B*B - 4*A*C)) / (2*A)<br />
X2=(-B - SQR(B*B - 4*A*C)) / (2*A)<br />
<strong>The</strong>se are both solutions of <strong>the</strong> equation AX2 + BX + C = 0.<br />
ST<br />
Type: Reserved variable<br />
Syntax: ST is treated like a numeric variable, except that no value can be assigned to<br />
ST. (For example, X=ST is correct, but ST=X is not allowed.)<br />
Modes: Direct and program modes are both valid.<br />
Token: Not applicable<br />
Abbreviated entry: Not applicable<br />
Purpose: ST indicates <strong>the</strong> status of <strong>the</strong> system after any input or output operation to<br />
tape, disk, or o<strong>the</strong>r peripheral. ST is set to 0 before GET, INPUT, and PRINT as well<br />
as CMD, GET#, INPUT# and PRINT#, so ST is ra<strong>the</strong>r ephemeral; where it is used it<br />
should be used after every command.<br />
ST is a compromise method of signaling errors to BASIC without stopping it. It<br />
can often be ignored. Table 3-1 shows <strong>the</strong> meaning of different values of ST for dif<br />
ferent devices. (Where more than one error occurs, <strong>the</strong>y are ORed toge<strong>the</strong>r, so<br />
ST=66 combines <strong>the</strong> conditions indicated by <strong>64</strong> and 2.) Chapters 14, 15, and 17<br />
provide details on ST with tape units, disk drives, and modems, respectively.<br />
Table 3-1. Status Variable (ST) Values<br />
ST<br />
Read<br />
Tape<br />
Write<br />
Modem<br />
Serial Bus (e.g., Disk)<br />
Read<br />
Write<br />
1<br />
2<br />
4<br />
8<br />
16<br />
32<br />
<strong>64</strong><br />
-128<br />
Short block on input<br />
Long block on input<br />
Mismatch on checking<br />
Checksum error<br />
End-of-file on input<br />
End-of-tape marker<br />
None<br />
Parity error<br />
Framing error<br />
Rx buffer full<br />
Rx buffer empty<br />
CTS missing<br />
DSR missing<br />
Break detected<br />
Print time-out<br />
Input time-out<br />
End-of-file (EOI)<br />
Device not present<br />
Note: ST for tape and disks is stored in location 144; ST for RS-232 devices is held<br />
in location 663. ST (like TI and TI$) is checked when a variable is set up; normally,<br />
no ST variable exists in RAM, and ST is processed by special routines. ST isn't a<br />
tokenized keyword or even a normal variable; this is why BEST=2 is accepted, and<br />
means BE=2, despite <strong>the</strong> apparent presence of ST.<br />
61
BASIC Reference Guide<br />
ST (like TI and TI$) can be POKEd to any value in <strong>the</strong> legal range. ST can be<br />
used from ML. See Chapter 8, which deals with Kernal routines for information on<br />
this and on methods for reading errors from <strong>the</strong> disk drive.<br />
Examples:<br />
1. OPEN 11,11: PRINT#11,X$<br />
<strong>The</strong> above line opens a file to a nonexistent device: This sets ST= —128.<br />
2. 150 INPUT#8,X$: IF ST=<strong>64</strong> GOTO 1000<br />
This is a typical end-of-file check, for use when reading data from disk or<br />
tape. Line 1000 might be an exit routine to print totals of all <strong>the</strong> data, <strong>the</strong>n finish.<br />
STOP<br />
Type: Statement<br />
Syntax: STOP<br />
Modes: Direct and program modes are both valid.<br />
Token: $90 (144)<br />
Abbreviated entry: S SHIFT-T<br />
Purpose: Like <strong>the</strong> RUN/STOP key, <strong>the</strong> STOP statement returns <strong>the</strong> program to<br />
READY mode and prints a BREAK message showing <strong>the</strong> line number at which <strong>the</strong><br />
program stopped. Like END, STOP can set breakpoints in BASIC, but it's better be<br />
cause <strong>the</strong> line numbers allow you to insert as many STOPs as you want. See CONT<br />
(and GOTO if CONT can't continue) for information on using breakpoints.<br />
Example:<br />
80 GET X$: IF X$="" GOTO 100<br />
90 IF X$="*" THEN STOP :REM STOP IF ASTERISK PRESSED<br />
This is typical of a test for keypress, which allows a program to be stopped at a<br />
particular point.<br />
STR$<br />
Type: String function<br />
Syntax: STR$(numeric expression)<br />
Modes: Direct and program modes are both valid.<br />
Token: $C4 (196)<br />
Abbreviated entry: ST SHIFT-R (This includes <strong>the</strong> $.)<br />
Purpose: STR$ converts any floating-point number into a string, so that <strong>the</strong> number<br />
can be edited. It formats numbers as PRINT does, so STR$(10.0) is " 10" (with a<br />
leading space), and STR$(-123) is "-123".<br />
Examples:<br />
1. FOR J=l TO 100: PRINT STR$g)+".0" NEXT<br />
This line prints 1 as 1.0, 2 as 2.0, and so forth.<br />
2. PRINT "0" + MID$(STR$(X),2)<br />
62
BASIC Reference Guide<br />
outputs X as "0.57", etc., where X is between 0.01 and 1.0; MID$ and STR$ to<br />
ge<strong>the</strong>r remove <strong>the</strong> leading space. (Remember that numbers from 0 to 0.01 are out<br />
put in exponential notation.)<br />
SYS<br />
Type: Statement<br />
Syntax: SYS numeric expression<br />
<strong>The</strong> expression must evaluate to a number between 0 and 65535.<br />
Modes: Direct and program modes are both valid.<br />
Token: $9E (158)<br />
Abbreviated entry: S SHIFTY<br />
Purpose: SYS transfers control to ML at <strong>the</strong> address following SYS. <strong>The</strong> ML is exe<br />
cuted and will return to BASIC and execute <strong>the</strong> statement after SYS if <strong>the</strong> machine<br />
language routine ends with an RTS instruction (or <strong>the</strong> equivalent). <strong>The</strong> registers A,<br />
X, Y, and SR are loaded with values from locations 780-783 by SYS, and <strong>the</strong>ir val<br />
ues after <strong>the</strong> subroutine call are replaced in 780-783. This offers a useful way to<br />
check short ML routines for errors.<br />
Notes:<br />
1. Chapter 7 introduces ML programming on <strong>the</strong> <strong>64</strong>; Chapter 6 has many examples<br />
which use SYS. Many of <strong>the</strong>m end with a DATA value of 96, which is <strong>the</strong> decimal<br />
value of RTS. A jump to a ROM subroutine ending with RTS (DATA 76,XX,XX)<br />
also works, and RTI is sometimes used, too (a decimal value of <strong>64</strong>).<br />
2. Careless SYS calls may crash or corrupt BASIC, and perhaps cause odd results<br />
sometime later in <strong>the</strong> same programming session. Try:<br />
SYS 47175<br />
as an example. (This sets <strong>the</strong> decimal flag in <strong>the</strong> chip.)<br />
3. ROM occupies locations 40960-49151 and 57344-65535 in <strong>the</strong> <strong>Commodore</strong> <strong>64</strong>,<br />
unless one or both ROMs have been switched out (Chapter 5 explains this).<br />
Usually, SYS calls into <strong>the</strong>se regions have repeatable and predictable effects with<br />
<strong>the</strong> <strong>64</strong>, as explained in Chapter 11. Such calls will not work with o<strong>the</strong>r computers,<br />
which means <strong>the</strong>y are machine-specific.<br />
Examples:<br />
1. 10 SYS PEEK(43) + 256*PEEK(44) + 30<br />
<strong>The</strong> above SYS calls ML stored within BASIC; this form works regardless of<br />
BASIC'S start address. Chapter 9 explains <strong>the</strong>se techniques in depth.<br />
2. SYS <strong>64</strong>738<br />
This well-known ROM routine call resets <strong>the</strong> <strong>64</strong> as though it were turned<br />
off, <strong>the</strong>n on again. Chapter 11 lists ROM call addresses, and some (such as <strong>the</strong><br />
screen routines in Chapter 6) are listed elsewhere.<br />
3. POKE 780,ASC("$"): SYS 65490: REM KERNAL ROUTINE<br />
This puts <strong>the</strong> dollar character in <strong>the</strong> storage location for A, <strong>the</strong>n calls a<br />
Kernal output routine at $FFD2. (Kernal routines are explained in depth in Chap<br />
ter 8.) <strong>The</strong> effect is to print a dollar sign.<br />
63
BASIC Reference Guide<br />
TAB(<br />
Type: Special output function<br />
Syntax: TAR(numeric expression)<br />
TAB appears only in PRINT or PRINT# statements. <strong>The</strong>re must be no space be<br />
tween B and ( and <strong>the</strong> expression must evaluate to a number in <strong>the</strong> range 0-255.<br />
Modes: Direct and program modes are both valid.<br />
Abbreviated entry: T SHIFT-A (This includes <strong>the</strong> open paren<strong>the</strong>sis.)<br />
Purpose: TAB prints an expression at <strong>the</strong> desired position on <strong>the</strong> line (values be<br />
tween 0 and 255) specified by <strong>the</strong> parameter, unless this position is to <strong>the</strong> left of an<br />
earlier TAB in <strong>the</strong> same PRINT statement (like typewriter TABs, TAB doesn't work<br />
from right to left).<br />
Note: TAB is nearly identical to SPC; <strong>the</strong> difference is that TAB subtracts its current<br />
position on <strong>the</strong> line from <strong>the</strong> TAB value, <strong>the</strong>n issues that number of moves right.<br />
TAB's use of cursor-rights and spaces is <strong>the</strong> same as SPC's. In reverse mode, cursorrights<br />
show as square brackets.<br />
Example:<br />
FOR J=l TO 10: PRINT J TAB(4)J*J TAB(10)J*J*J: NEXT<br />
TAN<br />
<strong>The</strong> above example produces a tabbed table of squares and cubes.<br />
Type: Numeric function<br />
Syntax: TAN(numeric expression)<br />
Modes: Direct and program modes are both valid.<br />
Token: $C0 (192)<br />
Abbreviated entry: None<br />
Purpose: TAN calculates <strong>the</strong> tangent of any numeric expression, which is assumed to<br />
be an angle in radians. <strong>The</strong> values tt/2 (90 degrees) and o<strong>the</strong>r equivalent values<br />
cause 7DIVISION BY ZERO errors and should be tested for to avoid program<br />
crashes. TAN divides SIN by COS; it is slower and less accurate than ei<strong>the</strong>r.<br />
Example:<br />
90 A=ATN(TAN(A))*180/tt<br />
This converts any radian measurement into its equivalent from —90 to +90<br />
degrees.<br />
Tl<br />
and Tl$<br />
Type: Reserved variables<br />
Syntax: TI is treated like a numeric variable, and TI$ like a string variable, except<br />
that no value may be assigned to TI (TI=X is never allowed). TI$ = string expression<br />
of length 6 is allowed, but expressions with more or fewer than six characters cause<br />
an error.<br />
Modes: Direct and program modes are valid.<br />
<strong>64</strong>
BASIC Reference Guide<br />
Token: Not applicable<br />
Abbreviated entry: Not applicable<br />
Purpose: TI and TI$ access <strong>the</strong> internal software clock. It's kept running by BASIC as<br />
a normal part of its operation. A feature known as an interrupt operates this (and al<br />
lows access to <strong>the</strong> keyboard); about every 1/60 second, locations 160-162 are in<br />
cremented, and <strong>the</strong>ir collective value is used to obtain TI and TI$. See Chapter 5 for<br />
information on <strong>the</strong> hardware side of this and <strong>the</strong> end of Chapter 8 for programming<br />
information. (<strong>The</strong> interrupt rate can be changed by POKEing 56324 and 56325 with<br />
different values.) <strong>The</strong> clock isn't all that reliable; tape operation makes it run much<br />
faster than usual, and any programs which disable (turn off) interrupts stop it. <strong>The</strong><br />
<strong>64</strong> does include two time-of-day clocks which are completely accurate to 1/10 sec<br />
ond. Chapter 5 explains <strong>the</strong>ir use.<br />
<strong>The</strong> maximum value for TI is 5184000, <strong>the</strong> number of 1/60 second intervals in a<br />
day. TI is equal to:<br />
65536*PEEK(160) + 256*PEEK(161) + PEEK(162)<br />
where <strong>the</strong> last of <strong>the</strong> three bytes changes fastest. Try writing a loop that repeatedly<br />
PEEKs location 162 for a demonstration. <strong>The</strong> easiest way to change <strong>the</strong> clock setting<br />
is with <strong>the</strong> statement:<br />
TI$="101500"<br />
<strong>The</strong> above line would set <strong>the</strong> clock to a quarter after ten. Note that ML can be<br />
used to set and read TI$; Chapter 8's section on Kernal ROM routines gives full<br />
information.<br />
Note: Like ST, <strong>the</strong>se variables are intercepted by BASIC, not set up in <strong>the</strong> normal<br />
variables section of memory located above BASIC program space. TIME and TIME$<br />
are treated like TI and TI$, but ANTIC is treated as AN, and <strong>the</strong> characters TI are<br />
ignored.<br />
Examples:<br />
1. 50 TI$=HH$ + MM$ + SS$<br />
This program line combines three previously entered two-digit strings into<br />
TI$<br />
2. T$=TI$: PRINT MID$(T$,1,2) + ":" + MID$(T$,3,2) ":" + MID$(T$,5,2)<br />
<strong>The</strong> above line prints TI$ in <strong>the</strong> format HH:MM:SS. Note that T$ stores <strong>the</strong><br />
value in case TI$ changes while <strong>the</strong> strings are being calculated (for example, from<br />
11:59:59 to 12:00:00).<br />
3. 10 DIM T1/T2J: T1=TI<br />
20 FOR J= 1 TO 1000:NEXT<br />
30 T2=Tl-58 :REM FIGURE MAY VARY A BIT<br />
40 PRINT T2/60 'THOUSANDTHS OF SEC"<br />
Shows how individual BASIC operations can be timed. <strong>The</strong> first line insures<br />
that TI, T2, and J are placed at <strong>the</strong> start of variables; <strong>the</strong> number in line 30 must<br />
be set so that <strong>the</strong> program as it stands prints a value of 0. This means that any<br />
additional commands put in <strong>the</strong> loop in line 20 are timed exactly. You'll find that:<br />
POKE 7680,123<br />
takes 8/1000 second, a colon takes about 1/10,000 second, and so on. See Chap<br />
ter 6 for more on this topic.<br />
65
BASIC Reference Guide<br />
USR<br />
Type: Numeric function<br />
Syntax: USR(numeric expression)<br />
Modes: Direct and program modes are both valid.<br />
Token: $B7 (183)<br />
Abbreviated entry: U SHIFTS<br />
Purpose: USR allows you to define a function in machine language. This requires a<br />
thorough understanding of ML; in BASIC, it's nearly always easier to use a DEF FN<br />
expression, and not much slower. In Chapter 8, <strong>the</strong> section on calculations has a<br />
complete explanation of this function with examples.<br />
VAL<br />
Type: Numeric function<br />
Syntax: VAL(string expression)<br />
Modes: Direct and program modes are both valid.<br />
Token: $C5 (197)<br />
Abbreviated entry: V SHIFT-A<br />
Purpose: VAL converts a string into a number, so calculations can be performed on<br />
it. If <strong>the</strong> string is not a valid number representation, as much as possible is con<br />
verted, and <strong>the</strong> remainder ignored with no error message. Valid characters are<br />
spaces, signs, numerals, unSHIFTed E, and periods in certain combinations. VAL is<br />
<strong>the</strong> converse of STR$.<br />
Example:<br />
PRINT VALr 0.77") :REM PRINTS .77<br />
PRINT VAL(//1.72E3//) :REM PRINTS 1720<br />
PRINT VALC' +773 DOLLARS'') :REM PRINTS 773<br />
IN$=//1.2.3//: PRINT VAL(IN$) :REM PRINTS 1.2<br />
PRINT VAL("12"+"."+"01") :REM PRINTS 12.01<br />
IF VAL(IN$)10 THEN PRINT "ERROR"<br />
<strong>The</strong>se should be self-explanatory. Note that <strong>the</strong> last of <strong>the</strong>se tests an input num<br />
ber, avoiding bugs caused by comparing strings with each o<strong>the</strong>r.<br />
VERIFY<br />
Type: Command<br />
Syntax: VERIFY [string expression [.numeric expression^ numeric expression]]]<br />
Identical syntax as LOAD. Syntax should match that of <strong>the</strong> LOAD or SAVE<br />
statement preceding VERIFY.<br />
Modes: Direct and program modes are both valid.<br />
Token: $95 (149)<br />
Abbreviated entry: V SHIFT-E<br />
66
BASIC Reference Guide<br />
Purpose: VERIFY reads and compares a BASIC or ML program from disk or tape<br />
with <strong>the</strong> program already in memory. If <strong>the</strong>y aren't identical, 7VERIFY ERROR is re<br />
ported. When VERIFY is used at all (it often isn't), it's generally to verify that a pro<br />
gram has saved correctly. (It can be used in program mode, so a program can verify<br />
itself.)<br />
Because programs may load into different addresses depending on LOAD's<br />
parameters, VERIFY should match <strong>the</strong> parameters of LOAD. Even so, BASIC can<br />
generate spurious 7VERIFY ERROR messages, as explained in <strong>the</strong> notes at <strong>the</strong> end of<br />
this chapter.<br />
VERIFY cannot be used with most data files, since <strong>the</strong>se cannot be loaded like<br />
programs.<br />
Examples:<br />
1. SAVE "NEWPROG",8<br />
VERIFY "NEWPROG",8<br />
<strong>The</strong> above commands save a program to disk, <strong>the</strong>n verify that it has been<br />
saved correctly.<br />
2. 10 PRINT "REWIND TO VERIFY"<br />
20 GET X$: IF X$="" GOTO 20: REM WAIT FOR KEY PRESS TO VERIFY<br />
30 VERIFY<br />
At <strong>the</strong> start of a tape program, this verifies in program mode.<br />
3. LOAD "VERSION6",8<br />
VERIFY "VER 6",8<br />
If you have two programs which you believe may be identical, VERIFY will<br />
compare <strong>the</strong>m. OK means that your two programs are indeed identical. This is<br />
often useful when a disk contains lots of versions of a program, including security<br />
duplicates, saved during program development.<br />
WAIT<br />
Type: Statement<br />
Syntax: WAIT numeric expression, numeric expression [.numeric expression]<br />
<strong>The</strong> first parameter is an address (in <strong>the</strong> range 0-65535); <strong>the</strong> o<strong>the</strong>rs are in <strong>the</strong><br />
range 0-255 and will be converted to integers. <strong>The</strong> optional third parameter defaults<br />
to 0.<br />
Modes: Direct and program modes are both valid.<br />
Token: $92 (146)<br />
Abbreviated entry: W SHIFT-A<br />
Purpose: This statement waits until one or more bits of <strong>the</strong> memory location are<br />
cleared (given a value of 0) or set (given a value of 1) in <strong>the</strong> way specified by <strong>the</strong><br />
two parameters. <strong>The</strong> contents of <strong>the</strong> location are Exclusive-ORed with <strong>the</strong> third<br />
parameter, <strong>the</strong>n ANDed with <strong>the</strong> second parameter. If all bits are still 0, <strong>the</strong><br />
comparison is repeated; o<strong>the</strong>rwise, BASIC continues with <strong>the</strong> next instruction.<br />
Notes:<br />
1. <strong>The</strong> location read by WAIT must be one whose contents can change, or <strong>the</strong> pro<br />
gram will wait indefinitely. Chapter 11 has a list of locations which WAIT might<br />
67
BASIC Reference Guide<br />
use. Note, however, that WAIT commands don't usually work on o<strong>the</strong>r computers.<br />
In fact, <strong>the</strong>y're often better replaced, as <strong>the</strong>y always can be, by an equivalent<br />
statement using PEEK.<br />
2. <strong>The</strong> operation of WAIT can be hard to explain. First, consider Exclusive-OR (EOR<br />
is <strong>the</strong> 6502 mnemonic, so we'll use it as an abbreviation). Its truth table is:<br />
0 EOR 0 = 0<br />
0 EOR 1 = 1<br />
1 EOR 0 = 1<br />
1 EOR 1 = 0<br />
If both bits tested are <strong>the</strong> same, <strong>the</strong> result of an EOR is 0. If <strong>the</strong> bits are different,<br />
<strong>the</strong> result is 1. To put it ano<strong>the</strong>r way, EOR is true if ei<strong>the</strong>r but not both bits are set.<br />
<strong>The</strong> statement:<br />
WAIT address,a,b<br />
first EORs <strong>the</strong> byte in address with b. This allows any bit, or bits, to be flipped (set<br />
bits will be cleared and clear bits will be set). <strong>The</strong> result is ANDed with a, which<br />
allows any bit to be turned off (in this case, ignored). Since a zero result makes<br />
WAIT loop again (continue waiting), we can select a and b to respond so that any<br />
single bit changing ei<strong>the</strong>r to on or off, can cause <strong>the</strong> program to exit from WAIT<br />
and execute <strong>the</strong> next statement. In <strong>the</strong> special case:<br />
WAIT address,a<br />
<strong>the</strong>re is no EOR parameter. (Actually, <strong>the</strong> value in address is EORed with a zero<br />
byte, so <strong>the</strong> result is always <strong>the</strong> same as <strong>the</strong> original value in address.) Thus:<br />
WAIT address,16<br />
waits until bit 4 is set. If it never is, WAIT continues forever. This is why WAIT<br />
addresses should only be in RAM or in a hardware register which can change.<br />
Examples:<br />
1. POKE 162,0: WAIT 162,16<br />
This line causes <strong>the</strong> computer to wait until <strong>the</strong> jiffy clock TI counts to 16<br />
(about 1/4 second).<br />
2. 100 POKE 198,0: WAIT 198,1<br />
This example waits for a keypress (until one character is in <strong>the</strong> keyboard<br />
buffer).<br />
3. WAIT 56321,32,32<br />
This waits until bit 5 of location 56321 is off. This happens when <strong>the</strong> Com<br />
modore key is pressed.<br />
4. 10 WAIT 53265,128: POKE 53281,RND(1)*16: GOTO 10<br />
<strong>The</strong> above line waits until bit 8 of <strong>the</strong> raster line becomes 1, indicating bot<br />
tom of screen is reached, before changing screen color. Chapter 12 has more<br />
examples of <strong>the</strong>se techniques.<br />
68
BASIC Reference Guide<br />
BASIC Error<br />
Message Dictionary<br />
(Disk error messages are handled separately from BASIC: see Chapter 15.)<br />
?BAD SUBSCRIPT<br />
<strong>The</strong> value given an array subscript is negative, or larger than that in <strong>the</strong> DIM state<br />
ment (larger than 10 if <strong>the</strong> array has not been explicitly dimensioned). This message<br />
is also given if <strong>the</strong> wrong number of subscripts is used.<br />
?BREAK<br />
<strong>The</strong> RUN/STOP key was pressed before LOAD or SAVE was complete.<br />
?CAN'T CONTINUE<br />
<strong>The</strong> program cannot be continued using CONT because of one of <strong>the</strong> following<br />
conditions:<br />
• <strong>The</strong> program halted due to a SYNTAX ERROR, instead of <strong>the</strong> RUN/STOP key,<br />
STOP, or END;<br />
• CLR has erased its variables;<br />
• <strong>the</strong> program was edited after it stopped, effectively erasing variables;<br />
• a direct mode error occurred, which <strong>the</strong> system can't distinguish from a program<br />
error; or<br />
• <strong>the</strong> program has not been run.<br />
?DEVICE NOT PRESENT<br />
This means <strong>the</strong> printer, disk drive, or o<strong>the</strong>r device does not respond, typically on<br />
GET# or INPUT#, because it is unplugged, off, addressed by a wrong device num<br />
ber, or is nonstandard and unresponsive. Also, this error occurs when an end-of-tape<br />
marker is found.<br />
7DIVISION BY ZERO<br />
An attempt has been made to divide by zero, which BASIC does not allow, generally<br />
when a denominator underflows to zero. TAN(7r/2) contains an implicit division by<br />
zero.<br />
?EXTRA IGNORED<br />
Given when <strong>the</strong> response to INPUT contains more items than asked for by INPUT'S<br />
parameter list. <strong>The</strong> extra items are lost. INPUT# behaves identically, but doesn't<br />
print <strong>the</strong> error message. Often this is caused by <strong>the</strong> inclusion of commas or colons in<br />
an input string; avoid this with leading quotes.<br />
?FILE DATA<br />
<strong>The</strong> type of data in a file doesn't match <strong>the</strong> variables to which it is assigned by<br />
GET# or INPUT#. This happens when INPUT#X tries to read a string.<br />
69
BASIC Reference Guide<br />
?FILE<br />
NOT FOUND<br />
A disk file or program is not present on current disk, or <strong>the</strong> name is misspelled.<br />
(Tape gives 7DEVICE NOT PRESENT if <strong>the</strong> end of <strong>the</strong> tape is reached before <strong>the</strong><br />
specified file is found.)<br />
?FILE NOT OPEN<br />
This indicates that <strong>the</strong> logical file number referred to in a statement has not been<br />
opened.<br />
?FILE OPEN<br />
This means that a logical file number referred to in an OPEN statement has already<br />
been opened.<br />
7FORMULA TOO COMPLEX<br />
This is given if a string expression contains three or more paren<strong>the</strong>sized<br />
subexpressions. String storage (<strong>the</strong> string descriptor stack at $19—$21) is exhausted.<br />
For example, PRINT //A//+(//A//+(//A'/+//A//)) will give this error.<br />
?ILLEGAL DEVICE NUMBER<br />
This means ei<strong>the</strong>r that a command has been issued to an unacceptable device, like<br />
saving to <strong>the</strong> keyboard or loading from <strong>the</strong> screen, for example, or that <strong>the</strong> tape<br />
buffer has been moved below $0200.<br />
?ILLEGAL DIRECT<br />
This indicates that a statement requiring <strong>the</strong> input buffer has been entered in direct<br />
mode, typically GET or INPUT, or that DEF FN was entered in direct mode.<br />
?ILLEGAL QUANTITY<br />
An expression used as <strong>the</strong> argument of a function or in a BASIC command is outside<br />
<strong>the</strong> legal range. Attempting a POKE with ei<strong>the</strong>r parameter negative, using a logical<br />
file number greater than 255, and asking for <strong>the</strong> square root of a negative number<br />
are examples.<br />
I/O ERROR (1-9)<br />
<strong>The</strong>se are Kernal error messages, only visible in BASIC after executing POKE 157,<strong>64</strong>.<br />
See Kernal notes in Chapter 8.<br />
?LOAD<br />
A tape or disk program was not loaded successfully. See Chapter 14 (tape) or Chap<br />
ter 15 (disk) for information on how to read <strong>the</strong> status byte to determine <strong>the</strong> cause of<br />
<strong>the</strong> error.<br />
?MISSING FILE<br />
NAME<br />
LOAD and SAVE must include a program name when using <strong>the</strong> disk drive.<br />
70
BASIC Reference Guide<br />
?NEXT WITHOUT FOR<br />
This message is given when <strong>the</strong> interpreter cannot find a FOR entry on <strong>the</strong> stack<br />
corresponding to <strong>the</strong> NEXT it has just encountered. This may happen if one of <strong>the</strong><br />
following conditions exists:<br />
• <strong>The</strong> stack has no FOR entries on it at all, because more NEXTs than FORs have<br />
been encountered;<br />
• <strong>the</strong> variable in <strong>the</strong> NEXT statement is misspelled and doesn't match any FOR en<br />
tries on <strong>the</strong> stack;<br />
• <strong>the</strong> required FOR entry has been flushed from <strong>the</strong> stack by an incorrectly ordered<br />
NEXT in a nested loop; or<br />
• an active GOSUB exists, as in:<br />
10 FOR J=l TO 20: GOSUB 100<br />
100 NEXT<br />
?NOT INPUT FILE<br />
Given in response to an attempt to INPUT# or GET# from a file opened to be writ<br />
ten to. For example, a tape data file opened in write mode cannot be read.<br />
?NOT OUTPUT FILE<br />
An attempt has been made to PRINT to an input file. A disk file opened in read<br />
mode cannot be written to, and <strong>the</strong> attempt will give this error. A file to <strong>the</strong> key<br />
board may be OPENed, and read from, but ?NOT OUTPUT FILE will be given if <strong>the</strong><br />
attempt is made to write to it, as <strong>the</strong> keyboard cannot act as an output device.<br />
?OUT OF DATA<br />
<strong>The</strong>re were no remaining unread DATA items when a READ statement was en<br />
countered. Pressing RETURN over <strong>the</strong> READY prompt generates this message. RE<br />
STORE resets <strong>the</strong> data pointer.<br />
?OUT OF MEMORY<br />
This message indicates one of <strong>the</strong> following:<br />
• <strong>The</strong> <strong>64</strong> does not have enough RAM for <strong>the</strong> program and its variables (especially if<br />
dimensioning large arrays or inputting long strings);<br />
• temporary storage on <strong>the</strong> stack has run out, having been filled with GOSUBs<br />
(about 24 maximum), FOR-NEXT loops (about 10 maximum), and intermediate<br />
calculation results:<br />
PRINT (l+(2+(3+(4+(5+(6+(7+(8+(9+(10+(ll+(12)))))))))»)<br />
• <strong>the</strong> end-of-program pointer in locations 45 and 46 has been set (perhaps by a<br />
LOAD into high memory) greater than <strong>the</strong> end-of-BASIC-stOrage pointer in 55 and<br />
56. (Reset <strong>the</strong> pointer or use NEW to correct this.)<br />
71
BASIC Reference Guide<br />
7OVERFLOW<br />
<strong>The</strong> value of a calculation is outside <strong>the</strong> valid range for floating-point numbers,<br />
approximately — 1.7E38 to +1.7E38. If a result is within <strong>the</strong> valid range, this error<br />
may be avoidable by restructuring <strong>the</strong> computation using, for example:<br />
PRINT (5/4)tl00<br />
instead of:<br />
5U00/4T100<br />
?REDIM'D ARRAY<br />
An attempt has been made to dimension an array that has already been dimen<br />
sioned. It may have been dimensioned automatically. A reference to X(8), for ex<br />
ample, implicitly performs DIM X(10) if <strong>the</strong> array doesn't yet exist in memory.<br />
?REDO FROM START<br />
This message is given when <strong>the</strong> response to an INPUT statement (but not to IN-<br />
PUT#) contains items of <strong>the</strong> wrong type. <strong>The</strong> whole INPUT statement is executed<br />
again.<br />
7RETURN WITHOUT GOSUB<br />
A RETURN has been encountered without a GOSUB having first been executed.<br />
7STRING TOO LONG<br />
String expressions must have 0 to 255 characters; this error is given if a string ex<br />
pression evaluates to a string longer than this. This message is output when an at<br />
tempt has been made to read a string 89 or more characters long into <strong>the</strong> input<br />
buffer, typically by INPUT#.<br />
?SYNTAX<br />
This indicates that a BASIC statement is unacceptable. <strong>The</strong>re are many causes. <strong>The</strong><br />
<strong>64</strong> anticipates a sequence of statements; if a statement doesn't start with a keyword<br />
or <strong>the</strong> equivalent of LET, if a variable name isn't ASCII, if a statement isn't ter<br />
minated with colon or null, or if paren<strong>the</strong>ses, commas, and o<strong>the</strong>r symbols are mis<br />
placed, 7SYNTAX ERROR often results. This message is also given after NEW if <strong>the</strong><br />
first byte of BASIC is nonzero.<br />
POKE PEEK(44)*256,0: NEW<br />
typically avoids this error.<br />
?TOO MANY FILES<br />
This is given in response to an OPEN statement if ten logical files, <strong>the</strong> maximum<br />
number, have already been opened.<br />
72
BASIC Reference Guide<br />
?TYPE MISMATCH<br />
This message is output if <strong>the</strong> interpreter detects a numeric expression where a string<br />
expression is expected, or vice versa.<br />
?UNDEF'D FUNCTION<br />
An undefined function has been used in an expression; it should first have been de<br />
fined with DEF FN.<br />
?UNDEF'D STATEMENT<br />
<strong>The</strong> target line number of a GOTO, GOSUB, or RUN does not exist.<br />
7VERIFY<br />
<strong>The</strong> program in memory isn't identical to <strong>the</strong> disk or tape file it is being compared<br />
with by VERIFY. Spurious VERIFY errors occur if BASIC programs are loaded into<br />
<strong>64</strong> memory at different addresses from where <strong>the</strong>y were saved; <strong>the</strong> link pointers be<br />
tween lines are different, but <strong>the</strong> BASIC statements may be <strong>the</strong> same.<br />
73
<strong>Programming</strong><br />
^^^<br />
0^^
Chapter 4<br />
Effective <strong>Programming</strong> in BASIC<br />
How to Become Fluent in BASIC<br />
BASIC is a language, with its own vocabulary and syntax, which requires a certain<br />
amount of creativity on <strong>the</strong> programmer's part to get good results, just as <strong>the</strong> ability<br />
to write novels or articles requires more than only a knowledge of words and syntax.<br />
<strong>The</strong> challenge for a novice programmer is to develop style and fluency.<br />
Perhaps <strong>the</strong> best way to learn to write is to read and write a lot; similarly, <strong>the</strong><br />
best way to learn BASIC, once you know <strong>the</strong> vocabulary, is to examine o<strong>the</strong>r<br />
people's programs and adopt good techniques while developing your own style. At<br />
first, use only a few BASIC statements in your programs, and limit your attempts to<br />
small tasks. As you gain more experience, you can add new BASIC words to your<br />
vocabulary. <strong>The</strong>re are, however, some BASIC statements that you may never need to<br />
use, just as <strong>the</strong>re are probably words in your native language that you have never<br />
spoken.<br />
When writing programs, you have an advantage over writers who don't use<br />
BASIC; you can experiment freely and find out immediately whe<strong>the</strong>r your way of<br />
expressing your intentions is acceptable or not. If you fail, no harm is done, provided<br />
<strong>the</strong> experiments are kept away from your working programs and important data.<br />
With this in mind, it makes good sense to try several approaches to any program<br />
ming task.<br />
No matter what kind of programming you would like to do in BASIC, it is im<br />
portant that you have an appreciation of <strong>the</strong> capabilities and limitations of your<br />
computer, BASIC, and <strong>the</strong> intended user. This is largely a matter of experience. <strong>The</strong><br />
remainder of this chapter consists of advice and information that may help you write<br />
reliable and easy-to-use programs. <strong>The</strong> final programming decisions, of course, are<br />
always yours.<br />
Programs, Systems, and People<br />
Before considering program design, we'll overview <strong>the</strong> software market and <strong>the</strong> atti<br />
tudes of software producers and users. First, <strong>the</strong>re are three main program types:<br />
Autonomous, stand-alone programs. <strong>The</strong>se don't depend on o<strong>the</strong>r programs,<br />
but stand alone. <strong>The</strong>se programs contain all graphics and data necessary to function<br />
properly without support. Most games are like this. "Diet Calculator" (later in this<br />
chapter) is an example of an autonomous program.<br />
Program systems (groups of programs). <strong>The</strong>se generally have many programs<br />
and files of data stored outside <strong>the</strong> computer. Interactive systems allow information<br />
to be put into or taken out of files directly; batch systems store new data on file,<br />
after which ano<strong>the</strong>r program processes it, perhaps merging it into an already existing<br />
file. "Wordscore" (below) is a simple system which <strong>the</strong> <strong>64</strong> can run.<br />
Pseudosystems (programs resembling systems). Single programs with a family<br />
resemblance to each o<strong>the</strong>r might be classified as midway between autonomous pro<br />
grams and systems. For example, multiple-choice and o<strong>the</strong>r educational programs<br />
collectively can be regarded as systems.<br />
<strong>The</strong> concepts are important here, not <strong>the</strong> names. Systems are likely to be more<br />
77
Effective <strong>Programming</strong> in BASIC<br />
difficult to program than autonomous programs, needing validation and checks un<br />
necessary in <strong>the</strong> o<strong>the</strong>r types. Programs that resemble systems are likely to be easy to<br />
program, provided standardized methods have been developed.<br />
Second, <strong>the</strong>re are different types of users. Microcomputer owners can generally<br />
be classified as business, scientific, educational, or personal users.<br />
Business. <strong>The</strong> <strong>64</strong>, with disk drives and a printer, is capable of handling data in<br />
moderate quantities, where speed isn't crucial in day-to-day organization. Mailing<br />
lists, telephone lists, customer information, billing notices, small financial calcula<br />
tions using spreadsheets, and letter writing on word processors are examples of <strong>the</strong><br />
uses for <strong>the</strong> <strong>64</strong> in small businesses.<br />
A number of problems exist, though, such as unacceptable slowness. People<br />
who purchase a system program may not be able to describe accurately <strong>the</strong> features<br />
<strong>the</strong>y want, or understand that new features, like fast searches and sorts, may be<br />
impossible or may require <strong>the</strong> entire system to be completely rewritten. <strong>The</strong> office<br />
staff may not be willing to key in data, particularly if instructions aren't provided in<br />
nontechnical language. And <strong>the</strong>re will be problems if programs don't have thorough<br />
error trapping, or if correction and updating of information is difficult. <strong>The</strong>re may be<br />
security problems in addition to <strong>the</strong>se concerns, since most businesses keep records<br />
(even interoffice memos) that are not intended for all to see. This is perhaps a ra<strong>the</strong>r<br />
negative picture, but programmers should keep <strong>the</strong>se possible difficulties in mind<br />
when designing systems.<br />
Commercially sold software packages may be inadequate for several reasons.<br />
First, published reviews are unlikely to be of much value, because comprehensive<br />
testing takes months of work. Second, software packages are continually under<br />
development, and a purchaser may be unable to establish how recent and reliable<br />
<strong>the</strong> version being shown is. Third, <strong>the</strong>re may not be a commercial program system<br />
that will do what <strong>the</strong> particular business needs. Still, for many small businesses,<br />
commercial software will be adequate, and programmers should try to include simi<br />
lar features to those of successful programs in <strong>the</strong>ir own works, improving <strong>the</strong>m as<br />
much as possible.<br />
Scientific. Controlling external hardware for monitoring experiments or control<br />
ling equipment (like large computer-operated telescopes) is a specialized area (see<br />
Chapter 5 for more information on software). Calculations and simulations are <strong>the</strong><br />
o<strong>the</strong>r uses for which micros are suitable. Desk-top computers are often used to solve<br />
complex equations; anything with a definite formula is a potential task for a com<br />
puter program, from architectural stress calculations to zoo nutrition, provided <strong>the</strong><br />
computer's memory is large enough to hold <strong>the</strong> data.<br />
Educational. <strong>The</strong> continued drop in <strong>the</strong> price of computing (apparent ra<strong>the</strong>r<br />
than real in many cases, after making allowance for separate disk units, printers,<br />
RAM expanders, and so on), plus skillful marketing (playing on parents' fears that<br />
<strong>the</strong>ir children might miss out on <strong>the</strong> computer revolution), have created a boom in<br />
computer education. Never<strong>the</strong>less, in spite of <strong>the</strong> huge sums paid for education, not<br />
that much is spent on computers. And it is unreasonable to expect great computer<br />
expertise from teachers who haven't <strong>the</strong>mselves been trained.<br />
A common situation has evolved where classes have one model of an expensive<br />
computer, while <strong>the</strong> students who have one at home own a less expensive or dif<br />
ferent computer. <strong>The</strong>re are two different attitudes toward microcomputer education,<br />
78
Effective <strong>Programming</strong> in BASIC<br />
broadly dividing people who do not have programming skills and those who do.<br />
<strong>The</strong> first group sees, with some relief, a rdomful of unruly children settle down to<br />
play a number game, and thinks, "It is remarkable to see <strong>the</strong>m working in an or<br />
derly way for hours. Increased equality of education is possible. Unmotivated stu<br />
dents find <strong>the</strong>ir intejr^st reawakened, and <strong>the</strong>ir confidence grows."<br />
<strong>The</strong> o<strong>the</strong>r group's argument is, "Computers are unparalleled at teaching logical<br />
thought. <strong>The</strong>y provide great oppbrtunities for students to display <strong>the</strong>ir creativity, and<br />
this may be <strong>the</strong> most important part of <strong>the</strong>ir schooling." When <strong>the</strong> experts disagree,<br />
it is hard to know what makes good educational software.<br />
Multiple-choice tests, with question-and-answer programs, graded by year and<br />
subject, make a potentially attractive package. In principle, dozens of programs could<br />
be used as refreshers and tests in a range of subjects. Multiple-choice questions are<br />
easy to program, since <strong>the</strong> only reply needed is typically 1, 2, 3, or 4, without <strong>the</strong><br />
need to interpret a verbal answer. To discourage guessing, wrong answers could<br />
score —1/4 point, so completely random answers would score around zero.<br />
Single-concept programs, like children's counting programs and alphabeticrecognition<br />
programs, are becoming available commercially. Good graphics can add<br />
a lot of appeal and help to hold <strong>the</strong> user's attention longer. More advanced examples<br />
include foreign language vocabulary and translation tests; economics concepts like<br />
price elasticity, supply-and-demand curves, and marginal costs; musical relationships<br />
between frequency and pitch; population simulations; and math techniques and con<br />
cepts like graph plotting, limits, sums of series, calculus, and simulations of random<br />
ness with coins, roulette, and so on. (See "Dice" page 97.)<br />
Personal. This rapidly expanding area of <strong>the</strong> market includes games and educa<br />
tional and home business programs. Several magazines are currently being published<br />
which include programs in <strong>the</strong> magazine that can be typed in at home, and <strong>the</strong>refore<br />
are practically free.<br />
Program Design<br />
We've distinguished programs from systems, and noted that systems require more<br />
planning and knowledge; in <strong>the</strong> commercial world this is reflected in <strong>the</strong> job separa<br />
tion between analysts and programmers. On <strong>the</strong> relatively mddest scale of <strong>the</strong> <strong>64</strong>,<br />
it's equally true; experienced programmers can almost unconsciously plan ambitious<br />
projects out of reach of beginners. This section covers <strong>the</strong> sort of thought processes<br />
necessary in programming and in design, with a concrete example of each to give<br />
substance to <strong>the</strong> generalities. Bear in mind that many programmers write in an un<br />
organized, ad hoc fashion and don't always worry about tidy, <strong>the</strong>oretical schemes. If<br />
your programs are messy and patched toge<strong>the</strong>r, don't worry too much—many o<strong>the</strong>r<br />
people's programs are, too.<br />
Program Example:<br />
Number Guessing Game<br />
We'll write a program which thinks of a number from 1 to 99, <strong>the</strong>n accepts guesses<br />
typed into <strong>the</strong> keyboard. Where <strong>the</strong> guess is wrong, it prints TOO LARGE or TOO<br />
SMALL, as <strong>the</strong> case may be. Correct input is rewarded by an encouraging message<br />
plus <strong>the</strong> total number of guesses. Putting this into BASIC requires four steps, which<br />
may be formally written down or simply carried out mentally, but always take <strong>the</strong><br />
form outlined below.<br />
79
Effective <strong>Programming</strong> in BASIC<br />
Understand <strong>the</strong> problem. <strong>The</strong> example is quite simple: Many computer prob<br />
lems are not.<br />
Express it in a computerizable way. This is where programming experience is<br />
essential. For example, if you haven't grasped <strong>the</strong> idea of computer fU.es, you'll obvi<br />
ously not be able to appreciate <strong>the</strong>ir use in storing data. If you haven't understood<br />
that <strong>the</strong> computer has to count lines of print to know where it is on a page, you<br />
won't be able to print titles on page tops. Knowledge of <strong>the</strong> logic of programming<br />
equips you with methods and tricks to process data, but experience is probably <strong>the</strong><br />
best way to learn <strong>the</strong> physical limitations and capabilities of a particular computer.<br />
This flow chart expresses an approach to our game in a form that can be written<br />
as BASIC. Entries in <strong>the</strong> boxes are shorter than usual to avoid clutter. You should be<br />
able to trace how <strong>the</strong> variable, N, records <strong>the</strong> number of guesses, and how all three<br />
possible outcomes of <strong>the</strong> comparison between <strong>the</strong> correct number, X, and <strong>the</strong> current<br />
guess are processed.<br />
Figure 4-1.<br />
Number Guessing Game Flow Chart<br />
80
Effective <strong>Programming</strong> in BASIC<br />
Flow charts generally use diamonds to indicate options and rectangles for opera<br />
tions; direction of flow is usually down, with loops and branches generated by <strong>the</strong><br />
options arranged clockwise, as in this diagram. Many o<strong>the</strong>r, less common symbols<br />
are also used. Virtually all programs have loops and decision points, and flow charts<br />
show <strong>the</strong>se clearly. However, <strong>the</strong>y are hard to modify and <strong>the</strong>y take up space, so<br />
many people prefer to make outlines and notes—stylized lines of English resembling<br />
programs. <strong>The</strong>re's no correct notation; and <strong>the</strong> sad fact is that any complex program<br />
remains complex in whatever way it is written down.<br />
Write it in BASIC. If it's a complex program, write parts of it and test <strong>the</strong>m in<br />
dividually as subroutines. This is where past practice is invaluable, not only because<br />
of skill in BASIC per se, but because experience suggests efficient ways of getting<br />
results.<br />
Algorithms are rules with explicit instructions and no exceptions, which generate<br />
correct results. Math algorithms can be used by anyone, without understanding any<br />
of <strong>the</strong> underlying <strong>the</strong>ory. For example, linear programming (solving such problems<br />
as finding <strong>the</strong> least expensive combination of foods which supply all known nutri<br />
ents) involves long calculations, which give <strong>the</strong> right answer. At a simpler level,<br />
arranging dates in <strong>the</strong> format YYMMDD makes <strong>the</strong>m sort numerically into chrono<br />
logical sequence, while MMDDYY requires more work to sort properly. A version of<br />
3-D tic-tac-toe requires <strong>the</strong> winner to avoid a line; <strong>the</strong> algorithm start in <strong>the</strong> center<br />
and make opposite moves always wins for <strong>the</strong> first player.<br />
Algorithms can be used to deal with very complex situations: often <strong>the</strong> rule is<br />
found to give good results and is <strong>the</strong>refore used in lieu of anything better.<br />
Warnsdorf's rule in chess, to generate knight's tours around <strong>the</strong> whole board, illus<br />
trates this. <strong>The</strong> rule is: Move <strong>the</strong> knight to <strong>the</strong> square with fewest exit squares. This<br />
often (but not always) gives a solution. Many games—bridge, for example—are in<br />
effect often played algorithmically, as <strong>the</strong> players follow rules that sum up <strong>the</strong> expe<br />
rience of good players. Chess openings can be generated with simple algorithms as<br />
well; a common example is moving to maximize <strong>the</strong> area under attack by your<br />
pieces, while minimizing <strong>the</strong> opponent's range of replies. Reversi (or O<strong>the</strong>llo)<br />
played on an 8 X 8 board, with pieces white on one side and black on <strong>the</strong> o<strong>the</strong>r,<br />
can be played by <strong>the</strong> following simple algorithm: For about 10 moves, occupy cen<br />
tral squares, reversing as few of <strong>the</strong> opponent's pieces as possible; for ano<strong>the</strong>r 10<br />
moves, keep <strong>the</strong> total number of your pieces to about ten; after this, go for maxi<br />
mum points.<br />
Our number game, Program 4-1, is too simple to require such intricate al<br />
gorithms, though.<br />
Program 4-1.<br />
Number Guessing Game<br />
10 PRINT "{CLRjGUESS MY NUMBER (1-99)": PRINT<br />
20 X=INT(RND(1)*99)+1: N=0<br />
30 INPUT "YOUR GUESS";G: N=N+1<br />
40 IF GX THEN PRINT "TOO LARGE": GOTO 30<br />
60 PRINT "GOT IT1 IN " N "TRIES"<br />
81
Effective <strong>Programming</strong> in BASIC<br />
Lines 0-30 correspond exactly to <strong>the</strong> first boxes of <strong>the</strong> flow chart; after this, be<br />
cause IF allows only two options, <strong>the</strong> lines cannot exactly match boxes, but <strong>the</strong> logic<br />
is identical. Note that line 60 doesn't need to test IF G=X, since no o<strong>the</strong>r possibility<br />
exists.<br />
Test and improve. Our example could include:<br />
70 FOR J=l TO 3000: NEXT: GOTO 10<br />
effectively replacing <strong>the</strong> box END with a delay loop and a branch back to <strong>the</strong> PRINT<br />
TITLE box. Values could be checked to insure that <strong>the</strong>y are integers in <strong>the</strong> correct<br />
range, and you could add color.<br />
Testing is difficult, and many commercial programmers spend most of <strong>the</strong>ir time<br />
removing bugs from programs. Ideally, with good planning, bugs would not appear,<br />
but in practice it's seldom possible to foresee every potential problem.<br />
System Example: Wordscore Analysis<br />
"Wordscore" is a demonstration program (see <strong>the</strong> end of this chapter under string<br />
handling) which evaluates five-letter words on a score-per-letter basis. In o<strong>the</strong>r<br />
words, each letter is assigned a value, and <strong>the</strong> value of a word is <strong>the</strong> sum of <strong>the</strong> val<br />
ues of all <strong>the</strong> letters. <strong>The</strong> letters B-I-N-G-0 must be included vertically, horizontally,<br />
or diagonally to form an acceptable combination of words.<br />
<strong>The</strong> first step toward finding an algorithm is to consider <strong>the</strong> potential data base<br />
of words, and several assumptions will provide <strong>the</strong> basis for this algorithm. A typical<br />
dictionary lists about 2000 five-letter words (based on a sampling of pages), so only<br />
about 400 can be expected to exist which contain one or more letters of BINGO. <strong>The</strong><br />
<strong>Commodore</strong> <strong>64</strong>, even without disk or tape, easily has enough memory to store 400<br />
short words. <strong>The</strong> demonstration shows how values can be assigned to letters A to Z<br />
before checking <strong>the</strong> words, stored on a tape or disk file. Using BASIC to select <strong>the</strong><br />
highest-scoring words in <strong>the</strong> form Bxxxx, Ixxxx, and so on (<strong>the</strong>re are 29 relevant for<br />
mats) isn't difficult. <strong>The</strong> conclusion is that <strong>the</strong> <strong>64</strong> could be valuable for this applica<br />
tion. (O<strong>the</strong>r factors—acceptable dictionaries, competition rules—are likely to<br />
complicate matters.)<br />
System Design<br />
In addition to <strong>the</strong> normal programming concerns, system planning takes three major<br />
steps, which are discussed below.<br />
Ask if <strong>the</strong> project is feasible. Time may be a problem; sorts, searches, graphics,<br />
and tape processing may be too slow; <strong>the</strong> program's response time may be inad<br />
equate; <strong>the</strong> data may take too long to key in. You may want to write a test program<br />
to check <strong>the</strong> feasibility of <strong>the</strong> task. Machine language always outperforms BASIC,<br />
but is often more difficult to program.<br />
Generally, if much data is to be processed, estimate <strong>the</strong> total storage needed in<br />
bytes and estimate whe<strong>the</strong>r it can coexist with BASIC, or whe<strong>the</strong>r stored files would<br />
help. Perhaps splitting a program into smaller subprograms would be advisable. Less<br />
tangible problems might be user attitudes, reliability, and recovery of lost data if<br />
problems should occur. A little time spent in advance on all <strong>the</strong>se questions is<br />
usually worthwhile. Even so, <strong>the</strong>re will be cases where a feasibility study requires a<br />
lot of work.<br />
82
A<br />
Effective <strong>Programming</strong> in BASIC<br />
Write a solution. Often it is helpful to do some prewriting, to organize your<br />
ideas before putting <strong>the</strong>m into program form.<br />
Write <strong>the</strong> programs. <strong>The</strong>se should preferably be structured so that <strong>the</strong>y are easy<br />
to understand later. Programs written in modules, each having only one entry and<br />
one exit point, are generally easier to modify and debug. Figure 4-2 and Table 4-1<br />
show two ways of analyzing system programming. <strong>The</strong> first shows a file's structure,<br />
and a diagram of a modular program structured to read it, which has a left-right<br />
flow. Table 4-1 is a condition table, which lists alternative actions in tabular form,<br />
which may allow complex decisions to be checked more easily than long sections of<br />
IF statements would permit.<br />
Figure 4-2. File Structure and Related Program<br />
Program Control<br />
Control Level<br />
File<br />
Open<br />
Files<br />
Read<br />
Header<br />
Read<br />
Until<br />
End<br />
Modules<br />
/<br />
\<br />
Header<br />
"Live"<br />
Record<br />
"Dead"<br />
Record<br />
Trailer<br />
"Live"<br />
/ \<br />
"Dead"<br />
Read a<br />
Record<br />
Print a<br />
Record<br />
Subroutines<br />
Table 4-1.<br />
Condition Table<br />
Conditions<br />
Stock > reorder level?<br />
Y<br />
Y<br />
Y<br />
N<br />
N<br />
N<br />
Stock minus stock out > reorder level?<br />
Y<br />
N<br />
N<br />
N<br />
N<br />
N<br />
Stock out > stock?<br />
N<br />
N<br />
Y<br />
N<br />
Y<br />
N<br />
Actions<br />
Issue stock<br />
X<br />
X<br />
-<br />
X<br />
-<br />
-<br />
Issue reorder request<br />
-<br />
X<br />
X<br />
-<br />
-<br />
-<br />
Part issue stock / increase commitments<br />
-<br />
-<br />
X<br />
-<br />
X<br />
-<br />
Serious and Less Serious<br />
<strong>Programming</strong><br />
<strong>The</strong>re's no single correct way to program. If it's your computer, you can do what<br />
you like; o<strong>the</strong>rwise, you may need to conform to some set style, ei<strong>the</strong>r of program<br />
ming or of finished appearance. This section lists a number of considerations which<br />
83
Effective <strong>Programming</strong> in BASIC<br />
are relevant when deciding on a program's readability, ease of maintenance, use,<br />
modification, and so on.<br />
Conventions for line numbers, variable names, and remarks. You may want<br />
to avoid putting REM statements on lines that are <strong>the</strong> target of ei<strong>the</strong>r GOTO or<br />
GOSUB statements, so deleting <strong>the</strong>m will have no effect on <strong>the</strong> program. If standard<br />
subroutines are used, consider retaining <strong>the</strong> same line numbers in different pro<br />
grams. As for BASIC variables, to be sure that variables can't be accidentally<br />
changed, ei<strong>the</strong>r list every variable as it's used (and make <strong>the</strong> names meaningful) or<br />
establish a convention. For example, local variables could end in 9 (A9, B9, ...), or I,<br />
J, K, and so on could be for local use only—initialized, <strong>the</strong>n used anywhere in <strong>the</strong><br />
program. It is still a good idea to keep track of <strong>the</strong> o<strong>the</strong>r variables.<br />
REM statements make a program more readable, but take up space and slow<br />
execution speed. You may find it worthwhile to document standard routines for fu<br />
ture reference and delete <strong>the</strong> REM statements when <strong>the</strong> routines are used in larger<br />
programs. Program 4-2 converts a four-digit hex number (see Chapter 5) to decimal<br />
and prints it. <strong>The</strong> subroutine does not perform error checking. This is a feature you<br />
may want to add later. Notice <strong>the</strong> remarks which tell what <strong>the</strong> routine does, how to<br />
use it, and which variables are used.<br />
Program 4-2.<br />
Hex-to-Decimal Conversion Subroutine<br />
560 REM{2 SPACES}SHORT SUBROUTINE TO CONVERT A STR<br />
ING OF<br />
565 REM 4 HEX DIGITS TO DECIMAL AND PRINT RESULT<br />
575 REM EXAMPLE OF USE:<br />
580 REM L$=MABCD": GOSUB 600{4 SPACES}PRINTS 43981<br />
590 REM USES VARIABLES H,J,L, AND L$<br />
600 L=0:FORJ=lTO4:L%=ASC(MID$(L$,J)):L%=L%-48+(L%><br />
<strong>64</strong>)*7:L=16*L+L%:NEXT:PRINTL<br />
610 RETURN<br />
A similar decimal-to-hexadecimal conversion subroutine follows; it uses <strong>the</strong><br />
same four variables, except L is used instead of L$.<br />
1000L=L/4096:FORJ=lT04:L%=L:L$=CHR$(48+Lo/o-(L%>9)*7)<br />
1010PRINTL$;:L=16*(L-L%):NEXT:RETURN<br />
Documentation. Program documentation could include an operator manual<br />
(explaining how to use <strong>the</strong> computer, handle and copy disks, and so on), a user's<br />
manual (explaining file structure, validation methods, <strong>the</strong> correct sequence of pro<br />
grams), and a system manual (providing a complete reference to <strong>the</strong> system pro<br />
grams and files).<br />
Ease of modification. <strong>The</strong> term hard coding means that significant parts of a<br />
program use constants; soft coding means variables are used. Soft coding is easier to<br />
modify, but as a rule more tedious to write. See <strong>the</strong> program "Payroll Analyzer" be<br />
low; <strong>the</strong> first line can be altered to change <strong>the</strong> program. <strong>The</strong> BASIC line:<br />
OPEN N,N:PRINT#N<br />
illustrates soft coding as well. When N=3, output is to <strong>the</strong> TV, and when N=4, out<br />
put is to a printer. A program may include a menu of parameters at <strong>the</strong> start, so <strong>the</strong><br />
84
Effective <strong>Programming</strong> in BASIC<br />
user's own requirements can be keyed in. For example, modem programs often begin<br />
with a menu for setting baud rate, parity, and stop bits.<br />
Error messages. <strong>The</strong>se signal that a mistake has been made and should indicate<br />
<strong>the</strong> error. Program 4-3 is a subroutine that handles error messages. Before sending<br />
<strong>the</strong> program to <strong>the</strong> subroutine, place <strong>the</strong> error message text in <strong>the</strong> variable EM$:<br />
EM$="TOO LONG": GOSUB 10000<br />
This will print <strong>the</strong> message on <strong>the</strong> screen in reverse video to attract attention. You<br />
could use this in a large program before resetting cursor position and returning for<br />
reinput.<br />
Program 4-3. Error Message Subroutine<br />
10000 PRINT "{HOME}11: FOR J=l TO 23: PRINT "{DOWN)<br />
117 : NEXT<br />
10010 PRINT "{RVS}"EM$7: FOR J=l TO 2500: NEXT<br />
10020 FOR J=l TO LEN(EM$): PRINT "{LEFT} {LEFT}";:<br />
NEXT<br />
10030 RETURN<br />
Easy data input. <strong>The</strong> BASIC INPUT statement is fine in many cases, but doesn't<br />
give <strong>the</strong> programmer full control. To make a program as easy to use as possible, un<br />
desirable keys should be blocked out or ignored. Integer input (see <strong>the</strong> section below<br />
on string and integer input) will accept only numbers, not cursor keys, color keys, or<br />
alphabetic characters. RUN/STOP and RESTORE may need to be disabled (see<br />
Chapter 6), and <strong>the</strong> length of <strong>the</strong> integer checked if <strong>the</strong>re's a maximum value. None<br />
of this is very difficult, but it takes time and memory.<br />
How easy a program is to use is important. Prompts, telling <strong>the</strong> user what to<br />
type, and instructions, providing an overview, are helpful. <strong>The</strong> programmer must al<br />
ways balance <strong>the</strong> program's features with memory usage and execution speed. <strong>The</strong><br />
lines below illustrate how to combine PRINT and INPUT into a relatively friendly<br />
input routine and show that this requires extra memory.<br />
100 PRINT "ENTER THE DISCOUNT"<br />
110 PRINT "PERCENTAGE (E.G., 13.25)"<br />
120 INPUT "AND PRESS RETURN"; PC<br />
Menus. <strong>The</strong>se are elaborate prompts, which help <strong>the</strong> user select his or her own<br />
path through a program; often a help option is available from <strong>the</strong> menu. Data entry<br />
can be simplified by presenting a summary of input at appropriate places in <strong>the</strong> pro<br />
gram, allowing for easy corrections.<br />
<strong>The</strong> best menu design allows <strong>the</strong> user to indicate <strong>the</strong> desired option by pressing<br />
a single number or letter. If a menu program stands alone, it has to load and run a<br />
new program (see "Chain" in Chapter 6). Of course, all or most of <strong>the</strong> options may<br />
exist as one program in memory, if <strong>the</strong>re is room. Tape units don't have <strong>the</strong> flexibil<br />
ity of disk drives when it comes to loading one program of several, of course, be<br />
cause tape stores programs in sequence, ra<strong>the</strong>r than allowing equally rapid access to<br />
each one.<br />
Program 4-4 is a menu which calls one of three routines based on user input.<br />
85
Effective <strong>Programming</strong> in BASIC<br />
Program 4-4.<br />
Simple Menu<br />
100 PRINT "I, INTEREST RATE<br />
110 PRINT "2. TIME PERIOD<br />
120 PRINT "3. MORTGAGE<br />
200 GET X$: IF X$M3" GOTO 200<br />
210 ON VAL(X$) GOTO 1000, 2000, 3000<br />
Formatting output. Tidy output, particularly of numbers, requires some work.<br />
See "Rounding" (later) and "PRINT USING" (Chapter 6), which show how to print<br />
numbers using a standard format.<br />
Subroutines. Standard subroutines allow programs to be developed and tested<br />
as modules; it's easier to check isolated parts of a program than entire programs, and<br />
it's also possible for several people to work simultaneously, provided <strong>the</strong> variables<br />
and line numbers are determined beforehand (see <strong>the</strong> section on conventions, above)<br />
so no conflicts arise. Subroutines often save space and improve clarity.<br />
Testing. Thorough testing ideally requires every possible combination of data to<br />
be tried. Generally, this is impossible. In practice, depending on <strong>the</strong> program or sub<br />
routine, you can use a loop to generate ascending values and check <strong>the</strong> effect, or use<br />
RND to make up strings or numbers of <strong>the</strong> right size. Rounding includes a loop<br />
demonstration; <strong>the</strong> sort routines in Chapter 6 use random data to test sorting.<br />
In practice, <strong>the</strong>re are complications. First, <strong>the</strong>re may be extreme or boundary<br />
values which have strange effects. Negative numbers, numbers below .01 (which are<br />
printed in exponential notation), and <strong>the</strong> quotation mark key, are all likely to crash<br />
INPUT subroutines unless <strong>the</strong>y're tested for. Second, programming errors may show<br />
up only when several events occur at once, making bugs hard to trace because of<br />
<strong>the</strong>ir apparent random appearance. Third, unconscious bias may influence <strong>the</strong> choice<br />
of test data, so that tricky areas may be avoided. For this reason, commercial systems<br />
are tested with data supplied by <strong>the</strong> user, who also checks that <strong>the</strong> output is what it<br />
should be. This, of course, is ra<strong>the</strong>r unfair, since <strong>the</strong> user may not appreciate <strong>the</strong> im<br />
portance of testing with obviously wrong data which <strong>the</strong> system ought to reject. In<br />
any case, it is best to have someone else test your programs, instead of simply rely<br />
ing on your own testing. #<br />
Validation. This is <strong>the</strong> process of checking to make sure that data is <strong>the</strong> correct<br />
type, without checking <strong>the</strong> actual values. For example, a date entered as 19/19/86 is<br />
invalid and should be rejected; 9/9/86 would be valid, but may be incorrect. In its<br />
simplest form, validation simply causes <strong>the</strong> program to wait for data, as in <strong>the</strong> menu<br />
example above. More sophisticated checking routines include error messages.<br />
Checksums provide additional validation and are easy to implement with<br />
computers. Typically, a single letter or number is added to <strong>the</strong> end of a reference<br />
number (or even a program line listed in a book). <strong>The</strong> suffix is calculated from <strong>the</strong><br />
data, using an algorithm, so <strong>the</strong> composite data is internally consistent. For example,<br />
International Standard Book Numbers (ISBNs) have nine digits, plus an extra<br />
checkdigit. This final digit is computed by multiplying <strong>the</strong> first number by 1, <strong>the</strong> sec<br />
ond by 2, and so forth, up to <strong>the</strong> ninth, <strong>the</strong>n adding <strong>the</strong> results toge<strong>the</strong>r, dividing by<br />
11 and using <strong>the</strong> remainder, 0-9, or X, (to represent a remainder of 10). <strong>The</strong> system<br />
is not foolproof, but it is simple, and <strong>the</strong> most common errors (entry of one wrong<br />
digit or transposition of adjacent digits) are trapped.<br />
86
Effective <strong>Programming</strong> in BASIC<br />
Debugging BASIC Programs<br />
This section lists common faults in BASIC programs. While such a listing cannot be<br />
exhaustive, it should help pinpoint errors. <strong>The</strong> BASIC STOP statement allows you to<br />
set breakpoints at which you can check <strong>the</strong> values of important variables, and<br />
PRINT allows you to check key variables while <strong>the</strong> BASIC program runs.<br />
SYNTAX ERRORS. <strong>The</strong>se occur when <strong>the</strong> <strong>64</strong> finds something which isn't<br />
BASIC. Generally, it's up to you to find <strong>the</strong> mistake.<br />
RUN errors. <strong>The</strong>se occur in BASIC that is syntactically correct, but which is try<br />
ing to manipulate data that isn't valid. <strong>The</strong> final section of Chapter 3 is a list of all<br />
<strong>the</strong>se errors. Validation routines which pass only acceptable values are a solution.<br />
Errors of program logic. <strong>The</strong> program may run without errors, but still do <strong>the</strong><br />
wrong thing. <strong>The</strong>se are often caused by <strong>the</strong> following:<br />
• <strong>The</strong>re may be a keyword misunderstanding, so that <strong>the</strong> statement does something<br />
unexpected. This is common with logical expressions where paren<strong>the</strong>ses have been<br />
omitted.<br />
• A variable's value may be altered by mistake. All BASIC variables are global, not<br />
local, and a subroutine which uses J can easily be called without its effect on J be<br />
ing noticed. In fact, <strong>the</strong> same variable may be repeated by mistake—you may forget<br />
that D already means decimal position and use it for dollars. Also, <strong>the</strong> variable may<br />
be misspelled.<br />
• Subroutines may be poorly structured, so that program flow drops through to <strong>the</strong><br />
following lines. This occurs when <strong>the</strong> RETURN statement is omitted in one of<br />
many subroutines.<br />
• <strong>The</strong> BASIC pointers may be wrong: graphics definitions and ML at <strong>the</strong> top of<br />
BASIC memory need to be protected from being overwritten by BASIC strings.<br />
Chaining (see Chapter 6) may be difficult. BASIC may assume a hardware or soft<br />
ware arrangement which is incorrect.<br />
• Omitting FN will cause a function to be read as an array; PRINT FN HYPTN(5) is<br />
not <strong>the</strong> same as PRINT HYPTN(5). <strong>The</strong> latter will print <strong>the</strong> value of <strong>the</strong> array ele<br />
ment HY(5).<br />
• System errors are usually caused by errors in loops, particularly <strong>the</strong> zeroth and fi<br />
nal elements in buffers. Loops are often used to POKE data into memory, and <strong>the</strong>se<br />
are prime sources of errors.<br />
• DATA statements may have been put in <strong>the</strong> wrong order by typing an incorrect<br />
line number.<br />
Unusual characteristics of BASIC itself. BASIC has a number of small pe<br />
culiarities, some of which are:<br />
• ASC of a null character crashes.<br />
• CLOSE to printer or disk file should be preceded by a PRINT#.<br />
• FOR-NEXT and GOSUB-RETURN require caution.<br />
• FRE is slow if <strong>the</strong>re are very many strings.<br />
• INPUT# has no error message if it finds extra data.<br />
• PRINT attempts to print anything; for instance, a stray decimal point can appear as<br />
a zero or can cause a number to be split into two numbers.<br />
• Numbers are not held with infinite accuracy, as Chapter 6 explains in detail.<br />
87
Effective <strong>Programming</strong> in BASIC<br />
Examples in<br />
BASIC<br />
<strong>The</strong> following sections illustrate some of <strong>the</strong> fundamentals of BASIC programming.<br />
One of <strong>the</strong> best ways to learn BASIC is by looking at program examples and modify<br />
ing <strong>the</strong>m to suit your needs. Most of your programming will involve <strong>the</strong> elementary<br />
skills discussed here, in one way or ano<strong>the</strong>r.<br />
Input<br />
Programs 4-5 and 4-6 use GET to build an input string, IN$. In Program 4-4, <strong>the</strong><br />
cursor flash POKEs in lines 110 and 130 simulate <strong>the</strong> way BASIC'S INPUT looks to<br />
<strong>the</strong> user. <strong>The</strong> program gets individual characters into X$ in line 120. Line 140 allows<br />
<strong>the</strong> INST/DEL key to operate. All o<strong>the</strong>r special keys are disallowed, except<br />
RUN/STOP and RUN/STOP-RESTORE, which can be disabled if you wish (see<br />
Chapter 6). line 150 defines <strong>the</strong> range of acceptable characters, so for integer input<br />
<strong>the</strong> line should be changed (by placing a 0 inside <strong>the</strong> first pair of quotation marks<br />
and a 9 inside <strong>the</strong> second pair).<br />
Program 4-5. String and Integer Input<br />
10 GOSUB 100: PRINT: PRINT IN$: GOTO 10<br />
100 IN$=""<br />
110 POKE 204,0: POKE 207,0<br />
120 GET X$: IF X$=IMI GOTO 120<br />
130 IF X$=CHR$(13) THEN PRINT " ";: POKE 204,1: RE<br />
TURN<br />
140 IF ASC(X$)=20 AND LEN(IN$)>0 THEN IN$=LEFT$(IN<br />
$,LEN(IN$)-1):GOTO 170<br />
150 IF NOT (X$>=M " AND X$
Effective <strong>Programming</strong> in BASIC<br />
146 IF X$=M." AND (J=LEN(D$)+1) THEN D=0: GOTO 160<br />
:rem 222<br />
150 IP NOT (X$>="0M AND X$0 AND MID$(T9$,E9+1,1)=" + " THEN T9$="***<br />
OVERFLOW11: GOTO 150 :rem 80<br />
120 IF MID$(T9$,2,1)=M." THEN T9$=LEFT$(T9$,1)+M0"<br />
+MID$(T9$,2) :rem 127<br />
125 D9=0: FOR J9=l TO LEN(T9$): IF MID$(T9$,J9,1)=<br />
"." THEN D9=J9 :rem 8<br />
130 NEXT :rem 211<br />
135 IF D9=0 THEN D9=LEN(T9$)+l: T9$=T9$+M."<br />
:rem 174<br />
140 T9$=T9$+M00M :rem 3<br />
145 T9$=LEFT$(T9$,D9+2) :rem 223<br />
150 V$= RIGHT$(M{12 SPACES}M+T9$,12) :rem 239<br />
155 RETURN :rem 123<br />
89
Effective <strong>Programming</strong> in BASIC<br />
Line 20 demonstrates <strong>the</strong> routine by producing test values and going to <strong>the</strong> sub<br />
routine at line 100, where <strong>the</strong> number is converted to a string. Lines 105-115 test for<br />
an E in <strong>the</strong> string equivalent of <strong>the</strong> value, V, and check for under- or overflow. Line<br />
120 retains <strong>the</strong> minus sign, where applicable, so every possibility is tested for. Lines<br />
125-150 handle <strong>the</strong> decimal point and trailing zeros, and control <strong>the</strong> length of <strong>the</strong><br />
string, V$, in its processed form.<br />
Routines like this are valuable for such purposes as printing invoices, receipts,<br />
and reports. Chapter 6 (see "PRINT USING") contains a machine language im<br />
plementation of <strong>the</strong> same idea. <strong>The</strong> following line of BASIC is a simple method for<br />
rounding a number to two decimal places:<br />
X=INT(100*X 4- .5)/100<br />
Calculations<br />
Below are some examples of calculation and report programs. <strong>The</strong> first of <strong>the</strong>se pre<br />
dicts weight change based on information entered by <strong>the</strong> user (weight, sex, calorie<br />
intake, and level of activity).<br />
Program 4-8. Diet Calculator<br />
100 PRINT "{CLR}"<br />
110 INPUT "WEIGHT (POUNDS)";P<br />
120 INPUT "INTENDED DAILY CALORIE INTAKE";C<br />
130 INPUT "INACTIVE, FAIRLY, OR VERY ACTIVE (0-2)"<br />
7A<br />
140 INPUT "MALE, FEMALE (M OR F)";S$: S$=LEFT$(S$,<br />
1)<br />
150 S=l: IF S$="F" THEN S=.9<br />
200 PRINT "{CLR}" S$ "{2 SPACESjWEIGHT NOW:" P<br />
210 PRINT "CALORIE INTAKE:" C<br />
220 PRINT<br />
300 FOR W=0 TO 16<br />
310 PRINT "WEEK" W INT(P*10)/l0<br />
400 FOR J=l TO 7<br />
410 M=P*(14.3+A)*S + C/10<br />
420 D=M-C<br />
430 DW=D/3500<br />
440 P=P-DW<br />
450 NEXT J<br />
500 NEXT W<br />
Lines 400-450 calculate weight change per week. Line 410 contains <strong>the</strong> formula<br />
to determine <strong>the</strong> number of calories needed to maintain <strong>the</strong> same weight; lines 420<br />
and 430 calculate DW, <strong>the</strong> change in weight for one day. <strong>The</strong> results of 16 weeks are<br />
printed out. <strong>The</strong> algorithm makes standard assumptions that one pound of fat is<br />
equivalent to 3500 calories, and that a fairly constant ratio exists between total<br />
weight and static weight calorie intake.<br />
Program 4-9 works out <strong>the</strong> smallest bill and coin combinations to pay <strong>the</strong> sepa<br />
rate amounts of a payroll. Line 60 is a DATA line, which can be changed, for ex-<br />
90
Effective <strong>Programming</strong> in BASIC<br />
ample, to eliminate hundred dollar bills, add twenty dollar bills, or convert to o<strong>the</strong>r<br />
currencies (<strong>the</strong> first value on this line is <strong>the</strong> number of different denominations<br />
used). Line 130 adds a small correction to each figure so <strong>the</strong>re is no chance of round<br />
ing errors.<br />
Program 4-9. Payroll Analyzer<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix G<br />
60 DATA 11,100,50,10,5,2,1,.5,.25,.1,-05,.01<br />
:rem 46<br />
70 READ NUMBER OF DENOMS: DIM NC(NU),QU(NU) :rem 6<br />
80 FOR J=l TO NU: READ NC(J): NEXT :rem 72<br />
110 INPUT M{CLR}# OF EMPLOYEES"; EMPLOYEES: DIM SA<br />
LARIES OF (EMPLOYEES) :rem 83<br />
120 FOR J=l TO EM: PRINT "EMPLOYEE #"J; :rem 122<br />
130 INPUT SALARY OF (J): SA(J)=SA(J) + NC(NU)/2<br />
: rem 6<br />
140 NEXT :rem 212<br />
210 FOR J=l TO EMPLOYEES :rem 136<br />
220 FOR K=l TO NUMBER :rem 160<br />
230 X=INT(SAL(J)/NC(K)): SAL(J)=SAL(J)-X*NC(K): QU<br />
(K)=QU(K)+X :rem 4<br />
240 NEXT K :rem 32<br />
250 NEXT J :rem 32<br />
310 PRINT "{CLR} ANALYSIS:" :rem 150<br />
320 FOR J=l TO NU: IF QU(J)=0 THEN 340 :rem 183<br />
330 PRINT QU(J) "OF $" NC(J) :rem 141<br />
340 NEXT :rem 214<br />
Program 4-10 employs a math technique to find solutions to equations; lines 2<br />
and 3 are examples, and <strong>the</strong> program is set up to perform an interest calculation. It<br />
will tell you, for example, that if ten payments of $135 will cancel a $1,000 loan,<br />
<strong>the</strong>re was a 5.865 percent interest rate per payment period. This calculation is or<br />
dinarily difficult, because <strong>the</strong> formula assumes that <strong>the</strong> interest rate is known.<br />
<strong>The</strong> program allows for guesses to be entered as well, which is sometimes im<br />
portant if a problem has more than one solution. Line 60 controls <strong>the</strong> precision of<br />
<strong>the</strong> answer—greater precision takes longer. <strong>The</strong> program is not foolproof and could<br />
be improved by asking for a guess and by adding error checking.<br />
Program 4-10.<br />
Equation Solver<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
2 REM *** EXAMPLE:{2 SPACESjDEF FN Y(X)=X*X - 2 SO<br />
LVES SQR(2) :rem 10<br />
3 REM *** EXAMPLE:{2 SPACESjDEF FN Y(X)=Xt3 + 5*Xt<br />
2-3 SOLVES Xt3+5Xf2=3 :rem 181<br />
10 DEF FN Y(X) = P*(l-l/(l+X)tN)/ X - S :rem 68<br />
11 INPUT "NO. OF PAYMENTS";N :rem 144<br />
12 INPUT "TOTAL SUM";S :rem 62<br />
13 INPUT "EACH PAYMENT IS";P :rem 142<br />
91
Effective <strong>Programming</strong> in BASIC<br />
20 GUESS=.1{2 SPACES}:REM SET GUESS AT 10% PER PAY<br />
MENT INTERVAL :rem 155<br />
30 DX=1/1024 :REM SMALL INCREMENT WITH NO ROUNDING<br />
ERROR :rem 104<br />
40 GRADIENT = (FN Y(GUESS+DX) - FN Y(GUESS))/DX<br />
:rem 137<br />
50 GUESS=GUESS - FN Y(GUESS)/GRADIENT :rem 31<br />
60 IF ABS(GUESS-Gl)
\<br />
Effective <strong>Programming</strong> in BASIC<br />
operator (+). Program 4-7, "Rounding," showed how to scan a string for <strong>the</strong> charac<br />
ter E. Program 4-12 shows how a number can be scanned (in its string form) to re<br />
place <strong>the</strong> number 0 with <strong>the</strong> letter O, which many people prefer.<br />
Program 4-12.<br />
Oh, Zeros<br />
10 INPUT "WHAT NUMBER" ;N$<br />
20 L=LEN(N$)<br />
30 FOR J=l TO L: IF MID$(N$,J,1)="0" THEN N$=LEFT$<br />
(N$,J-1) +"O"+ RIGHT$(N$,L-J)<br />
40 NEXT<br />
50 PRINT N$<br />
When storage space is short, data compression may be necessary, and Program<br />
4-13 illustrates how long numbers can be packed into about half <strong>the</strong>ir normal length,<br />
using string handling:<br />
Program 4-13. Packing Numbers<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
10 INPUT "NUMBER"; NS$ :rem 254<br />
99 REM PACK NUMBER STRING NS$ INTO NP$ :rem 214<br />
100 IF LEN(NS$) INT(LEN(NS$)/2)*2 THEN NS$="0"<br />
{SPACE}+ NS$ :rem 18<br />
110 NP$="": FOR J=l TO LEN(NS$) STEP 2 :rem 180<br />
120 NP$ = NP$ + CHR$(VAL(MID$(NS$,J,2))+33): NEXT<br />
:rem 247<br />
199 REM UNPACK NP$ INTO NUMBER STRING NS$ :rem 170<br />
200 NS$="": FOR J=l TO LEN(NP$): NI$=STR$(ASC(MID$<br />
(NP$#J))-33) :rem 40<br />
210 NI$=RIGHT$(NI$,LEN(NI$)-1):REMOVE LEADING SPAC<br />
E :rem 22<br />
220 NI$=RIGHT$("00"+NI$,2): NS$=NS$+NI$: NEXT:REM<br />
{SPACE}ADD LEADING ZEROS :rem 53<br />
300 PRINT NS$ " " NP$: GOTO 10 :rem 191<br />
Analogous tricks include collecting similar characters toge<strong>the</strong>r and selecting from<br />
<strong>the</strong>m with MID$. For example, <strong>the</strong>re's no simple connection between color keys and<br />
<strong>the</strong>ir ASCII values, but:<br />
C$="{BLK} {WHT} {RED} {CYN} {PUR} {GRN} {BLU} {YEL}"<br />
is a character string holding eight of <strong>the</strong>m, and PRINT MID$(C$,J,1) prints <strong>the</strong> Jth<br />
color, where J is 1-8.<br />
Program 4-14, a simple version of <strong>the</strong> word game Bingo, illustrates a smallsystem<br />
program that evaluates five-letter words by giving each letter a point value<br />
(which you may vary between runs).<br />
93
Effective <strong>Programming</strong> in BASIC<br />
Program 4-14.<br />
Wordscore<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
10 REM 'RUN1 BUILDS FILE OF WORDS; :rem 202<br />
11 REM 'RUN 200* ASSESSES WORDS. :rem 66<br />
13 REM TAPE USE{2 SPACES}101 OPEN 1,1,1,"5-LETTER<br />
{SPACE}WORDS" :rem 63<br />
14 REM AND{7 SPACES}301 OPEN 1,1,0,"5-LETTER WORDS<br />
:rem 253<br />
98 REM BUILD FILE OF WORDS ON DISK :rem 209<br />
101 OPEN 1,8,2,"5-LETTER WORDS,S,W" :rem 234<br />
110 INPUT W$ :rem 157<br />
120 IF LEN(W$)5{2 SPACESjGOTO 110: REM 5 LETTERS<br />
ONLY :rem 255<br />
130 PRINT*1,W$ :rem 28<br />
140 IF W$O"END**" GOTO 110: REM SIGNALS END<br />
:rem 89<br />
150 CLOSE 1: END :rem 78<br />
198 REM PUT IN LETTER VALUES :rem 182<br />
200 DIM V(26) :rem 123<br />
210 FOR J=l TO 26 :rem 61<br />
220 PRINT CHR$(<strong>64</strong>+J); :rem 141<br />
230 INPUT " VALUE"; V(J) :rem 18<br />
240 NEXT :rem 213<br />
298 REM READ DISK & PRINT WORD VALUES : rem 141<br />
301 OPEN 1,8,2,"5-LETTER WORDS,S,R" :rem 231<br />
310 INPUT#1,W$ :rem 31<br />
320 IF W$="END**" THEN CLOSE 1: END :rem 50<br />
330 PRINT W$; :rem 217<br />
340 S=0: FOR J=l TO 5:REM COMPUTE SCORE BY EVALUAT<br />
ING EACH LETTER :rem 43<br />
350 L$=MID$(W$,J) :rem 133<br />
360 A=ASC(L$) - <strong>64</strong> :rem 70<br />
370 S=S + V(A): NEXT : rem 9<br />
380 PRINT S :rem 123<br />
390 GOTO 310 :rem 105<br />
<strong>The</strong> first part of <strong>the</strong> program accepts five-letter words (note <strong>the</strong> check in line<br />
120) and writes <strong>the</strong>m to disk, stopping when <strong>the</strong> end-of-file indicator (END**) is<br />
typed in. RUN 200 runs <strong>the</strong> second phase: 26 values corresponding to A-Z are en<br />
tered by <strong>the</strong> user, and <strong>the</strong> <strong>Commodore</strong> <strong>64</strong> reads back all <strong>the</strong> words on file and prints<br />
word values. Line 360 converts each letter into a number from 1 (for A) to 26 (for Z).<br />
<strong>The</strong> variable, S, in line 370 is <strong>the</strong> total for <strong>the</strong> word which has been read from file;<br />
this shows how MID$ can analyze <strong>the</strong> individual letters in a word.<br />
<strong>The</strong> program can be refined by categorizing <strong>the</strong> words, for example, into those<br />
beginning B, I, and so on, and rejecting words whose total value is less than <strong>the</strong><br />
highest so far, or by using a different file for each type of word.<br />
94
Effective <strong>Programming</strong> in BASIC<br />
Sorting, Searching, Shuffling, and Randomizing<br />
Sorting is commercially important in applications like bill processing, book catalogu<br />
ing, and mail distribution. Chapter 6 has examples of sorts for <strong>the</strong> <strong>64</strong>, written in<br />
both BASIC and ML.<br />
Searching is necessary whenever you have a lot of data in memory or on a file,<br />
but have no index to directly locate a record. For example, a corporate mailing data<br />
base might store names and addresses/ surname first, so that a printout of all names<br />
and addresses is easy. Often, however, it is necessary to access a given name quickly.<br />
Ra<strong>the</strong>r than read through all <strong>the</strong> names, a typical search method (<strong>the</strong> binary search)<br />
is fast and effective.<br />
<strong>The</strong> binary search technique assumes <strong>the</strong> data is already sorted, hence its inclu<br />
sion here. <strong>The</strong> idea is simple; it's <strong>the</strong> method you probably use everyday to find a<br />
name in a phone book or a word in <strong>the</strong> dictionary. You open <strong>the</strong> book exactly mid<br />
way, comparing <strong>the</strong> name you want with <strong>the</strong> names at <strong>the</strong> top of <strong>the</strong> open page, and<br />
repeat <strong>the</strong> process with earlier or later halves, depending on whe<strong>the</strong>r <strong>the</strong> target<br />
name is before or after <strong>the</strong> current position.<br />
A binary search works like this (don't type this in, since it is not a program):<br />
X Input and validate item to be searched for (NA$ = name item).<br />
Set Nl and N2 to lowest and highest record numbers.<br />
Y R=INT ((Nl + N2)/2): Calculate new midpoint.<br />
Read <strong>the</strong> appropriate field of record number R, for instance R$.<br />
IF R$=NA$ THEN Z: Go to line Z if item is found.<br />
IF N1>=N2 THEN PRINT "RECORD NOT ON FILE":GOTO X: This handles<br />
failed searches.<br />
IF R$>NA$ THEN N2=R-1: GOTO Y: This revises <strong>the</strong> upper limit downward.<br />
N1=R+1: GOTO Y: This revises <strong>the</strong> lower limit upward.<br />
Z Continue processing once record is located.<br />
Nl and N2 converge, sandwiching <strong>the</strong> correct record number, R, between <strong>the</strong>m.<br />
<strong>The</strong> binary search is easy to program and converges quite rapidly. Even <strong>the</strong> first and<br />
last items, which take <strong>the</strong> most tests to find, can be located quickly. Table 4-2 shows<br />
<strong>the</strong> approximate average number of searches to find an item.<br />
Table 4-2. Average Binary Searches Required to Locate an Item<br />
Number of data items<br />
50<br />
100<br />
200<br />
500<br />
1000<br />
2000<br />
4000<br />
9000<br />
Average number of searches<br />
5<br />
6<br />
7<br />
8<br />
9<br />
10<br />
11<br />
12<br />
Shuffling is <strong>the</strong> converse of sorting. Program 4-15 prints a randomly dealt whole<br />
deck of electronic cards:<br />
Program 4-15.<br />
Shuffler<br />
10 POKE 53280,1: POKE 53281,1<br />
100 DIM S(52): FOR J=l TO 52: S(J)=J: NEXT<br />
95
Effective <strong>Programming</strong> in BASIC<br />
110 FOR J=l TO 51<br />
120 J% = J + INT(RND(1)*(53-J))<br />
130 TEMP=S(J): S(J)=S(J%): S(J%)=TEMP<br />
140 NEXT<br />
300 FOR J=l TO 52<br />
310 N=S(J)-1<br />
400 S=INT(N/13)<br />
410 PRINT MID$("{BLK}A{RED}j3{RED}£{BLK}XM,S*2+l,2)<br />
500 V=N - INT(N/13)*13<br />
510 IF V=l{2 SPACES}THEN PRINT "A, ";: GOTO 600<br />
520 IF V=ll THEN PRINT "J, ";: GOTO 600<br />
530 IF V=12 THEN PRINT "Q, ";: GOTO 600<br />
540 IF V=0{2 SPACESjTHEN PRINT "K, ";: GOTO 600<br />
550 PRINT MID$(STR$(V),2,LEN(STR$(V))-1)M{BLK},<br />
600 NEXT<br />
Lines 100-140 of <strong>the</strong> above program generate numbers from 1 to 52, without<br />
producing <strong>the</strong> same number twice. Lines 300-410 print <strong>the</strong> suit and set <strong>the</strong> correct<br />
color, while lines 500-600 convert <strong>the</strong> number to <strong>the</strong> card's value. Although Pro<br />
gram 4-15 is fast, it is not as easy to understand as Program 4-16, a simpler method<br />
of shuffling <strong>the</strong> cards.<br />
Program 4-16.<br />
Simpler Shuffler<br />
100 DIM S(52): FOR J=l TO 52<br />
110 C=INT(52*RND(1)) + 1: REM RANDOM NUMBER 1-52<br />
120 IF S(C)>0 THEN 110:REM IF USED RETRY<br />
130 S(C)=J: PRINTC: NEXT: REM SET J: GO BACK<br />
"Simpler Shuffler" puts each of <strong>the</strong> 52 numbers into an array element. If <strong>the</strong><br />
random array position is already occupied, it tries again with ano<strong>the</strong>r random num<br />
ber. This way, every possible number is used and none is repeated.<br />
Randomizing is using somewhat unpredictable (pseudorandom) numbers for<br />
games, simulations, problem solving, and so forth. Program 4-17 uses random num<br />
bers to find <strong>the</strong> chessboard positions of queens, such that no queens attack each<br />
o<strong>the</strong>r. Ra<strong>the</strong>r than testing completely random boards, <strong>the</strong> program retains most of an<br />
unsuccessful test, moving an attacking queen at random, and producing results quite<br />
rapidly. <strong>The</strong> speed decreases greatly with larger boards; analysis of a 20 X 20 chess<br />
board could take hours.<br />
Lines 10-50 generate <strong>the</strong> starting positions of <strong>the</strong> queens, and lines 100-150 test<br />
<strong>the</strong> board to see if any queens are attacking each o<strong>the</strong>r. Lines 200-300 generate <strong>the</strong><br />
printout of <strong>the</strong> positions.<br />
Program 4-17.<br />
Queens<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
10 INPUT "SIZE OF BOARD";N :rem 246<br />
20 DIM Q(N) :rem 44<br />
96
Effective <strong>Programming</strong> in BASIC<br />
30 FOR J=l TO N: Q(J)=0: NEXT: FOR J=1 TO N<br />
:rem 201<br />
40 R=l + INT(RND(1)*N): IF Q(R)>0 GOTO 40 : rem 49<br />
50 Q(R)=J: NEXT :rem 89<br />
100 FOR J=l TO N-l :rem 127<br />
110 FOR K=J+1 TO N :rem 152<br />
120 IF ABS(Q(J)-Q(K)) K-J THEN NEXT: NEXT: GOTO<br />
200 :rem 119<br />
130 R=l + INT(RND(1)*N) :rem 153<br />
140 IF R=J OR R=K GOTO 130 :rem 69<br />
150 TEMP=Q(R): Q(R)=Q(K): Q(K)=TEMP: GOTO 100<br />
:rem 243<br />
200 FOR J=l TO N: FOR K=l TO N :rem 237<br />
210 IF KQ(J) THEN PRINT "{RVS}{WHTJw"; :rem 252<br />
220 IF K= Q(J) THEN PRINT "{RVSHbLK} "; : rem 116<br />
230 NEXT: PRINT: NEXT: PRINT :rem 219<br />
300 GOTO 30 :rem 47<br />
Random numbers can be used to solve simulation problems of many kinds. Pro<br />
gram 4-18 prints <strong>the</strong> sum of <strong>the</strong> values of two dice and also keeps a running score. It<br />
keeps track of <strong>the</strong> average number of throws required to produce a 7 (<strong>the</strong> answer is<br />
it takes an average of six throws to score a 7).<br />
Program 4-18.<br />
Dice<br />
10 S=S+1<br />
20 D1%=1 + 6*RND(1)<br />
30 D2%=1 + 6*RND(1)<br />
40 PRINT Dl% D2%<br />
50 IF Dl%+D2% 7 GOTO 10<br />
60 N=N+1: T=T+S: S=0:{2 SPACES}REM N=NO OF 7S; T=T<br />
HROWS<br />
70 PRINT flSEVENl"{2 SPACES}T/N: GOTO 10<br />
Data Structures<br />
BASIC'S data structures are files, DATA statements, variables, and RAM storage.<br />
Files, which store data externally on tape or disk, aren't limited by available RAM<br />
and are necessary in handling large amounts of data. Disk files have more scope<br />
than tape, since several files can be accessed at once and search time is greatly re<br />
duced. Chapters 14 and 15 give full details of tape and disk programming, respec<br />
tively. "Wordscore" (above) is an introductory example of file handling.<br />
We've seen examples using DATA statements as well. Obviously, data cannot be<br />
changed in <strong>the</strong> same way variables can, and we needn't say more about it here.<br />
Simple variables are used often in BASIC. Chapter 6 explains exactly where <strong>the</strong>y<br />
are placed in memory and how <strong>the</strong>ir values are stored.<br />
Arrays (subscripted variables) offer a powerful extension to <strong>the</strong> concept of vari<br />
ables and are worth mastering for many serious applications. <strong>The</strong>y provide a single<br />
name for a whole series of related strings or numbers, using one or more subscripts<br />
to distinguish <strong>the</strong> separate items or elements.<br />
97
Effective <strong>Programming</strong> in BASIC<br />
One-dimensional arrays have a single subscript, which may take any value from<br />
0 to <strong>the</strong> value used in <strong>the</strong> DIM statement that defined <strong>the</strong> array (or 10 if DIM wasn't<br />
used). <strong>The</strong> line:<br />
DIM A$(50), N%(100), SX(12)<br />
defines three arrays: string, integer, and real number, respectively. Space is allocated<br />
in memory for <strong>the</strong>m, except for <strong>the</strong> string arrays. Arrays can be visualized as a set of<br />
consecutively numbered pigeon holes, each capable of storing one value, and initial<br />
ized with contents 0. A typical application is <strong>the</strong> lookup table. A string array might<br />
hold values like this:<br />
A$(0)="ZERO", A$(1)="ONE",<br />
and so forth, so that this line:<br />
PRINT A$(P<br />
would print <strong>the</strong> value of J as a word, provided J fell into <strong>the</strong> correct range. It might<br />
hold a list of names, ready for sorting, so that A$(0), A$(l), and so on, would be ar<br />
ranged alphabetically after sorting. Numeric arrays can be used to store <strong>the</strong> results of<br />
calculations. Many of <strong>the</strong> examples in this section use such arrays. For example,<br />
numbers ranging from 1 to 52 can represent playing card values; numbers from 1 to<br />
8 can represent <strong>the</strong> position of queens on a chessboard, indicating <strong>the</strong> row on which<br />
that column's queen is placed. It's often worthwhile to set up tables of <strong>the</strong> results of<br />
calculations, which can be looked up ra<strong>the</strong>r than recalculated. Chapter 13's sound<br />
calculations illustrate this technique.<br />
Array variables are slower than simple variables, because of <strong>the</strong> processing re<br />
quired, but <strong>the</strong>y are versatile. DIM A$(50) makes room for 51 variables (remember<br />
<strong>the</strong> zero element) and assigns each a unique name. Without this facility you'd have<br />
to define 51 individual names, and <strong>the</strong> resulting slowing effect would be<br />
considerable.<br />
Two-dimensional arrays have two subscripts.<br />
DIM C(8,8)<br />
defines a number array with room for 81 numbers, which might be used to record a<br />
chess position, pieces being represented by positive or negative numbers, with sign<br />
representing color, and magnitude, <strong>the</strong> type of piece. Two-dimensional arrays are<br />
valuable for storing data for business reports. (Three dimensions or more are concep<br />
tually a bit more difficult, but are occasionally useful.) For example, sales figures<br />
may be available for ten items in 12 different outlets. An array can keep <strong>the</strong> sets of<br />
data distinct. Subtotals and overall totals can be conveniently stored in <strong>the</strong> often ne<br />
glected zeroth elements.<br />
Integer arrays, which store numbers from —32768 to +32767 in slightly more<br />
than two bytes apiece, are particularly efficient in storing data and can be loaded<br />
from disk as a block of memory, as <strong>the</strong> following section explains. It's possible to<br />
telescope surprisingly large amounts of data into memory like this, although <strong>the</strong><br />
programming is likely to be difficult.<br />
Multi-dimensional arrays—at least those with more than two or three dimen<br />
sions—aren't used much, probably because of <strong>the</strong> difficulty of visualizing <strong>the</strong> data's<br />
storage pattern. Three-dimensional arrays can be pictured as rooms in a building,<br />
98
Effective <strong>Programming</strong> in BASIC<br />
having height, width, and depth. After this, depiction becomes progressively more<br />
complicated. In practice, large numbers of dimensions soon exhaust <strong>the</strong> <strong>64</strong>'s<br />
memory.<br />
RAM storage: Data may be poked into RAM for future use, or loaded from disk<br />
or tape, although this is not strictly BASIC. Chapter 6 discusses this technique and<br />
gives many examples.<br />
BASIC data can be treated in <strong>the</strong> same way, although generally this is worth<br />
doing only when integer arrays store data to be saved directly to disk or tape—<br />
which is far more efficient than writing to a file. Chapter 6 explains block SAVEs, <strong>the</strong><br />
relevant area being that from PEEK(47)+256*PEEK(48) to PEEK(49)+256*PEEK(50).<br />
Control Structures<br />
Some BASIC enhancement utilities offer structures like REPEAT-UNTIL and DO-<br />
WHILE. It's possible to simulate <strong>the</strong>se forms with BASIC; see FOR in Chapter 3, and<br />
this example:<br />
100 FOR J=0 TO -1 STEP 0<br />
110 REM INSERT UNTIL COMMANDS HERE<br />
120 J= (A=B)<br />
130 NEXT<br />
This has <strong>the</strong> same effect as REPEAT-UNTIL A=B, since J becomes -1 only<br />
when <strong>the</strong> logical expression in line 120 sets J true.<br />
IF-THEN-ELSE is ano<strong>the</strong>r structure missing from <strong>the</strong> <strong>64</strong>'s BASIC. ON-GOTO or<br />
ON-GOSUB is <strong>the</strong> nearest approach. Where ON isn't suitable, because an expression<br />
evaluating to 1, 2, 3, ... doesn't exist, GOTOs will probably be necessary to process<br />
both <strong>the</strong> THEN and ELSE parts of <strong>the</strong> program.<br />
Processing Dates<br />
Dates are sometimes difficult to handle; this section has routines to help validate<br />
<strong>the</strong>m, to compute <strong>the</strong> day of <strong>the</strong> week given <strong>the</strong> date, and to calculate <strong>the</strong> number of<br />
days between two dates. (Note that leap years are, of course, allowed for, but <strong>the</strong><br />
years 2000 and 1600, which don't count as leap years, have not been corrected for.)<br />
Program 4-19 is a date validation routine, which checks to make sure <strong>the</strong> day,<br />
month, and year combination is valid. D, M, and Y should be input as one- or twodigit<br />
numbers.<br />
If OK is true, D, M, and Y are acceptable. Line 1005 expects M to be from 1 to<br />
12, and Y to be 85 or 86; you can modify <strong>the</strong> limits for your own purposes. Line<br />
1010 checks that <strong>the</strong> day does not exceed 28, 29, 30, or 31, whichever applies to its<br />
month and year.<br />
Program 4-19.<br />
Date Validator<br />
1000 INPUT "D,M,YM;D,M,Y<br />
1005 OK = M>0 AND M0 AND Y>84 AND Y
Effective <strong>Programming</strong> in BASIC<br />
Program 4-20 calculates <strong>the</strong> day of <strong>the</strong> week from <strong>the</strong> date. <strong>The</strong> weekday is<br />
found by an algorithm usually called Zeller's Congruence.<br />
Program 4-20. Day of <strong>the</strong> Week Calculator<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader/' Appendix C.<br />
3 REM ENTER 3,12,86 FOR MARCH 12 1986 :rem 126<br />
4 REM C=19 FOR 1900S; C=18 FOR 1800S :rem 45<br />
20 DATA SUN,MON,TUE,WED,THU,FRI,SAT :C=19 :rem 16<br />
30 FOR J=0 TO 6: READ D$(J): NEXT :rem 172<br />
40 INPUT "MONTH,DAY,YEAR"; M,D,Y :rem 162<br />
50 M = M-2: IF M
Effective <strong>Programming</strong> in BASIC<br />
• Reducing <strong>the</strong> interrupt rate by POKE 56325,255 gives <strong>the</strong> 6510 more time to pro<br />
cess BASIC, less to scan <strong>the</strong> keyboard. Speed is increased by a couple of percentage<br />
points.<br />
• You should DIM variables in <strong>the</strong>ir order of importance at <strong>the</strong> start of <strong>the</strong> program.<br />
This has some effect on speed, depending on <strong>the</strong> number of variables in <strong>the</strong> pro<br />
gram; Chapter 6 explains why. New variables defined after arrays have been set up<br />
cause a one-time delay, too.<br />
► Using large numbers of strings causes garbage collection delays; <strong>the</strong>se can be cut<br />
down only by reducing <strong>the</strong> number of strings or by using fewer string operations.<br />
See Chapter 6.<br />
• Crunching (removing spaces from BASIC programs, except within quotes, and<br />
collecting <strong>the</strong> program into as few lines as possible) improves speed by an amount<br />
which depends on <strong>the</strong> waste in <strong>the</strong> original program.<br />
» Altering BASIC to run efficiently comes with practice, but a typical example is mov<br />
ing REM statements outside loops; if <strong>the</strong>y're inside, <strong>the</strong> REM statements are exe<br />
cuted many times. Streamlining unnecessarily repetitive programs will generally<br />
provide significant speed improvement.<br />
• <strong>The</strong> discussion in Chapter 8 about moving BASIC into RAM includes a few simple<br />
commands to alter <strong>the</strong> operation of RUN. As a result, BASIC programs run about<br />
3-1/2 percent faster.<br />
101
Chapter 5<br />
<strong>Commodore</strong> <strong>64</strong><br />
Architecture<br />
• Introductory Hardware Topics<br />
• <strong>The</strong> <strong>64</strong>'s Memory Configurations<br />
• <strong>Commodore</strong> <strong>64</strong> Ports<br />
• <strong>Programming</strong> <strong>the</strong> CIAs<br />
• Program Recovery and Resetting<br />
• Commercial Software and Hardware
Chapter 5<br />
<strong>Commodore</strong> <strong>64</strong> Architecture<br />
Introductory<br />
Hardware Topics<br />
This chapter takes you a step beyond general knowledge of <strong>the</strong> BASIC programming<br />
language, into <strong>the</strong> specifics of <strong>the</strong> <strong>Commodore</strong> <strong>64</strong> as a machine. <strong>The</strong> information<br />
here can be used with BASIC or machine language (ML) programming on <strong>the</strong> <strong>64</strong>,<br />
but much of it will not apply to o<strong>the</strong>r computers because it is so detailed. This in<br />
troductory section discusses several topics that will help you understand <strong>the</strong> rest of<br />
<strong>the</strong> book: binary and hexadecimal numbers, microchips, memory maps, <strong>the</strong> use of<br />
computers with televisions, and o<strong>the</strong>r hardware (machine ra<strong>the</strong>r than programoriented)<br />
subjects.<br />
Binary Numbers<br />
A bit, or frmary digit, is a single, tiny electronic switch, which can be ei<strong>the</strong>r on or off.<br />
It can be pictured as an ordinary switch, which ei<strong>the</strong>r passes or blocks current. It is<br />
actually a tiny area of a computer chip that ei<strong>the</strong>r holds an electrical charge or<br />
doesn't. Hundreds of thousands of <strong>the</strong>se switches are contained in a <strong>Commodore</strong> <strong>64</strong>,<br />
inside <strong>the</strong> black rectangular integrated circuit chips.<br />
Each bit has a choice of only two values: on or off. According to convention, <strong>the</strong><br />
binary voltage values are assigned numbers (no voltage is represented by 0, and<br />
voltage high is represented by 1). <strong>The</strong> systems are <strong>the</strong>n structured so that binary<br />
arithmetic works correctly. <strong>The</strong> values aren't actually 0 or 1, but this provides a<br />
convenient way of talking about <strong>the</strong>m.<br />
In principle, three values could be used, making trinary arithmetic possible, but<br />
binary hardware is by now so firmly established that this is not likely to become im<br />
portant. As several computer manufacturers have found already, <strong>the</strong>re is often little<br />
economic sense in introducing hardware based on such novel processes.<br />
All <strong>Commodore</strong> <strong>64</strong> operations are binary. In hardware terms this is reflected in<br />
<strong>the</strong> large number of electronic lines needed to carry data within <strong>the</strong> <strong>Commodore</strong> <strong>64</strong>.<br />
<strong>The</strong> expansion port, for example, has 44 separate lines. Every line, apart from those<br />
which supply power or are grounded, is treated by <strong>the</strong> system as carrying ei<strong>the</strong>r<br />
high or low voltage values. Each additional line roughly doubles <strong>the</strong> system's poten<br />
tial for information handling.<br />
A full understanding of programming requires a grasp of <strong>the</strong> relation between<br />
binary numbers and ordinary numbers. Fortunately, this is not difficult, although it<br />
can look forbidding at first. Binary arithmetic uses a notation of 0's and l's only.<br />
However, it represents ordinary numbers and is merely a different way of writing<br />
<strong>the</strong>m, just as MCMLIX is a different way of writing 1959.<br />
A digit's position within a decimal number determines <strong>the</strong> magnitude of that<br />
digit; thus, 123 and 1230 mean different things. In <strong>the</strong> same way, <strong>the</strong> positions of 0's<br />
and l's within a binary number determine <strong>the</strong> value of that number. Binary<br />
10101100 is different from 00101011, with <strong>the</strong> leftmost number being <strong>the</strong> most<br />
significant. And just as decimal digit positions increase in value by powers of ten (1,<br />
10, 100, 1000, 10000, . . .), from right to left, binary digits increase in value by pow<br />
ers of two (1, 2, 4, 8, 16, . . .).<br />
To avoid confusion, a binary number will be written as a series of 0's and l's<br />
105
<strong>Commodore</strong> <strong>64</strong> Architecture<br />
prefaced by a percentage sign (%). This lets us be sure, for example, that <strong>the</strong> decimal<br />
number 10 is not confused with %10, which represents <strong>the</strong> decimal value 2 in binary<br />
notation.<br />
<strong>The</strong> word byte is a derivation of <strong>the</strong> word bit. It is supposed to imply a larger,<br />
scaled-up version of a bit, and that is more or less what it is. A byte is a collection of<br />
8 bits which are dealt with by <strong>the</strong> system as though <strong>the</strong>y were a single unit. This is<br />
purely a hardware matter: IBM invented 8-bit bytes, but o<strong>the</strong>r numbers such as 4 or<br />
6 bits are in use, too. A 4-bit binary number is called a nybble.<br />
<strong>The</strong> 6502-based (6510) microprocessor which operates your <strong>Commodore</strong> <strong>64</strong> is<br />
called an 8-bit chip because it is designed to deal with data 8 bits at a time. Its<br />
addressing uses 16 bits, but only by dividing up each address into two sets of 8 bits<br />
each, called <strong>the</strong> low byte (<strong>the</strong> less significant) and <strong>the</strong> high byte (<strong>the</strong> more significant).<br />
Since each of <strong>the</strong> 8 bits can be ei<strong>the</strong>r on or off, you have 28 = 2*2*2*2*2*2*2*2<br />
= 256 potential combinations for each byte. That is <strong>the</strong> reason that PRINT<br />
PEEK(address) always yields a value between 0 and 255. It also explains why a 16-bit<br />
address cannot exceed 65535, which is 216 — 1 (since 0 is one of <strong>the</strong> combinations).<br />
<strong>The</strong>re is a total of 65536 addressable memory locations, with addresses ranging from<br />
0 to 65535.<br />
Binary and Decimal Arithmetic<br />
Since binary and decimal are only different notations for <strong>the</strong> same thing, it is pos<br />
sible to translate binary numbers into decimal. Consider only 8-bit numbers now,<br />
since <strong>the</strong>y are common in programming <strong>the</strong> <strong>64</strong>. <strong>The</strong> convention is to number <strong>the</strong> bits<br />
by <strong>the</strong>ir corresponding power of two, reading from <strong>the</strong> left, as 7, 6, 5, 4, 3, 2, 1, 0.<br />
Table 5-1 shows how this works.<br />
Table 5-1. 8-Bit Binary and Decimal Conversion<br />
Bit Number: 7 6 5 4 3 2 10<br />
Decimal Value: 128 <strong>64</strong> 32 16 8 4 2 1<br />
Sample Bytes:<br />
%0000 0000=<br />
%0000 1111 =<br />
%1000 0001 =<br />
%11111111=<br />
0<br />
0<br />
128<br />
128<br />
0<br />
0<br />
0<br />
<strong>64</strong><br />
0<br />
0<br />
0<br />
32<br />
0<br />
0<br />
0<br />
16<br />
0<br />
8<br />
0<br />
8<br />
0<br />
4<br />
0<br />
4<br />
0<br />
2<br />
0<br />
2<br />
0 = decimal 0<br />
1 = decimal 15<br />
1 = decimal 129<br />
1 = decimal 255<br />
Table 5-1 shows some binary numbers and <strong>the</strong>ir decimal equivalents and also<br />
demonstrates certain features of <strong>the</strong> bit pattern of binary numbers. For example, if<br />
bit 7 is 1, <strong>the</strong> number must have a decimal value of 128 or more. If bit 0 is 0, <strong>the</strong><br />
number must be even because only bit 0 can take <strong>the</strong> value 1 and make a number<br />
odd. If a bit pattern is moved as a whole to <strong>the</strong> right one position, its value is exactly<br />
halved (provided <strong>the</strong> smallest bit isn't a 1 and <strong>the</strong>refore lost when <strong>the</strong> shift takes<br />
place). Finally, if a bit pattern is inverted (so that all l's become 0's and all 0's be<br />
come l's), <strong>the</strong> two individual values will always add to 255 (because 255 is<br />
%11111111). Observations like <strong>the</strong>se, which are analogous to ordinary base 10<br />
arithmetic, are crucial to <strong>the</strong> understanding of ML programs.<br />
106
<strong>Commodore</strong> <strong>64</strong> Architecture<br />
Hexadecimal Numbers<br />
Hexadecimal (base 16, called hex for short) is ano<strong>the</strong>r notation that is useful when<br />
programming. BASIC programmers usually ignore hex, but ML programmers find it<br />
useful. It relates directly to <strong>the</strong> internal design of <strong>the</strong> computer, and this relation<br />
helps ML programmers and hardware designers.<br />
Hex uses 16 different numeral symbols for its arithmetic—<strong>the</strong> ordinary numeral<br />
symbols 0-9 and <strong>the</strong> letters A-F. To avoid confusion with ordinary numbers, hex<br />
numbers are preceded by <strong>the</strong> dollar sign ($). Thus, $1 is a valid hex number, as are<br />
$A000, $1234, $21, $ACE, and $BEER <strong>The</strong> decimal equivalents of <strong>the</strong>se numbers are<br />
40960, 4660, 33, 2766, and 48879. <strong>The</strong>y can be looked up in conversion tables or<br />
converted with a programmer's calculator.<br />
It is important to notice <strong>the</strong> relationship between binary and hex numbers,<br />
which results from <strong>the</strong> fact that 16 (hex's base) is a power of 2 (binary's base). It<br />
turns out that any 16-digit binary number (2 bytes) can be represented in only 4 hex<br />
digits. This saves space and is easier to remember.<br />
Because each memory location in <strong>the</strong> <strong>64</strong> can hold one 8-bit byte (having a deci<br />
mal value 0-255), it is common to write single bytes in hex using two digits, even if<br />
<strong>the</strong> leading digit is 0. Thus, <strong>the</strong> range is $00-$FR That makes for neater programs,<br />
because <strong>the</strong> numbers line up evenly. Similarly, since memory addresses can only<br />
range from 0 to 65535, <strong>the</strong>y are written as 4-digit hex numbers. It is not necessary<br />
that leading zeros be included ($033C and $33C are <strong>the</strong> same number), but many<br />
ML programs are written to expect such an arrangement.<br />
Hex and Decimal Arithmetic<br />
While programming in BASIC, you are likely to use decimal ra<strong>the</strong>r than hex. But<br />
<strong>the</strong>re will be times when you wish to convert between <strong>the</strong> two bases (for example,<br />
to find a SYS address). It is usually easiest to convert using a program, a calculator,<br />
or a set of conversion tables. With practice, though, translation between decimal and<br />
hex as well as addition and subtraction in hex become easier.<br />
Like binary and decimal numbers, hex values use a position convention; <strong>the</strong> fur<br />
<strong>the</strong>r left <strong>the</strong> digit, <strong>the</strong> greater its value. <strong>The</strong> numeral 1 can mean 1, 10, 100, or 1000<br />
(or any power of 10), depending on where it is located within a number. Similarly, a<br />
1 in hex can represent a decimal value of 1, 16, 256, or 4096 (or any power of 16).<br />
Remember that hex arithmetic uses all 16 digits (0-F). Thus, $09 plus $01 is<br />
$0A, not $10, and $0F plus $01 is $10. Similarly, $1234 plus $0F is $1243. Table 5-2<br />
illustrates some hexadecimal numbers and <strong>the</strong>ir decimal equivalents.<br />
Table 5-2<br />
Hexadecimal-to-Decimal Number Conversion<br />
Decimal Value:<br />
Sample Hexadecimal Numbers:<br />
$A0<br />
$11<br />
$1000<br />
$FFFF<br />
4096 256 16 1<br />
0<br />
0<br />
1*4096<br />
15*4096<br />
0<br />
0<br />
0<br />
15*256<br />
10*16<br />
1*16<br />
15*16<br />
0<br />
1<br />
0<br />
15<br />
= Decimal 160<br />
= Decimal 17<br />
= Decimal 4096<br />
= Decimal 65535<br />
107
<strong>Commodore</strong> <strong>64</strong> Architecture<br />
To convert $1234 into decimal, multiply 1 by 163 (or 4096), adding 2 times 162<br />
(or 256), adding 3 times 161 (or 16), and finally adding 4 times 16° (or 1). Conver<br />
sion from decimal to hex can be done by dividing by 4096 first, <strong>the</strong>n repeatedly mul<br />
tiplying remainders by 16 to find <strong>the</strong> next digit.<br />
Computer memory is measured in kilobytes (K), where IK is 1024 (210) bytes.<br />
Note that $1000 is 4096, exactly 4K, and that <strong>the</strong>re are exactly sixteen 4K blocks of<br />
memory in <strong>the</strong> <strong>64</strong>, $0000-0FFF, $1000-$lFFF, and so on.<br />
Hardware, Chips,<br />
and Addressing<br />
<strong>The</strong> <strong>64</strong> contains a printed circuit board where <strong>the</strong> integrated circuit chips are<br />
mounted, a power supply, and ports for input and output. A quartz crystal clock,<br />
generating several million pulses per second, controls overall timing. <strong>The</strong> <strong>64</strong>'s central<br />
processing unit (CPU) is a 6510 chip, which is an updated version of <strong>the</strong> 6502<br />
microprocessor. It handles most of <strong>the</strong> <strong>64</strong>'s work. <strong>The</strong> address bus consists of 16 lines<br />
to <strong>the</strong> 6510 that allow a choice of 216 (65536, or <strong>64</strong>K) different memory locations to<br />
be written to or read from. <strong>The</strong> o<strong>the</strong>r lines determine which chips are active at any<br />
time, and carry signals between chips; most lines carry ei<strong>the</strong>r about 5 volts or 0<br />
volts.<br />
<strong>The</strong> 6510 can address <strong>64</strong>K because, although it deals in 8-bit bytes, it can treat 2<br />
bytes as one address, by using <strong>the</strong> first byte as <strong>the</strong> low byte and <strong>the</strong> second byte as<br />
<strong>the</strong> high byte. (<strong>The</strong> second byte is multiplied by 256, <strong>the</strong>n added to <strong>the</strong> first byte.<br />
<strong>The</strong> result is <strong>the</strong> address, which can range from 0 to 65535, since 255 added to <strong>the</strong><br />
quantity, 255 X 256, equals 65535.) <strong>The</strong> 6510, which controls all of this data and<br />
addressing, has no position (no address) in memory.<br />
Chips often start at addresses like $8000 because it's easier to wire <strong>the</strong>m that<br />
way. This is one reason why hex is often more meaningful than decimal when<br />
discussing computers. BASIC expressions which convert hex, like MEM =13*4096 or<br />
SYS 8*16f 3, make it clear that $D000 or $8000 (in <strong>the</strong>se two examples) is <strong>the</strong> rele<br />
vant address.<br />
Random Access Memory (RAM) is one kind of memory in <strong>the</strong> <strong>64</strong>. It is volatile,<br />
which simply means that <strong>the</strong> information is lost when you turn <strong>the</strong> computer off.<br />
RAM can be changed and examined—written to and read from—and can hold pro<br />
grams or data. When RAM is changed (overwritten) accidentally, <strong>the</strong> data is de<br />
scribed as being corrupt. Meaningless data left over from earlier programs is called<br />
garbage. <strong>The</strong> <strong>Commodore</strong> <strong>64</strong> can be set, or configured, to have <strong>64</strong>K of RAM.<br />
Read Only Memory (ROM) cannot be overwritten, modified, or corrupted. <strong>The</strong><br />
<strong>64</strong>'s built-in ROMs hold BASIC and <strong>the</strong> Kernal, among o<strong>the</strong>r things, but <strong>the</strong>y can be<br />
switched out—replaced by RAM or external cartridge ROM.<br />
Briefly, here is how <strong>the</strong> <strong>64</strong>'s memory works. POKEing or PEEKing a value<br />
means <strong>the</strong> hardware has to set 16 address lines high or low in <strong>the</strong> correct bit pattern,<br />
and simultaneously set <strong>the</strong> 8 data bus lines to <strong>the</strong> correct value to be POKEd. <strong>The</strong><br />
circuit must be designed so that only <strong>the</strong> correct location is addressed. <strong>The</strong> chip se<br />
lect line of <strong>the</strong> appropriate chip must be turned on, and <strong>the</strong> write line enabled. Dur<br />
ing read or write, <strong>the</strong> power consumption of <strong>the</strong> chip rises considerably. Data is<br />
transferred only when voltages have settled, at one specific stage in <strong>the</strong> clock cycle.<br />
ROMs are made with <strong>the</strong>ir programs permanently burned in. <strong>The</strong>y are massproduced<br />
in long production runs and are subject to risks of inventory holding and<br />
108
<strong>Commodore</strong> <strong>64</strong> Architecture<br />
manufacturing problems, and are <strong>the</strong>refore not designed for direct use of <strong>the</strong> average<br />
consumer.<br />
On <strong>the</strong> o<strong>the</strong>r hand, an EPROM (Erasable Programmable Read Only Memory)<br />
chip resembles a ROM chip, but is made in much smaller quantities. Any program<br />
can be burned in with inexpensive, widely available equipment. EPROMs have a<br />
window in <strong>the</strong> top, usually covered by a label. If this label is removed, exposure to<br />
strong ultraviolet light will erase <strong>the</strong> EPROM for reprogramming. This is how bugs<br />
in an EPROM program can be corrected. A PROM (Programmable Read Only<br />
Memory) is similar to an EPROM, but not erasable. Many cartridge products, printer<br />
interfaces, and o<strong>the</strong>r products under development use EPROMs or PROMs.<br />
<strong>The</strong>re is an intermediate form of memory in which data stored in RAM can be<br />
in effect converted to ROM, by disabling <strong>the</strong> RAM chip's write-enable line. Battery<br />
backup allows such a package to store data even when disconnected from <strong>the</strong> usual<br />
house current. This capability could become important, in view of <strong>the</strong> relatively low<br />
price of RAM.<br />
<strong>The</strong> <strong>64</strong>'s Input/Output (I/O) chips are <strong>the</strong> VIC-II (Video Interface Chip), which<br />
generates <strong>the</strong> signal for <strong>the</strong> color TV; SID (Sound Interface Device), which controls<br />
<strong>the</strong> <strong>64</strong>'s sound output; and two CIAs (Complex Interface Adapters), which are ex<br />
plained later in this chapter. <strong>The</strong>y control most timing and input/output (keyboard,<br />
tape, and disk). All <strong>the</strong>se chips have special addresses in <strong>the</strong> memory map.<br />
Like <strong>the</strong> 6510, <strong>the</strong> PLA (Programmed Logic Array) is invisible, or transparent, to<br />
<strong>the</strong> programmer. It supervises hardware operations within <strong>the</strong> <strong>64</strong>. For example, it<br />
turns off RAM when a ROM cartridge is plugged in (so <strong>the</strong> cartridge can be read)<br />
and turns off <strong>the</strong> 6510 at intervals to allow <strong>the</strong> VIC-II to generate <strong>the</strong> TV picture.<br />
Most computer hardware has <strong>the</strong>se features. For example, a typical printer has<br />
an I/O chip to read input, a CPU with a program in ROM to process and decide<br />
what to do with its input, RAM for temporary storage of text, and character ROM to<br />
select dot-matrix characters. Printers will behave differently according to <strong>the</strong> ROM<br />
fitted inside. Ano<strong>the</strong>r example which illustrates how simple hardware fixes work is<br />
Chapter 15's modification to change disk drive device numbers.<br />
Ano<strong>the</strong>r interesting if not useful feature of <strong>the</strong> <strong>64</strong> is incomplete address de<br />
coding, which happens when some address lines are left unconnected. Since <strong>the</strong><br />
VIC, SID, and CIA chips all have repeating images in memory, <strong>the</strong>re's actually a<br />
choice of many equivalent addresses for <strong>the</strong> same functions on <strong>the</strong>se chips. For ex<br />
ample, POKE 53600,0 has <strong>the</strong> same effect as POKE 53280,0—it changes <strong>the</strong> border<br />
color to black.<br />
Finding Your Way with<br />
a Memory Map<br />
A memory map is a list of a computer's addresses and functions. <strong>The</strong> <strong>64</strong> is designed<br />
to allow different features to be bank-selected, like choosing cartridge ROM ra<strong>the</strong>r<br />
than RAM. It <strong>the</strong>refore has several different memory maps. Moreover, every chip<br />
has its own memory map—usually a small one. <strong>The</strong> VIC-II chip's map is significant,<br />
and crucial to understanding <strong>Commodore</strong> <strong>64</strong> graphics (see Chapter 12). Most of this<br />
book uses <strong>the</strong> term memory map to mean <strong>the</strong> memory locations that are connected to<br />
<strong>the</strong> 6510 chip.<br />
<strong>The</strong> ROM in <strong>the</strong> <strong>64</strong> contains several different kinds of information, all of which<br />
109
<strong>Commodore</strong> <strong>64</strong> Architecture<br />
contribute to its overall operation. <strong>The</strong>y are discussed below (see Chapter 11 for a<br />
detailed look at <strong>Commodore</strong> <strong>64</strong> ROM).<br />
Tables. <strong>The</strong>se contain data, not programs, and have innumerable uses. <strong>The</strong><br />
screen link table and ROM keywords are typical examples. <strong>The</strong> screen table is in<br />
RAM because it has to be able to change to reflect <strong>the</strong> screen's organization. <strong>The</strong><br />
high bit of <strong>the</strong> last character of each ROM keyword is on, which makes <strong>the</strong> character<br />
appear reversed on <strong>the</strong> screen when using Program 5-1 below. File tables, which<br />
hold details about each currently open file, are ano<strong>the</strong>r example.<br />
Buffers. A buffer is a section of RAM reserved for input or output. Buffers in <strong>the</strong><br />
<strong>64</strong> include <strong>the</strong> input buffer, <strong>the</strong> keyboard buffer, and <strong>the</strong> 192-byte tape buffer at<br />
$033C-$03FB (828-1019), which is important when reading from and writing to<br />
tape.<br />
Pointers. Zero page (memory locations 0-255) contains many pairs of adjacent<br />
bytes that are pointers to special locations. Information about <strong>the</strong> top and bottom of<br />
BASIC text, arrays, and strings is held in this manner, for example. <strong>The</strong> pair of bytes<br />
forms an address in <strong>the</strong> low-byte/high-byte format described above. For example,<br />
locations 43 and 44 are <strong>the</strong> pointer to <strong>the</strong> beginning of BASIC program storage. On<br />
<strong>the</strong> <strong>64</strong>, <strong>the</strong> normal values held in <strong>the</strong>se locations are 1 ($01) and 8 ($08), indicating<br />
that program storage starts at location 1+(8*256)=2049 ($0801).<br />
Vectors. <strong>The</strong>se resemble pointers, as <strong>the</strong>y are also pairs of bytes that constitute<br />
addresses. However, while pointers merely hold address information, vectors are<br />
used to tell <strong>the</strong> computer where to find routines to perform important operations.<br />
Each vector is set up to point to a routine within BASIC or <strong>the</strong> Kernal operating sys<br />
tem when <strong>the</strong> system is turned on or reset. Altering <strong>the</strong>se values enables many func<br />
tions of <strong>the</strong> <strong>64</strong> to be modified.<br />
<strong>The</strong> memory examination program described below changes <strong>the</strong> vector to <strong>the</strong><br />
interrupt routine, which looks at <strong>the</strong> keyboard every 1/60 second. Sometimes ROM<br />
contains vectors; <strong>the</strong> Kernal jump table is a good illustration. It is different, though,<br />
in that each address is preceded by a 6502/6510 JMP instruction and <strong>the</strong>refore occu<br />
pies three bytes instead of two.<br />
Flags. <strong>The</strong>se keep track of a wide variety of events while a program is run,<br />
ranging from whe<strong>the</strong>r <strong>the</strong> machine is in immediate mode to <strong>the</strong> position of <strong>the</strong><br />
cursor on <strong>the</strong> screen.<br />
Programs. Most of ROM is subdivided into <strong>the</strong> BASIC interpreter and <strong>the</strong><br />
Kernal, a collection of related machine language routines. <strong>The</strong> only substantial pro<br />
gram outside ROM is CHRGET, a routine at locations $73-$8A (115-138) that<br />
fetches individual BASIC characters. CHRGET is copied out of ROM into RAM when<br />
<strong>the</strong> system is turned on or reset. Having <strong>the</strong> routine in RAM is faster than using a<br />
ROM routine, and it permits new BASIC keywords to be added using a program<br />
called a wedge, which will be explained later.<br />
Accumulators. Several number storage areas exist in RAM: two floating-point<br />
accumulators, where numbers are added, multiplied, and so on ($61-$70), and <strong>the</strong><br />
realtime clock ($A0-$A2). You can use Program 5-1 to view <strong>the</strong> three bytes of <strong>the</strong><br />
clock changing.<br />
<strong>The</strong> stack. <strong>The</strong> stack is discussed in more detail in later chapters. Essentially, it<br />
is 256 bytes of RAM from $100 to $1FF (256-511) that are used by <strong>the</strong> 6510<br />
microprocessor to store temporary information, particularly information relating to<br />
110
<strong>Commodore</strong> <strong>64</strong> Architecture<br />
subroutines. It is normally best left alone. Short machine language routines can be<br />
stored in <strong>the</strong> lower portion of <strong>the</strong> stack; if tMpe drive is in use, a safe starting location<br />
is $013F.<br />
Looking Inside Your <strong>64</strong><br />
Program 5-1 display^ <strong>the</strong> contents of any section of <strong>the</strong> <strong>64</strong>'s memory, up to 255<br />
bytes long, at <strong>the</strong> top of <strong>the</strong> screen. Characters are POKEd into screen RAM 60 times<br />
a second, so you can watch as <strong>the</strong> bytes in some registers change. (Because <strong>the</strong><br />
characters are POKEd into <strong>the</strong> screen memory section instead of being printed, <strong>the</strong><br />
@ represents a zero byte, and setting lowercase mode with SHIFT-<strong>Commodore</strong> key<br />
makes alphabetic characters more readable).<br />
To use <strong>the</strong> "MicroScope" program, type it in and run it. <strong>The</strong> PRINT statements<br />
suggest a few interesting areas of memory. Press RUN/STOP-RESTORE to turn <strong>the</strong><br />
display off, and SYS49152 to turn it on again.<br />
Program 5-1.<br />
MicroScope<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader/' Appendix G<br />
4 DATA 32,115,0,240,27,32,138,173,32,247 :rem 163<br />
5 DATA 183,132,252,133,253,32,155,183,134 :rem 223<br />
6 DATA 254,120,169,44,141,20,3,169,192,141 :rem 15<br />
7 DATA 21,3,96,120,169,49,141,20,3,169,234 :rem 18<br />
8 DATA 141,21,3,96,1<strong>64</strong>,254,136,177,252,153 :rem 26<br />
9 DATA 0,4,169,1,153,0,216,192,0,208,241,76,49,234<br />
:rem 150<br />
10 FOR J=49152 TO 49215: READ X: POKE J,X: NEXT<br />
:rem 218<br />
20 PRINT M{CLR}{YEL}{6 DOWN}SYS 49152, START, NUMB<br />
ER OF LOCATIONS :rem 156<br />
40 PRINT H{DOWN}SYS 49152,512,80 IS INPUT BUFFER,<br />
:rem 146<br />
50 PRINT "SYS 49152,217,24=SCREEN LINK TABLE,<br />
:rem 47<br />
60 PRINT "SYS 49152,255,18=NUMBER OUTPUT BUFFER,<br />
:rem 83<br />
70 PRINT "SYS 49152,41110,255=SOME ROM KEYWORDS.<br />
:rem 10<br />
100 PRINT "{BLK}{DOWN}SYS 49152, TURNS ROUTINE OFF<br />
:rem 174<br />
You can see how 40-column lines are linked by a table into 80-character lines or<br />
watch <strong>the</strong> activity in zero page. PRINT USING (Chapter 6) relies on <strong>the</strong> number out<br />
put routine. Watch ten characters (normally) line up in <strong>the</strong> keyboard queue (which<br />
starts at 631), as you press keys quickly. For more interesting places to peek around,<br />
check a memory map.<br />
<strong>The</strong> <strong>64</strong> and Your TV<br />
<strong>Commodore</strong>'s designers had <strong>the</strong> problem of interfacing <strong>the</strong> <strong>64</strong> with TVs, which<br />
aren't directly controlled by <strong>the</strong> computer. <strong>The</strong>ir solution was effective and relied on<br />
111
<strong>Commodore</strong> <strong>64</strong> Architecture<br />
isolating <strong>the</strong> TV-specific parts of <strong>the</strong> computer's operations as much as possible.<br />
Computers generate three types of video signals: RGB, which directly controls<br />
<strong>the</strong> voltages applied to red, green, and blue circuits, and isn't available on <strong>the</strong> <strong>64</strong>;<br />
composite video, a mixture of <strong>the</strong>se signals, available from <strong>the</strong> audio-video socket;<br />
and modulated, where <strong>the</strong> audio and video signals are superimposed on a highfrequency<br />
carrier to mimic ordinary TV signals. This is available from <strong>the</strong> <strong>64</strong>'s phono<br />
plug, but some loss of quality occurs in <strong>the</strong> process.<br />
Some TVs do not work well with personal computers. In general, newer TVs are<br />
better than old ones because <strong>the</strong> manufacturers now consider computers when<br />
designing <strong>the</strong>ir sets. Automatic tuning circuitry and field synchronization (without<br />
which <strong>the</strong> picture flutters up) have been two difficult areas, but nei<strong>the</strong>r square-wave<br />
sound nor static-charge noise (when a screen suddenly blanks) has posed major<br />
problems. However, it is still a good idea to keep <strong>the</strong> brightness level below its<br />
maximum.<br />
Monitors are modified TVs designed to give good-quality monochrome or color<br />
pictures from computer output. <strong>Commodore</strong>'s 1700 series, for example, works with<br />
<strong>the</strong> <strong>64</strong>'s composite signals. <strong>The</strong> <strong>64</strong>'s audio-video socket has luminance (brightness)<br />
and composite video, and <strong>the</strong> later 8-pin sockets have a chroma pin, which is pure<br />
color information. A monochrome monitor should be connected to use luminance<br />
alone, since color information will degrade <strong>the</strong> picture. <strong>The</strong> situation is more com<br />
plex with color monitors. With 5-pin DINs, <strong>the</strong> luminance output (pin 1) should be<br />
wired to <strong>the</strong> luminance, or luma, input and <strong>the</strong> composite video output (pin 4), to<br />
<strong>the</strong> chroma input. With 8-pin DINs, <strong>the</strong> chroma signal should be connected to <strong>the</strong><br />
chroma input and <strong>the</strong> luminance output to <strong>the</strong> monitor's luma input. <strong>The</strong> cable pro<br />
vided may not make <strong>the</strong>se connections; you may have to make up leads yourself.<br />
VCR recording requires a TV/computer switch and a band separator. This al<br />
lows switching between recording regular TV programs and recording <strong>the</strong> <strong>64</strong>'s<br />
output.<br />
If you are planning to take pictures of <strong>the</strong> screen, use an exposure not faster<br />
than about 1/30 second. Faster shutter speeds will capture <strong>the</strong> image of a dark band<br />
on <strong>the</strong> screen, because all TVs use interlace, tracing only half <strong>the</strong> picture in each<br />
scan. This creates <strong>the</strong> illusion of movement without flicker.<br />
TV sets in <strong>the</strong> United States, Canada, Japan, and much of South America use<br />
<strong>the</strong> NTSC (National Television Standards Committee) standard of 525 lines per<br />
screen. <strong>The</strong> screen is refreshed 60 times per second. Most of Europe (excluding<br />
France), Australia, and some o<strong>the</strong>r countries use PAL (Phase Alternation by Line),<br />
which uses a 625-line picture and refreshes it 50 times per second. France and <strong>the</strong><br />
USSR use SECAM, a system resembling PAL but with certain parameters changed.<br />
<strong>64</strong> Hardware Tidbits<br />
<strong>The</strong> Schematic. This is a useful diagram in <strong>the</strong> <strong>Commodore</strong> <strong>64</strong> Programmer's Ref<br />
erence Guide. It corresponds, more or less, to <strong>the</strong> internal arrangement of your <strong>64</strong>.<br />
<strong>The</strong> power input is defined in terms of a 5-volt DC line, two 9-volt AC lines, and<br />
ground. Since <strong>the</strong>se are supplied outside <strong>the</strong> <strong>64</strong>, <strong>the</strong> same <strong>64</strong> can be used in coun<br />
tries with different house current types, provided a local <strong>Commodore</strong> adapter is<br />
used, and <strong>the</strong> crystal, jumper, and VIC-II chip match <strong>the</strong> TV. <strong>The</strong> VIC-II's power<br />
supply is separate from <strong>the</strong> SID's.<br />
112
<strong>Commodore</strong> <strong>64</strong> Architecture<br />
A system reset connects all <strong>the</strong> major chips' RESET pins and is triggered by a<br />
timer. <strong>The</strong> address bus (labeled A0-A15) and <strong>the</strong> data bus (D0-D7) appear as solid<br />
lines. <strong>The</strong> game control ports have power supplied to <strong>the</strong>m, and joysticks are wired<br />
with <strong>the</strong> keyboard, which has two pairs of eight wires, plus a RESTORE key. Note<br />
how CIA l's interrupt request line connects to IRQ on <strong>the</strong> 6510, while CIA 2's con<br />
nects to NMI. Port A of CIA 2 controls <strong>the</strong> serial bus and also selects <strong>the</strong> VIC-II's<br />
bank; port B is connected to <strong>the</strong> user port and <strong>the</strong>refore handles RS-232<br />
communications.<br />
<strong>The</strong> PLA. Inputs are drawn on <strong>the</strong> left, outputs on <strong>the</strong> right. <strong>The</strong> 16 input lines<br />
allow 65,536 combinations, all of which are processed within <strong>the</strong> chip to give 8 out<br />
puts, though, as we've seen, only 5 input lines determine most of <strong>the</strong> memory<br />
configuration, selecting BASIC or Kernal ROMs, <strong>the</strong> character generator, or some<br />
o<strong>the</strong>r area of memory. <strong>The</strong> PLA controls which banks of memory are active and<br />
distinguishes between reading from and writing to chips, allowing o<strong>the</strong>rwise<br />
meaningless BASIC statements like POKE J, PEEKfl) which PEEK from ROM but<br />
POKE to underlying RAM at <strong>the</strong> same address.<br />
Different <strong>64</strong>s. All computers are subject to redesign as improved layouts and<br />
technology are introduced and errors removed. This process has helped <strong>Commodore</strong><br />
reduce prices while generally improving <strong>the</strong> hardware.<br />
At present <strong>the</strong>re are three main types of <strong>64</strong>s. All have Microsoft BASIC 2.0<br />
(which CBM owns <strong>the</strong> rights to). <strong>The</strong> BASIC statement PRINT PEEK(65408) returns<br />
<strong>the</strong> Kernal release number.<br />
<strong>The</strong> earlier <strong>64</strong>s (5-pin audio-video socket) have a 1982 printed circuit board with<br />
two CIAs, BASIC ROM 01, Kernal ROM 01 (release 0), character-generator ROM<br />
(socketed), 6510, and PLA. <strong>The</strong> SID and VIC are enclosed in a box. RAM chips oc<br />
cupy <strong>the</strong> bottom left, with <strong>the</strong> fuse at <strong>the</strong> right. BASIC had a few bugs, notably an<br />
occasional lockup on screen editing, and an INPUT bug.<br />
<strong>The</strong> 1983 revision (8-pin audio-video socket) has Kernal ROM 03 (release 2),<br />
which removed most bugs (and also made screen POKEs invisible). <strong>The</strong> design is<br />
somewhat different, with improved video layout (under a perforated screen) and a<br />
better TV modulator and power supply. Most chips, except VIC-II and SID, are sol<br />
dered, not socketed.<br />
<strong>The</strong> SX-<strong>64</strong> (release 96) has a restyled casing and small monitor, but is very simi<br />
lar to <strong>the</strong> <strong>64</strong>. Its power-up message is different and it has a white background de<br />
fault color, since light blue on blue is unsuitable for <strong>the</strong> small monitor. Tape<br />
software, although mostly still in ROM, is branched over, so device 1 no longer ex<br />
ists, since <strong>the</strong>re is no tape port. SHIFT-RUN/STOP puts LOAD "*",8 (with a RE<br />
TURN character), <strong>the</strong>n RUN (with ano<strong>the</strong>r RETURN) into <strong>the</strong> keyboard buffer, so it<br />
will load and run <strong>the</strong> first program from disk. <strong>The</strong> rest of BASIC is unchanged.<br />
MAX. <strong>The</strong> ULTIMAX was to have been a cartridge-compatible games machine.<br />
It never appeared. It was to have no keyboard, BASIC or Kernal ROM, or character<br />
generator, a different VIC-II memory map, and only 2K RAM. Some <strong>64</strong> hardware<br />
features were designed with it in mind.<br />
VIC-II and SID Chips. Chapter 12 explains how to program <strong>the</strong> VIC-II chip.<br />
<strong>The</strong> SID chip is explained in Chapter 13.<br />
From <strong>the</strong> hardware viewpoint <strong>the</strong> VIC-II chip is important because it sometimes<br />
disturbs <strong>the</strong> timing of o<strong>the</strong>r chips, since <strong>the</strong> calculations it has to do are so complex,<br />
113
<strong>Commodore</strong> <strong>64</strong> Architecture<br />
particularly with sprites enabled. This can affect <strong>the</strong> use of tape, disk, and RS-232,<br />
though not SID's sound output. VIC-II has two on/off bits, one (DEN) in register 17,<br />
which turns off screen processing (but not sprites), and ano<strong>the</strong>r (RESET) in register<br />
22, which in later VIC chips has no effect. To cut out this effect, sprites must be off<br />
and DEN set. <strong>The</strong> BA (Bus Acknowledge) line on <strong>the</strong> cartridge port allows for this<br />
effect when, for example, an external Z80 microprocessor controls <strong>the</strong> <strong>64</strong>. <strong>The</strong> fact<br />
that VIC-II doesn't work instantaneously, but is continually calculating whicji dots to<br />
put on <strong>the</strong> screen, can be important to grasp. For example, sprite collision registers<br />
cannot be instantly updated after <strong>the</strong>y've been read; some time has to pass before<br />
<strong>the</strong> next screen scan.<br />
<strong>The</strong> <strong>64</strong>'s<br />
Memory Configurations<br />
Five of <strong>the</strong> PLA's input lines are vital to <strong>the</strong> <strong>64</strong>'s memory management. <strong>The</strong>se lines<br />
are CHAREN, HIRAM, and LORAM (which are controlled by RAM locations 0 and<br />
1), as well as EXROM and GAME, which are controlled by hardware plugged into<br />
<strong>the</strong> cartridge socket.<br />
CHAREN, HIRAM, and LORAM<br />
Location 1 has six active bits, three controlling tape. Only bits 2, 1, and 0 involve<br />
memory allocation.<br />
Location 0 is <strong>the</strong> data direction register. As long as its bits 2, 1, and 0 are set,<br />
location 1 controls <strong>the</strong> lines. POKE 0,0 disables <strong>the</strong> control.<br />
EXROM and GAME are pins 9 and 8 of <strong>the</strong> cartridge socket. When a cartridge is<br />
connected, it may ground one or both of <strong>the</strong>se lines, causing <strong>the</strong> PLA to reinterpret<br />
<strong>the</strong><br />
memory map.<br />
Figures 5-1 through 5-4 show all possible memory configurations:<br />
No Cartridge Connected<br />
When you turn on <strong>the</strong> <strong>64</strong>, you have 38K BASIC RAM plus 4K free RAM at $C000-<br />
$CFFF. BASIC ROM can be switched out, giving 52K RAM plus <strong>the</strong> Kernal, or both<br />
BASIC and Kernal can be switched out. Chapter 11 deals thoroughly with <strong>the</strong> tech<br />
niques for modifying BASIC in RAM.<br />
Memory with Cartridge and BASIC<br />
At power up, this arrangement has an 8K cartridge, usually designed to autostart, 4K<br />
free RAM, and BASIC with 30K RAM, which you may or may not be returned to.<br />
Most cartridges using 8K or less—even pure ML—use this arrangement, since <strong>the</strong>y<br />
can also borrow BASIC subroutines. Some utilities coexist with BASIC or intercept<br />
BASIC in order to add <strong>the</strong>ir own commands. Some utilities relocate <strong>the</strong>ir cartridge<br />
ML to RAM at $C000, altering BASIC vectors, <strong>the</strong>n switch <strong>the</strong>mselves out by reset<br />
ting EXROM high. This allows ano<strong>the</strong>r cartridge to operate, but means that RAM<br />
from $C000 must remain untouched.<br />
114
<strong>Commodore</strong> <strong>64</strong> Architecture<br />
Figure 5-1. Memory with No Cartridge Connected<br />
Software<br />
Hardware<br />
CHAREN<br />
HIRAM<br />
LORAM<br />
EXROM<br />
GAME<br />
0<br />
A000 C000 D000 E000 FFFF<br />
1<br />
1<br />
1<br />
1<br />
1<br />
RAM<br />
BASIC RAM I/O Kernal<br />
0<br />
1<br />
1<br />
1<br />
1<br />
RAM<br />
BASIC<br />
RAM<br />
Chr.<br />
ROM<br />
Kernal<br />
1<br />
1<br />
0<br />
1<br />
1<br />
RAM<br />
I/O<br />
Kernal<br />
0<br />
1<br />
0<br />
1<br />
1<br />
RAM<br />
Chr.<br />
ROM<br />
Kernal<br />
1<br />
0<br />
1<br />
1<br />
1<br />
RAM<br />
I/O<br />
RAM<br />
0<br />
0<br />
1<br />
1<br />
1<br />
RAM<br />
Chr.<br />
ROM<br />
RAM<br />
1<br />
0<br />
0<br />
1<br />
1<br />
RAM<br />
0<br />
0<br />
0<br />
1<br />
1<br />
RAM<br />
If EXROM is grounded with no cartridge present, <strong>the</strong> <strong>64</strong> will print 30719 bytes<br />
free when turned on; it loses 8K of ROM, so $8000-$9FFF is read as garbage, but<br />
written as RAM. If GAME alone is grounded when <strong>the</strong> computer is turned on, <strong>the</strong><br />
<strong>64</strong> crashes, since <strong>the</strong> Kernal is deactivated. <strong>The</strong> examples in Figure 5-2 show how<br />
<strong>the</strong> PLA detects cartridges.<br />
115
<strong>Commodore</strong> <strong>64</strong> Architecture<br />
Figure 5-2. Memory with Cartridge and BASIC<br />
Software<br />
Hardware<br />
CHAREN HIRAM LORAM<br />
EXROM GAME 0<br />
8000 A000 C000 D000 E000 FFFF<br />
1 1 1<br />
0 1<br />
RAM<br />
8K<br />
Cartridge BASIC RAM I/O Kernal<br />
RAM<br />
8K<br />
Cartridge BASIC RAM<br />
Chr.<br />
ROM<br />
Kernal<br />
1 1<br />
Identical to EXROM high (Figure 5-1)<br />
Memory with Cartridge but Without BASIC<br />
This allows a 16K ML autostart cartridge to use Kernal and I/O. It's often called <strong>the</strong><br />
application configuration, based on <strong>the</strong> <strong>the</strong>ory that 16K will hold a serious program.<br />
However, it's often not enough and it's common to find cartridges using bank<br />
switching <strong>the</strong>mselves. COMAL (a structured programming language) has four banks<br />
here, using <strong>64</strong>K of ROM.<br />
116
<strong>Commodore</strong> <strong>64</strong> Architecture<br />
Figure 5-3. Memory with Cartridge but Without BASIC<br />
Software<br />
Hardware<br />
CHAREN<br />
HIRAM<br />
LORAM<br />
EXROM<br />
GAME<br />
0<br />
8000 A000 C000 D000 E000 FFFF<br />
1<br />
1<br />
1<br />
0<br />
0<br />
RAM<br />
1<br />
16K Cartridge<br />
RAM I/O Kernal<br />
0<br />
1<br />
1<br />
0<br />
0<br />
RAM<br />
16K Cartridge<br />
RAM<br />
Chr.<br />
ROM<br />
Kernal<br />
1<br />
1<br />
0<br />
0<br />
0<br />
RAM<br />
8K<br />
Cartridge<br />
RAM I/O Kernal<br />
0<br />
1<br />
0<br />
0<br />
0<br />
RAM<br />
8K<br />
Cartridge<br />
RAM<br />
Chr.<br />
ROM<br />
Kernal<br />
1<br />
0<br />
1<br />
0<br />
0<br />
RAM<br />
I/O<br />
RAM<br />
0<br />
0<br />
1<br />
0<br />
0<br />
—Same—<br />
1<br />
0<br />
0<br />
0<br />
0<br />
RAM<br />
0<br />
0<br />
0<br />
0<br />
0<br />
—Same—<br />
MAX<br />
Intended to allow a 16K autostart cartridge, including its own I/O routines with 4K<br />
of RAM.<br />
Figure 5-4. Max Memory<br />
Software<br />
CHAREN HIRAM LORAM<br />
Hardware<br />
EXROM GAME 1000 8000 A000 D000 E000 FFFF<br />
_ANY —<br />
RAM<br />
Unused<br />
8K<br />
Cartridge Unused I/O<br />
8K<br />
Cartridge<br />
117
<strong>Commodore</strong> <strong>64</strong> Architecture<br />
Overview of <strong>64</strong> Memory Maps<br />
<strong>64</strong>K of RAM is available under ROM. This is because <strong>the</strong> PLA insures that whenever<br />
ROM coexists with RAM, reading comes from ROM, but writing goes to <strong>the</strong> hidden<br />
RAM (or <strong>the</strong> I/O chips). You'll need to alter <strong>the</strong> LORAM or HIRAM bits to read <strong>the</strong><br />
RAM back, as explained in Chapter 8. However, <strong>the</strong> VIC-II chip is wired to read<br />
RAM—except where it sees character ROM. Also, of course, <strong>the</strong> PLA has to switch<br />
in external ROM cartridges when detected, giving <strong>the</strong>m priority over internal RAM<br />
and ROM. Note that, when <strong>the</strong> <strong>64</strong> is turned on, CHAREN, HIRAM, and LORAM are<br />
all set to 1, so <strong>the</strong> maps with lots of RAM must be switched in using software. <strong>The</strong>y<br />
aren't necessarily easy to use; <strong>the</strong> Kernal and I/O are important if you wish to use<br />
<strong>the</strong> keyboard and screen, for example.<br />
<strong>The</strong>re are severe limitations on <strong>the</strong> amount of external ROM which <strong>the</strong> <strong>64</strong> can<br />
take. No external ROM can be added below $8000 without external decoding (so<br />
you must use RAM below $8000), and ROM above $8000 is confined to several<br />
blocks, arranged around <strong>the</strong> BASIC, Kernal, and character ROMs. Paradoxically, <strong>the</strong><br />
system is in some ways less flexible than <strong>the</strong> VIC-20, where several chunks of empty<br />
memory can be filled with ROM or RAM packs.<br />
Turnkey (ready to go) systems use <strong>the</strong> $8000 autostart feature. A cartridge can be<br />
mimicked in RAM by POKIng five bytes into $8004-$8008 to defeat a reset switch<br />
(unless EXROM is grounded). However, a cartridge which uses its own area of<br />
underlying RAM won't work if it's simply copied in RAM, and an external RAM<br />
pack, which would mimic ROM, can't be written to. So, from <strong>the</strong> software security<br />
point of view, this design is good.<br />
<strong>Commodore</strong> <strong>64</strong> Ports<br />
CBM computers are designed so that similar ports are compatible and dissimilar<br />
ports, incompatible. For example, <strong>the</strong> <strong>64</strong>'s cartridge port is a different size from <strong>the</strong><br />
VIC-20's and <strong>the</strong> Plus/4's, since ML programs can't usually run on more than one of<br />
<strong>the</strong>se computers. Tape ports on <strong>the</strong> <strong>64</strong> and VIC-20, but not <strong>the</strong> Plus/4, are compat<br />
ible. And <strong>the</strong> <strong>64</strong> and VIC-20 user ports are similar enough for VICModem to operate<br />
correctly with ei<strong>the</strong>r. <strong>Commodore</strong> reversed <strong>the</strong> VIC's pin numbering on <strong>the</strong> cartridge<br />
socket of <strong>the</strong> <strong>64</strong>.<br />
Audio/video ports. All <strong>64</strong>s have TV-modulated output through a phono-plug.<br />
Early <strong>64</strong>s have five-pin audio/video sockets, with luminance, audio in, audio out,<br />
composite video, and ground; later models have eight pins, partly to avoid confusion<br />
with VIC-20's five-pin socket (6V, ground, audio, two videos) which has to go<br />
through a modulator. Connecting <strong>the</strong> audio signal and ground to a hi-fi is quite easy,<br />
but putting signals into <strong>the</strong> port requires some electronics experience.<br />
Cartridge port. This is <strong>the</strong> port at <strong>the</strong> left of <strong>the</strong> <strong>64</strong>, looking from <strong>the</strong> rear. It has<br />
44 connections, 22 on each side, all of which are connected. Two tracks, usually<br />
wired toge<strong>the</strong>r, carry <strong>the</strong> +5-volt power supply to <strong>the</strong> cartridge; <strong>the</strong>se are pins 2 and<br />
3, near <strong>the</strong> top right, from <strong>the</strong> back of <strong>the</strong> <strong>64</strong>. (As mentioned, <strong>the</strong> VIC-20 has a re<br />
versed numbering convention.) <strong>The</strong> tracks are ra<strong>the</strong>r close, and <strong>the</strong> possibilities of a<br />
short-circuit or arcing make it inadvisable to insert or remove cartridges when <strong>the</strong><br />
118
<strong>Commodore</strong> <strong>64</strong> Architecture<br />
power is on, though with care it is generally safe. Note that edge connectors are de<br />
signed for <strong>the</strong> replacement of faulty computer parts during maintenance; <strong>the</strong>y aren't<br />
really ideal for cartridges.<br />
<strong>The</strong> pinouts on <strong>the</strong> cartridge circuit board (not viewing <strong>the</strong> computer, but <strong>the</strong><br />
cartridge) are as follows:<br />
Figure 5-5. <strong>64</strong> Cartridge Pinout Diagram<br />
Top:<br />
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22<br />
<strong>The</strong> pins function as described below:<br />
Top<br />
1 Ground. All four ground lines are usually tied toge<strong>the</strong>r.<br />
2,3 5-volt power supply to <strong>the</strong> cartridge.<br />
4 IRQ. As long as this is low, it requests an interrupt.<br />
5<br />
6<br />
7<br />
8<br />
9<br />
10<br />
11<br />
12<br />
Read/write line. Reads when low, writes when high.<br />
8 MHz dot clock input, for your own video control.<br />
I/O 1 goes low when <strong>64</strong> detects use of $DE00-$DEFF; can be used with<br />
CP/M.<br />
GAME replaces BASIC ROM with external cartridge ROM when grounded.<br />
EXROM replaces RAM from $8000 to $9FFF with cartridge ROM when<br />
grounded.<br />
I/O 2 goes low when <strong>64</strong> detects use of $DF00-$DFFF.<br />
ROML chip enable selects ROM $8000-$9FFF when EXROM is low; needs<br />
address bits A0-A12.<br />
BA (Bus Acknowledge). To use, pull DMA low. An external device can control<br />
<strong>the</strong> <strong>64</strong> while BA is high.<br />
DMA (Direct Memory Access). See BA.<br />
13<br />
14-21 D7 through DO. <strong>The</strong> data bus carries eight bits of data.<br />
22 Ground.<br />
Bottom<br />
A Ground.<br />
B ROMH selects external ROM at $A000-$BFFF (or $E000-$FFFF, for MAX) when<br />
GAME or EXROM is low; needs address bits A0-A12.<br />
C RESET detects a positive voltage, resetting when rising from ground to +5 volts.<br />
119
<strong>Commodore</strong> <strong>64</strong> Architecture<br />
D NMI connects to 6510 Non-Maskable Interrupt line. It is spike sensitive—needs<br />
a pulse in ei<strong>the</strong>r direction. Normally high, so many devices can signal NMI.<br />
E 02 system clock. Essential for I/O timing, but not necessary for external<br />
ROM.<br />
F,H,J,K,L,M,N,P,R,S,T,U,V,W,X,Y, Address bus (A15-A0). <strong>The</strong> full 16 address lines<br />
are necessary for DMA.<br />
Z Ground.<br />
A typical 16K game or word processor on cartridge uses <strong>the</strong> ground and power<br />
lines, GAME and EXROM, ROML and ROMH (for access to cartridge ROM at<br />
$8000-$BFFF), and <strong>the</strong> data bus, plus address lines A0-A12. All ROM addresses<br />
from OXXX through 1XXX, plus ROML or ROMH, are <strong>the</strong>refore accessible. An 8K<br />
cartridge doesn't need GAME or ROMH.<br />
Interfaces typically use I/O 1 and I/O 2 to control two storage buffers and R/W<br />
and 2 to control timing.<br />
Cassette port. Most CBM machines (excluding <strong>the</strong> SX-<strong>64</strong>, C16, and Plus/4)<br />
have identical tape ports. See Chapter 14. Sometimes this port's 5-volt power supply<br />
is used to drive o<strong>the</strong>r hardware.<br />
Controller and game ports. See Chapter 16 for full details.<br />
Serial port. This operates disk drives, printers, and plotters, allowing<br />
daisychaining (stringing devices toge<strong>the</strong>r in series, each separately addressable by<br />
device number). Chapter 17 has information. It is a slow, nonstandard modification<br />
of <strong>the</strong> IEEE system found in PET/CBM machines (see <strong>Programming</strong> <strong>the</strong> PET/CBM for<br />
a description). CBM IEEE devices can operate with <strong>the</strong> <strong>64</strong>, but require interfacing.<br />
User port. This is a 24-pin port, mostly connected to CIA 2. <strong>The</strong> name is in<br />
tended to suggest that users (with hardware expertise) can interface gadgetry to <strong>the</strong><br />
<strong>64</strong> All CBM machines (except <strong>the</strong> Plus/4 series) have a similar user port, though <strong>the</strong><br />
<strong>64</strong>'s has (for example) CNT (CIA counter) lines which <strong>the</strong> VIC-20's doesn't, and <strong>the</strong><br />
VIC-20 port has cassette, joystick, and light pen lines missing from <strong>the</strong> <strong>64</strong> port. As a<br />
result, not all hardware items can be expected to work on both machines.<br />
This is sometimes called <strong>the</strong> parallel user port to emphasize its potential for<br />
simultaneous eight-bit transmission, but this is a misnomer, since it can handle serial<br />
data transmission just as well.<br />
Figure 5-6 shows <strong>the</strong> pinout as it appears from <strong>the</strong> back of <strong>the</strong> <strong>64</strong>:<br />
Figure 5-6. <strong>64</strong> User Port Pinout<br />
GND +5V RESET CNT1 SP1 CNT2 SP2 PC2 ATN +9VAC+9VAC GND {<br />
Top I "<br />
! 2 3 4 5 6 7 8 9 10 11 12<br />
ABCDEFHJK L M N ^<br />
Top I "<br />
GND FLAG2 PBO PB1 PB2 PB3 PB4 PB5 PB6 PB7 PA2 GND<br />
120
<strong>Commodore</strong> <strong>64</strong> Architecture<br />
<strong>Programming</strong> <strong>the</strong> CIAs<br />
<strong>The</strong> Complex Interface Adapter (CIA, or 6526) is a 40-pin chip, in <strong>the</strong> same series as<br />
<strong>the</strong> 6510, designed to handle interfacing. It's a descendant of <strong>the</strong> PIA and VIA chips<br />
in o<strong>the</strong>r <strong>Commodore</strong> machines, but it's easier to program. CIAs are versatile, but<br />
much of <strong>the</strong>ir capability isn't needed by <strong>the</strong> <strong>64</strong>: <strong>the</strong> user port makes it possible for<br />
hardware specialists to use it fully. Some knowledge of CIAs is necessary to under<br />
stand input/output routines, setting and reading <strong>the</strong> internal timers, and changing<br />
<strong>the</strong> interrupt rate.<br />
Understanding <strong>the</strong> CIA<br />
<strong>The</strong> <strong>64</strong> has two CIAs, numbered Ul and U2 on <strong>the</strong> schematic in <strong>the</strong> Programmer's<br />
Reference Guide, <strong>The</strong>y are connected to <strong>the</strong> keyboard (though not <strong>the</strong><br />
RUN/STOP-RESTORE key), joysticks, <strong>the</strong> cassette read line, <strong>the</strong> <strong>64</strong>'s serial port, <strong>the</strong><br />
PLA lines controlling VIC-II's bank switching, both interrupt pins (IRQ and NMI) of<br />
<strong>the</strong> 6510 processor, and to many pins of <strong>the</strong> user port. As we've seen, RS-232 input<br />
and output (usually with a modem), which is serial, goes via <strong>the</strong> user port. CIAs also<br />
help control light pen and potentiometer (paddle) readings.<br />
CIA 1, labeled Ul on <strong>the</strong> schematic, appears in <strong>the</strong> <strong>64</strong>'s memory at<br />
$DC00-$DC0F (56320-56335), occupying 16 bytes. CIA 2, labeled U2, appears at<br />
$DD00-$DD0F (56576-56591). Both chips have repeated images.<br />
Both chips have an eight-bit data bus, usually labeled DB0-DB7, <strong>The</strong>re are four<br />
address, or register select, bits, RS0-RS3, allowing 16 addresses to be distinguished,<br />
plus a chip select line, which activates <strong>the</strong> chip when low. <strong>The</strong>re are also reset,<br />
clock, IRQ, read/write, +5V power, and ground lines, which have <strong>the</strong> normal func<br />
tions of setting <strong>the</strong> chip when <strong>the</strong> computer is turned on, synchronizing timing,<br />
sending interrupt request signals when <strong>the</strong> IRQ line is held low, deciding whe<strong>the</strong>r to<br />
read <strong>the</strong> chip or write to it, and so on. In addition, <strong>the</strong> TOD (Time Of Day) pin has a<br />
50 or 60 Hz AC input, which is used for accurate timing to 1/10 second.<br />
<strong>The</strong> o<strong>the</strong>r 20 lines actually used in CIA interfacing are <strong>the</strong> 8 lines making up<br />
port A, <strong>the</strong> 8 lines of port B, and <strong>the</strong> 4 control lines, one of which is connected to<br />
<strong>the</strong> eight-bit serial register (not used by <strong>the</strong> <strong>64</strong>). All <strong>the</strong>se locations operate in <strong>the</strong><br />
normal way of being ei<strong>the</strong>r high (about +5 volts) or low (about 0 volts). Port A,<br />
port B, and <strong>the</strong> serial register each occupy one byte and take up 3 of <strong>the</strong> 16 locations<br />
of each CIA's memory map. <strong>The</strong>ir individual bits are referred to as PA0-PA7,<br />
PB0-PB7, and SP0-SP7. <strong>The</strong> o<strong>the</strong>r 13 registers all control <strong>the</strong> CIA. Note that <strong>the</strong><br />
control lines don't actually show up on <strong>the</strong> memory map; <strong>the</strong>ir effects have to be in<br />
ferred. And remember that both CIAs have <strong>the</strong> same structure: each has a port A, for<br />
example, so be sure you've got <strong>the</strong> correct chip in mind when programming.<br />
Figure 5-7 is a general memory map of <strong>the</strong> CIA, showing <strong>the</strong> locations and <strong>the</strong>ir<br />
functions.<br />
121
f:<br />
Figure 5-7. CIA Memory Map<br />
0 Port A<br />
8-bit parallel port A<br />
1 PortB<br />
8-bit parallel port B<br />
2DDRA<br />
Controls data directions of port A<br />
3 DDRB<br />
Controls data directions of port B<br />
4 Timer A-Lo<br />
—<br />
16-bit timer A<br />
5 Timer A-Hi<br />
6 Timer B-Lo<br />
16-bit timer B<br />
7 Timer B-Hi<br />
8 TOD-Tenths<br />
0<br />
0 0<br />
0<br />
9 TOD-Sec<br />
0<br />
This nybble 0-5<br />
10 TOD-Min<br />
0<br />
This nybble 0-5<br />
11 TOD-Hour<br />
A.M. = 0<br />
P.M. = 1<br />
0 0 Oorl<br />
12 Serial<br />
Register<br />
8-bit serial data + shift register<br />
Write:<br />
13ICR<br />
0=clear, l=set<br />
not used<br />
Read:<br />
0=noint, l=int<br />
Flag<br />
interrupt<br />
SPline<br />
interrupt<br />
TOD AC Freq.<br />
14CRA<br />
0=60 Hz<br />
1=50 Hz<br />
0=Serial Reg. In<br />
1=Serial Reg. Out<br />
0=TA counts 02<br />
1=TA counts CNT<br />
0=no effect<br />
l=load latch in TA<br />
0=TA continuous<br />
1=TA one-shot<br />
0=Normal TOD<br />
1=Latch Alarm<br />
00 TB counts 02 / 01 TB counts CNT<br />
10 TB counts TA / 11 TB counts TA<br />
when CNT high<br />
0=no effect<br />
1 = load latch in TB<br />
0=TB continuous<br />
1=TB one-shot<br />
General Diagram of CIA<br />
This nybble 0-9<br />
This nybble 0-9<br />
This nybble 0-9<br />
This nybble 0-9 or 0-2<br />
Alarm<br />
Timer B<br />
interrupt<br />
0=PB6 pulse<br />
1=PB6 toggle<br />
0=PB6 normal<br />
1=PB6 outputs TA<br />
0=PB7 pulse<br />
1=PB7 toggle<br />
0=PB7 normal<br />
1=PB7 outputs TB<br />
Timer A<br />
interrupt<br />
0=Stop TA<br />
1=Start TA<br />
0=Stop TB<br />
l=StartTB<br />
—<br />
-
<strong>Commodore</strong> <strong>64</strong> Architecture<br />
A discussion of <strong>the</strong> CIA memory map follows:<br />
CIA bit conventions. Fortunately, <strong>the</strong> CIA registers are accessed in <strong>the</strong> usual<br />
way, for <strong>the</strong> most part. Many registers, including <strong>the</strong> clock, timers, and ports, are<br />
simply POKEd in <strong>the</strong> usual way with an eight-bit value (or PEEKed for reading).<br />
Single bits are also used as flags. A single bit value can configure a line for output,<br />
set or read an output line, enable an interrupt to occur (or show that an interrupt has<br />
been detected), or set o<strong>the</strong>r features of <strong>the</strong> CIA.<br />
If you're unacquainted with I/O chips, you may be surprised to find that many<br />
registers don't behave like RAM. For example, reading <strong>the</strong> interrupt control register<br />
clears <strong>the</strong> interrupt flags automatically.<br />
Ports. Ports A and B have individual lines labeled PA0-PA7 in port A and<br />
PB0-PB7 in port B. Each line (controlled by a single bit) can be configured for ei<strong>the</strong>r<br />
input or output; very often all eight are configured identically (for instance, to scan<br />
<strong>the</strong> keyboard). When configured for input, PEEKing shows whe<strong>the</strong>r bits are high or<br />
low. When configured for output, POKEs of 1 or 0 set high or low voltages. Hard<br />
ware expertise is needed to insure that too much power isn't drawn from <strong>the</strong> chip or<br />
put into it.<br />
PB6 and PB7 in port B can be programmed to override <strong>the</strong>ir normal function<br />
and act like control lines, carrying timer information, from timer A or B, respectively,<br />
ei<strong>the</strong>r as a pulse or alternating like a square wave (toggling). We'll see an example of<br />
this feature later.<br />
<strong>The</strong> CIA serial register (not used by <strong>the</strong> <strong>64</strong>, and not to be confused with <strong>the</strong><br />
more complex serial bus) is connected to a shift register, which allows user-written<br />
serial-parallel conversion. <strong>The</strong> direction and timing are controlled by control register A.<br />
Data direction registers. DDRA and DDRB are <strong>the</strong> data direction registers for<br />
ports A and B, which set each bit of <strong>the</strong>se ports for input or output. POKEing $00<br />
into a data direction register configures <strong>the</strong> entire port for input, while $FF sets <strong>the</strong><br />
entire port for output, and so on. Note that when <strong>the</strong> computer is turned on, <strong>the</strong> re<br />
set line to <strong>the</strong> CIA causes all <strong>the</strong> internal registers to be set to 0, so <strong>the</strong> data direction<br />
is set for input.<br />
Control lines. Each CIA has four control lines, each connected to its own pin on<br />
<strong>the</strong> chip. Only one of <strong>the</strong> eight is used by <strong>the</strong> unmodified <strong>64</strong>, while many go to <strong>the</strong><br />
user port. Control lines are necessary when transmitting or reading data between de<br />
vices, which o<strong>the</strong>rwise would hardly ever synchronize. For example, <strong>the</strong>y signal<br />
when data is ready to be transmitted. <strong>The</strong> control lines are discussed below:<br />
CNT is an event counter for counting inputs, or a signal to line SP when <strong>the</strong> se<br />
rial register generates output. CNT is not used by <strong>the</strong> <strong>64</strong>, but is available on <strong>the</strong> user<br />
port.<br />
SP is <strong>the</strong> shift-register pin, which is connected to <strong>the</strong> user port for RS-232.<br />
FLAG sets <strong>the</strong> FLAG interrupt when low. This can be used with ano<strong>the</strong>r CIA's<br />
PC line.<br />
PC is used for handshaking. Since PC goes low for one cycle after a port B read<br />
or write, use port A first when transferring 16 bits. PCI is not connected. PC2 goes<br />
to <strong>the</strong> user port.<br />
Timers. Each CIA has two 16-bit timers, TA and TB, occupying two registers<br />
^apiece. Both always count down. <strong>The</strong>y always generate an interrupt request upon<br />
123
<strong>Commodore</strong> <strong>64</strong> Architecture<br />
underflow (when <strong>the</strong>y decrement below 0), but <strong>the</strong> interrupt must be enabled to ac<br />
tually occur.<br />
Timers have two functions. One is timing in <strong>the</strong> usual sense; <strong>the</strong> o<strong>the</strong>r is count<br />
ing. <strong>The</strong> first provides a regular measure of time, for instance, when sending out bits<br />
at accurate intervals; <strong>the</strong> o<strong>the</strong>r can perform such tasks as counting eight incoming<br />
bits, even when <strong>the</strong>se are received irregularly.<br />
Each timer has its own 8-bit control register. This includes a bit to start <strong>the</strong> time<br />
(if <strong>the</strong> bit is set to 1) or stop it (if it is cleared to 0). Any POKE into a timer is latched<br />
(<strong>the</strong> value is kept): Once <strong>the</strong> timer underflows or is started, it reloads with <strong>the</strong><br />
POKEd value, so <strong>the</strong> timer's interrupt rate can be altered. However, a strobe bit al<br />
lows a new value to be loaded instantly, without waiting for <strong>the</strong> timer to pass 0.<br />
A timer can run in continuous mode (generating regular interrupts) or one-time<br />
mode (counting only once instead of continually repeating). <strong>The</strong> first mode is used to<br />
scan <strong>the</strong> keyboard; <strong>the</strong> second is used for such purposes as tracing ML commands<br />
one at a time, or detecting <strong>the</strong> presence of hardware by timing its response.<br />
If a timer counts down with <strong>the</strong> <strong>64</strong>'s clock, <strong>the</strong> maximum time interval between<br />
interrupts is about $FFFF millionths (roughly 1/15) of a second. Timer B can count<br />
timer A, though, extending this interval considerably. Ano<strong>the</strong>r feature allows timer B<br />
to count timer A, but only when CNT is held high.<br />
<strong>The</strong> serial register. This 8-bit register is connected to <strong>the</strong> line SP. On com<br />
mand, it performs eight shifts, ei<strong>the</strong>r moving <strong>the</strong> byte in <strong>the</strong> serial register out onto<br />
SP, as eight single bits, high bit first, or (if configured for input) reading eight bits<br />
from SP one at a time into <strong>the</strong> serial register. A shift register within <strong>the</strong> CIA does <strong>the</strong><br />
work. When a whole byte has been shifted, an interrupt flag is set, so serial-parallel<br />
conversion can be made automatic.<br />
<strong>The</strong> shift register is controlled by TA. It can be timed by <strong>the</strong> <strong>64</strong>'s clock or by <strong>the</strong><br />
CNT line, which <strong>the</strong>refore can be used in handshaking.<br />
Control registers of <strong>the</strong> CIA. Three bytes control <strong>the</strong> configuration of every<br />
thing about <strong>the</strong> CIA. Register 13, called <strong>the</strong> interrupt control register (ICR), controls<br />
<strong>the</strong> five sources of CIA interrupts.<br />
Writing to <strong>the</strong> ICR with bit 7 low clears sources of interrupts whose bits are set;<br />
this is why POKEing <strong>the</strong> register with 127 disables all interrupts. Writing with bit 7<br />
high enables interrupts whose bits are POKEd high, so POKEing with 129 enables<br />
timer A's interrupts, but no o<strong>the</strong>rs.<br />
After reading <strong>the</strong> register, if <strong>the</strong> high bit is set, an interrupt has been triggered<br />
by that CIA. <strong>The</strong> bit pattern will indicate <strong>the</strong> cause. If <strong>the</strong> high bit is not set, no<br />
interrupt took place, but it's still possible for one or more of <strong>the</strong> five bits to be high,<br />
since <strong>the</strong>y register even if <strong>the</strong>ir interrupt isn't enabled. <strong>The</strong>re are examples in Chap<br />
ter 8, Chapter 12 (on VIC-II), and later in this section.<br />
CIAs in <strong>the</strong> <strong>64</strong><br />
CIAl<br />
Port A is normally set for output and port B defaults to input. Timer A generates<br />
IRQ interrupts at regular intervals to service <strong>the</strong> keyboard, which shares lines with<br />
<strong>the</strong> joysticks and paddles. CIA 1 reads tape, using <strong>the</strong> FLAG line.<br />
CIA 2<br />
This chip controls NMIs, <strong>the</strong> serial bus, and RS-232 processing.<br />
124
<strong>Commodore</strong> <strong>64</strong> Architecture<br />
Figure 5-8. Diagram of CIA 1<br />
10 Port 1 Paddles<br />
01 Port 2 Paddles<br />
BTN2<br />
Joy 2/E Joy 2/W<br />
Joy 2/S<br />
Joy 2/N<br />
DC00=56320<br />
PA<br />
Paddle BTNS Port 2<br />
Keyboard Columns (Write)<br />
BTN1<br />
JOY 1/E Joy 1/W<br />
Joy 1/S<br />
Joy 1/N<br />
DC01 =56321<br />
PB<br />
light Pen<br />
Paddle BTNS Port 1<br />
Keyboard Rows (Read Back)<br />
DC02=56322<br />
DDRA<br />
Usually 255<br />
DC03=56323<br />
DDRB<br />
Usually 0<br />
DC04-DC05<br />
TA<br />
IRQ Rate for Keyboard. Tape, Serial Timing<br />
DC06-DC07<br />
TB<br />
Tape, Serial Timing<br />
DC08-DC0B<br />
TOD<br />
Not Used<br />
DC0C=56332 SR<br />
To User Port Only<br />
DC0D=56333 ICR<br />
SRQ, Tape<br />
Read<br />
Timer B<br />
Int.<br />
Timer A<br />
Int.<br />
DCOE=56334<br />
CRA<br />
TA Start<br />
DC0F=56335<br />
CRB<br />
TB Start<br />
125
<strong>Commodore</strong> <strong>64</strong> Architecture<br />
Figure 5-9. Diagram of CIA 2<br />
DATA In<br />
CLK In DATA Out CLK Out ATN Out RS-232<br />
Tx&Rx<br />
VIC-II Bank Select<br />
DD00=56576 PA<br />
Serial Bus / VIC-II Control / RS-232<br />
DSR<br />
CTS<br />
CD DTR RTS RD<br />
DD01 =56577 PB<br />
User Port / RS-232<br />
DD02=56578<br />
DDRA<br />
Typically 63<br />
DD03=56579 DDRB<br />
Typically 0<br />
DD04-DD05<br />
TA<br />
RS-232 Timer Out<br />
DD06-DD07<br />
TB<br />
RS-232 Timer In<br />
DD08-DD0B<br />
TOD<br />
DD0C=56588 SR<br />
To User Port<br />
DD0D=56589ICR<br />
RS-232<br />
Read<br />
DDOE=56590 CRA<br />
DD0F=56591<br />
CRB<br />
CIA <strong>Programming</strong> Methods<br />
<strong>The</strong> same programming principles apply in both BASIC and ML, apart from speedsensitive<br />
processing; so <strong>the</strong> user port can often be controlled from BASIC with<br />
POKEs and PEEKs.<br />
Program 5-2 lets you watch <strong>the</strong> CIA bit patterns change. To change a register,<br />
press any key, <strong>the</strong>n type in <strong>the</strong> register address and its bit pattern. For example, you<br />
can start <strong>the</strong> internal time-of-day clock by using <strong>the</strong> register address 56328 and <strong>the</strong><br />
bit pattern 00000000.<br />
Program 5-2. Investigating <strong>the</strong> CIA<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader/' Appendix C.<br />
2 REM FOR CIA#2, USE FORJ=56576TO56591 IN LINE 30<br />
:rem 130<br />
4 REM LINES 50 & 120 HANDLE BIT PATTERN :rem 90<br />
10 PRINT "{CLR}" :rem 197<br />
20 PRINT "{HOME}11 s rem 70<br />
30 FOR J=56320 TO 56335 :rem 122<br />
40 PRINT J;: P=PEEK(J) :rem 253<br />
126
<strong>Commodore</strong> <strong>64</strong> Architecture<br />
50 Q=P/256: FOR K=l TO 8; Q=2*Q: PRINT INT (Q)M<br />
{LEFT}";: Q=Q-INT(Q): NEXT :rem 245<br />
60 PRINT " " P "{LEFT}{3 SPACES}" :rem 104<br />
70 NEXT :rem 166<br />
80 GET X$: IF X$="" GOTO 20 :rem 37<br />
100 INPUT "WHICH REGISTER";R :rem 202<br />
110 INPUT "BIT PATTERN";B$ :rem 4<br />
120 X=0: FOR K=l TO 8 :X=X+2T(8-K)*VAL(MID$(B$,K,1<br />
)): NEXT :rem 80<br />
130 POKE R,X: GOTO 20 :rem 110<br />
CIA 1 and <strong>the</strong> keyboard. Chapter 6 explains how CIA 1 reads and decodes<br />
information from <strong>the</strong> keyboard. Setting PB6 or PB7 for output makes <strong>the</strong> keyboard<br />
unreadable and prevents <strong>the</strong> use of RUN/STOP-RESTORE to reset <strong>the</strong> machine.<br />
Joysticks, light pens, and potentiometers. All <strong>the</strong>se are shared with <strong>the</strong> key<br />
board, under <strong>the</strong> control of CIA 1. See Chapter 16 for full details.<br />
Interrupt handling. Each CIA has an IRQ (Interrupt ReQuest) line, which is<br />
normally set, but can be cleared. CIA 1 connects to <strong>the</strong> 6510's IRQ line, and CIA 2<br />
to <strong>the</strong> NMI line; each chip generates a different type of interrupt.<br />
You should temporarily turn off IRQs to alter <strong>the</strong> IRQ vector to insert your own<br />
ML program or to read <strong>the</strong> character-generator ROM. POKE $DC0D (56333) with<br />
127 to turn off all CIA 1 interrupts. Later, POKE with 129 (%1000 0001) to turn on<br />
timer A interrupts, which normally control <strong>the</strong> IRQ rate. Chapter 8 shows how NMI<br />
interrupts can be used with BASIC.<br />
Tape drives. Only tape reading is controlled by a CIA (see Chapter 14 on writ<br />
ing tape, checking <strong>the</strong> cassette keys, and controlling <strong>the</strong> motor). CIA l's FLAG line<br />
reads tape. Tape input is signaled on FLAG, and any negative transition sets an<br />
interrupt. Reading <strong>the</strong> interrupt register clears <strong>the</strong> interrupt flag, and as long as it's<br />
reset a low condition exists.<br />
Timers. Program 5-2 allows you to see <strong>the</strong> timers updating on <strong>the</strong> screen. Timer<br />
Abnormally controls <strong>the</strong> IRQ rate, which is why POKEs to $DC04 and $DC05 (56324<br />
and 56325) alter <strong>the</strong> cursor flash rate. TA counts down with <strong>the</strong> system clock about<br />
60 times per second, too fast for Program 5-2 to show a pattern. Note, though, that<br />
register 5 ($DC05 or 56335) never exceeds 66; this latched value was selected be<br />
cause 65*256/1,000,000 is about 1/60 second. When <strong>the</strong> computer is turned on,<br />
$DC0D (56333) has bit 0 set high, enabling an IRQ interrupt to <strong>the</strong> 6510, although<br />
PEEKing won't show this.<br />
To link timers, set $DC0F (56335) to 65 (%0100 0001). This sets timer B to fol<br />
low timer A (decrementing once each time A underflows) and starts timer B. Now<br />
timer B counts down from $FFFF relatively slowly—about 60 times per second—tak<br />
ing about four seconds for <strong>the</strong> low register to reach 0, and <strong>the</strong>refore about 15 min<br />
utes for <strong>the</strong> whole register B to count down.<br />
If a timer isn't started, latching has no visible effect. Set $DC06 (56326) to 0,<br />
$DC07 (56327) to 1, so CIA 1 timer B's latched value is $0100. Now set $DC0F<br />
(56335) to 81 (%0101 0001), which follows timer A, forces in timer B's value, and<br />
starts timer B. Now timer B still counts down with timer A, but is reloaded with $100.<br />
127
<strong>Commodore</strong> <strong>64</strong> Architecture<br />
Enter direct mode, and type <strong>the</strong> following line to set $DC0D to %1000 0010:<br />
POKE 56333,127: POKE 56333,130<br />
This turns off timer A and enables timer B as an interrupt source. You'll see that <strong>the</strong><br />
cursor is flashing much slower than it normally does. <strong>The</strong> first POKE is necessary,<br />
since without it both interrupt sources are enabled.<br />
<strong>The</strong> CNT line. Line 4 of <strong>the</strong> user port connects to CIA l's CNT line. Latch<br />
timer B with $FFFF by POKEing 255 ($FF) into $DC06 and $DC07 (56326 and<br />
56327). Now POKE $DC0F (56335) with 49 (%0011 0001), which sets timer B to<br />
count CNT, forces $FFFF into timer B, and starts timer B. Now, timer B remains at<br />
$FFFF until pin 4 is grounded. Note that proper debounce circuitry must be used or<br />
<strong>the</strong> count will decrement very rapidly.<br />
Time-of-day (TOD) clocks. Every CIA has a TOD clock, but we'll use CIA l's.<br />
Four registers hold hours (1-12, high bit for p.m.), minutes, seconds, and tenths of<br />
seconds in binary coded decimal (BCD) format. <strong>The</strong> clock starts only when <strong>the</strong><br />
tenths register is POKEd or PEEKed, and it stops whenever <strong>the</strong> hours register is<br />
POKEd or PEEKed. This prevents errors when reading <strong>the</strong> time. For example, if <strong>the</strong><br />
time in <strong>the</strong> clock registers happened to be 10:59:59.9, <strong>the</strong> clock could advance be<br />
tween <strong>the</strong> time you read <strong>the</strong> hours and <strong>the</strong> time you read minutes. This means your<br />
reading could be wrong by an hour. With CIAs, read <strong>the</strong> hours register first and <strong>the</strong><br />
tenths register last.<br />
You'll need BCD conversion equations to use <strong>the</strong> TOD with BASIC. This func<br />
tion converts a number from 0 to 59 into its BCD form:<br />
DEF FN BCD(T) = INT(T/10)*16 + T - INT(T/10)*10<br />
<strong>The</strong> following function converts a BCD value into <strong>the</strong> corresponding number in <strong>the</strong><br />
range 0-59:<br />
DEF FN TD(P) = INT(P/16)*10 + (P AND 15)<br />
Program 5-3 is a machine language program, POKEd into memory with a<br />
BASIC loader, which reads clock 1 and prints <strong>the</strong> time. Type it in and save it, being<br />
sure to use <strong>the</strong> "Automatic Proofreader." Run <strong>the</strong> program, and <strong>the</strong>n SYS 49152<br />
each time you want to see <strong>the</strong> time. ML programmers may want to add special fea<br />
tures to this simple clock routine.<br />
Program 5-3.<br />
ML Clock<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
10 REM FOR 12-HR CLOCK, REPLACE 18 IN LINE 501 WIT<br />
H 0 :rem 20<br />
100 FOR J=49152 TO 49220:READ XrPOKE J,X:NEXT<br />
: rem 6<br />
500 DATA 169,5,32,210,255,24,173,11,220,16,6,248<br />
:rem 46<br />
501 DATA 41,127,105,18,216,32,46,192,173,10,220,32<br />
:rem 139<br />
502 DATA 46,192,173,9,220,32,46,192,173,8,220,9,48<br />
:rem 166<br />
128
<strong>Commodore</strong> <strong>64</strong> Architecture<br />
503 DATA 32,210,255,169,154,32,210,255,96,72,74,74<br />
srem 166<br />
504 DATA 74,74,9,48,32,210,255,104,41,15,9,48,32<br />
:rem 60<br />
505 DATA 210,255,169,58,76,210,255 :rem 148<br />
510 POKE56328,0 :rem 41<br />
To use <strong>the</strong> alarm feature, POKE <strong>the</strong> registers from BASIC to start <strong>the</strong> clock, have<br />
interrupt processing ready, and set register 15's alarm bit high before POKEing in<br />
<strong>the</strong> alarm value.<br />
POKE 56333,127: POKE 788,0: POKE 789,192: POKE 56333,129: POKE 56335,PEEK(56335)<br />
OR 128<br />
directs interrupts to $C000 (49152), with this ML outline:<br />
C000 LDA $DC0D ; READ AND CLEAR INTERRUPT FLAGS<br />
AND #04<br />
BEQ EXIT<br />
PROCESS ALARM ...<br />
EXIT JMP $EA31<br />
This checks <strong>the</strong> alarm interrupt flag. In this way, lapse of a measured amount of<br />
time can end a game, display a score, or whatever. TOD alarms have a minor bug:<br />
<strong>the</strong> alarm is often triggered a second or third time, so it makes sense to turn off <strong>the</strong><br />
alarm bit in register 15 when it's not needed.<br />
Users of 50-cycle house current should note that <strong>the</strong> TOD is timed by house<br />
alternating current. Its accuracy relies on <strong>the</strong> electricity supplied to it, ra<strong>the</strong>r than<br />
interrupts. In register 14, 50 Hz or 60 Hz is selectable. But all <strong>64</strong>s set 60 Hz even<br />
with PAL TVs. Press RUN/STOP-RESTORE or execute a SYS <strong>64</strong>738 to return to 60<br />
Hz. To insure 50 Hz:<br />
POKE 56334, PEEK(56334) OR 128<br />
or, if <strong>the</strong> Kernal is in RAM, POKE <strong>64</strong>943,136 to alter <strong>the</strong> reset.<br />
Program Recovery and Resetting<br />
No matter how good you are at programming <strong>the</strong> <strong>64</strong>, occasionally a program will<br />
crash. Suddenly, you have no control and no backup copy. <strong>The</strong>re are several ways to<br />
recover your program, and <strong>the</strong>y are discussed below.<br />
<strong>The</strong> RUN/STOP key. This key works with BASIC, unless it has been disabled<br />
(see Chapter 6). It can be checked for in an ML routine, using <strong>the</strong> Kernal STOP rou<br />
tine (Chapter 8).<br />
<strong>The</strong> RUN/STOP-RESTORE key combination. This is a panic button, de<br />
signed to return BASIC and ML to <strong>the</strong> READY state. Hold down <strong>the</strong> RUN/STOP<br />
key and tap <strong>the</strong> RESTORE key sharply. <strong>The</strong> restore sequence has some bugs. Since it<br />
doesn't initialize <strong>the</strong> SID chip, sound may still leak out. It doesn't alter location <strong>64</strong>8;<br />
if you've moved <strong>the</strong> screen you'll need POKE <strong>64</strong>8,4, and <strong>the</strong> keys can be disabled<br />
(see Chapter 6).<br />
RUN/STOP-RESTORE doesn't work in <strong>the</strong> following cases:<br />
129
<strong>Commodore</strong> <strong>64</strong> Architecture<br />
Serial bus problems can cause a lockup. For example, <strong>the</strong> printer may be in a<br />
loop or disabled; turn it off, <strong>the</strong>n on.<br />
In early <strong>64</strong>s (release 0), <strong>the</strong> screen editor can corrupt a CIA, locking <strong>the</strong> key<br />
board. (Enter a long BASIC line starting at <strong>the</strong> screen bottom. Now type a line num<br />
ber at <strong>the</strong> bottom left and delete back. <strong>The</strong> keyscan sequence may not work now.<br />
Press #, <strong>the</strong>n PLAY on tape, <strong>the</strong>n RUN/STOP after a few seconds to recover.)<br />
When you have an X2 crash caused by an internal loop in <strong>the</strong> 6510, only a reset<br />
switch (see below), followed by OLD can recover BASIC. SYS 40965 is an example.<br />
(See Chapter 6.) A so-called X2 crash occurs when <strong>the</strong> microprocessor tries to exe<br />
cute any opcode (o<strong>the</strong>r than $A2) that ends with 2 (see Appendices).<br />
When <strong>the</strong> Computer Is<br />
Reset<br />
First, <strong>the</strong> 6510 and all special chips are reset—<strong>the</strong>y're wired to <strong>the</strong> common RESET<br />
line. Next, <strong>the</strong> reset routine at $FCE2 (<strong>64</strong>738) is entered, provided <strong>the</strong> Kernal isn't<br />
switched out by GAME. After this, locations $8003-$8007 are tested. If a specific se<br />
quence of bytes (see below) is found, JMP ($8000) takes place. This allows a car<br />
tridge to take over <strong>the</strong> <strong>64</strong>.<br />
O<strong>the</strong>rwise, four subroutines are called. <strong>The</strong>se subroutines set CIA registers, clear<br />
pages 0, 2, and 3, <strong>the</strong>n test RAM nondestructively by POKEing $55's into RAM,<br />
<strong>the</strong>n $AA's, and replacing <strong>the</strong> original bytes if RAM is indicated. A few o<strong>the</strong>r things<br />
are set, including BASIC memory and <strong>the</strong> screen. CHAREN, HIRAM, and LORAM<br />
are all set to 1. Because BASIC RAM isn't changed, it follows that <strong>the</strong> startup routine<br />
leaves RAM from 2051 containing garbage.<br />
JMP ($A000) is next. This is an indirect jump, which usually sends execution to<br />
<strong>the</strong> BASIC cold start routine at $E394. But if both EXROM and GAME are grounded,<br />
and a cartridge hasn't been signaled at $8000, <strong>the</strong>n $A000 provides an autostart.<br />
Note that RESTORE jumps indirectly through (8002) or (A002), using <strong>the</strong> same<br />
tests. This is <strong>the</strong> so-called warm start at $E37B.<br />
Reset switch. Pin C of <strong>the</strong> expansion port or pin 3 of <strong>the</strong> user port starts <strong>the</strong> re<br />
set sequence when grounded. Because <strong>the</strong> <strong>64</strong>'s circuitry varies, it's crucial to use a<br />
properly designed circuit. Be careful to get <strong>the</strong> right pins; grounding a 9V user-port<br />
line will blow <strong>the</strong> fuse on <strong>the</strong> <strong>64</strong> (or worse).<br />
A reset switch has a similar effect to switching on, except that <strong>the</strong> contents of<br />
RAM from $0800 up are left completely intact, apart from a few zero bytes at <strong>the</strong><br />
start, caused, in effect, by NEW. <strong>The</strong> whole of BASIC, and its variables, can be<br />
recovered; Chapter 6's OLD shows how.<br />
A reset gives control of <strong>the</strong> <strong>64</strong> to BASIC and <strong>the</strong> Kernal ROM, but after giving<br />
priority to <strong>the</strong> hardware EXROM and GAME lines. This insures that <strong>the</strong> computer al<br />
ways starts correctly when turned on. Use of BASIC in RAM is overridden by a reset<br />
switch. But if <strong>the</strong> reset sequence detects <strong>the</strong> five bytes that indicate a cartridge is<br />
present (whe<strong>the</strong>r one is actually <strong>the</strong>re or not), execution can be deflected to any ad<br />
dress you choose. This is how software can be made reasonably invulnerable to<br />
resetting.<br />
SYS <strong>64</strong>738 behaves very much like a hardware reset: both call <strong>the</strong> same routines<br />
and set <strong>the</strong> <strong>64</strong> to its startup condition, except that RAM above $0800 is retained by<br />
130
<strong>Commodore</strong> <strong>64</strong> Architecture<br />
SYS <strong>64</strong>738. OLD can <strong>the</strong>n be used to restore BASIC, and ML in memory remains in<br />
tact. SYS <strong>64</strong>738 is <strong>the</strong>refore better than turning <strong>the</strong> <strong>64</strong> off, <strong>the</strong>n on again, but it has a<br />
few odd effects when BASIC'S ROMs are switched to RAM, as detailed in Chapter 8.<br />
Autostarting<br />
Autostart cartridge ROMs usually start at $8000, like this:<br />
$8000-$8001: Startup jump address in cartridge.<br />
$8002-$8003: RESTORE key jump address in cartridge.<br />
$8004-$8008: Standard five-byte identifier sequence, $C3, $C2, $CD, $38, $30<br />
(CBM80).<br />
If <strong>the</strong> reset routine detects <strong>the</strong> correct identifier sequence, <strong>the</strong> jump address is taken.<br />
Typically, it still goes through most of <strong>the</strong> normal initialization routines before<br />
continuing with its own program.<br />
A cartridge starting at $A000 will autostart without needing identifying bytes,<br />
unless it finds <strong>the</strong> five-byte sequence at $8004. Obviously, such a cartridge must re<br />
place <strong>the</strong> BASIC ROM by grounding GAME and EXROM.<br />
Bypassing cartridge autostart. If you want to run BASIC or ML normally, but<br />
have a cartridge which you don't want to unplug, you may want to reset <strong>the</strong> <strong>64</strong> to its<br />
normal state by bypassing autostart. This is easy if you have a good expansion<br />
board: just switch <strong>the</strong> unwanted cartridge off and use SYS <strong>64</strong>738.<br />
Cartridges that return you to BASIC are fairly easy to disable. Try <strong>the</strong> following:<br />
• RUN/STOP-RESTORE.<br />
• SYS <strong>64</strong>760.<br />
• Mimic <strong>the</strong> reset sequence without <strong>the</strong> cartridge test, by POKEing <strong>the</strong>se bytes into<br />
49152 (and subsequent locations): 120, 162, 255, 154, 232, 76, 239, 252. Call <strong>the</strong><br />
routine with SYS 49152.<br />
This leaves <strong>the</strong> cartridge in memory, so SYS <strong>64</strong>738 will act like turning <strong>the</strong> com<br />
puter on and SYS 49152 will return <strong>the</strong> machine to normal. Thus, with OLD, BASIC<br />
could be switched at will to run with or without some BASIC utility.<br />
Cartridges that don't return you to BASIC can't be bypassed without an expan<br />
sion board (which has a switch or o<strong>the</strong>r means to disconnect EXROM or GAME or<br />
both). It is not recommended that you plug in a cartridge while <strong>the</strong> power is on; you<br />
may damage <strong>the</strong> computer or at least cause it to crash. If this is done, however, <strong>the</strong><br />
cartridge may become fully present in memory without autostarting.<br />
Commercial Software and Hardware<br />
Programs for <strong>the</strong> <strong>64</strong> come in several formats, and new devices are being introduced<br />
every month. <strong>Commodore</strong> is only one company making products for <strong>the</strong> <strong>64</strong>, and<br />
outside manufacturers are making hardware that is <strong>Commodore</strong>-compatible. <strong>The</strong><br />
following section briefly discusses some of <strong>the</strong> devices which are available.<br />
Cartridge Programs<br />
ROM programs plug into <strong>the</strong> cartridge port and usually autostart when <strong>the</strong> computer<br />
is turned on—this is convenient and fast.<br />
131
<strong>Commodore</strong> <strong>64</strong> Architecture<br />
Because cartridges, even with EPROMs, are much more expensive to make than<br />
<strong>the</strong> easily duplicated tape or disk software, major programs are often supplied on<br />
disk. Cartridges <strong>the</strong>refore often contain games. Sometimes <strong>the</strong>re's a compromise:<br />
plug-in software may immediately load ano<strong>the</strong>r program from disk. Since many<br />
applications require a disk anyway, this isn't a problem, and it has <strong>the</strong> advantage of<br />
permitting corrections to be made in <strong>the</strong> code if bugs are found in <strong>the</strong> disk program.<br />
<strong>The</strong> <strong>64</strong>'s design insures that cartridges compete for <strong>the</strong> same parts of memory,<br />
so some form of expansion board is necessary if you want a choice of easily acces<br />
sible cartridges. <strong>The</strong> only exception is some interface cartridges, which relocate <strong>the</strong>ir<br />
ML and <strong>the</strong>n turn <strong>the</strong>mselves off. But even <strong>the</strong>se can't really be made with an exten<br />
sion piggyback socket, since o<strong>the</strong>r cartridges may have incompatible software. In<br />
o<strong>the</strong>r words, be prepared for your cartridges only to operate individually.<br />
Most cartridges have software protection to prevent a RAM copy of <strong>the</strong> cartridge<br />
from working properly. For instance, an ML instruction like INC $8300 increments<br />
<strong>the</strong> contents of $8300 in RAM, but has no effect on ROM, so an ML program that al<br />
ters memory locations within itself will work in ROM but not RAM.<br />
It's actually possible to store BASIC programs in cartridge form, with <strong>the</strong>ir vari<br />
ables lower in RAM, so that <strong>the</strong>y can autostart without having to be loaded. This is<br />
rarely done, since <strong>the</strong>re's only space for 8K.<br />
Cartridges tend to look rough when taken apart. Often <strong>the</strong>re is just a printed cir<br />
cuit board with a ROM or EPROM, plus some o<strong>the</strong>r circuitry. If you open <strong>the</strong> car<br />
tridge, you'll be able to follow <strong>the</strong> tracings and infer <strong>the</strong> function of much of <strong>the</strong><br />
hardware. Note how <strong>the</strong> dominant direction of tracks on top is often perpendicular<br />
to that below, allowing connections between <strong>the</strong> surfaces to be made more easily.<br />
<strong>The</strong> casing provides protection, but isn't necessary to <strong>the</strong> working of most of <strong>the</strong>se<br />
devices, which can be plugged in as plain printed circuit boards.<br />
Examples. Many games and utility programs are packaged as autostart car<br />
tridges, as are programming languages like COMAL. Most languages switch BASIC<br />
out completely.<br />
BASIC extensions and aids are available on cartridges as well. Simons' BASIC is<br />
16K of ROM from $8000 to $BFFF. It switches <strong>the</strong> BASIC ROM in and out by<br />
controlling <strong>the</strong> GAME line. O<strong>the</strong>r utilities have features like fast tape operating sys<br />
tems, additions to <strong>the</strong> disk operating system, extra graphics, and so on. Unfortu<br />
nately, <strong>the</strong> programs you write using <strong>the</strong>se cartridges may not run without <strong>the</strong><br />
cartridge in place. And watch for subtle changes in BASIC—<strong>the</strong> ',8' may no longer<br />
be needed to use <strong>the</strong> disk drive, or you may find tape no longer works.<br />
Voice and music syn<strong>the</strong>sizers, with extra commands like SPEAK, generally use<br />
<strong>the</strong> SID chip if <strong>the</strong>y don't add hardware to <strong>the</strong> audio-video socket.<br />
Eighty-column software can be prepared as a cartridge as well. It intercepts vec<br />
tors, controlling screen output and graphics format to produce characters four dots<br />
wide. (<strong>The</strong>re are 320 pixels across <strong>the</strong> screen to use, so <strong>64</strong> columns are possible, too,<br />
with characters five dots wide.) All <strong>the</strong> normal colors may not be available. Eightycolumn<br />
output usually requires a monitor, not a TV.<br />
CP/M is an operating system which runs on <strong>the</strong> Z80 chip. <strong>The</strong> <strong>64</strong>'s DMA (Di<br />
rect Memory Access) line allows a Z80 or o<strong>the</strong>r chip to control <strong>the</strong> <strong>64</strong>: when held<br />
low, <strong>the</strong> Z80 controls data, addressing, and input/output, though VIC-II still has ac-<br />
132
<strong>Commodore</strong> <strong>64</strong> Architecture<br />
cess to and controls <strong>the</strong> display. <strong>The</strong> Z80's memory addresses are offset $1000 bytes<br />
from 6510 addresses, seeing $1000 as $0, and so on.<br />
CP/M has three parts: console processing (CPP), disk operating system (BDOS),<br />
and input/output (BIOS), all at <strong>the</strong> high end of memory. <strong>The</strong> programs start at $100.<br />
But <strong>the</strong> disk and input/output is specific to <strong>the</strong> <strong>64</strong> and not transferrable. <strong>The</strong> Kernal,<br />
of course, can be run only by <strong>the</strong> 6510, not <strong>the</strong> Z80. In <strong>the</strong> <strong>Commodore</strong> package, a<br />
hardware switch at $DE00 selects <strong>the</strong> processor. A CP/M cartridge is necessary to<br />
use <strong>the</strong> Z80; <strong>the</strong>n software is usually loaded from <strong>the</strong> 1541 disk drive. You may need<br />
to transfer information to o<strong>the</strong>r machines by modem, since <strong>the</strong> 1541 disk drives are<br />
not compatible with those of most o<strong>the</strong>r manufacturers. If you want to try CP/M<br />
with minimum hassle, be sure that <strong>the</strong> programs you'd like to run are available for<br />
<strong>the</strong> <strong>64</strong>.<br />
Programs on Disk<br />
Disk programs are often long and slow to load. Because <strong>the</strong>se programs go into<br />
RAM, copying can be relatively easy, and this is a problem for <strong>the</strong> software publish<br />
ers. Various methods are used to deter copying, like making <strong>the</strong> directory unreadable<br />
and causing <strong>the</strong> program to force-load in memory, <strong>the</strong>n decode itself in complicated<br />
ways (explained in Chapter 15). Don't be surprised if <strong>the</strong> disk directory looks<br />
strange.<br />
Examples. Most cartridge programs of <strong>the</strong> types listed above are available on<br />
disk as well: games, 80-column software, terminal software, some BASIC enhance<br />
ments. Exceptions include <strong>the</strong> rare, very long ROM programs, which require bank<br />
switching and can't fit into <strong>the</strong> <strong>64</strong>'s RAM.<br />
Applications software (word processing, spreadsheets, and data bases) is often<br />
available on disk. Unfortunately, <strong>the</strong> <strong>64</strong> can't autoboot (automatically load and run)<br />
software from <strong>the</strong> disk when <strong>the</strong> computer is turned on. You have to type in at least<br />
a LOAD command. <strong>Commodore</strong>'s EasyScript word processor is a typical disk-based<br />
application package. LOAD "ES",8,1 forces a load which automatically runs.<br />
EasyScript's ML actually starts at $8000.<br />
Programs on Tape<br />
Tape is perhaps <strong>the</strong> cheapest storage medium, and programs of most types listed<br />
above come on tape. Tape access is also usually very slow. But <strong>the</strong> <strong>64</strong> has plenty of<br />
room for alternative tape LOAD programs. Some commercial tapes first load a fast<br />
tape-read routine, which reads <strong>the</strong> specially formatted main program very rapidly.<br />
<strong>The</strong>re are several recording methods like this, which are often harder to audio-copy<br />
because of <strong>the</strong> higher frequencies <strong>the</strong>y use.<br />
Ano<strong>the</strong>r example is BASICODE, which uses a subset of Microsoft BASIC in or<br />
der to make each program run on a number of different computers. It includes stan<br />
dard routines, different for each computer (for example, GOSUB 100 to clear <strong>the</strong><br />
screen), and is without significant color, sound, or graphics. Programs can be broad<br />
cast by radio and recorded with an ordinary cassette recorder. A special program for<br />
<strong>the</strong> <strong>64</strong> reads this as normal BASIC, which can be saved in conventional <strong>64</strong> tape or<br />
disk format.<br />
Transferring tape software to disk. Straightforward programs can be read from<br />
tape, <strong>the</strong>n stored on disk. <strong>The</strong>re are public domain utilities available which help with<br />
133
<strong>Commodore</strong> <strong>64</strong> Architecture<br />
this. Chapter 14 explains how to load standard tape programs anywhere in RAM.<br />
But protection methods make this more difficult. For example, programs which use<br />
<strong>the</strong> tape buffer can cause problems. And programs that load and run ano<strong>the</strong>r pro<br />
gram won't work unless <strong>the</strong> device number is changed from 1 to 8. Nonstandard for<br />
mats are difficult to put on disk as well: If <strong>the</strong> loaded program disables RESTORE,<br />
mimics a ROM cartridge at $8000, and also uses RAM around $300, resetting with<br />
EXROM grounded will sometimes work, but will wipe out <strong>the</strong> software in low<br />
memory.<br />
Hardware Devices<br />
<strong>The</strong>se peripherals perform functions which software alone can't. <strong>The</strong>re's a wide vari<br />
ety, made somewhat confusing because <strong>the</strong>re are often several different ways to<br />
achieve <strong>the</strong> same result. Here is a quick look at <strong>the</strong> most important broad categories.<br />
Interfaces. Interfaces connect <strong>the</strong> <strong>64</strong> to non-<strong>Commodore</strong> equipment so that <strong>the</strong><br />
external hardware device will work properly. <strong>The</strong> problem is that since <strong>the</strong> <strong>64</strong> has no<br />
standard interface, it must use an interfacing device with a <strong>64</strong>-compatible socket on<br />
one end and a socket to fit standard equipment on <strong>the</strong> o<strong>the</strong>r.<br />
If you want to connect a daisywheel typewriter or non-CBM matrix printer to<br />
<strong>the</strong> <strong>64</strong>, you'll find this equipment typically uses ei<strong>the</strong>r Centronics or RS-232 inputs,<br />
or both. Be warned that <strong>the</strong>re may well be problems with incompatibility: Test<br />
equipment before you buy it to make sure that your software works correctly with it.<br />
Ano<strong>the</strong>r interfacing problem is CBM's IEEE disks and printers. <strong>The</strong> double disk<br />
drives, like <strong>the</strong> 8050, are much faster and store far more data than <strong>the</strong> 1541, but <strong>the</strong>y<br />
won't plug in directly to <strong>the</strong> <strong>64</strong>.<br />
<strong>The</strong> Centronics interface is a 36-pin parallel system found on most good-quality<br />
printers and also on some typewriters. It has its own handshaking system and has<br />
no need for a baud rate to be set. Some of <strong>the</strong>se interfaces have special features rele<br />
vant to <strong>Commodore</strong> machines, like printing BASIC control characters as {RED} or<br />
{HOME}, so program listings are more readable.<br />
User port interfaces use port B (plus two lines to control handshaking) as a par<br />
allel port. All that's required in hardware is a Centronics cable fitted with an edge<br />
connector to plug in <strong>the</strong> user port. Software controls <strong>the</strong> user port.<br />
Serial port interfaces connect <strong>the</strong> serial socket in <strong>the</strong> <strong>64</strong> (or at <strong>the</strong> disk) to <strong>the</strong><br />
Centronics device via hardware, which usually includes an external power supply,<br />
some way to define its device number, and some control over <strong>the</strong> type of ASCII it<br />
passes to <strong>the</strong> printer.<br />
<strong>The</strong> cartridge port can be used as well, but of course is likely to conflict with<br />
o<strong>the</strong>r software on cartridge.<br />
RS-232 is a standard for transmitting serial data, which tends to be slower but<br />
less expensive to implement than parallel transmission. <strong>The</strong> RS-232 connector has 25<br />
pins arranged in two rows. <strong>The</strong> <strong>64</strong> requires voltage conversion to convert its user<br />
port output to <strong>the</strong> correct levels of —12 and +12 volts; Chapter 17 has more details<br />
on CBM interfaces.<br />
RS-232 devices are assigned device number 2 when a file is opened with a state<br />
ment like OPEN 10,2,secondary address,baud rate, and subsequent input or output<br />
uses two buffers at <strong>the</strong> top of BASIC memory for storage. Software has to be written<br />
134
<strong>Commodore</strong> <strong>64</strong> Architecture<br />
with this in mind; if it assumes a printer is device 4, it won't work without modifica<br />
tion. Also, <strong>the</strong> baud rate must be set. (Generally, Centronics interfaces are likely to<br />
be easier to work with.)<br />
Some IEEE interfacing cartridges plug into <strong>the</strong> main socket. One type prints its<br />
sign-on message, relocates its ML to start at $C000, alters BASIC vectors, <strong>the</strong>n dis<br />
connects EXROM so that it disappears from <strong>the</strong> memory map. Finally, it loads and<br />
runs a program from disk. <strong>The</strong> 24-pin IEEE edge connector protrudes from <strong>the</strong> car<br />
tridge to run CBM IEEE disks and printers.<br />
Ano<strong>the</strong>r type also has an IEEE connector, but adds BASIC 4.0 commands like<br />
CATALOG (which can be found on <strong>the</strong> later CBM/PET machines) and an ML mon<br />
itor program, making it highly compatible with o<strong>the</strong>r CBM products. <strong>The</strong> extra soft<br />
ware is relocatable. While this is versatile, to make full use of it, you need to know<br />
how to determine where programs are stored in memory.<br />
It is also possible to move BASIC into RAM and alter <strong>the</strong> output routines. This<br />
way you can customize and enhance BASIC to meet your needs.<br />
Linking devices. Interpod is a CBM product which plugs into <strong>the</strong> serial port,<br />
converting <strong>the</strong> <strong>64</strong>'s serial bus signals into a form acceptable to IEEE devices, and vice<br />
versa. Since it has an external power supply, ROM and RAM, an IEEE socket and an<br />
o<strong>the</strong>r serial socket, ei<strong>the</strong>r or both types of hardware can be connected to <strong>the</strong> <strong>64</strong>'s se<br />
rial bus. This system has <strong>the</strong> advantage of being transparent, not interfering with<br />
normal operations. But since <strong>64</strong> software will not nominally expect IEEE devices to be<br />
present, programs which use <strong>the</strong> system may need to be specially written.<br />
Interpod is an example of linking; however, some adapters are designed to allow<br />
more than one <strong>64</strong> to share <strong>the</strong> same printer or disk drive. This can be useful in a<br />
classroom environment. Some caution is required, though; <strong>the</strong> simplest interfacing<br />
methods don't allow for simultaneous operation, so users must warn each o<strong>the</strong>r<br />
when <strong>the</strong>y're about to use <strong>the</strong> shared devices.<br />
Expansion boards. To save wear on <strong>the</strong> <strong>64</strong>'s ports, you may want to purchase<br />
an expansion board. A typical board has three to five slots, in which cartridges fit<br />
upright, facing <strong>the</strong> user. Each slot has an on/off switch. To work properly this must<br />
disconnect <strong>the</strong> power line and also GAME and EXROM. In this way, several car<br />
tridges designed for <strong>the</strong> same area of memory can be in position simultaneously,<br />
though only one can be in operation at any one time. Some boards have a fuse, and<br />
a reset switch is a good idea. <strong>The</strong>se boards are relatively simple, and if you have<br />
experience with printed circuit board equipment, you could make one using ribbon<br />
connectors, ra<strong>the</strong>r than rigid boards.<br />
EPROM boards. <strong>The</strong>se are printed circuit boards, with edge connectors to fit <strong>the</strong><br />
cartridge port, wired to sockets for Erasable Programmable Read Only Memory<br />
chips. Some boards allow you to switch between EPROMS with a POKE to a spe<br />
cially reserved location, as well as to select <strong>the</strong> address of <strong>the</strong> EPROM. Essentially,<br />
<strong>the</strong>y allow you to design and run your own cartridge software. <strong>The</strong>re's considerable<br />
scope, since <strong>the</strong> expansion port has access to all <strong>the</strong> bus signals.<br />
An EPROM programmer is a hardware device that puts your software into semi<br />
permanent form in an EPROM. You will also need a special ML monitor program<br />
with a command to start <strong>the</strong> burn-in. Self-contained units are sold which fit <strong>the</strong> user<br />
port, drawing <strong>the</strong> high voltage required from it, and in effect using RS-232 to control<br />
135
<strong>Commodore</strong> <strong>64</strong> Architecture<br />
EPROM burning. An EPROM can be read back through <strong>the</strong> user port, but not run as<br />
a program; to run <strong>the</strong> EPROM, it should be mounted on a board.<br />
Communications devices. Modems fit <strong>the</strong> user port and require terminal soft<br />
ware to control communications. Cartridge port modems include <strong>the</strong>ir own sign-on<br />
message, request to connect, and menu of options. All types, of course, have a<br />
connection to <strong>the</strong> phone line or to an acoustic coupler. Chapter 17 discusses terminal<br />
software.<br />
Controllers. Ano<strong>the</strong>r type of adapter is a controller, or I/O board. This set of<br />
relays, or digital/analog converters, connected to <strong>the</strong> user port, allows <strong>the</strong> <strong>64</strong> to con<br />
trol external equipment—lighting, machinery, test equipment, alarms, whatever. A<br />
set of BASIC subroutines often is adequate to read and drive <strong>the</strong> controller. Obvi<br />
ously, hardware and software experience are both needed here.<br />
O<strong>the</strong>r devices. A tape interface can connect <strong>the</strong> <strong>64</strong> to an ordinary recorder, but<br />
this is more difficult and not much less expensive than using <strong>the</strong> <strong>Commodore</strong> tape<br />
units. An ordinary recorder isn't wired to sense keys, so it can't prompt with PRESS<br />
PLAY or PRESS PLAY AND RECORD, and <strong>the</strong> recorder must be switched on just<br />
before use. Some recorders play back an inverted signal, which sounds identical but<br />
cannot be read properly. See Chapter 14 for more on this.<br />
Cartridges are sometimes fitted with <strong>the</strong>ir own video chips (so <strong>the</strong> TV connec<br />
tion comes from <strong>the</strong> cartridge, not <strong>the</strong> <strong>64</strong>). This allows 80-column display, variable<br />
line spacing, digital clock display, and so on, but at a price, of course. And cartridges<br />
can take speech syn<strong>the</strong>sis chips, again with output derived from <strong>the</strong> cartridge, ei<strong>the</strong>r<br />
bypassing or mixing with <strong>the</strong> sound from <strong>the</strong> SID chip.<br />
Special hardware modifications are becoming available for <strong>the</strong> <strong>64</strong> as well. One<br />
autostart cartridge causes a 1541 disk drive to work at several times <strong>the</strong> normal<br />
speed, subject to some serial bus problems. (It switches itself out of ROM, leaving<br />
BASIC modified with connectors to two lines within <strong>the</strong> <strong>64</strong>.) Ano<strong>the</strong>r reported<br />
modification allows <strong>the</strong> <strong>64</strong> and Apple to share some software.<br />
136
Chapter 6<br />
Advanced<br />
BASIC<br />
How BASIC Is Stored in Memory<br />
Special Locations and Features of<br />
BA^IC<br />
DiGtionary of Extensions to BASIC
Chapter 6<br />
Advanced BASIC<br />
This chapter explains advanced BASIC methods for programming <strong>the</strong> <strong>64</strong>. It should<br />
help you learn new techniques and avoid programming bugs. <strong>The</strong> following sections<br />
introduce some special features of BASIC, like programming <strong>the</strong> function keys, for<br />
example. <strong>The</strong>re's a dictionary of enhancements to BASIC, some of <strong>the</strong>m utilities to<br />
assist programming, o<strong>the</strong>rs subroutines which can be incorporated into programs.<br />
How BASIC Is Stored in Memory<br />
Major Memory Locations Controlling BASIC in <strong>the</strong> <strong>64</strong><br />
<strong>The</strong> previous chapter explained how pointers controlling BASIC are set up when <strong>the</strong><br />
<strong>64</strong> is turned on. <strong>The</strong>se pointers are listed below (in decimal):<br />
START OF BASIC PROGRAM = PEEK (43) + 256 * PEEK (44)<br />
END OF PROGRAM + 1 = PEEK (45) + 256 * PEEK (46) = START OF SIMPLE<br />
VARIABLES<br />
END OF SIMPLE VARIABLES + 1 = PEEK (47) + 256 * PEEK (48) = START OF<br />
ARRAYS<br />
END OF ARRAYS + 1 = PEEK (49) + 256 * PEEK (50)<br />
CURRENT BOTTOM OF STRINGS = PEEK (51) + 256 * PEEK (52)<br />
PREVIOUS BOTTOM OF STRINGS = PEEK (53) + 256 * PEEK (54)<br />
END OF MEMORY USABLE BY BASIC = PEEK (55) + 256 * PEEK (56)<br />
START OF BASIC RAM = PEEK (<strong>64</strong>1) + 256 * PEEK (<strong>64</strong>2)<br />
END OF BASIC RAM = PEEK (<strong>64</strong>3) + 256 * PEEK (<strong>64</strong>4)<br />
START OF SCREEN MEMORY = 256 * PEEK (<strong>64</strong>8), normally 1024<br />
START OF COLOR RAM = 55296<br />
START OF CURRENT TABLE OF CHARACTER GENERATOR BITS = 16384 * (3-<br />
(PEEK (56576) AND 3)) + 1024 * (PEEK(53272) AND 14)<br />
<strong>The</strong> main set of pointers occupies 14 consecutive bytes, from location 43 to 56,<br />
an arrangement found in all <strong>Commodore</strong> BASICs. Most of <strong>the</strong>se pointers mark a<br />
boundary between one type of item and <strong>the</strong> o<strong>the</strong>rs, which is why END OF PRO<br />
GRAM + 1 = START OF VARIABLES, for example.<br />
All this means is that <strong>the</strong> program ends one byte before <strong>the</strong> pointer and that <strong>the</strong><br />
variables start exactly at <strong>the</strong> pointer, so <strong>the</strong>re's no overlap. Such a convention is ob<br />
viously necessary, and this pattern is common among <strong>Commodore</strong> machines; this is<br />
why <strong>the</strong> last byte can easily be lost if you are not careful when saving ML programs.<br />
<strong>The</strong>se pointers are most important when considering BASIC on its own, and<br />
much of this chapter is devoted to <strong>the</strong>m. For completeness <strong>the</strong> list also includes<br />
o<strong>the</strong>r pointers set by <strong>the</strong> <strong>64</strong> when it is turned on or reset. <strong>The</strong> start and end of<br />
BASIC RAM are stored in an extra set of <strong>the</strong>se pointers when <strong>the</strong> position of <strong>the</strong><br />
screen is set, but <strong>the</strong>se have little function and are less useful than <strong>the</strong> main pointers.<br />
<strong>The</strong> screen and character generator pointer values are dependent on <strong>the</strong> VIC chip,<br />
and setting <strong>the</strong>ir values is relatively complex; <strong>the</strong>y aren't ordinary two-byte pointers.<br />
Experimenting with BASIC Storage in Memory<br />
As an introduction to BASIC pointers, turn on your <strong>64</strong> and PRINT some of <strong>the</strong>se<br />
two-byte PEEKs, starting with PRINT PEEK (<strong>64</strong>1) + 256*PEEK (<strong>64</strong>2). You should<br />
139
Advanced BASIC<br />
find that <strong>the</strong> BASIC RAM extends from 2048 ($0800) to 40960 ($A000), and <strong>the</strong><br />
screen starts at 1024 ($0400). <strong>The</strong> <strong>64</strong> allows 1024 bytes for <strong>the</strong> screen, because<br />
40 X 25 = 1000, and 1024 (210, or IK) is a convenient amount of space to allocate<br />
for it. <strong>The</strong> screen ends at 2023 ($07E7) and <strong>the</strong>re are 24 bytes left over, 2024-2047.<br />
<strong>The</strong> final 8 of <strong>the</strong>se store sprite data, while <strong>the</strong> rest are free.<br />
You will also find that <strong>the</strong> actual start of BASIC is one byte beyond <strong>the</strong> start of<br />
BASIC RAM. In o<strong>the</strong>r words, locations 43 and 44 toge<strong>the</strong>r point to 2049 ($0801).<br />
This is because BASIC always starts with a zero byte. Usually, <strong>the</strong>refore, PRINT<br />
PEEK (2048) will print a 0 on <strong>the</strong> screen. <strong>The</strong> BYTES FREE message in this case has<br />
already calculated that bytes from 2048 to 40959 (a total of 38,911) are available to<br />
BASIC. <strong>The</strong> pointers to <strong>the</strong> end of program text, simple variables, and arrays are all<br />
set to 2051. BASIC programs have two consecutive zero (or null) bytes marking <strong>the</strong><br />
end, and since <strong>the</strong>re are no variables yet, all <strong>the</strong>se pointers are in <strong>the</strong>ir starting po<br />
sitions, just after <strong>the</strong> program, which is where variables will be stored. Zero bytes<br />
(with a PEEK value 0) are convenient for markers because <strong>the</strong>y are easily tested for<br />
in ML. So PEEK (2049) and PEEK (2050) both return 0 at present.<br />
BASIC is stored as a set of numbered lines. As stated above, <strong>the</strong> first byte of a<br />
program is a zero, or null. Each line begins with a two-byte forward link address, a<br />
two-byte line number, <strong>the</strong> BASIC line itself, and a zero byte which marks <strong>the</strong> end.<br />
<strong>The</strong> link address is simply a pointer to <strong>the</strong> next line—in fact, it points to <strong>the</strong> next<br />
line's link address, forming a chain which can be scanned at high speed. Each link<br />
needs two bytes, and line numbers also have two bytes, enabling line numbers<br />
greater than 255. Figure 6-1 illustrates <strong>the</strong> concept of linked lines in BASIC and<br />
shows that a line link of 0 0 (two consecutive nulls) is used to indicate <strong>the</strong> end of <strong>the</strong><br />
program.<br />
Figure 6-1. Linked Lines of BASIC<br />
Storage of BASIC as Linked Lines<br />
Start End of Line End of Line End of Line Program End<br />
0 Link Line#<br />
BASIC Line 0 Link Line# BASIC Line 0 Link Line# BASIC 0 0 0<br />
i<br />
<strong>The</strong> built-in operating system automatically arranges BASIC as lines are typed in<br />
and entered (by pressing <strong>the</strong> RETURN key) at <strong>the</strong> keyboard. When you understand<br />
how lines are entered, you can modify BASIC to produce nonstandard effects. You<br />
could insert lines longer than usually possible, add normally unavailable line num<br />
bers, or arrange a line of BASIC to contain things it ordinarily couldn't.<br />
Here's an easy way to look at BASIC line storage in practice. Type in this simple<br />
program:<br />
140
Advanced BASIC<br />
Program 6-1. BASIC Line Peeker<br />
1 PRINT "HELLO"<br />
30 FOR J = 2048 TO 2062<br />
40 PRINT J;PEEK (J);CHR$ (PEEK (J))<br />
50 NEXT<br />
When you run this, you will see <strong>the</strong> following numbers on <strong>the</strong> computer screen<br />
(without <strong>the</strong> comments):<br />
2048<br />
2049<br />
2050<br />
2051<br />
2052<br />
2053<br />
2054<br />
2055<br />
2056<br />
2057<br />
2058<br />
2059<br />
2060<br />
2061<br />
2062<br />
0<br />
15<br />
8<br />
1<br />
0<br />
153<br />
32<br />
34<br />
72<br />
69<br />
76<br />
76<br />
79<br />
34<br />
0<br />
H<br />
E<br />
L<br />
L<br />
O<br />
"<br />
; Zero byte at <strong>the</strong> beginning<br />
; Link address; points to start of next line<br />
; at 2063 = 15 + 8 * 256<br />
; Line number; this line number is 1<br />
; = 1 + 0 * 256<br />
; Tokenized form of PRINT<br />
; Space<br />
; Quote and following characters in PETASCII<br />
; End of line null byte<br />
Program 6-1 shows <strong>the</strong> contents of every byte of a single typical line of BASIC,<br />
with notes to show <strong>the</strong>ir function. Incidentally, you will get slightly different results<br />
if you make small changes to line 1. For example, if you remove <strong>the</strong> single space<br />
character between PRINT and <strong>the</strong> first quotation mark, this will be missing from<br />
location 2054, as you would expect. A link address pointer will point to 2062 instead<br />
of 2063, because <strong>the</strong> next line starts a byte earlier. Similarly, try a different line num<br />
ber in place of 1, and see it reproduced in locations 2051 and 2052 when <strong>the</strong> pro<br />
gram is run.<br />
As noted above, BASIC lines have five bytes of overhead, consisting of a twobyte<br />
pointer, a two-byte line number, and a null byte. <strong>The</strong> end of BASIC is marked<br />
by two zero bytes—that is, when a link address is found to be 0, RUN and LIST will<br />
automatically treat this as an END, and return to direct mode with READY.<br />
Before performing <strong>the</strong> following POKEs, save Program 6-1, if you want to be sure to<br />
have a copy. Try POKE 2063,0: POKE 20<strong>64</strong>,0 with <strong>the</strong> program (in <strong>the</strong> exact form<br />
shown above) in memory. It will now LIST only one line. Now POKE 2063,37:<br />
POKE 20<strong>64</strong>,8 which will replace <strong>the</strong> original values, if <strong>the</strong> spacing was identical to<br />
<strong>the</strong> version above. LIST now shows <strong>the</strong> entire program again.<br />
Normally <strong>the</strong> end-of-BASIC pointer in 45 and 46 will point just beyond <strong>the</strong>se<br />
three consecutive zeros. If you think about it, you will realize that only <strong>the</strong> second of<br />
<strong>the</strong> pair of terminating bytes is necessary to signal an end; it is, of course, possible<br />
that <strong>the</strong> low byte of a link address could validly be zero, but any normal BASIC line<br />
will be in $0800 at <strong>the</strong> absolute minimum, so its high byte will never be less than<br />
eight.<br />
141
Advanced BASIC<br />
Watching BASIC Work<br />
<strong>The</strong>re are several o<strong>the</strong>r methods to watch BASIC and its variables as <strong>the</strong>y do <strong>the</strong>ir<br />
work. Chapter 5's ML program, "MicroScope," can display a dynamic picture of<br />
BASIC'S storage, an excellent way to get <strong>the</strong> feel of BASIC lines, variables, and<br />
strings. Ano<strong>the</strong>r way is to move <strong>the</strong> screen to coincide with <strong>the</strong> start of BASIC. Type<br />
in and run Program 6-2 below.<br />
Program 6-2.<br />
BASIC Screen<br />
10 FOR J=55296 TO 56296: POKE J,0: NEXT<br />
20 POKE <strong>64</strong>8,8: POKE 53272,37: POKE 53281,1<br />
30 PRINT "{HOME}{4 DOWN}";CHR$(14): REM YOUR NAME<br />
{SPACE}HERE<br />
40 FOR J=l TO 3000:NEXT<br />
50 REM TYPE POKE <strong>64</strong>8,4 THEN PRESS RUN/STOP & RESTO<br />
RE TO GET BASIC PROGRAM BACK<br />
Line 20 moves <strong>the</strong> screen to $0800, where BASIC'S program storage area begins.<br />
Line 30's REM and line 40's loop activity will both be visible onscreen. Try adding<br />
new lines of BASIC and new variables. <strong>The</strong> result is ra<strong>the</strong>r unpredictable—clearing<br />
or scrolling <strong>the</strong> screen will remove or alter BASIC.<br />
Ano<strong>the</strong>r way to display a BASIC program's contents is to POKE it, byte by byte,<br />
into <strong>the</strong> screen, as Program 6-3 demonstrates:<br />
Program 6-3. POKEing BASIC to <strong>the</strong> Screen<br />
10 FOR J=2048 TO PEEK(45) + 256*PEEK(46)<br />
20 POKE 1024+Q, PEEK(J): POKE 55296+Q,l: Q=Q+1: NE<br />
XT<br />
30 PRINT "{HOME}{4 DOWN}";CHR$(14): REM LOWERCASE<br />
{SPACE}MODE<br />
Table 6-1 shows <strong>the</strong> significance of each BASIC byte (apart from <strong>the</strong> links and<br />
line numbers). Note that all BASIC keywords are stored as a single byte with bit 7<br />
set (which means <strong>the</strong>y have a value of 128 or more in decimal). This makes it easy<br />
for <strong>the</strong> ML routine to detect a keyword. It also means that when BASIC is POKEd<br />
into <strong>the</strong> screen, BASIC keywords appear as single reverse-video characters on <strong>the</strong><br />
screen. Generally, BASIC as stored in RAM looks strange, partly because of this<br />
compression and partly because <strong>the</strong> pointers and line numbers become visible.<br />
<strong>The</strong> LIST instruction has <strong>the</strong> function of presenting this stored collection of<br />
bytes in <strong>the</strong> familiar form, by expanding each token into its correct keyword. Of<br />
course, compressing BASIC like this is a very valuable space-saving feature. This<br />
special coded form is different from <strong>the</strong> <strong>64</strong>'s ASCII and also different from <strong>the</strong><br />
screen display system—which may cause some confusion. Table 6-1 shows all valid<br />
bytes as held in BASIC. Some of those not shown will list as apparently meaningful<br />
BASIC, but will not run properly.<br />
142
Advanced BASIC<br />
Table 6-1.<br />
Internal Storage of BASIC Bytes<br />
0-31 32-<br />
<strong>64</strong>-<br />
96- 128-<br />
160-<br />
192-<br />
244-255<br />
3220sp<br />
65 41A<br />
3422" 6<strong>64</strong>2B<br />
3523# 6743C<br />
3624$ 6844D<br />
3725% 6945E<br />
4028(<br />
4129)<br />
46 2E.<br />
48300<br />
49311<br />
50322<br />
51333<br />
52344<br />
53355<br />
54366<br />
55377<br />
56388<br />
57399<br />
58 3A:<br />
59 3B;<br />
7046F<br />
7147G<br />
7248H<br />
73491<br />
744AJ<br />
754BK<br />
7<strong>64</strong>CL<br />
77 4DM<br />
784EN<br />
794FO<br />
8050P<br />
8151Q<br />
8252R<br />
8353S<br />
8454T<br />
8555U<br />
8656 V<br />
8757W<br />
8858X<br />
8959Y<br />
905AZ<br />
128 80 END<br />
129 81 FOR<br />
130 82 NEXT<br />
13183 DATA<br />
132 84 INPUT #<br />
133 85 INPUT<br />
134 86 DIM<br />
135 87 READ<br />
136 88 LET<br />
137 89 GOTO<br />
138 8A RUN<br />
1398BIF<br />
140 8C RESTORE<br />
1418DGOSUB<br />
142 8E RETURN<br />
1438FREM<br />
144 90 STOP<br />
145 91 ON<br />
146 92 WATT<br />
147 93 LOAD<br />
148 94 SAVE<br />
149 95 VERIFY<br />
15096DEF<br />
15197 POKE<br />
15298PRINT#<br />
153 99 PRINT<br />
1549ACONT<br />
155 9B LIST<br />
1569CCLR<br />
1579DCMD<br />
1589ESYS<br />
159 9F OPEN<br />
160 A0 CLOSE<br />
161 Al GET<br />
162 A2 NEW<br />
163A3TAB(<br />
1<strong>64</strong>A4TO<br />
165A5FN<br />
166A6SPQ<br />
167 A7 THEN<br />
168 A8 NOT<br />
169 A9 STEP<br />
170AA +<br />
171AB -<br />
172 AC*<br />
173 AD /<br />
174 AET<br />
175 AF AND<br />
176 B0 OR<br />
177 Bl ><br />
178 B2 =<br />
179 B3 <<br />
180 B4 SGN<br />
181B5 INT<br />
182B6ABS<br />
183B7USR<br />
184B8FRE<br />
185B9POS<br />
186BASQR<br />
187BBRND<br />
188 BC LOG<br />
189BDEXP<br />
190 BE COS<br />
191BF SIN<br />
192 CO TAN<br />
193C1ATN<br />
194 C2 PEEK<br />
195C3LEN<br />
196C4STR$<br />
197C5VAL<br />
198C6ASC<br />
199C7CHR$<br />
200 C8 LEFTS<br />
201C9 RIGHTS<br />
202CAMID$<br />
203CBGO<br />
204 CC SYNTAX<br />
ERROR<br />
255FF7T<br />
Note: This table shows all valid bytes as <strong>the</strong>y are held within BASIC. Bytes not listed will list as appar<br />
ently meaningful BASIC, but will not run. Within quotes, <strong>the</strong> full range of <strong>64</strong> ASCII characters can be ob<br />
tained; see Appendix H for a table.<br />
Use this when PEEKing BASIC or modifying BASIC with an ML monitor.<br />
It's easy to show how a table like <strong>the</strong> one above can be compiled. Type NEW<br />
and enter this one-line program:<br />
OX<br />
<strong>The</strong> <strong>64</strong> stores X at location 2049 + 4 = 2053. <strong>The</strong>refore, POKEing 2053 with<br />
some value, <strong>the</strong>n listing, reveals how BASIC treats <strong>the</strong> value. POKE 2053,128 lists as<br />
0 END, for example, as <strong>the</strong> table indicates. If you wish to investigate this methodi<br />
cally, enter:<br />
143
Advanced BASIC<br />
or something similar, and POKE values from within a loop. <strong>The</strong> results may be un<br />
expected; if you POKE a value of 5, this will change <strong>the</strong> color of <strong>the</strong> characters<br />
printed on <strong>the</strong> screen after that to white.<br />
If non-BASIC bytes are POKEd in, LIST will often list <strong>the</strong>m as something appar<br />
ently sensible, but <strong>the</strong> line will crash with a 7SYNTAX ERROR message when you try<br />
to run <strong>the</strong> program. However, literals within quotes, or after REM or DATA state<br />
ments, can generally take any value (except a null byte, which will be treated as an<br />
end-of-line). This is why REM lines are a favorite place for simple anti-LIST meth<br />
ods, as we 11 see.<br />
Relatively few values out of <strong>the</strong> possible 256 comprise valid BASIC. Note that<br />
numbers are stored without any attempt at compression, so <strong>the</strong> 10000 in GOTO<br />
10000 takes five bytes and 123.456 in PRINT 123.456 takes seven; all <strong>the</strong> compo<br />
nents are stored in ways which prevent ambiguity, and <strong>the</strong>re is no way that numbers<br />
could be compressed without making <strong>the</strong>m resemble tokens or o<strong>the</strong>r BASIC features.<br />
Note also that <strong>the</strong> operators +,—,*,/, and <strong>the</strong> up-arrow (T) don't appear in <strong>the</strong><br />
ASCII list; <strong>the</strong>y are not stored in this form, but as tokens. Finally, note that BASIC<br />
punctuation includes <strong>the</strong> comma, <strong>the</strong> colon, and <strong>the</strong> semicolon, but not <strong>the</strong> period,<br />
which is treated as <strong>the</strong> decimal point.<br />
How BASIC Stores Its Variables<br />
Suppose you type A=123 on <strong>the</strong> <strong>64</strong>'s keyboard and press RETURN; PRINT A will<br />
now print 123. Simple variables are allocated space after <strong>the</strong> program and before ar<br />
rays. Even if <strong>the</strong>re is no program, variables are stored in <strong>the</strong> same way, beginning<br />
after <strong>the</strong> three zero bytes at <strong>the</strong> start of BASIC RAM. Because variables are stored<br />
when <strong>the</strong>y're first used, <strong>the</strong> sequence in memory is <strong>the</strong> order in which <strong>the</strong>y were en<br />
countered by <strong>the</strong> <strong>64</strong>.<br />
Four types of variables can be stored in this area: floating-point variables (X),<br />
integer variables (X%), strings (X$), and function definitions (DEF FN X(Y)). <strong>The</strong><br />
name is always stored in two bytes (though <strong>the</strong> second may be a space character),<br />
and as Figure 6-2 shows, <strong>the</strong> four types are distinguished by <strong>the</strong> high bit of each let<br />
ter (of <strong>the</strong> variable name) being set or unset. This obviously gives four permutations<br />
from each name and is <strong>the</strong> reason that only <strong>the</strong> first two characters of a name are<br />
significant; NUMERAL and NUMBER are both treated as NU. <strong>The</strong> name carries an<br />
implicit variable type identifier, which is converted from <strong>the</strong> BASIC type declarators<br />
%, $, and FN (and paren<strong>the</strong>ses for arrays).<br />
Each variable type is allocated seven bytes. This means that when BASIC looks<br />
for a simple variable, it always adds a constant offset of seven as it searches <strong>the</strong><br />
table, thus minimizing search time. However, with this scheme three bytes are<br />
wasted with integer variables, two are wasted with strings, and one is wasted with<br />
function definitions. Here are some of <strong>the</strong> different ways that <strong>the</strong> seven bytes rang<br />
ing in value from 0 to 255 are interpreted:<br />
144
Advanced BASIC<br />
Figure 6-2. Storage of Simple Variables<br />
Variable Type Name Details of Storage<br />
Floating-point<br />
ASCII<br />
ASCII<br />
orO<br />
Exponent<br />
Mantissa<br />
| Ml M2 M3 M4<br />
t<br />
Sign bit<br />
Integer<br />
ASC + 128<br />
ASC+128<br />
or 128<br />
Hi Byte LoByte 0 0 0<br />
t<br />
Sign bit<br />
String<br />
ASCII<br />
ASC+128<br />
or 128<br />
Length<br />
Pointer<br />
LoByte | Hi Byte<br />
0 0<br />
Function Defn<br />
ASC+128<br />
ASCII<br />
orO<br />
Pointer to Defn<br />
LoByte<br />
Hi Byte<br />
Pointer to Variable<br />
LoByte<br />
Hi Byte<br />
Initial<br />
ofVar.<br />
Floating-point, or real variables (X). <strong>The</strong> value is held in five bytes to an ac<br />
curacy of one-part in 2T31 (about two billion).<br />
<strong>The</strong>se numbers are subject to rounding errors. Floating-point storage is dis<br />
cussed in more detail later, for <strong>64</strong> owners interested in insuring accuracy in financial<br />
or o<strong>the</strong>r calculations.<br />
Integer variables (X%). Integer variables are held in signed, two-byte form,<br />
within <strong>the</strong> range —32768 to +32767. <strong>The</strong> following formula, which allows for <strong>the</strong><br />
sign bit, gives <strong>the</strong> value of <strong>the</strong> integer variable:<br />
(HI AND 127)*256 + LO + (HI> 127)*32768<br />
For example, HI=0 and LO=100 correspond to 100; HI=255 and LO=156 repre<br />
sent — 100. <strong>The</strong> two expressions add to 0 with overflow.<br />
Strings (X$). Strings cannot be fitted into seven bytes. To allow freedom in<br />
assigning strings (without needing to specify <strong>the</strong>ir lengths, as some languages re<br />
quire), <strong>the</strong>y are stored dynamically in RAM. Three of <strong>the</strong> seven bytes allotted to a<br />
string variable are relevant; two are wasted. One byte holds <strong>the</strong> length of <strong>the</strong> string;<br />
LEN (X$) simply PEEKs this value. Ano<strong>the</strong>r pair of bytes points to <strong>the</strong> starting ad<br />
dress of <strong>the</strong> string. Between <strong>the</strong>m, <strong>the</strong>se provide a complete definition. This storage<br />
system explains why any string has a maximum of 255 characters. It also explains<br />
why CHR$(0) gives no SYNTAX ERROR in cases where a null string (" ") does; <strong>the</strong><br />
former character has length 1, but <strong>the</strong> latter has length 0.<br />
145
Advanced BASIC<br />
Most strings are stored in <strong>the</strong> area of free RAM high above <strong>the</strong> BASIC program<br />
and variable storage. String storage begins fit <strong>the</strong> top of <strong>the</strong> BASIC program space,<br />
and grows downward. As strings are redefined (or as new string variables are de<br />
fined), <strong>the</strong>y fill progressively lower memory locations until a so-called garbage<br />
collection is forced. To save space, some strings aren't stored after BASIC but reside<br />
within <strong>the</strong> program itself; X$'s pointer in <strong>the</strong> assignment:<br />
10 X$="HELLO"<br />
points back jyjthin <strong>the</strong> BASIC program, where HELLO is already stored.<br />
Generally; any string involving calculation is stored after BASIC. For example,<br />
10 X$="HELLO" + " " assigns exactly <strong>the</strong> same string to X$, but it is stored after<br />
BASIC. Again, this has consequences which will be examined shortly. For <strong>the</strong> mo<br />
ment, note that because of this internal storage feature:<br />
10 DIM X$ (200): FOR J=0 TO 200: X$ (J) - "1234567890": NEXT<br />
uses 2000 bytes less memory than <strong>the</strong> same program with:<br />
X$g)="12345"+"67890"<br />
In <strong>the</strong> first case, every string pointer points to <strong>the</strong> same string inside <strong>the</strong> BASIC pro<br />
gram. In <strong>the</strong> second case, every pointer points to a separate ten-byte string stored<br />
above <strong>the</strong> program. Any string with any element of calculation is stored after BASIC<br />
(for example, one defined via INPUT or GET or even by A$=B$).<br />
Function definitions. <strong>The</strong>se appear in <strong>the</strong> variables table, too, and it's quicker<br />
to store <strong>the</strong>m here than to search <strong>the</strong> whole program for a definition.<br />
Like strings, function definitions store <strong>the</strong> solid information elsewhere. A func<br />
tion definition has two pointers, one pointing to <strong>the</strong> defining formula and one point<br />
ing to its principal variable, which is set up in <strong>the</strong> table if it doesn't yet exist.<br />
Running:<br />
0 DEF FN Y(X) = XT2+5*X+3<br />
creates two entries in <strong>the</strong> variable table (X and of <strong>the</strong> function definition Y). Actually,<br />
<strong>the</strong> formula pointer holds <strong>the</strong> address of <strong>the</strong> equal sign (=) in <strong>the</strong> function defi<br />
nition, and <strong>the</strong> variable pointer marks <strong>the</strong> address of <strong>the</strong> first byte of <strong>the</strong> floating<br />
point value of its argument. <strong>The</strong> five bytes are also used as temporary storage for<br />
calculations when running.<br />
Storage of Arrays<br />
Arrays (subscripted variables) are stored after <strong>the</strong> simple variables; since <strong>the</strong>y can be<br />
of any length, <strong>the</strong>y don't lend <strong>the</strong>mselves to <strong>the</strong> normal seven-byte memory<br />
allocation.<br />
All three array types—real, integer, and string—have a similar layout, except for<br />
<strong>the</strong> data, which is stored in five-byte batches for real numbers, two-byte batches for<br />
integers, and three-byte pointers plus characters for strings. Figure 6-3 summarizes<br />
how arrays are stored.<br />
146
...<br />
Advanced BASIC<br />
Figure 6-3. Storage of Arrays<br />
Subscripted Variables (Arrays)<br />
Array Name<br />
Low<br />
Offset<br />
High<br />
No. of<br />
DIMs<br />
LastDIM+1<br />
High<br />
Low<br />
First DIM+1<br />
High<br />
Low<br />
... Data or String Lengths & Pointers...<br />
Arrays are stored in <strong>the</strong> order <strong>the</strong>y are first used. <strong>The</strong> array defined last is <strong>the</strong>re<br />
fore immediately below free memory. Because of this, it's possible to erase an array<br />
which is no longer needed; this can be a useful memory-saving trick if an array is<br />
used for some utility purpose (like sorting or merging). <strong>The</strong> general approach is:<br />
AL=PEEK(49): AH=PEEK(50)<br />
<strong>The</strong>n DIMension a new array and use it in <strong>the</strong> program. When finished with <strong>the</strong> ar<br />
ray, delete it with:<br />
POKE 49,AL: POKE 50,AH<br />
This method simply stores <strong>the</strong> low and high bytes of <strong>the</strong> previous top-of-arrays<br />
pointer, <strong>the</strong>n restores <strong>the</strong>m after using a new array in some way.<br />
<strong>The</strong> DIM (DIMension) command defines <strong>the</strong> size of arrays. Obviously, this is<br />
necessary unless you have unlimited RAM, since <strong>the</strong> computer can't know in ad<br />
vance how much storage you'll need. DIM defaults to 10, so it is not necessary with<br />
small arrays. Without DIM:<br />
X(8)=l<br />
is accepted, but:<br />
gives a ?BAD SUBSCRIPT ERROR.<br />
Housekeeping with arrays is more complex than that with simple variables, al<br />
though <strong>the</strong> stored items are essentially identical. <strong>The</strong> bit 7 conventions for <strong>the</strong> name<br />
are identical to those for simple variables.<br />
<strong>The</strong> first two bytes are <strong>the</strong> array name, followed by <strong>the</strong> two-byte offset. <strong>The</strong> off<br />
set is <strong>the</strong> length of <strong>the</strong> entire array, including numeric data or string pointers; how<br />
ever, it excludes strings, which are stored elsewhere.<br />
<strong>The</strong> number of dimensions is <strong>the</strong> number of subscripts: A(X) is one-dimensional,<br />
A(X,Y) is two-dimensional, and so on. <strong>The</strong> number of elements in each dimension<br />
must be stored as well, requiring two bytes, to allow for arrays like A(500) with<br />
more than 256 items. DIM+1 in Figure 6-3 is <strong>the</strong> number of elements dimensioned,<br />
since <strong>the</strong> first item is numbered 0. Finally, <strong>the</strong>re is <strong>the</strong> data (or, with a string array,<br />
<strong>the</strong> string lengths and pointers). Spare bytes are not wasted; for instance, each inte<br />
ger takes only two bytes.<br />
<strong>The</strong> data or string lengths and pointers are held in ascending order of argument,<br />
with <strong>the</strong> lattermost arguments changing least often. For example, DIM A(l,2) stores<br />
its variables in <strong>the</strong> order A(0,0) A(l,0) A(0,l) A(l,l) A(0,2) A(1,2).<br />
147
Advanced BASIC<br />
<strong>The</strong> position of any one item of an array can be calculated. For example, X(a,b,c)<br />
is at a+b*(l+DIMl)+c*(l+DIMl)*(l+DIM2) elements along <strong>the</strong> array, where<br />
DIM1 is <strong>the</strong> number of elements for which a was dimensioned and DIM2 is <strong>the</strong><br />
dimension of b.<br />
Figure 6-4 shows a typical BASIC program during a run, followed by its vari<br />
ables and arrays. This illustrates how memory sections are allocated for each dif<br />
ferent kind of BASIC information.<br />
Figure 6-4. A Typical BASIC Program During Its Run<br />
Start End End of<br />
of of BASIC<br />
Program Program RAM<br />
BASIC<br />
Program<br />
Simple Variables<br />
(7 bytes each)<br />
Arrays<br />
(varying length)<br />
Simple & Array Strings.<br />
(Stored without spaces.<br />
Varying length).<br />
Maximum Space<br />
Available for Strings<br />
Consequences of BASIC'S Storage Methods<br />
A number of consequences follow from <strong>the</strong>se methods of storage. Because strings<br />
and arrays are of particular significance with serious programs, it's worthwhile to ex<br />
plain <strong>the</strong>m thoroughly. If you understand <strong>the</strong>m, you'll be able to write better<br />
programs.<br />
String storage. All strings are stored as ASCII characters in ascending order in<br />
memory. For instance, <strong>the</strong> program lines below show how a pair of pointers (S and<br />
E, for Start and End) delimit any current string in memory, and how a string is held<br />
in conventional sequence in ASCII.<br />
10 X$="HELLO"+""<br />
20S=PEEK(51)+256*PEEK(52):E=PEEK(53)+256*PEEK(54)<br />
30 FOR J=S TO E-1:PRINT CHR$(PEEK(P);:NEXT<br />
Add lines 11, 12, 13, and 14, like line 10; <strong>the</strong> program as it stands prints <strong>the</strong> last<br />
of <strong>the</strong>m. But if E is altered to PEEK(55)+256*PEEK(56), which is <strong>the</strong> top of memory<br />
available to BASIC, you'll see how each string is stored and <strong>the</strong> top-down way each<br />
is positioned. Figure 6-5 illustrates this, and it is important to note that if a string is<br />
redefined, <strong>the</strong> old string is still left behind in memory; redundant data like this is<br />
called garbage.<br />
Some loops are heavy users of memory space. For example, FOR J=l TO 40:<br />
X$=X$ + " ": NEXT could be used to generate a string of 40 spaces. But <strong>the</strong> first<br />
time <strong>the</strong> loop executes, X$ is defined as a string of one space character; <strong>the</strong> next<br />
time, as two space characters, and so on; so <strong>the</strong> total memory used is 1 + 2 + 3 +<br />
. . . + 40 bytes, or 820 bytes, of which 780 are garbage.<br />
148
Advanced BASIC<br />
Figure 6-5. String Storage<br />
x$ Y$<br />
BASIC<br />
Program<br />
String variables, simple<br />
and array. Length of<br />
string and pointer<br />
stored here.<br />
Strings in<br />
high memory<br />
X$ is stored only in a BASIC program line (for example, 10 X$="HELLO"). Strings which must be built<br />
irom o<strong>the</strong>r strings are stored in high memory (for example, 20 Y$=H$+Z$).<br />
Loops to input data in a controlled way, using GET, do something very similar.<br />
<strong>The</strong>y rely fundamentally on routines like this one:<br />
10 GET X$: IF X$="" GOTO 10<br />
20 Y$=Y$+X$<br />
30 GOTO 10<br />
and use a lot of memory. A short word like SIMON processed like this leaves<br />
SIMONSIMOSIMSIS in memory.<br />
Corruption of data in RAM. Provided <strong>the</strong> end of BASIC is correctly set, this<br />
will not be a problem. For example, user-defined characters are popularly stored<br />
from 12288 ($3000). Provided POKE 55,0: POKE 56,48: CLR is executed early in <strong>the</strong><br />
BASIC program, and it is shorter than 10K, <strong>the</strong> program will run perfectly because<br />
all strings are separated from <strong>the</strong> graphics definitions.<br />
Garbage Collection<br />
Programs using many strings are subject to garbage collection delays. It is fairly easy<br />
to see why. Figure 6-6 shows a simplified situation where RAM contains several<br />
strings, some of which are now redundant.<br />
Figure 6-6.<br />
Garbage Collection<br />
Before garbage collection: A$ was ELEPHANT, B$ is DOG, A$ is now CAT.<br />
^ 1<br />
c|a<br />
T D O | G E k E<br />
free RAM A$ B$ garbage<br />
i<br />
P H A N T<br />
t<br />
Top of<br />
BASIC RAM<br />
After garbage collection: \<br />
C A TD OG EL C A T DO G<br />
free RAM A$ B$<br />
149
Advanced BASIC<br />
Let's suppose BASIC tries to set up a new string but finds <strong>the</strong>re's insufficient<br />
room. It calls a routine, <strong>the</strong> same as FRE(O), to find out which of <strong>the</strong> strings after<br />
BASIC are still being used. Strings stored within BASIC itself are outside <strong>the</strong> scope<br />
of this algorithm and are ignored.<br />
<strong>The</strong> routine has to check every string variable to determine which is nearest <strong>the</strong><br />
top end of memory. This string is moved up as far as possible. <strong>The</strong> process is<br />
repeated with <strong>the</strong> remaining strings until <strong>the</strong> whole collection is cleaned up. <strong>The</strong><br />
number of strings is important, not <strong>the</strong>ir lengths; generally, with N strings this takes<br />
time proportional to N plus N-l plus N-2, etc. Ma<strong>the</strong>matically inclined people<br />
will realize this adds up to an expression on <strong>the</strong> order of N squared. What this<br />
means is that, like <strong>the</strong> well-known bubble sort, a process that is acceptably fast with<br />
a small number of items can become painfully slow with a larger number. In fact,<br />
<strong>the</strong> whole process is analogous to <strong>the</strong> bubble sort: intermediate results are thrown<br />
away, which saves space but wastes time.<br />
<strong>The</strong> <strong>64</strong> takes roughly 0.00075 seconds times <strong>the</strong> number of strings squared to<br />
free memory. <strong>The</strong> actual relationship is a quadratic, while this is only an approxima<br />
tion. For instance, 100 strings take 0.9 seconds, 200 take over three seconds, 300<br />
take over seven seconds, and so on.<br />
Note that, during garbage collection, <strong>the</strong> keyboard locks in an apparent hang<br />
up. This is normal; if a long ML routine runs, <strong>the</strong> RUN/STOP key has no chance to<br />
work. RUN/STOP-RESTORE will interrupt <strong>the</strong> collection if you find it necessary. In<br />
practice, you'll be likely to encounter garbage collection only if you're using string<br />
arrays; 100 ordinary strings will cause an occasional delay of less than a second. Try<br />
<strong>the</strong> following example, which calculates <strong>the</strong> time required to perform an FRE(0); as<br />
stated above, this uses <strong>the</strong> garbage collection routine.<br />
10 INPUT D:DIM X$(D):FOR J=l TO D:X$(J)=STR$(RND(1)):NEXT<br />
20 T=TI:J=FRE(0):PRINT(TI-T)/60 "SECONDS''<br />
If garbage collection is a problem, you must rewrite <strong>the</strong> program to reduce <strong>the</strong><br />
number of strings. <strong>The</strong>re is no o<strong>the</strong>r easy solution. For example, pairing strings to<br />
ge<strong>the</strong>r roughly divides <strong>the</strong> delay by 4. Note that performing FRE(0) whenever <strong>the</strong>re's<br />
time available can help (by shifting <strong>the</strong> delay to an acceptable period). It's also pos<br />
sible to do a limited FRE on part of <strong>the</strong> strings, altering <strong>the</strong> pointers at 55 and 56<br />
down or 53 and 54 up.<br />
Calculating<br />
Storage Space<br />
Simple variables and function definitions all take seven bytes, plus allowance for<br />
strings, so reusing variables saves memory. <strong>The</strong> RAM occupied by an array is easy to<br />
calculate. <strong>The</strong> figure is identical to its own offset pointer, plus strings where ap<br />
plicable. <strong>The</strong> number of bytes is:<br />
5+2*NUMBER OF DIMENSIONS+(DIM1+1)*(DIM2+1) *...* 2, 3, or 5<br />
where <strong>the</strong> value 2, 3, or 5 depends on <strong>the</strong> type of array (integer=2, string=3,<br />
real=5). In addition, <strong>the</strong> strings of a string array must be counted.<br />
Integer arrays are economical. If you have a large amount of numeric data, it<br />
often pays to convert it into this form, provided <strong>the</strong> range —32768 to +32767 is suf<br />
ficient. It may be worthwhile combining two smaller numbers to increase <strong>the</strong><br />
efficiency.<br />
150
Advanced BASIC<br />
Examples:<br />
1. X°/o(500) has one dimension, and DIMl=500. <strong>The</strong>refore, it occupies 5 + 2 +<br />
501*2 = 1009 bytes.<br />
2. AB(10,10) has two dimensions; DIMl = 10 and DIM2=10. This much data will oc<br />
cupy 5 + 4 + 121*5 = 614 bytes.<br />
3. X$(100) defined with strings on average 10 bytes long occupies about 5 + 2 +<br />
101*3 + 101*10 = 1320 bytes.<br />
Order of Definition of Variables<br />
<strong>The</strong> order in which variables are defined may have a significant effect on <strong>the</strong> speed<br />
of BASIC. This occurs for two reasons. First, whenever BASIC uses a variable, it has<br />
to search <strong>the</strong> table for it. If much-used variables are encountered first, less time will<br />
be necessary. Second, if BASIC finds a new simple variable and <strong>the</strong>re are arrays in<br />
memory, <strong>the</strong> array table has to be moved up in memory.<br />
DIM is usually <strong>the</strong> most efficient way to define variables. It operates on simple<br />
variables just as it does on arrays. A statement like DIM J, X, S%, M$, X$(23) has <strong>the</strong><br />
same effect on BASIC as searching for each variable, not finding it, and <strong>the</strong>refore<br />
positioning it with its default value of zero or <strong>the</strong> null string after <strong>the</strong> program.<br />
LOAD and SAVE<br />
In <strong>the</strong> direct mode, SAVE stores a program to tape or disk. It assumes that <strong>the</strong> pointers<br />
at locations 43 and 44 and locations 45 and 46 mark <strong>the</strong> start and end of <strong>the</strong> BASIC<br />
program, and it saves <strong>the</strong> bytes between <strong>the</strong>se pointers. As a consequence, it is pos<br />
sible to save a program with its variables by moving <strong>the</strong> end-of-program pointer up<br />
to include variables. This technique works very well with integer arrays, which are<br />
an economical way to store numeric data. A similar technique can save character<br />
definitions along with BASIC; see Chapter 12 for <strong>the</strong> method.<br />
In <strong>the</strong> program mode, LOAD chains. <strong>The</strong> next tape (or ano<strong>the</strong>r disk program) is<br />
loaded, generally into normal BASIC memory, overlaying <strong>the</strong> program which per<br />
formed <strong>the</strong> LOAD. Automatically, a GOTO is executed which points to <strong>the</strong> first line<br />
of <strong>the</strong> LOADed program, so <strong>the</strong> new program runs while retaining variables from <strong>the</strong><br />
earlier program. In this way, extremely long BASIC programs can still be run; for ex<br />
ample, a series of visual screens in hi-res mode could be chained to provide an in<br />
teresting advertising display. Of course, <strong>the</strong>re's a delay between programs while <strong>the</strong><br />
next one is loaded.<br />
Figure 6-7.<br />
Program Chaining<br />
BASIC Program<br />
Variables<br />
LOAD<br />
BASIC Program<br />
Note: LQAD command on first program causes new BASIC program to load, <strong>the</strong>n run. In <strong>the</strong><br />
diagram, <strong>the</strong> new program is shorter than <strong>the</strong> old, so variables' values are mostly retained.<br />
151
Advanced BASIC<br />
This technique, illustrated in Figure 6-7, can be extremely powerful. However,<br />
<strong>the</strong>re are two complications. First, <strong>the</strong> new program may be longer than <strong>the</strong> second;<br />
in this case, <strong>the</strong> variables will be overwritten. More seriously, <strong>the</strong> end-of-BASIC<br />
pointer still thinks <strong>the</strong> new program ends where <strong>the</strong> old program did; so whenever a<br />
variable is defined or changed, <strong>the</strong> program material toward <strong>the</strong> end will be cor<br />
rupted. For a complete solution to this problem, see OLD, later in this chapter.<br />
A second problem, which is relevant to <strong>the</strong> storage of variables, is that some<br />
strings and all function definitions are stored within BASIC. Thus, a chained pro<br />
gram cannot generally make use of function definitions or strings within BASIC. If<br />
this is ever a problem (and it could be if strings of user-defined characters are being<br />
passed between chained programs), it is easily avoided with strings. Simply use<br />
something like A$="ABCDE" + " " in <strong>the</strong> loader, which forces <strong>the</strong> string into<br />
higher RAM. Function definitions, if used, must be redefined in each new program.<br />
Accuracy of Number Storage<br />
All number systems have limitations. Just as <strong>the</strong> decimal system cannot exactly ex<br />
press thirds, <strong>the</strong> square root of 2, or pi, so computers with digital storage all have<br />
problems with rounding errors. <strong>The</strong> difficulty is inherent in <strong>the</strong> machines. Some<br />
computer chips designed to perform calculations have registers which indicate when<br />
a result has been rounded, and also <strong>the</strong> lower and upper limits of <strong>the</strong> result. In prac<br />
tice, great precision is usually unnecessary (or misleading), and for many purposes<br />
this subsection will not be needed.<br />
<strong>The</strong> only reason accuracy is a possible difficulty with <strong>the</strong> <strong>64</strong> is <strong>the</strong> fact that<br />
numbers as <strong>the</strong>y are printed don't always match <strong>the</strong> precision with which <strong>the</strong>y are<br />
stored. If <strong>the</strong>y were printed in full, errors would be obvious.<br />
Try <strong>the</strong>se examples:<br />
PRINT 8.13<br />
PRINT 3/5*5: PRINT 3/5*5 =3<br />
<strong>The</strong> first example yields 8.13000001. This is <strong>the</strong> smallest value where an evaluation<br />
stores a number in a form which appears changed on PRINT. <strong>The</strong> second evaluates<br />
<strong>the</strong> result of 3/5*5 and prints it as 3, but <strong>the</strong> subsequent test shows that it isn't<br />
considered equal to 3. In fact, it is stored as 3.0000000009.<br />
Obviously, PRINT is designed to work sensibly in most cases. However, since<br />
precision is inevitably lost in some calculations, <strong>the</strong>re must be rounding rules, and<br />
exceptional cases are likely to turn up.<br />
Special techniques can be used to avoid <strong>the</strong>se problems. <strong>The</strong> first is to allow a<br />
range of possibilities (for example, treating X and Y as identical if ABS(X—Y)
Advanced BASIC<br />
Storage and Errors with Floating-Point Numbers<br />
Floating-point variable values are stored in five bytes (see Figure 6-8). Extra bits are<br />
used during calculations, but <strong>the</strong>se are lost when <strong>the</strong> final computed value is stored.<br />
Figure 6-8. Storage of Floating-Point Variables<br />
Bytel<br />
Byte 2<br />
Byte 3<br />
Byte 4<br />
Byte 5<br />
Exponent<br />
Sign Bit and<br />
Mantissa 1<br />
Mantissa 2<br />
Mantissa 3<br />
Mantissa 4<br />
This is a standard arrangement in which every increase in <strong>the</strong> exponent doubles<br />
<strong>the</strong> value, and where <strong>the</strong> mantissas are stored in decreasing order of significance. A<br />
single high bit holds <strong>the</strong> sign, corresponding with <strong>the</strong> minus flag of <strong>the</strong> 6510 chip.<br />
<strong>The</strong> 31 bits that hold <strong>the</strong> mantissa span a range of 1 to 1.9999. . . which, when<br />
multiplied by <strong>the</strong> exponent (in <strong>the</strong> form 2 to <strong>the</strong> nth power), takes in <strong>the</strong> entire range<br />
from about 10~38 to 1038 with an accuracy of one part in 231. Outside <strong>the</strong>se limits,<br />
ei<strong>the</strong>r an overflow error will occur or a very small number will be rounded to zero.<br />
<strong>The</strong>re's no underflow error to indicate that a number is too small to be handled.<br />
<strong>The</strong> following formula will convert any number stored in this form into a more<br />
understandable form:<br />
(-l)t(Ml AND 128)*2t(EX-129)*(l+((Ml AND 127)+(M2+(M3+M4/256)/256)/256)/128)<br />
<strong>The</strong> examples in Table 6-2, PEEKed from <strong>64</strong> memory, will help to clarify this:<br />
Table 6-2.<br />
Floating-Point Storage<br />
Bytel<br />
Byte 2<br />
Byte 3<br />
Byte 4<br />
Byte 5<br />
-1.5<br />
129<br />
192<br />
0<br />
0<br />
0<br />
0<br />
0<br />
any<br />
any<br />
any<br />
any<br />
.1234<br />
125<br />
124<br />
185<br />
35<br />
163<br />
1.5<br />
129<br />
<strong>64</strong><br />
0<br />
0<br />
0<br />
3<br />
130<br />
<strong>64</strong><br />
0<br />
0<br />
0<br />
4<br />
131<br />
0<br />
0<br />
0<br />
0<br />
5<br />
131<br />
32<br />
0<br />
0<br />
0<br />
6<br />
131<br />
<strong>64</strong><br />
0<br />
0<br />
0<br />
7<br />
131<br />
96<br />
0<br />
0<br />
0<br />
8<br />
132<br />
0<br />
0<br />
0<br />
0<br />
144.75<br />
136<br />
16<br />
192<br />
0<br />
0<br />
99999999<br />
155<br />
62<br />
188<br />
31<br />
224<br />
153
Advanced BASIC<br />
Note that numbers from 4 to 7.9999 . . . have <strong>the</strong> same exponent; <strong>the</strong>ir bit pat<br />
terns run from 00000 ... to 11111 ... as <strong>the</strong> value increases. Adding 1 to <strong>the</strong> expo<br />
nent doubles <strong>the</strong> value, subtracting 1 halves it, and so on. Note how negative<br />
numbers have <strong>the</strong> sign bit set. Note also that an exponent of zero always indicates a<br />
zero value with this number system.<br />
To decode a number, <strong>the</strong> easiest method is to start at <strong>the</strong> lowest significant byte,<br />
divide by 256, add <strong>the</strong> next, divide by 256, add <strong>the</strong> next, divide by 256, add Ml<br />
(less 128 if necessary), divide by 128, and add 1. Scale up <strong>the</strong> result (which will be<br />
from 1 to 1.999 . . .) by 2"129.<br />
Conversely, if you wish to express a number in this format, ei<strong>the</strong>r PEEK <strong>the</strong> val<br />
ues from RAM or (if you can't access a computer) use <strong>the</strong> method outlined below.<br />
Example. Expressing —13.2681. <strong>The</strong> minus sign means you must set <strong>the</strong> high<br />
bit of Ml. <strong>The</strong> nearest power of 2 below 13 is 8 (23), so <strong>the</strong> exponent is<br />
129+3=132. 13.2681/8 is equal to (1.6585125), and <strong>the</strong> fractional portion is <strong>the</strong><br />
number stored by <strong>the</strong> 31 bits in <strong>the</strong> mantissa:<br />
.6585125 * 128 = 84.2896<br />
.2896 * 256 = 74.1376<br />
.1376 * 256 = 35.2256<br />
.2256 * 256 = 57.75.<br />
Thus, <strong>the</strong> nearest floating-point approximation of —13.2681 is 132 I 212 I 74 I 35 I 58.<br />
Storage errors. Typically, a number giving aberrant results is stored with <strong>the</strong> fi<br />
nal bit, or bits, incorrect. For instance, X=3/5*3 stores X as 130 I <strong>64</strong> I 0 I 0 11, and <strong>the</strong><br />
final bit makes X unequal to 3.<br />
Integers and fractions. Any whole number between 1 — 232 and 232 — 1 is held<br />
exactly by <strong>the</strong> <strong>64</strong>, without any error. This is why loops like FOR J=l TO 100000:<br />
PRINT J: NEXT can continue without error, while <strong>the</strong> same loop with STEP .9 soon<br />
prints numbers with rounding errors.<br />
Note that 232-l is stored as 160 1127 I 255 I 255 I 255 I 255 and is <strong>the</strong> highest ac<br />
curately stored integer; 232 is stored as 161 I 0 I 0 I 0 I 0. Similar rules apply to frac<br />
tions; provided <strong>the</strong>y are combinations of 1/2, 1/4, 1/8, . . . 1/231, <strong>the</strong>y can be held<br />
exactly. Because of this, it may be best (particularly in financial calculations) to store<br />
values as integers.<br />
Special Locations and Features of BASIC<br />
BASIC uses a lot of <strong>the</strong> low end of memory for temporary storage, and many of<br />
<strong>the</strong>se storage locations are programmable from BASIC. This section describes some<br />
of <strong>the</strong> more useful methods. <strong>The</strong> keyboard and some aspects of screen handling are<br />
also included here, as <strong>the</strong>y are special points of interest in BASIC programming.<br />
Buffers<br />
<strong>The</strong> input buffer, keyboard buffer, and tape buffer occupy locations 512-600 ($0200-<br />
$0258), 631-<strong>64</strong>0 ($0277-$0280), and 828-1019 ($033C-$q3FB), respectively. During<br />
normal operations, each of <strong>the</strong>se areas has a specific function. <strong>The</strong> program "Micro-<br />
Scope" (from <strong>the</strong> preceding chapter) allows you to watch <strong>the</strong> first two of <strong>the</strong>se in<br />
action.<br />
Input buffer. Program 6-4 demonstrates <strong>the</strong> use of <strong>the</strong> input buffer.<br />
154
Advanced BASIC<br />
Program 6-4. Using <strong>the</strong> Input Buffer<br />
10 N$=MFORX=1TO5:PRINT X:NEXT X" + CHR$(0)<br />
20 FOR J=l TO LEN(N$)<br />
30 POKE 511+J,ASC(MID$(N$,J)): NEXT<br />
40 POKE 781,255: POKE 782,1<br />
50 SYS 42118<br />
An ASCII string, terminated by a null (zero) byte and POKEd into <strong>the</strong> buffer,<br />
behaves exactly as <strong>the</strong> same line would if typed in from <strong>the</strong> keyboard. Lines 40 and<br />
50 execute <strong>the</strong> command in <strong>the</strong> buffer, after first setting a pointer to $01FF (one less<br />
than <strong>the</strong> start of <strong>the</strong> buffer). <strong>The</strong> buffer would also be executed if <strong>the</strong> end of <strong>the</strong> pro<br />
gram were reached, or if an END statement were encountered, but using <strong>the</strong> SYS<br />
shown in Program 6-4 is often more useful.<br />
Keyboard buffer. It's easy to show that <strong>the</strong> <strong>64</strong> has a queuing system for key<br />
strokes. <strong>The</strong> short routine:<br />
1 GET X$: PRINT X$: FOR J=l TO 2000: NEXT: GOTO 1<br />
prints characters which have been typed faster than <strong>the</strong> computer can process <strong>the</strong>m.<br />
Up to ten characters can be stored here. You can POKE <strong>64</strong>9 ($289) to change this,<br />
but if <strong>the</strong> value exceeds 10, you could corrupt some pointers (it is usually possible to<br />
use up to 15, however).<br />
Location 198 ($C6) independently stores <strong>the</strong> number of characters in <strong>the</strong> buffer.<br />
POKE 198,0 <strong>the</strong>refore causes all characters to be ignored; it has <strong>the</strong> same effect as<br />
FOR J=l TO 10: GET X$: NEXT. <strong>The</strong> key combination, SHIFT-RUN/STOP, puts<br />
LOAD, carriage return, RUN, carriage return into this buffer, using a routine at<br />
$E5EE.<br />
Many examples in this chapter (AUTO, DELETE, LIST) rely on this buffer. <strong>The</strong><br />
following short routine shows how POKEs into <strong>the</strong> buffer work. This is ano<strong>the</strong>r im<br />
portant programming technique.<br />
10 DATA 72,69,76,76,79<br />
15 FOR J=631 TO 635:READX:POKE J,X:NEXT<br />
20 POKE 198,5<br />
POKEing one or more RETURN characters, CHR$(13), into <strong>the</strong> queue is also a<br />
popular trick, since it allows messages printed on <strong>the</strong> screen to be input later. In ef<br />
fect, that extends <strong>the</strong> range of <strong>the</strong> command beyond ten characters.<br />
<strong>The</strong> next example, Program 6-5, puts a quote in <strong>the</strong> line just before INPUT. This<br />
is very useful when a string which is to be input may contain commas, colons, or<br />
o<strong>the</strong>r separators. A quote allows <strong>the</strong> entire string to be input without error. Run this<br />
program, typing in something like A, B, C, and contrast <strong>the</strong> result with that achieved<br />
by an unadorned INPUT statement.<br />
Program 6-5. Using a Quote Before INPUT<br />
1000 P=PEEK(198): P=P+1: IF P>9 THEN P=9<br />
1010 FOR J=631+P TO 631 STEP -1i POKE J, PEEK(J-l)<br />
: NEXT<br />
1020 POKE 198,P: POKE 631,34: INPUT X$<br />
1030 PRINT X$: FORJ=1 TO 1000: NEXT: GOTO 1000<br />
155
Advanced BASIC<br />
Program 6-5 moves <strong>the</strong> characters along <strong>the</strong> buffer, just as <strong>the</strong> <strong>64</strong>'s operating<br />
system does.<br />
A more exotic use is to transfer BASIC programs to <strong>the</strong> <strong>64</strong> from ano<strong>the</strong>r com<br />
puter by inputting <strong>the</strong>m in ASCII via a modem, printing individual lines on <strong>the</strong><br />
screen, and inputting each line, adding a RETURN at <strong>the</strong> end. It is quicker than typ<br />
ing <strong>the</strong>m in, although <strong>the</strong> work of conversion is likely to be a problem.<br />
Tape buffer. <strong>The</strong> <strong>64</strong>'s operating system reserves this area for tape use, and it is<br />
<strong>the</strong>refore a popular place to put ML routines once no more tape activity is expected<br />
(after a program has been loaded and is running). It is not actively programmable<br />
like <strong>the</strong> two previous buffers. In addition, it is overwritten whenever tape is written<br />
to or read from; don't put ML here if you're using tape to load or save data, or if you<br />
are chaining programs.<br />
Spare RAM areas. <strong>The</strong>se aren't buffers in <strong>the</strong> usual sense. <strong>The</strong> <strong>64</strong> has IK of<br />
RAM at <strong>the</strong> low end of memory for its own use, but some isn't allocated and can be<br />
used safely. Locations 251-254 ($FB-$FE), 679-767 ($02Al-$02FF), 784-787<br />
($0310-$0313), 820-827 ($0334-$033B), and 1020-1023 ($03FC-$03FF) are avail<br />
able. <strong>The</strong> second of <strong>the</strong>se areas is 95 bytes long; <strong>the</strong> tape buffer has 192 bytes, but<br />
820-1023 are free for noncassette users (204 bytes). BASIC does not use <strong>the</strong> 4K of<br />
RAM from 49152-53247 ($C000-$CFFF), which is also free for ML programs or<br />
o<strong>the</strong>r purposes.<br />
Clock<br />
<strong>The</strong> three-byte jiffy clock is stored at locations 160-162. Location 162 is <strong>the</strong> fastestchanging<br />
byte. At each interrupt (about every 1/60 second, a unit of time called a<br />
jiffy—hence <strong>the</strong> name, jiffy clock), that location is incremented, with overflow when<br />
necessary into <strong>the</strong> higher bytes. Thus, location 161 is incremented every 256/60 sec<br />
onds, or about every 4.2667 seconds; location 160 is incremented every 65536/60<br />
seconds, or about every 18.204 minutes. <strong>The</strong> PAUSE routine, later in this chapter,<br />
shows a possible use of <strong>the</strong> jiffy clock.<br />
<strong>The</strong> TI and TI$ reserved variables discussed in Chapter 3 are derived from <strong>the</strong>se<br />
bytes by a straightforward conversion. TI equals PEEK(162) + 256*PEEK(161) +<br />
256*256*PEEK(160); for TI$, <strong>the</strong> value of TI (in jiffies) is converted into hours, min<br />
utes, and seconds. Although <strong>the</strong> speed of <strong>the</strong> clock is constant, it is not identical to<br />
that of real clocks, since <strong>the</strong> interrupts aren't at precise 1/60-second intervals. <strong>The</strong><br />
error varies with power sources, and between VICs and <strong>64</strong>s, but <strong>the</strong> maximum error<br />
will not be more than one part in 33,000 (a couple of minutes a day).<br />
Disabling<br />
RUN/STOP and RUN/STOP-RESTORE<br />
Blocking out <strong>the</strong> RUN/STOP key is a useful way to provide some program security,<br />
to guard against accidental use of SHIFT-RUN/STOP (which will cause <strong>the</strong> <strong>64</strong> to try<br />
to load and run a program), and to keep <strong>the</strong> user from exiting a machine language<br />
program inappropriately.<br />
Four software methods are given below. Remember, however, that if your com<br />
puter goes into an infinite loop with RUN/STOP disabled, you may have to turn<br />
your <strong>64</strong> off to correct <strong>the</strong> problem. Be sure to include a subroutine to reenable<br />
RUN/STOP.<br />
156
Advanced BASIC<br />
To disable both RUN/STOP and RUN/STOP-RESTORE (method 1): POKE<br />
808,54: POKE 809,188. To reenable, POKE 808,237: POKE 809,246. This is one of<br />
<strong>the</strong> best methods, since it leaves <strong>the</strong> clock working, disables both RUN/STOP and<br />
RUN/STOP-RESTORE, doesn't affect tape operations, and leaves LIST working<br />
normally.<br />
To disable both RUN/STOP and RUN/STOP-RESTORE (method 2): POKE<br />
808,234. To reenable, POKE 808,237. This simple POKE disables both RUN/STOP<br />
and RUN/STOP-RESTORE. It leaves <strong>the</strong> clock working, but it may have an effect<br />
on tape loading. If you're not using tape once <strong>the</strong> program is loaded, this method is<br />
fine. Note, however, that LIST will be scrambled. Every time LIST checks for <strong>the</strong><br />
RUN/STOP key, <strong>the</strong> pointer telling it <strong>the</strong> length of <strong>the</strong> current line is changed. It is<br />
strange to see a listing composed of apparent garbage run properly.<br />
To disable RUN/STOP: POKE 788,52. To reenable, POKE 788,49. This POKE<br />
modifies <strong>the</strong> interrupt vector so that it bypasses <strong>the</strong> Kernal routine to increment <strong>the</strong><br />
clock and check RUN/STOP. However, it doesn't disable RESTORE. Tape operations<br />
defeat this procedure; during READing from tape <strong>the</strong> interrupt sequence is reset, and<br />
RUN/STOP breaks into <strong>the</strong> program. <strong>The</strong> jiffy clock is turned off by this POKE.<br />
RUN/STOP can also be disabled with POKE 808,239.<br />
To disable RUN/STOP-RESTORE only: POKE 792,193. To reenable, POKE<br />
792,71. This alters <strong>the</strong> NMI (Non-Maskable Interrupt) vector so that it returns with<br />
out having any effect.<br />
How RUN/STOP works: <strong>The</strong> RUN/STOP key is not an interrupt-like device, as<br />
it may appear to be. In fact, every 1/60 second <strong>the</strong> Kernal routine which tests <strong>the</strong><br />
RUN/STOP key is called. That routine looks at location $91 (145), and if <strong>the</strong> highest<br />
bit is low—that is, if <strong>the</strong> contents equal $7F (127)—RUN/STOP is currently being<br />
pressed. ML programmers can <strong>the</strong>refore check RUN/STOP with JSR $FFE1:LDA<br />
$91:BPL.<br />
LIST and RUN also use <strong>the</strong> Kernal routine that tests <strong>the</strong> RUN/STOP key, which<br />
is why a listing or a running program can be stopped. Tape LOAD and SAVE also<br />
use it, as does RESTORE.<br />
<strong>The</strong> Kernal routine at location 65505 ($FFE1) jumps to <strong>the</strong> address vectored in<br />
locations 808 and 809. Method 1 changes this destination from $F6ED to $BC36.<br />
<strong>The</strong> ML it finds <strong>the</strong>re is simply LDA #$1/RTS. This value insures that RUN/STOP<br />
will never occur.<br />
Function<br />
Keys<br />
<strong>The</strong> simplest programming method is to use a simple GET; <strong>the</strong> range of ASCII val<br />
ues for fl-f8 are 133, 137, 134, 138, 135, 139, 136, and 140. Program 6-6 is a BASIC<br />
loader which enables all eight function keys to be defined with individual strings up<br />
to 32 characters long.<br />
Program 6-6. Function Keys<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
0 DATA 32,253,174,32,158,173,32,141,173,32,247,183<br />
,136,152,10,10,10 :rem 204<br />
1 DATA 10,10,133,253,169,29,133,254,32,253,174,32,<br />
158,173,32,143,173 :rem 12<br />
157
Advanced BASIC<br />
2 DATA 160,0,177,100,240,22,170,200,177,100,133,25<br />
1,200,177,100,133 :rem 179<br />
3 DATA 252,160,0,177,251,145,253,200,202,208,248,1<br />
38,145,253,169,28 :rem 228<br />
4 DATA 141,144,2,169,75,141,143,2,96,24,165,215,23<br />
3,132,201,8,144,3 :rem 213<br />
5 DATA 76,72,235,170,189,124,28,133,253,169,29,133<br />
,254,160,0,177,253 :rem 40<br />
6 DATA 240,237,201,95,240,6,32,210,255,200,208,242<br />
,166,198,169,13,157 :rem 71<br />
7 DATA 119,2,230,198,208,216,0,<strong>64</strong>,128,192,32,96,16<br />
0,224 :rem 152<br />
10 REM PROGRAMMABLE FUNCTION KEYS FOR THE <strong>64</strong><br />
:rem 146<br />
20 REM TYPICAL EXAMPLES OF SYNTAX: :rem 113<br />
30 REM SYS 40448,1,"THIS IS FUNCTION KEY 1":CLR<br />
:rem 134<br />
40 REM SYS 40448,2,"[CTRL-RED]M:CLR :rem 41<br />
50 REM SYS 40448,3,"LIST 50-100V:CLR * ADDS RETUR<br />
N :rem 79<br />
60 REM SYS 40448,5,"LOAD"+CHR$(34)+"$"+CHR$(34)+",<br />
8":CLR:REM LOADS DIRECTORY :rem 205<br />
90 REM Sl=49192 TO PUT ROUTINE AT $C000 : rem 101<br />
100 POKE 56, PEEK(56)-2: CLR: REM LOWERS MEMORY BY<br />
512 BYTES :rem 63<br />
110 S=PEEK(56):S1=256*S :rem 24<br />
120 FOR J=S1 TO S1+131:READ X:POKE J,X:NEXT<br />
:rem 202<br />
130 POKE S1+22,S+1:POKE S1+65,S;POKE S1+90,S:POKE<br />
{SPACE}S1+94,S+1 :rem 19<br />
140 FOR J=Sl+256 TO S1+511:POKE J,0:NEXT:REM SET A<br />
LL FN KEYS NULL :rem 174<br />
150 PRINT M{CLR}USE SYNTAX:- :rem 19<br />
160 PRINT "SYS" SI ",N,STRING:CLR" :rem 199<br />
170 LIST 20-60 :rem 201<br />
<strong>The</strong> program reserves 512 bytes at <strong>the</strong> top of memory for <strong>the</strong> ML and <strong>the</strong> eight<br />
32-character strings. Strictly speaking, only 31 characters are available for each key<br />
definition, because each has a null byte as a terminator. When Program 6-6 is first<br />
run, all function definitions are initially blank. To define a function key, use a state<br />
ment of <strong>the</strong> form:<br />
SYS address, n, "string":CLR<br />
where address is <strong>the</strong> start of <strong>the</strong> ML routine (<strong>the</strong> program will tell you <strong>the</strong> proper<br />
value to use), n is <strong>the</strong> number of <strong>the</strong> function key you wish to define, and string is<br />
<strong>the</strong> string of up to 31 characters you wish to assign to that key. CLR sets <strong>the</strong> pointers<br />
properly. <strong>The</strong> program has REM statements which include examples.<br />
Typically, SYS 40448,5,"NAME": CLR calculates <strong>the</strong> starting point where <strong>the</strong><br />
string is to be stored and copies it <strong>the</strong>re. CLR sets BASIC pointers correctly. (This ex<br />
ample makes f5 print NAME on <strong>the</strong> screen.)<br />
158
Advanced BASIC<br />
<strong>The</strong> vector at $028F-90 is central to <strong>the</strong> method. Provided it isn't changed and<br />
provided <strong>the</strong> strings and ML aren't overwritten, <strong>the</strong> function keys will operate as<br />
programmed indefinitely.<br />
<strong>The</strong> software is written so that <strong>the</strong> left-arrow key can be used to insert a carriage<br />
return into <strong>the</strong> keyboard buffer:<br />
SYS 40448,l,"LIST 100-30
Advanced BASIC<br />
When <strong>the</strong> scan begins, $DCOO is set for output and $DC01 for input, by <strong>the</strong> de<br />
fault configuration of <strong>the</strong>ir data direction registers $DC02 and $DC03 (which hold<br />
$FF and $00, respectively). In turn, <strong>the</strong> row register is rotated to take one of 8 val<br />
ues, and each bit of <strong>the</strong> column is tested each time. A bit will have a 0 value if its<br />
corresponding key is pressed, or 1 if <strong>the</strong> key is not pressed. <strong>The</strong> value is $FF when<br />
no keys are pressed. A counter increments with each loop; it is this counter value<br />
(not <strong>the</strong> value read from <strong>the</strong> matrix) which is stored when a keypress is found. Since<br />
<strong>the</strong>re are eight rows and columns, <strong>64</strong> different values are <strong>the</strong>oretically possible, and<br />
<strong>the</strong> <strong>64</strong> uses all of <strong>the</strong>se.<br />
Reading more than one key simultaneously is generally possible only with ML;<br />
Chapter 13 has an example. Even with ML, it is difficult to detect more than 2 of <strong>the</strong><br />
<strong>64</strong> keys at once; for example, with keys 9 and K pressed, nei<strong>the</strong>r <strong>the</strong> + nor <strong>the</strong> : key<br />
can be distinguished.<br />
Table 6-3. Decoding <strong>the</strong> Keyboard<br />
Contents of $DC01 (Row)<br />
$7F<br />
(127)<br />
$BF<br />
(191)<br />
$DF<br />
(223)<br />
$EF<br />
(239)<br />
$F7<br />
(247)<br />
$FB<br />
(251)<br />
$FD<br />
(253)<br />
$FE<br />
(254)<br />
$7F (127)<br />
Q<br />
Space<br />
2<br />
CTRL<br />
-<br />
1<br />
o<br />
U<br />
$BF (191)<br />
/<br />
t<br />
=<br />
Right<br />
sh!ft<br />
;<br />
*<br />
£<br />
$DF (223)<br />
,<br />
@<br />
-<br />
L<br />
P<br />
+<br />
$EF (239)<br />
N<br />
O<br />
K<br />
M<br />
0<br />
J<br />
I<br />
9<br />
$F7 (247)<br />
V<br />
u<br />
H<br />
B<br />
8<br />
G<br />
Y<br />
7<br />
$FB (251)<br />
X<br />
T<br />
F<br />
C<br />
6<br />
D<br />
R<br />
5<br />
$FD (253)<br />
Left<br />
SHIFT<br />
E<br />
S<br />
Z<br />
4<br />
A<br />
W<br />
3<br />
$FE (254)<br />
3<br />
f5<br />
£3<br />
fl<br />
17<br />
<br />
RETURN<br />
w<br />
<strong>The</strong> column is always set to 127 (at $EB42 in ROM, to be precise) apart from<br />
during <strong>the</strong> actual reading; so RUN/STOP can be detected merely by testing whe<strong>the</strong>r<br />
$DC01 has bit 7 clear. Left SHIFT, X, and several o<strong>the</strong>r keys can easily be checked<br />
like this, too. Machine language is necessary to read <strong>the</strong> keyboard. <strong>The</strong> following<br />
BASIC program and ML routine illustrate <strong>the</strong> way <strong>the</strong> rows and columns interact. At<br />
this level, SHIFTed and unSHIFTed keys aren't distinguished.<br />
10 POKE 808,234 :REM DISABLE STOP<br />
20 INPUT "COLUMN",C :REM USE 127,191, ETC<br />
30 POKE 829,C<br />
40 SYS 828: GOTO 40<br />
$033C LDA #$00<br />
$033E STA $DC00 ;SET COLUMN<br />
160
Advanced BASIC<br />
$0341 LDX<br />
$0344 LDA<br />
$0346 JSR<br />
$0349 LDA<br />
$0346 JMP<br />
$DC01 ;FETCH ROW<br />
#$00<br />
$BDCD;PRINT X<br />
#$0D<br />
$FFD2 ;NEWLINE<br />
<strong>The</strong> keyboard cannot function correctly if you change <strong>the</strong> default values in <strong>the</strong><br />
data direction registers $DC02 (56322) or $DC03 (56323). POKE 56322,253 is an ex<br />
ample; this turns off 3,4, left SHIFT and several letters, so something like P SHIFT-O<br />
56222 + 100,252+2+1 is needed to reenter <strong>the</strong> normal value of 255. Joysticks and<br />
paddles are wired toge<strong>the</strong>r with <strong>the</strong> keyboard; games port 1 connects with <strong>the</strong> rows,<br />
port 2 with <strong>the</strong> columns. This is <strong>the</strong> reason that a joystick in port 1 generates appar<br />
ent keypresses, and pressing certain keys has <strong>the</strong> effect of closing switches in <strong>the</strong><br />
port 1 joystick.<br />
Decoding <strong>the</strong> keyboard. When a key is pressed, an identifying number from 0<br />
to 63 is stored in $CB (203). <strong>The</strong> value of <strong>the</strong> most recent keypress is in $C5 (197),<br />
and comparing <strong>the</strong> two shows whe<strong>the</strong>r a new key is being pressed. This prevents a<br />
long string of X's from being entered, for example, if you press X and hold it down<br />
too long. PEEKing $CB or $C5 (see Figure 6-9) is a very useful way to test for key<br />
depressions without using GET, and it has <strong>the</strong> advantage of working at all times.<br />
<strong>The</strong> default value is <strong>64</strong>, which is stored if no keys are pressed, so PEEK(203) = <strong>64</strong><br />
means no key is pressed. In practice, $C5 and CB are indistinguishable. Note that<br />
<strong>the</strong> values stored in <strong>the</strong>se locations are not ASCII codes.<br />
Figure 6-9. Keyboard Values Stored in $CB and $C5<br />
57<br />
1<br />
56<br />
2<br />
59<br />
3<br />
8<br />
4<br />
11<br />
5<br />
16<br />
6<br />
19<br />
7<br />
24<br />
8<br />
27<br />
9<br />
32<br />
0<br />
35<br />
+<br />
40<br />
43<br />
£<br />
48<br />
51<br />
0<br />
fl<br />
4<br />
CTRL<br />
Q<br />
62<br />
W<br />
9<br />
E<br />
14<br />
R<br />
17<br />
T<br />
22<br />
Y<br />
25<br />
U<br />
30<br />
I<br />
33<br />
O<br />
38<br />
P<br />
41<br />
@<br />
46<br />
*<br />
49<br />
t<br />
54<br />
RESTORE<br />
f3<br />
5<br />
RUN/<br />
STOP<br />
63<br />
o<br />
BBS<br />
SHIFT<br />
A<br />
10<br />
z<br />
12<br />
S<br />
13<br />
X<br />
23<br />
D<br />
18<br />
c<br />
20<br />
F<br />
21<br />
V<br />
31<br />
G<br />
26<br />
B<br />
28<br />
H<br />
29<br />
N<br />
39<br />
J<br />
34<br />
M<br />
36<br />
K<br />
37<br />
<<br />
47<br />
L<br />
42<br />
><br />
44<br />
[<br />
45<br />
?<br />
55<br />
5 0<br />
53<br />
SHIFT<br />
RETURN<br />
$<br />
7<br />
1<br />
<br />
2<br />
f5<br />
6<br />
£7<br />
3<br />
Space<br />
60<br />
SHIFT, <strong>Commodore</strong>, and CTRL. Location $028D (653) stores information on<br />
<strong>the</strong>se keys, which are assigned <strong>the</strong> values 1, 2, and 4, respectively. <strong>The</strong> following<br />
line of BASIC checks <strong>the</strong> value and prints it to <strong>the</strong> screen:<br />
FOR J=l TO 9E9: PRINT PEEK(653): NEXT<br />
<strong>The</strong> values add up, so this location will contain a number from 0 to 7, depending on<br />
161
Advanced BASIC<br />
which of <strong>the</strong> three keys is pressed. For example, if SHIFT and <strong>the</strong> <strong>Commodore</strong> key<br />
are held down simultaneously, <strong>the</strong> value should be 3 (since 1 + 2 = 3).<br />
RUN/STOP. Location $91 (145) stores a copy of <strong>the</strong> normal keyboard row and<br />
is used to indicate that RUN/STOP is pressed. If you PRINT PEEK(145) in a loop<br />
with STOP disabled, <strong>the</strong> result is 127.<br />
Converting <strong>the</strong>se values into ASCII is <strong>the</strong> final stage. <strong>The</strong> <strong>64</strong> has four character<br />
tables built into ROM, for unSHIFTed, SHIFTed, <strong>Commodore</strong> key, and CTRL sets,<br />
starting at $EB81, $EBC2, $EC03, and $EC78, respectively. Each is 65 bytes long and<br />
converts $C5's contents into ASCII values, making allowance for <strong>the</strong> SHIFT or Com<br />
modore keys. <strong>The</strong> last byte in each table contains $FF, <strong>the</strong> value used to show that<br />
no key is pressed.<br />
After copying <strong>the</strong> key value (0-<strong>64</strong>) into $CB, an indirect jump is executed via<br />
$028F to $EB48. This routine's function is to set <strong>the</strong> address in ($F5) to point to one<br />
of <strong>the</strong> four keyboard matrices, depending on <strong>the</strong> SHIFT keys in effect. It has a<br />
subsidiary function, that if $0291 (657) is less than 128, SHIFT-<strong>Commodore</strong> key will<br />
switch graphics sets from lowercase with uppercase to uppercase with graphics, or<br />
vice versa.<br />
Now, with $F5 pointing to one of <strong>the</strong> four keyboard tables, <strong>the</strong> routine at $EAE0<br />
is entered, and <strong>the</strong> key's ASCII value is determined. <strong>The</strong> keyscan routine <strong>the</strong>n deals<br />
with keyboard repeats, cursor control, and o<strong>the</strong>r special keys. Finally, <strong>the</strong> ASCII<br />
value of <strong>the</strong> key is put into <strong>the</strong> keyboard buffer (if room is available <strong>the</strong>re), and con<br />
trol returns to BASIC until <strong>the</strong> next IRQ interrupt.<br />
<strong>The</strong> vector at $028F can be changed to point to your own RAM routine so that<br />
keys can be intercepted and <strong>the</strong>ir effect changed. This is an ML technique only; an<br />
earlier example shows how to program <strong>the</strong> function keys to print out strings of<br />
characters.<br />
Intercepting<br />
Keys<br />
Generally, if you wish to intercept keys to trigger an activity, like printing a message,<br />
<strong>the</strong> technique is to test ei<strong>the</strong>r $CB or $D7 for your key, or keys. $D7 (215) stores <strong>the</strong><br />
ASCII value of <strong>the</strong> last key printed to <strong>the</strong> screen. Check $CB if you're only con<br />
cerned with <strong>the</strong> physical key, and check $D7 if you need to distinguish between<br />
unshifted and shifted keys. Left SHIFT can be tested with $91; SHIFT, CTRL, and<br />
<strong>Commodore</strong> key can be separately detected at $028D. Jump to $EB48 if <strong>the</strong> lookedfor<br />
key isn't pressed; end your own routine typically with JMP $EB42, which reloads<br />
$DC00 with <strong>the</strong> correct value for <strong>the</strong> next keyscan. In this way, keyboard processing<br />
is exactly as normal, SHIFT keys and all, apart from your own specially inserted rou<br />
tine. This very simple example changes <strong>the</strong> <strong>64</strong>'s background color whenever <strong>the</strong> leftarrow<br />
key is pressed:<br />
($028F should point to <strong>the</strong> starting address)<br />
LDA $CB<br />
CMP #$39 ;Left-arrow key<br />
BEQ LABEL<br />
JMP $EB48 ;Continue normal keyboard operation<br />
LABEL INC $D020 increment border color register<br />
JMP $EB42 ;This exit doesn't print
Advanced BASIC<br />
As a more complex example, consider how we might print BASIC keywords<br />
with single keystrokes (assisted by SHIFT keys, CTRL-<strong>Commodore</strong> key plus a key, or<br />
some o<strong>the</strong>r combination). <strong>The</strong>re are about <strong>64</strong> keywords, so just about every key can<br />
be assigned a unique word. <strong>The</strong> example program below uses <strong>the</strong> <strong>Commodore</strong> key in<br />
combination with o<strong>the</strong>r keys to print BASIC words. For example, <strong>the</strong> <strong>Commodore</strong><br />
key with A prints RUN. One of <strong>the</strong> points to watch for is <strong>the</strong> debouncing feature just<br />
after LABEL—without this, <strong>the</strong> words would print repeatedly, instead of only once.<br />
<strong>The</strong> following assembler listing shows <strong>the</strong> flow of <strong>the</strong> single key BASIC entry<br />
system. <strong>The</strong> vector at $028F should point to <strong>the</strong> start of this routine:<br />
EXIT<br />
LABEL<br />
LOOP<br />
PRINT<br />
LAST<br />
LDA<br />
CMP<br />
BEQ<br />
JMP<br />
LDY<br />
CPY<br />
BEQ<br />
CPY<br />
BEQ<br />
STY<br />
INY<br />
LDX<br />
INX<br />
LDA<br />
BPL<br />
DEY<br />
BNE<br />
INX<br />
LDA<br />
BMI<br />
JSR<br />
BMI<br />
BPL<br />
AND<br />
JSR<br />
JMP<br />
$028D<br />
#$02<br />
LABEL<br />
$EB48<br />
$CB<br />
#$40<br />
EXIT<br />
$C5<br />
EXIT<br />
$C5<br />
#$00<br />
$A09C,X<br />
LOOP<br />
LOOP<br />
$A09C,X<br />
LAST<br />
$FFD2<br />
PRINT<br />
PRINT<br />
#$7F<br />
$FFD2<br />
$EB48<br />
; Is <strong>Commodore</strong> key pressed?<br />
; Continue as usual if not.<br />
; Now look at ordinary scanned keys;<br />
; exit if no key pressed.<br />
; Exit if same key pressed as last time;<br />
; if new key, record it in $C5.<br />
; Loop to choose Yth BASIC word.<br />
; BASIC words are stored from $A09E.<br />
; Look for high bit set,<br />
; and, when found, decrement<br />
; Y until it counts down to 0.<br />
; Load and print consecutive characters.<br />
; end signaled by high bit set.<br />
; CHROUT<br />
; Make <strong>the</strong> routine freely relocatable.<br />
; Turn off high bit of last character<br />
; <strong>the</strong>n print it.<br />
; Continue normal keyscan.<br />
<strong>The</strong> routine described above is listed below in <strong>the</strong> form of a BASIC loader.<br />
Program 6-7.<br />
Single-Key Keyword Entry<br />
0 DATA 173,141,2,201,2,240,3,76,72,235,1<strong>64</strong>,203,192<br />
,<strong>64</strong>,240,247,196<br />
1 DATA 197,240,243,132,197,200,162,0,232,189,156,1<br />
60,16,250,136<br />
2 DATA 208,247,232,189,156,160,48,7,32,210,255,48,<br />
245,16,243,41<br />
3 DATA 127,32,210,255,76,72,235<br />
20 S=49152:REM S=828 WORKS ALSO<br />
163
Advanced BASIC<br />
30 FOR J=S TO S+54:READ X:POKE J,X:NEXT<br />
40 POKE 656,S/256:POKE 655,S-INT(S/256)*256:REM PU<br />
'TS S INTO ($028F)<br />
Variable S in Program 6-7 controls <strong>the</strong> place in memory into which <strong>the</strong> routine<br />
is POKEd; any free RAM area is acceptable since <strong>the</strong> routine is relocatable.<br />
Redefinition of Keyboard<br />
If you wish to redefine <strong>the</strong> keyboard, <strong>the</strong> most elegant way to do this with <strong>the</strong> <strong>64</strong> is<br />
to transfer BASIC and <strong>the</strong> Kernal into RAM, as explained in Chapters 5 and 8. This<br />
leaves <strong>the</strong> keyboard tables free to be redefined in any way you choose. <strong>The</strong> four<br />
tables, at 60289 ($EB81, unSHIFTED), 60354 ($EBC2, SHIFTED), 60419 ($EC03,<br />
<strong>Commodore</strong> key), and 60536 ($EC78, CTRL), each have <strong>64</strong> bytes and a terminating<br />
byte that holds $FF. Unused keys, such as CTRL-function keys, also appear as $FF<br />
and can be redefined. Special keyboards can be saved and reloaded later, and turned<br />
on or off at will by switching ROM out or in.<br />
As a simple example, <strong>the</strong>se four POKEs with RAM under ROM activated cause fl<br />
and f5 to output CTRL-Black and CTRL-White, and f2 and f6 to output CTRL-<br />
RVS/ON and CTRL-RVS/OFF:<br />
POKE 60293,144: POKE 60295,5: POKE 60358,18: POKE 60360,146<br />
It is also possible to cause BASIC to process keys differently; for example, CTRL-<br />
G could be used to set a graphics mode. This of course involves work beyond simple<br />
key redefinition. Ano<strong>the</strong>r possibility is to extend <strong>the</strong> character set for printing foreign<br />
characters to <strong>the</strong> screen.<br />
<strong>The</strong> keyboard as a device. <strong>The</strong> keyboard is treated as device number 0 by <strong>the</strong><br />
operating system. We can open a file to <strong>the</strong> keyboard and treat it as an input device:<br />
10 OPEN 5,0: REM OPEN FILE 5 FOR USE WITH DEVICE 0 (KEYBOARD)<br />
20 INPUT#5,X$<br />
30 PRINT X$: GOTO 20: REM LOOK AT WHAT'S BEEN INPUT<br />
Commas and o<strong>the</strong>r punctuation symbols which BASIC treats as separators won't<br />
now give 7EXTRA IGNORED, because a file is considered open, but parts of a string<br />
may be lost. <strong>The</strong> normal question mark prompt isn't printed.<br />
Repeat Keys<br />
Location 650 controls which keys, if any, repeat when <strong>the</strong> key is held down. For ex<br />
ample, <strong>the</strong> following POKEs easily modify <strong>the</strong> way <strong>the</strong> keyboard functions:<br />
POKE 650,0 :REM SPACE BAR AND CURSOR CONTROL KEYS REPEAT.<br />
POKE 650,<strong>64</strong> :REM NO KEYS REPEAT.<br />
POKE 650,128 :REM ALL KEYS REPEAT.<br />
With BASIC in RAM, <strong>the</strong> rate of repeat and delay before repeat takes place can be<br />
controlled by POKEing 60189 and 60138, respectively. O<strong>the</strong>rwise, an easy way to al<br />
ter <strong>the</strong> repeat rate is to change <strong>the</strong> rate at which interrupts occur.<br />
Location 652, <strong>the</strong> repeating key delay register, can be used to step from one<br />
value to ano<strong>the</strong>r through a range of values which may be very large. Program 6-8 is<br />
a simple example of <strong>the</strong> method.<br />
1<strong>64</strong>
Advanced BASIC<br />
Table 6-4. Summary of Keyboard Data Locations<br />
$91<br />
$C5<br />
$C6<br />
$CB<br />
$D7<br />
($F5)<br />
$0277-$0280<br />
$0289<br />
$028A<br />
$028B<br />
$028C<br />
$028D<br />
$028E<br />
($028F)<br />
$0291<br />
$EA31<br />
$FF9F<br />
145<br />
197<br />
198<br />
203<br />
215<br />
(245)<br />
631-<strong>64</strong>0<br />
<strong>64</strong>9<br />
650<br />
651<br />
652<br />
653<br />
654<br />
(655)<br />
657<br />
59953<br />
65439<br />
RUN/STOP key/record<br />
Newest key pressed<br />
Number of characters in keyboard buffer<br />
Previous key pressed<br />
ASCII value of key pressed *<br />
Keyboard table pointer *<br />
Keyboard buffer<br />
Maximum number of characters in <strong>the</strong> keyboard buffer<br />
Repeat key flag (0 space, cursor; <strong>64</strong> no keys; 128 all keys)<br />
Repeat delay (4 to 0, so 12 repeats per second) *<br />
Repeat countdown (16 to 0, so 1/4 second before repeat) *<br />
SHIFT, <strong>Commodore</strong> key, CTRL register (1, 2, and 4 respectively)<br />
Previous configuration of SHIFT, <strong>Commodore</strong> key, and CTRL<br />
Vector enabling user-written keyboard intercepts *<br />
<strong>Commodore</strong> key SHIFT mode switch enable/disable (128 disables)<br />
Interrupt sequence comes here *<br />
Kernal routine to read <strong>the</strong> keyboard (SCNKEY) also #EA87<br />
1 Means ML is required to use this.<br />
Program 6-8. Fast Step<br />
10 REM USE + OR - KEY<br />
100 GOSUB 1000: T=T+INC: PRINT T: GOTO 100<br />
1000 POKE 650,128: REM ALL KEYS REPEAT<br />
1010 GET X$: IF PEEK(652)>0 THEN INC=0<br />
1020 IF X$=" + M THEN INC=INC+1:IF INO20 THEN INC=2<br />
0<br />
1030 IF X$="-M THEN INC=INC-1:IF INC
Advanced BASIC<br />
<strong>the</strong> whole area up to $D000 as RAM (Chapter 8 explains in depth). <strong>The</strong> only o<strong>the</strong>r<br />
problem with $C000-$CFFF is that everyone's ML tends to start at $C000. If several<br />
routines are to coexist, some will have to be relocated; Chapter 9 shows how to do<br />
this.<br />
<strong>The</strong> <strong>64</strong> also has RAM beneath ROM from $A000 to $BFFF and from $E000 to<br />
$FFFF. However, this RAM cannot be used by BASIC without ML, except in <strong>the</strong><br />
sense that BASIC has some redundancy, so parts of BASIC or <strong>the</strong> Kernal could be<br />
used for storage. For example, if BASIC and <strong>the</strong> Kernal are both in RAM, locations<br />
$E4B7-$E4D9 can be used freely; on a larger scale, $E2<strong>64</strong>-$E377 can be used, pro<br />
vided <strong>the</strong> trigonometric functions (COS, SIN, TAN, ATN) are avoided.<br />
Use LDA #$35:STA $01 to flip out <strong>the</strong> BASIC and Kernal ROMs, and access<br />
<strong>the</strong>ir underlying RAM. To switch <strong>the</strong> ROMs back in, use LDA #$37:STA $01.<br />
Because of <strong>the</strong> way <strong>the</strong> VIC chip works, this hidden RAM can also store graph<br />
ics information, provided <strong>the</strong> screen is moved to <strong>the</strong> same general area. For example,<br />
<strong>the</strong> screen might start at $C000, and up to 352 sprite definitions or 11 sets of charac<br />
ter definitions could be stored simultaneously, ready for access. Chapter 8 provides<br />
more information about how to access <strong>the</strong> RAM under ROM.<br />
Small areas of free RAM. <strong>The</strong> <strong>64</strong> has IK of RAM at <strong>the</strong> start of memory which<br />
is largely allocated for BASIC. In zero page, <strong>the</strong> four bytes from 251 to 254<br />
($FB-$FE) are left untouched by BASIC. Locations 2-6 are rarely used (predomi<br />
nately by ML number conversion programs), and 247-250 ($F7-$FA) are used for<br />
RS-232 pointers. <strong>The</strong> stack (256-511, $100-$lFF) can be used with caution at <strong>the</strong><br />
low end; 318 ($13E) is a safe starting point if tape is going to be used. However,<br />
don't store ML in <strong>the</strong> stack if you don't understand it; for example an ?OUT OF<br />
MEMORY ERROR may delete your ML.<br />
679-767 ($2A7-$2FF) has 89 spare bytes; 784-787 ($310-$313) has 4; and<br />
820-1023 ($334-$3FF) has 204, of which <strong>the</strong> tape buffer (828-1019, $033C-$03FB)<br />
takes 192.<br />
Screen<br />
<strong>The</strong> screen is treated as device number 3, so files can be opened to <strong>the</strong> screen for in<br />
put and output with a statement like OPEN 3,3. This provides INPUT without <strong>the</strong><br />
normal prompt and can occasionally be useful in lines like <strong>the</strong> following one, which<br />
reads <strong>the</strong> top line from <strong>the</strong> screen, subject to <strong>the</strong> usual rules governing INPUT.<br />
OPEN 3,3: PRINT {HOME};: INPUT #3,X$<br />
Screen handling can be complicated; each ASCII value has to be converted into<br />
a POKE value, and if it has some special purpose like clearing <strong>the</strong> screen or setting<br />
reverse mode, a subroutine must carry this out. Color RAM and start-of-screen must<br />
be allowed for.<br />
ML programmers can trace this process at $FFD2, <strong>the</strong> Kernal routine CHROUT,<br />
which prints a character (which jumps to $F1CA and $E716). From here, an entire<br />
range of processes is traceable, including delete and insert, cursor movements, screen<br />
scrolling, and placing <strong>the</strong> character and its color into <strong>the</strong> screen.<br />
Sixteen bytes just after <strong>the</strong> screen (usually 2024-2039, $7E8-$7F7), and 16 color<br />
RAM nybbles, can store ML or data. (Eight bytes of sprite shape pointers follow<br />
166
Advanced BASIC<br />
this.) Clearing <strong>the</strong> screen leaves <strong>the</strong> area intact; but obviously incorrect POKEs to <strong>the</strong><br />
screen area can easily overwrite this storage position.<br />
Table 6-5 is a quick reference list for screen locations and ROM routines.<br />
Table 6-5. Summary of Screen Locations and ROM Routines<br />
Screen Locations<br />
$C7<br />
($C9)<br />
$CC<br />
$CD<br />
$CE<br />
$CF<br />
$D0<br />
($D1)<br />
$D3<br />
$D4<br />
$D5<br />
$D6<br />
$D9-$F2<br />
($F3)<br />
$0286<br />
$0288<br />
199<br />
(201)<br />
204<br />
205<br />
206<br />
207<br />
208<br />
(209)<br />
211<br />
212<br />
213<br />
214<br />
217-242<br />
(243)<br />
<strong>64</strong>6<br />
<strong>64</strong>8<br />
ROM Routines:<br />
$E544<br />
$E5A8<br />
$E632<br />
$E8CB<br />
$E8EA<br />
$E981<br />
$E9FF<br />
$EA13<br />
$EA24<br />
58692<br />
58792<br />
58930<br />
59595<br />
59626<br />
59777<br />
59903<br />
59923<br />
59940<br />
Reverse flag (0 reverse off, character reverse on)<br />
Cursor row and column for input from screen<br />
Cursor flash (0 flashes cursor, e.g., with GET)<br />
Cursor countdown (from 12 to 0)<br />
Character under cursor<br />
Cursor blink phase (0 or 1)<br />
Input from screen/keyboard (flag is 3 or 0)<br />
RAM address of start of current line<br />
Position of cursor on line<br />
Quotes flag (0 not in quotes, 1 in quotes)<br />
Current length of screen line (39 or 79)<br />
Cursor's row<br />
Table of screen line links, 4-7: Line continues; $84-$87: It doesn't<br />
Color RAM address<br />
Color code (0, black, through 15, light gray) in use<br />
High byte of start of screen (usually 4)<br />
Clear screen<br />
Set VIC chip to normal values<br />
Input from screen (or keyboard) comes here<br />
Converts CHR$(color) in A into 0-15<br />
Scrolls screen up 1 row<br />
Scrolls screen down 1 row (contents of 677 may affect this)<br />
Clear entire row (e.g., POKE 781,X:SYS 59903, when X is 0-24)<br />
Plots character and color in screen. A=Char, X=Color (0-15)<br />
Finds color RAM relevant to current cursor position<br />
Dictionary of Extensions to BASIC<br />
<strong>The</strong> <strong>64</strong>'s BASIC is limited, lacking many useful commands built into some o<strong>the</strong>r<br />
BASICs. Many can be simulated easily, though. <strong>The</strong> examples that follow are<br />
grouped under headings of typical keywords, which indicate <strong>the</strong>ir functions.<br />
<strong>The</strong>se are BASIC subroutines, which must be run as usual, or machine language<br />
routines called by SYS. <strong>The</strong> actual words listed (such as APPEND) are not new keywords<br />
in this case; <strong>the</strong>y will not by <strong>the</strong>mselves activate any of <strong>the</strong>se routines. A wedge altering<br />
a BASIC input vector is necessary to incorporate new keywords. <strong>The</strong> recently pub<br />
lished book COMPUTED Machine Language Routines for <strong>the</strong> <strong>Commodore</strong> <strong>64</strong> includes<br />
most of <strong>the</strong>se aids, as well as character and sprite editing systems—all in ML. <strong>The</strong><br />
following examples, though, will make it easier to understand how <strong>the</strong> commands<br />
work.<br />
167
Advanced BASIC<br />
APPEND<br />
This BASIC system command can ei<strong>the</strong>r add one file to <strong>the</strong> end of ano<strong>the</strong>r, making a<br />
composite file, or link two BASIC programs end to end in a single program. Machine<br />
language can be linked like this, too. Disk files can be appended as well (see Chap<br />
ter 15). And tape files can be appended, but since <strong>the</strong> <strong>64</strong> has only one tape port, <strong>the</strong><br />
process is more difficult.<br />
BASIC programs are easy to append because <strong>the</strong> LOAD address is easily altered.<br />
Standard subroutines with high line numbers can be put onto <strong>the</strong> end of programs<br />
without <strong>the</strong> usual need to list <strong>the</strong> subroutines to <strong>the</strong> screen, load <strong>the</strong> program, enter<br />
some subroutine lines, save, and repeat. If <strong>the</strong> line numbers of <strong>the</strong> programs overlap,<br />
<strong>the</strong> normal editing won't work and you'll have unremovable lines of BASIC.<br />
Figure 6-10 shows a program in memory, plus two of its pointers. Note how a<br />
link address of two zero bytes (following <strong>the</strong> normal end-of-line zero byte) signifies<br />
<strong>the</strong> end of <strong>the</strong> program. If <strong>the</strong> new program loads and overwrites <strong>the</strong> double zero<br />
link address with its own link address, <strong>the</strong> programs append perfectly.<br />
Figure 6-10.<br />
Appending Programs<br />
Start-of-Program Pointer . End-of-Program Pointer<br />
BASIC Program 1<br />
0<br />
0<br />
0<br />
Start-of-Program<br />
4- Append this:<br />
Li ik<br />
BASIC Program 2<br />
Gives:<br />
BASIC Program 1<br />
Hh<br />
BASIC Program 2<br />
New End-of-Program<br />
<strong>The</strong> easiest approach is to first enter POKE 43, PEEK(45)-2:POKE 44, PEEK(46)<br />
in direct mode; <strong>the</strong>n load or type in <strong>the</strong> new lines of BASIC and POKE 43,1: POKE<br />
44,8 to start BASIC at $800. Perfectly appended BASIC should be <strong>the</strong> result. Ac<br />
tually, this method is a shortcut; if PEEK(45) happens to be 0 or 1, you'll get an IL<br />
LEGAL QUANTITY ERROR and will need to edit your instructions to POKE 43,<br />
PEEK(45)+256-2: POKE 44, PEEK(46)-1, <strong>the</strong>n continue as before.<br />
AUTO<br />
AUTO is a system command, not available in BASIC, which automatically generates<br />
line numbers. Many utility packages include this command. This example is a BASIC<br />
subroutine, which uses <strong>the</strong> keyboard buffer to take in complete lines. <strong>The</strong> POKE in<br />
line 60010 flashes <strong>the</strong> cursor; line 60040 prints <strong>the</strong> current values of S and I, and<br />
line 60050 puts two carriage returns in <strong>the</strong> keyboard buffer. Obviously, this would<br />
be better if implemented in ML, wedging (for example) into <strong>the</strong> main BASIC loop<br />
IMAIN, at $A480. r<br />
168
Advanced BASIC<br />
Program 6-9.<br />
Auto<br />
60000 INPUT "ENTER START, INCREMENT";S,I<br />
60010 PRINT "{CLR}{2 DOWN}"; S;: POKE 204,0<br />
60020 GET X$: IP X$="" GOTO 60020<br />
60030 PRINT X$;: IF ASC(X$)13 GOTO 60020<br />
60040 PRINT "S=" S+I ":I=" I ":GOTO60010": PRINT "<br />
{HOME}";<br />
60050 POKE 631,13: POKE 632,13: POKE 198,2: END<br />
BLOCK LOAD and BLOCK SAVE<br />
<strong>The</strong> <strong>64</strong>'s LOAD and SAVE commands are designed solely for <strong>the</strong> benefit of users of<br />
BASIC. <strong>The</strong>y automate BASIC program storage and recovery in a way which is<br />
transparent. Programs load into <strong>the</strong> correct area of memory and are saved to tape or<br />
disk without any need to know about pointers or o<strong>the</strong>r inside information.<br />
However, <strong>the</strong>re are situations when <strong>the</strong> special assumptions connected with<br />
BASIC do not apply. When a block of machine language, a collection of graphics<br />
characters, or a set of variables and arrays after BASIC is to be saved to tape or disk,<br />
normal saving won't work since <strong>the</strong> machine can't know what area of memory you<br />
want saved. In addition, loading such blocks back into memory may be tricky; <strong>the</strong><br />
machine language or data is liable to be treated as though made up of BASIC lines<br />
and become corrupted by <strong>the</strong> BASIC line-linking routines.<br />
Note that ML monitors (like Supermon) have commands like .S "NAME",01,<br />
1000,2000 (save <strong>the</strong> contents of $1000-$lFFF to tape and call it NAME) and .S<br />
"NAME/',08,1000,2000 (save <strong>the</strong> same data to disk) to perform block loads and<br />
saves.<br />
BLOCK SAVE. <strong>The</strong> obvious way to save data o<strong>the</strong>r than BASIC programs is to<br />
POKE new start and end addresses. For example, you could use POKE 43,0: POKE<br />
44,48: POKE 45,0: POKE 46,<strong>64</strong>: SAVE"NAME",1,1. This will save data from loca<br />
tions $3000 to $3FFF, because <strong>the</strong> value in <strong>the</strong> start-of-BASIC pointer is changed to<br />
$3000 and <strong>the</strong> value in <strong>the</strong> end-of-BASIC pointer is changed to $4000. As far as <strong>the</strong><br />
<strong>64</strong> is concerned, this becomes <strong>the</strong> correct area to save. (Note that <strong>the</strong> last byte at<br />
$4000 is not saved; SAVE stops when it reaches it.) <strong>The</strong> secondary address of 1, with<br />
tape, forces <strong>the</strong> data to load back into <strong>the</strong> same area as that from which it was<br />
saved.<br />
However, <strong>the</strong>re may be problems in using this technique from within a BASIC<br />
program since it's necessary to restore <strong>the</strong> pointers after use.<br />
This short routine illustrates one technique:<br />
1000 SYS 57812 "NAME",1,1 : REM SET PARAMETERS FOR LOAD<br />
1010 POKE 193,0: POKE 194,48 : REM $3000 IS START ADDRESS ...<br />
1020 POKE 174,0: POKE 175,<strong>64</strong> : REM $4000 (-1) IS END ADDRESS.<br />
1030 SYS 62957 : REM PERFORMS SAVE<br />
SYS 57812 takes in <strong>the</strong> parameters which saving or loading needs: <strong>the</strong> device num<br />
ber, name length, pointer to name, and secondary address. You can watch its<br />
effect—using <strong>the</strong> following BASIC line, alter <strong>the</strong> parameters to see <strong>the</strong> effect on<br />
<strong>the</strong>se locations:<br />
169
Advanced BASIC<br />
SYS57812"Hr,8/l:PRINTPEEK(186);PEEK(183);PEEK(187)+256*PEEK(188);PEEK(185).<br />
BLOCK LOAD. To load a machine language routine into memory, <strong>the</strong> easiest<br />
way is simply to use LOAD "NAME",!,! (or ,8,1). Within a program, a flagging<br />
technique is needed to avoid <strong>the</strong> automatic chaining feature:<br />
0 IF X=l GOTO 20<br />
1 X=l: LOAD "NAME",1,1<br />
2 REM CONTINUE FROM HERE...<br />
Listed below is a simple, trouble-free method of loading, which works within pro<br />
grams without interrupting <strong>the</strong>ir flow:<br />
1000 POKE 147,0 : REM THE LOAD/VERIFY FLAG. 0 IS LOAD<br />
1010 SYS 57812 "SCREEN",8,1 : REM SETLFS IN THE KERNAL SETS PARAMETERS<br />
1020 SYS 62631 : REM NOW LOAD<br />
Program 6-10 saves a screen of information and reloads it. <strong>The</strong> technique can be<br />
useful for many applications, including games, notepads and word processors (which<br />
allow viewing two files on alternate screens).<br />
Program 6-10.<br />
Screen Save and Load<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader/' Appendix C.<br />
1 REM SAVES SCREEN,COLOR RAM AND VIC CHIP:RUN PERF<br />
ORMS SAVE,RUN 500 RELOADS :rem 71<br />
7 REM SCREEN ASSUMED TO START $400 :rem 214<br />
8 REM WRITE 3 NEW FILES TO DISK :rem 5<br />
9 REM FOR TAPE,USE 1 INSTEAD OF 8 :rem 77<br />
10 SYS 57812 "@:SCREEN",8,1:POKE 193,0:POKE 194,4:<br />
POKE 174,0:POKE 175,8 :rem 58<br />
15 SYS 62957 srem 114<br />
20 SYS 57812 "@:COLOR",8,1:POKE 194,216:POKE 175,2<br />
20:SYS 62957 :rem 62<br />
30 SYS 57812 "@:VIC REGISTERS",8,1:POKE 194,208:PO<br />
KE 175,209:SYS 62957 :rem 98<br />
50 END :rem 60<br />
499 REM LOAD BACK 3 FILES :rem 97<br />
500 POKE 147,0:SYS 57812 "SCREEN",8,1:SYS 62631<br />
:rem 252<br />
510 SYS 57812 "COLOR",8,1:SYS 62631 :rem 91<br />
520 SYS 57812 "VIC REGISTERS",8,1:SYS 62631<br />
:rem 119<br />
600 GOTO 600:REM DISPLAY SCREEN TILL 'RUN/STOP1 KE<br />
Y :rem 47<br />
Lines 10-30 save $0400-$07FF to disk under <strong>the</strong> name "SCREEN". Line 20<br />
saves <strong>the</strong> corresponding color RAM, from $D800 to $DBFF, and line 30 saves <strong>the</strong><br />
VIC registers. If user-defined characters were used, <strong>the</strong>y must be saved, too. Between<br />
<strong>the</strong>m, <strong>the</strong>y completely define any picture starting at $0400; for example, <strong>the</strong> border<br />
color and background are controlled by <strong>the</strong> VIC chip.<br />
170
Advanced BASIC<br />
Lines 500-520 reconstruct <strong>the</strong> picture. Try typing this program into a <strong>64</strong> at<br />
tached to a disk drive, <strong>the</strong>n put a few random colored characters on <strong>the</strong> screen. RUN<br />
will store <strong>the</strong> screen's information on disk, if connected. Clear <strong>the</strong> screen; type RUN<br />
500. You'll see <strong>the</strong> screen reconstruct itself.<br />
Tape is equally simple: Just change each device number from 8 to 1, and remove<br />
<strong>the</strong> redundant @: from within <strong>the</strong> filename. <strong>The</strong> screen files are always stored and<br />
reloaded in <strong>the</strong> correct sequence.<br />
CHAIN<br />
Chaining is <strong>the</strong> process by which one program loads and runs ano<strong>the</strong>r. For example,<br />
a set of programs may exist on disk, each separately accessible by a menu, so that<br />
only one program is in memory at a time, and <strong>the</strong> menu is reentered on exit from<br />
any called program. <strong>Commodore</strong> <strong>64</strong> BASIC (and PET/CBM and VIC BASICs) chains<br />
whenever LOAD takes place inside a program. A LOAD is automatically followed by<br />
RUN without CLR (to retain previous variables).<br />
Although simple, this is not quite as straightforward as it seems. Earlier in this<br />
chapter you saw how problems can occur when <strong>the</strong> chained program is longer than<br />
<strong>the</strong> program which loaded it. You may also encounter occasional difficulties with<br />
strings and function definitions.<br />
Try <strong>the</strong> following tape illustration:<br />
1. Save this on tape:<br />
10 PRINT "FIRST PROGRAM"<br />
20 A=10: B%=100: C$="HELLO" + " "<br />
30 LOAD "SECOND PROGRAM": REM CHAINS SECOND PROGRAM<br />
2. Now type this in, and save it as "SECOND PROGRAM":<br />
10 PRINT "SECOND PROGRAM"<br />
20 PRINT A,B%,C$<br />
Rewind <strong>the</strong> tape, and load and run <strong>the</strong> first program. It will almost immediately<br />
reach line 30, load <strong>the</strong> second program and run it, while retaining <strong>the</strong> variables. Line<br />
20 of <strong>the</strong> second program prints 10, 100, and HELLO, <strong>the</strong> values assigned by <strong>the</strong><br />
first program. Note that when <strong>the</strong> LOAD is within a program, <strong>the</strong>re are no LOADing<br />
messages unless <strong>the</strong> cassette button isn't pressed. (To try this example with a disk<br />
drive, use 30 LOAD"SECOND PROGRAM",8.)<br />
Chaining machine language. <strong>The</strong> easiest way to load and run ML is to use <strong>the</strong><br />
Kernal LOAD routine followed by a jump to <strong>the</strong> newly loaded ML program. This is<br />
explained in detail in Chapter 8.<br />
COLOR Border, Screen and COLOR Character<br />
BASIC graphics packages often have a command called COLOR. POKE<br />
53281,SC:POKE 53280,BC has <strong>the</strong> same effect as COLOR SC,BC. POKE <strong>64</strong>6,LC sets<br />
<strong>the</strong> color of <strong>the</strong> letters output to <strong>the</strong> screen with PRINT.<br />
Compile<br />
Compilation is a process by which a language like BASIC is converted into pure ma<br />
chine language. A program which performs this conversion is called a compiler.<br />
171
Advanced BASIC<br />
BASIC, <strong>the</strong> source code, is translated into ML, <strong>the</strong> object code. Typically, it will LIST<br />
as a single SYS command, which is followed by a large (but unlisted) ML program.<br />
Compilation is on a higher plane of sophistication than <strong>the</strong> o<strong>the</strong>r utilities discussed<br />
here, but a short discussion is justified.<br />
Briefly, any compiler of an unstructured language like BASIC must first build up<br />
a table of all <strong>the</strong> program's variables and arrange a position for each of <strong>the</strong>m in<br />
memory. Strings need pointers and will be subject to garbage collection problems<br />
unless <strong>the</strong>y are each assigned 256 bytes. When <strong>the</strong> variables are dealt with, every<br />
BASIC statement must be converted into its ML equivalent; <strong>the</strong> result is typically a<br />
set of segments which are linked to make up <strong>the</strong> compiled code.<br />
Speed increases of 10 to 50 or more times are claimed, but in practice even a<br />
tenfold increase is probably optimistic. Some of <strong>the</strong> improvement is directly due to<br />
<strong>the</strong> replacement of BASIC statements, with all <strong>the</strong>ir overhead and housekeeping, by<br />
relatively straightforward processing.<br />
By itself, this is not a major factor. Well-written compilers have <strong>the</strong>ir own<br />
arithmetic routines, using integers where possible to save time. <strong>The</strong>re's considerable<br />
room for ingenuity. For example, a line like 100 GET X$: IF X$="" GOTO 100,<br />
which is often found in BASIC, could be replaced by just five bytes of machine lan<br />
guage. <strong>The</strong> line 1000 FOR J=0 TO 1000: POKE SC+J,32: NEXT is a loop, and is <strong>the</strong><br />
sort of BASIC which a compiler has great difficulty turning into efficient ML. Tiny<br />
compilers working with a restricted set of BASIC (to save <strong>the</strong> effort of implementing<br />
every command) also exist and are interesting educational tools.<br />
Compilers invariably need disk drives, for speed and because multiple files are<br />
required. If you don't have a disk drive, someone else can compile your BASIC for<br />
you, in which case, <strong>the</strong> result will need to be transferred to tape; some compilers<br />
have a feature to permit this.<br />
Typical commercial compilers are PETSPEED and <strong>the</strong> DTL compiler. Each has a<br />
limit on <strong>the</strong> size of BASIC program that is compilable and <strong>the</strong> number of variables in<br />
it. You may find it necessary to shorten a very long program to compile it. <strong>The</strong> ML<br />
object code is often longer than <strong>the</strong> original BASIC because it has to include a long<br />
library of standard subroutines.<br />
Computed GOSUB and Computed GOTO<br />
<strong>The</strong>se functions use a formula or label, instead of a number, for <strong>the</strong>ir destination<br />
line. Some computer languages allow <strong>the</strong> use of GOSUB VALIDATE to perform a<br />
subroutine called VALIDATE. Obviously, statements like this are likely to be more<br />
readable than BASIC'S GOSUB 10000, or wherever. (Don't confuse computed<br />
destinations with ON-GOTO, which provides a choice of destinations according to<br />
<strong>the</strong> value just after ON.) Any parts of BASIC using computed destinations can't<br />
usually be renumbered by a utility.<br />
Program 6-11 shows how computed GOTO and GOSUB can both be im<br />
plemented on <strong>the</strong> <strong>64</strong>.<br />
172
Advanced BASIC<br />
Program 6-11.<br />
Computed GOTO and GOSUB<br />
90 FOR J=40960 TO 49191: POKE J,PEEK(J): NEXT<br />
91 FOR J=57344 TO 65535: POKE J,PEEK(J): NEXT<br />
92 POKE 1,53<br />
100 DATA 32,138,173,76,247,183<br />
110 FOR J=0 TO 5: READ X: POKE 58551+J,X: NEXT<br />
120 POKE 43169,183: POKE 43170,228<br />
With BASIC in RAM, all that's required is to intercept BASIC at $A8A0 and in<br />
clude a routine to evaluate a formula, ra<strong>the</strong>r than simply take in an ASCII line num<br />
ber. This version copies BASIC from ROM to RAM and stores <strong>the</strong> extra ML into a<br />
part of <strong>the</strong> RAM which isn't used by <strong>the</strong> copied-down BASIC.<br />
While ordinary BASIC runs a little slower in RAM, you can now have ex<br />
pressions like GOTO DATE or GOSUB CHECK, where perhaps DATE=1000 was<br />
previously defined, and line 1000 starts <strong>the</strong> routine called DATE.<br />
CRUNCH (and<br />
UNCRUNCH)<br />
<strong>The</strong> idea of crunching a program is to delete as much of it as is possible without<br />
altering its function negatively, with <strong>the</strong> aim of increasing BASIC'S execution speed<br />
and decreasing <strong>the</strong> memory required to store it. Conversely, uncrunching means<br />
spacing a program out to make it more readable. For example, if you wish to de<br />
cipher someone else's crunched program, a utility which lists each instruction on<br />
separate lines and puts in spaces may well help legibility. (See LIST in this chapter,<br />
and see also Chapter 8.)<br />
<strong>The</strong> rationale of CRUNCH is that REM statements, spaces, and short lines slow<br />
<strong>the</strong> BASIC translator by making it waste time jumping past spaces, switching to new<br />
lines, and so on. CRUNCH doesn't usually speed up programs a great deal, but<br />
many programmers like to pack <strong>the</strong>ir programs into <strong>the</strong> smallest space possible.<br />
Combined with renumbering lines starting at 0 and incrementing by ones, and add<br />
ing an extra line or two of DIM statements to order <strong>the</strong> main variables, you can re<br />
duce <strong>the</strong> execution time of your BASIC programs noticeably.<br />
Crunching should remove REMs, but if <strong>the</strong>se are referenced by GOTO or<br />
GOSUB, <strong>the</strong>y should ei<strong>the</strong>r be retained or <strong>the</strong> reference changed to point to <strong>the</strong> next<br />
line. It should remove all spaces not within quotation marks, but avoid confusion be<br />
tween keywords and variables (X = T AND U after crunching could be confused<br />
with <strong>the</strong> function TAN).<br />
As many lines as possible should be merged toge<strong>the</strong>r. Lines spanning more than<br />
255 bytes are unreliable, since many BASIC pointers are single bytes (those for<br />
DATA, for example). So <strong>the</strong> longest line is generally limited to 250 BASIC characters.<br />
A line in <strong>the</strong> program might be referenced by GOTO or GOSUB, and this should be<br />
handled properly. Lines of such lengths cannot be simply keyed in; <strong>the</strong>y must be<br />
POKEd in, <strong>the</strong>n <strong>the</strong> links must be readjusted.<br />
To make <strong>the</strong> CRUNCH even more interesting, it could renumber from 0 upward<br />
in steps of one, reduce all variable names to a single character (if possible), and re<br />
move spare semicolons from PRINT statements. It might modify CHRGET to remove<br />
its test for spaces (see Chapter 10), slow <strong>the</strong> rate of interrupts (or temporarily stop<br />
<strong>the</strong>m), and remove wedges which intercept BASIC and usually slow its operation.<br />
173
Advanced BASIC<br />
DEEK<br />
This double-byte PEEK returns <strong>the</strong> value in two consecutive addresses, assuming<br />
<strong>the</strong>y follow <strong>the</strong> 6510 convention of low byte, <strong>the</strong>n high byte. Use this formula:<br />
DEF FN DEEK(X) = PEEK(X) + 256*PEEK(X+1)<br />
DELETE (DEL)<br />
This command allows deletion of unwanted BASIC lines. DEL a-b is <strong>the</strong> syntax,<br />
which is similar to that of LIST (except that DEL alone should not delete every<br />
thing). DEL seems to have been omitted from <strong>Commodore</strong>'s original BASIC<br />
specifications. This subroutine in BASIC, designed to sit at <strong>the</strong> end of a program,<br />
works by searching for line numbers within a specified range, <strong>the</strong>n deleting <strong>the</strong> line<br />
by using a trick with <strong>the</strong> keyboard buffer, which simulates entry of <strong>the</strong> line number<br />
at <strong>the</strong> keyboard.<br />
Program 6-12.<br />
Delete<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
61000 INPUT "DELETE FROM, TO"; L,U:A=PEEK(43)+256*<br />
PEEK(44) :rem 238<br />
61010 DEF FN DEEK(A)=PEEK(A)+256*PEEK(A+1):rem 255<br />
61020 IF FN DEEK(A+2)U OR FN DEEK(A)=0 THEN END<br />
:rem 11<br />
61040 N=FN DEEK(A+2):PRINT "{CLR}" N :rem 14<br />
61050 PRINT "A=" A ":U=" U ":GOTO 61010" :rem 160<br />
61060 POKE 631,19:POKE 632,13:POKE 633,13:POKE 198<br />
,3:END :rem 0<br />
Line 61020 skips through link addresses until a line number in range is found,<br />
line 61030 stops ei<strong>the</strong>r out of <strong>the</strong> range or at <strong>the</strong> end of a program. Lines 61040-50<br />
print to <strong>the</strong> screen, and <strong>the</strong> rest of <strong>the</strong> subroutine simulates keypresses for HOME,<br />
RETURN, and ano<strong>the</strong>r RETURN.<br />
DOKE<br />
This double-byte POKE puts a value from 0 to 65535 into any two adjacent ad<br />
dresses, assuming <strong>the</strong> standard 6510 convention of low byte/high byte. <strong>The</strong>re's no<br />
way to write this as a function without writing a SYS routine of <strong>the</strong> form SYS m,n or<br />
using a wedge. Instead, DOKE ADDRESS, VALUE can be represented by POKE AD,<br />
VA-INT(VA/256)*256: POKE AD + 1, VA/256.<br />
DUMP<br />
Screen dump. This prints a duplicate of <strong>the</strong> screen onto paper. It is relatively<br />
easy to print normal characters, when user-defined characters aren't used, since all<br />
that's needed is a PEEK into RAM followed by printout of <strong>the</strong> corresponding charac<br />
ters. Complications include high-resolution graphics, color (where conversion to<br />
174
Advanced BASIC<br />
black-and-white may lose detail), and <strong>the</strong> fact that <strong>Commodore</strong> printers have <strong>the</strong><br />
<strong>Commodore</strong> character set, while o<strong>the</strong>rs may not.<br />
Program 6-13A is a BASIC screen dump which asslimes <strong>the</strong> uppercase character<br />
set and correctly prints all characters, including SHIFTed and <strong>Commodore</strong> key sym<br />
bols; however, <strong>the</strong> quotation mark character is not processed properly by some<br />
printers:<br />
Program 6-13A.<br />
Screen Dump<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
40010 OPEN 4,4:CMD 4 :rem 253<br />
40100 FOR J=0 TO 24:FOR K=0 TO 39:X=PEEK(1024+J*40<br />
+K) :rem §<br />
40110 IF X>128 THEN X=X-128:PRINT CHR$(18);:rem 87<br />
40120 IF X31 AND X63 AND X95 AND X
Advanced BASIC<br />
3 DATA 169,0,133,72,32,134,174,165,72,240,19,169,3<br />
2,133,122,173,34,2 :rem 12<br />
4 DATA 141,39,2,173,35,2,141,40,2,32,157,170,174,3<br />
5,2,232,224,58,144 :rem 0<br />
5 DATA 211,224,65,144,247,224,91,144,203,174,34,2,<br />
232,224,65,144,251 :rem 12<br />
6 DATA 224,91,144,171,160,36,204,41,2,240,174,144,<br />
139,200,208,136,34 :rem 6<br />
7 DATA 32,32,65,146,61,34,32,65,32,59 :rem 30<br />
10 REM SYS 828 (DIRECT MODE) DUMPS NON-ARRAY VARIA<br />
BLES1 VALUES :rem 199<br />
20 REM RELOCATE BY POINTING 185,3 IN LINE 0 TO 34<br />
{SPACE}AT END OF LINE 6 :rem 237<br />
100 FOR J=828 TO 963:READ X:POKE J,X:NEXT :rem 68<br />
FIND<br />
See SEARCH.<br />
LIST<br />
One of <strong>the</strong> most used commands in BASIC is LIST. Luckily, it can be modified<br />
easily. <strong>The</strong> two routines below are examples of modified listing. Program 6-14 cre<br />
ates a window on 12 lines of BASIC at a time, which can be scrolled up or down.<br />
This is helpful when examining BASIC without <strong>the</strong> benefit of a printer. Program 6-<br />
15 is in machine language; it alters LIST to expand <strong>the</strong> reverse characters of <strong>64</strong> list<br />
ings into a more readable text form. Printer owners may like to list <strong>the</strong>ir programs in<br />
this format.<br />
Window LIST. Append Program 6-14 to <strong>the</strong> end of your BASIC programs to<br />
use it. If you RUN 63000 with this subroutine in memory, it will list several lines on<br />
<strong>the</strong> screen—<strong>the</strong> number of lines listed can be adjusted in line 63010. Pressing <strong>the</strong> fl<br />
key causes <strong>the</strong> listing to move upward past <strong>the</strong> stationary window, while pressing<br />
<strong>the</strong> i7 key causes <strong>the</strong> listing to move downward. Obviously, since any single logical<br />
line of BASIC can take two physical screen lines, 13 lines of BASIC may be too<br />
much for <strong>the</strong> screen to hold.<br />
Lines 63020 and 63030 are printed on <strong>the</strong> screen, and <strong>the</strong>y list several lines in<br />
white before returning to test for f 1 or f7. <strong>The</strong> current starting line is <strong>the</strong> Mth line<br />
number, and subroutine 63300 scans <strong>the</strong> program, finding which line numbers to<br />
list. After LIST, <strong>the</strong> keyboard buffer is POKEd to simulate {CLR} RETURN {CLR}<br />
{DOWN} RETURN.<br />
Program 6-14.<br />
Window LIST<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader/' Appendix C.<br />
10 REM WINDOW LISTER :rem 240<br />
63000 DEP PN DEEK(L)=PEEK(L)+256*PEEK(L+1) :rem 33<br />
63010 N=M+1:GOSUB 63300:L1=L:N=M+12:GOSUB 63300<br />
:rem 122<br />
63020 PRINT " {CLR} {BLU}LIST11 LI "-" L:REM THIS LIS<br />
TS 12 LINES :rem 183<br />
176
Advanced BASIC<br />
63030 PRINT M{BLU}M=M M M:GOTO 63200{WHT}":rem 122<br />
63040 POKE631,19:POKE632,13:POKE633,19:POKE634,17:<br />
POKE635,13:POKE198,5:END :rem 57<br />
63200 GET X$:IF X$=MM GOTO 63200 :rem 81<br />
63210 IF X$-"{F1}" THEN M=M+1:REM OR LARGER INCREM<br />
ENT :rem 144<br />
63220 IF X$=M{F7}M AND M>0 THEN M=M-1:REM OR LARGE<br />
R :rem 127<br />
63230 GOTO 63010 :rem 49<br />
63299 REM FIND N'TH LINENUMBER OF BASIC :rem 11<br />
63300 J=0:L=FN DEEK(43) :rem 219<br />
63310 J=J+1:IF J0 GOTO 6<br />
3310 :rem 130<br />
63320 IF (L=0) OR (FN DEEK(L)=0) THEN L=63999:RETU<br />
RN :rem 38<br />
63330 IF J=N THEN L=FN DEEK(L+2) :rem 194<br />
63340 RETURN .rem 224<br />
BASIC can't be edited with Program 6-14 running—<strong>the</strong> entire process is under<br />
program control, and listing is all that's allowed. However, it would be possible to<br />
write a line-editing program with this method, plus parts of AUTO.<br />
Legible LIST. Program 6-15 is a transparent ML program which locates itself in<br />
memory.<br />
Program 6-15. Legible LIST<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix G<br />
3 REM POKE 49259,5 TO CANCEL [SPC] :rem 155<br />
4 REM POKE 49260,5 TO CANCEL [SH-SPC] :rem 92<br />
10 FOR J=49152TO 49493:READ X:POKE J,X:NEXT<br />
:rem 226<br />
20 SYS 49152 :rem 102<br />
30 PRINT M{CLR}{WHT}SYS 49152 TOGGLES SPECIAL LIST<br />
ON/OFF11 jrem 201<br />
500 DATA 173,7,3,73,103,141,7,3,173,6,3,73,11,141,<br />
6,3,96,8,133,254,152 :rem 98<br />
501 DATA 72,36,15,48,8,104,168,165,254,40,76,26,16<br />
7,162,0,232,189,80 :rem 36<br />
502 DATA 192,240,240,197,254,208,246,160,0,200,185<br />
,118,192,201,91,208 :rem 67<br />
503 DATA 248,202,208,245,32,210,255,200,185,118,19<br />
2,201,93,208,245,32 :rem <strong>64</strong><br />
504 DATA 210,255,104,168,165,254,40,76,246,166,144<br />
,5,28,159,156,30,31 :rem 81<br />
505 DATA 158,18,146,129,149,150,151,152,153,154,15<br />
5,147,19,148,20,145 :rem 86<br />
506 DATA 17,157,29,32,160,255,133,137,134,138,135,<br />
139,136,140,0,91,66 :rem 78<br />
507 DATA 76,65,67,75,93,91,87,72,73,84,69,93,91,82<br />
,69,68,93,91,67,89 :rem 111<br />
177
Advanced BASIC<br />
508 DATA 65,78,93,91,80 , 85 ,82,80, 76,69,93,91,71,82<br />
,69,69,78,93,91,66<br />
:rem 101<br />
509 DATA 76,85,69,93,91 , 89,69, 76, 76,79,87,93,91,82<br />
,86,83,93,91,82,86<br />
:rem 123<br />
510 DATA 83,32,79,70,70 ,93,91,79,82,65,78,71,69,93<br />
,91,66,82,79,87,78<br />
:rem 93<br />
, 69,68,93,91,68,32,71,82,65<br />
:rem 76<br />
511 DATA 93,91,76,32,82<br />
,89,93,91,77,32,71<br />
512 DATA 82,65,89,93,91<br />
,76,32,66,76,85,69<br />
513 DATA 93,91,76,32,71<br />
,91,72,79,77,69,93<br />
514 DATA 91,73,78,83,93<br />
,91,68,79,87,78,93<br />
515 DATA 91,76,69,70,84<br />
,83,80,67,93,91,83<br />
,76,32,71,82,69,69,78,93,91<br />
:rem 96<br />
,82,65,89,93,91,67,76,82,93<br />
:rem 95<br />
, 91,68,69, 76,93,91,85 ,80,93<br />
:rem 111<br />
, 93,91,82, 73,71,72,84,93,91<br />
:rem 80<br />
516 DATA 72,45,83,80,93 ,91,80,73,93,91,70,49,93,91<br />
,70,50,93,91,70,51<br />
:rem 54<br />
517 DATA 93,91,70,52,93 , 91,70,53,93,91,70,54,93,91<br />
,70,55,93,91,70,56, 93 :rem 209<br />
Most of Program 6-15 consists of two tables, one of ASCII character values and<br />
<strong>the</strong> o<strong>the</strong>r of <strong>the</strong>ir translated form within brackets. <strong>The</strong>refore, <strong>the</strong> program can easily<br />
be modified to allow for graphics characters or to output your own alternative forms.<br />
<strong>The</strong> ASCII values of <strong>the</strong> brackets [ and ] are 91 and 93. <strong>The</strong> ASCII values and special<br />
characters of <strong>the</strong> program in its current form are listed below:<br />
Table 6-6. Legible LIST ASCII Table<br />
[BLACK]<br />
[WHITE]<br />
[RED]<br />
[CYAN]<br />
[PURPLE]<br />
[GREEN]<br />
[BLUE]<br />
[YELLOW]<br />
[RVS]<br />
[RVS/OFF]<br />
144<br />
5<br />
28<br />
159<br />
156<br />
30<br />
31<br />
158<br />
18<br />
146<br />
[ORANGE]<br />
BROWN]<br />
LRED]<br />
DGRAY]<br />
MGRAY]<br />
L GREEN]<br />
L BLUE]<br />
L GRAY]<br />
129<br />
149<br />
150<br />
151<br />
152<br />
153<br />
154<br />
155<br />
CLR]<br />
HOME]<br />
INS]<br />
DEL]<br />
UP]<br />
DOWN]<br />
LEFT]<br />
RIGHT]<br />
SPC]<br />
SH-SP]<br />
147<br />
19<br />
148<br />
20<br />
145<br />
17<br />
157<br />
29<br />
32<br />
160<br />
[PI]<br />
Fl<br />
F2:<br />
F3:<br />
F4:<br />
F5:<br />
F6<br />
F7<br />
F8:<br />
255<br />
133<br />
137<br />
134<br />
138<br />
135<br />
139<br />
136<br />
140<br />
Program 6-15, activated by SYS 49152, modifies a LIST vector to point within<br />
<strong>the</strong> special ML routine, which checks all characters in quotation marks. This part of<br />
<strong>the</strong> program is quite small. <strong>The</strong> program <strong>the</strong>n outputs <strong>the</strong> special characters in brack<br />
ets. Printers can use this program, but lines containing special characters will be<br />
longer than usual. SYS 49152 also turns off <strong>the</strong> special listing function, acting as a<br />
toggle.<br />
Chapter 8 has a short ML routine which checks for colons and is able to LIST<br />
separate statements on new lines. If you wish to modify LIST in your own way, but<br />
178
Advanced BASIC<br />
have little ML experience, Program 6-16, an outline BASIC program, will help; it<br />
reads BASIC with PEEKs and is easy to understand. Append it to BASIC when you<br />
want to use it. Add your own selection of special characters at <strong>the</strong> end of <strong>the</strong><br />
program.<br />
Program 6-16.<br />
BASIC LIST<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
63499 REM SET UP VALUES :rem 84<br />
63500 A=2049:INPUT "LOWER, UPPER LINE NUMBERS";F,T<br />
:rem 223<br />
63510 DIM T$(76):FOR K=l TO 76:READ T$(K):NEXT<br />
:rem 220<br />
63520 DEF FN DEEK(A)=PEEK(A)+256*PEEK(A+1):CM=39:R<br />
EM SET MAX LINE LENGTH :rem 84<br />
63599 REM START NEW BASIC LINE :rem 246<br />
63600 L=FN DEEK(A+2):X=FN DEEK(A):Q=0:IF X=0 OR L><br />
T THEN END : rem 61<br />
63610 IF L127 THEN PRINT T$(P-127);:CC=<br />
CC+LEN(T$(P-127)):NEXT :rem 223<br />
63760 PRINTCHR$(P);:CC=CC+1:REM CC COUNTS CHARACTE<br />
RS :rem 175<br />
63770 NEXT K :rem 145<br />
63799 REM KEYWORDS IN TOKEN ORDER :rem 2<br />
63800 DATA " END "," FOR "," NEXT "," DATA "," INP<br />
UT# "," INPUT "," DIM " :rem 51<br />
63810 DATA " READ "," LET "," GOTO "," RUN "," IF<br />
{SPACE}"," RESTORE ", " GOSUB " :rem 98<br />
63820 DATA " RETURN ", " REM "," STOP "," ON "," WA<br />
IT "," LOAD "," SAVE " :rem 44<br />
63830 DATA " VERIFY "," DEF "," POKE "," PRINT# ",<br />
11 PRINT "f" CONT "," LIST " : rem 130<br />
63840 DATA " CLR "," CMD "," SYS "," OPEN ", " CLOS<br />
E "," GET "," NEW "," TAB(" :rem 152<br />
63850 DATA " TO "," FN "," SPC("," THEN "f" NOT ",<br />
11 STEP ",+,-,*, /,T," AND " :rem 99<br />
179
Advanced BASIC<br />
63860 DATA " OR »,>,=,
Advanced BASIC<br />
Program 6-17. Disk Merge<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader/' Appendix C.<br />
59000 FOR J=40960 TO 49151:POKE J,PEEK(J):NEXT:REM<br />
PUT ROM->RAM srem 126<br />
59010 POKE 42231,56:POKE 42288,96:POKE 42585,96<br />
:rem 217<br />
59020 FOR J=830 TO 900:READ V:POKE J,V:NEXT<br />
:rem 159<br />
60000 DATA 162,8,32,198,255,32,207,255,32,207,255,<br />
165 :rem 56<br />
60010 DATA 1,41,254,133,1,160,0,32,207,255,32,207<br />
:rem 77<br />
60020 DATA 255,240,32,32,207,255,133,20,32,207,255<br />
#133 :rem 81<br />
60030 DATA 21,32,207,255,153,0,2,240,3,200,208,245<br />
:rem 127<br />
60040 DATA 152,24,105,5,168,32,162,1<strong>64</strong>,76,79,3,165<br />
:rem 165<br />
60050 DATA 1,9,1,133,1,32,89,166,76,128,1<strong>64</strong>:rem 70<br />
Use this program by entering LOAD, RUN, and NEW. Load or type a program<br />
into memory, and additional programs can be merged into it with OPEN<br />
8,8,8/TROGRAM NAME": SYS 830. Turn off <strong>the</strong> disk light with OPEN 15,8,15/T':<br />
CLOSE 15.<br />
MOD<br />
This is an arithmetic function, found in some BASICs, which calculates <strong>the</strong> remain<br />
der when one integer is divided by ano<strong>the</strong>r. MOD is an abbreviation of modulo, a<br />
ma<strong>the</strong>matical term used in number <strong>the</strong>ory. <strong>The</strong> statement, "4=19 modulo 5," means<br />
that 4 and 19 have <strong>the</strong> same remainders when divided by 5. <strong>The</strong> simplest BASIC<br />
version is DEF FN MOD(N) = N-INT(N/D)*D, where D is <strong>the</strong> divisor. This<br />
formulation may be of use when converting o<strong>the</strong>r BASICs to CBM BASIC.<br />
Examples of <strong>the</strong> use of MOD are D=12:H=FN MOD(16), which converts 16<br />
hours to 4:00, and D=256: PRINT FN MOD (50000), which prints <strong>the</strong> low byte<br />
of 50000.<br />
OLD<br />
Originally, OLD was used to restore a program which had been inadvertently re<br />
moved by NEW. However, <strong>the</strong> <strong>64</strong> offers two o<strong>the</strong>r important uses, which can be<br />
considered under <strong>the</strong> heading BASIC recovery.<br />
Program 6-18.<br />
Old<br />
10 FOR J=53000 TO 53025:READ X:POKE J,X:NEXT<br />
20 DATA 169,148,141,0,160,169,1,168,145,43,32,51,1<br />
65<br />
30 DATA 165,34,105,2,133,45,165,35,105,0,133,46,96<br />
181
Advanced BASIC<br />
OLD for program recovery. This restores BASIC because NEW leaves most of<br />
<strong>the</strong> program intact. NEW simply arranges pointers as though no program were<br />
present, and puts a zero link address at <strong>the</strong> very start of BASIC; it also sets GETCHR<br />
and <strong>the</strong> RESTORE pointers, clears variables, and closes files. Once Program 6-18 is<br />
in memory, to recover from an unwanted NEW, type SYS 53000: LIST. <strong>The</strong> program<br />
is restored, and its variables are even retained. If BASIC has not been NEWed, or<br />
even if it's running, <strong>the</strong> SYS call does no harm.<br />
OLD after chaining. When used with LOAD in a program, this restores BASIC<br />
when <strong>the</strong> new program is longer than <strong>the</strong> old one. As explained under CHAIN, in<br />
order to pass variables from one chained program to <strong>the</strong> next, <strong>the</strong> end-of-program<br />
pointers are not set. Thus, if <strong>the</strong> newly loaded program is too long, its top end will<br />
be corrupted. However, if <strong>the</strong> ML routine has been POKEd in by <strong>the</strong> loader pro<br />
gram, 0 SYS 53000:CLR at <strong>the</strong> start of <strong>the</strong> new program will prevent corruption.<br />
(CLR is needed to remove garbage after <strong>the</strong> program, which may appear as<br />
pseudodata; it is not possible to recover <strong>the</strong> overwritten variables.)<br />
OLD restores BASIC after SYS <strong>64</strong>738. Old can be used after a hardware reset<br />
has occurred, returning <strong>the</strong> machine to its startup state. Both <strong>the</strong>se routines leave<br />
BASIC RAM unaltered and in effect perform NEW, so SYS 53000 is just as effective<br />
as with NEW. Any BASIC program, including one which disables RUN/STOP and<br />
RESTORE, can be reset and recovered by this method if you have a hardware reset<br />
switch.<br />
How OLD works. OLD makes use of <strong>the</strong> ROM routine which links BASIC lines.<br />
A nonzero value is POKEd into <strong>the</strong> first link address, correcting for <strong>the</strong> zero bytes<br />
NEW inserts, and JSR $A533 relinks <strong>the</strong> lines of BASIC. <strong>The</strong> end-of-program point<br />
ers must also be reset. OLD also corrects location $A000, without which a hardware<br />
reset can corrupt BASIC if it is stored in RAM.<br />
BASIC version of OLD. A BASIC equivalent of <strong>the</strong> above is simple, but <strong>the</strong><br />
end-of-program pointers get lost and take some effort to retrieve. If <strong>the</strong> end-ofprogram<br />
isn't moved up, variables will overwrite your program when it runs.<br />
POKE PEEK(44)*256+2/l:SYS 42291:POKE 46, PEEK(56)-1:CLR<br />
This assumes that BASIC starts in one of <strong>the</strong> normal places and that <strong>the</strong> end-ofprogram<br />
pointer's position isn't critical (it becomes set to a location 256 bytes below<br />
<strong>the</strong> end of BASIC memory). <strong>The</strong> program will LIST properly.<br />
ONERR<br />
<strong>The</strong> <strong>64</strong> has an indirect vector at $300-$301 (768-769) to process error messages.<br />
Usually, this is set to $E38B and <strong>the</strong> actual error is dictated by <strong>the</strong> byte in <strong>the</strong> X reg<br />
ister. POKE 781, number from 1 to 30: SYS 42042 will print a message to <strong>the</strong> screen.<br />
ONERR usually works by specifying a line number to GOTO in <strong>the</strong> event of<br />
error. <strong>The</strong> advantage is that <strong>the</strong> program cannot crash, while <strong>the</strong> drawback is that<br />
processing ONERR properly is liable to take a lot of memory space and slow execu<br />
tion time.<br />
PAUSE<br />
<strong>The</strong>re are two versions of this command: one waits for a timed delay and <strong>the</strong> o<strong>the</strong>r<br />
temporarily freezes BASIC or ML.<br />
182
Advanced BASIC<br />
Timed delays are useful with some types of music programs. BASIC delay loops<br />
(FOR J = 1 TO 500: NEXT) work well, though <strong>the</strong> actual timing varies with <strong>the</strong><br />
stored position of <strong>the</strong> loop variable in memory. If J is set up as <strong>the</strong> first variable, this<br />
solves <strong>the</strong> problem. <strong>The</strong> <strong>64</strong>'s internal clock is ano<strong>the</strong>r obvious way to get accurate<br />
timing. <strong>The</strong> clock is stored in 160, 161, and 162, with 162 changing fastest. One<br />
short routine is POKE 162,X: WAIT 162,2tN, which has a maximum delay of<br />
128/60, or about two seconds. To explain <strong>the</strong> formula, note that WAIT stops until<br />
just one bit is set. So POKE 162,0: WAIT 162,<strong>64</strong> delays until location 162 reaches<br />
<strong>64</strong>, pausing for <strong>64</strong>/60 seconds. <strong>The</strong> timing is reasonably constant, although <strong>the</strong> first<br />
POKE could occur any time between interrupts, so <strong>the</strong>re's 1/60 second maximum<br />
difference in pauses. Unless <strong>the</strong> interrupt rate is changed, resolution below about<br />
1/60 second isn't possible. Delays longer than about two seconds require <strong>the</strong> use of<br />
location 161. POKE 161,0: POKE 162,0: WAIT 161,2 pauses for 2*256/60 seconds<br />
(or about eight seconds).<br />
<strong>The</strong> easiest implementation of a system pause (halting execution until some<br />
event occurs) is to intercept <strong>the</strong> interrupt routine and check for a keypress. <strong>The</strong><br />
SHIFT key is useful, because SHIFT/LOCK can pause indefinitely. However, any<br />
SHIFTed entry will <strong>the</strong>n temporarily stop <strong>the</strong> program. <strong>The</strong> following ML routine<br />
will do <strong>the</strong> trick with normal keys, for example, <strong>the</strong> left-arrow. You could modify <strong>the</strong><br />
program to check <strong>the</strong> keyboard twice, so <strong>the</strong> key could toggle <strong>the</strong> function on and<br />
off, or to test for <strong>Commodore</strong>, SHIFT, or CTRL keys:<br />
Send interrupt routine here: PAUSE JSR<br />
$FF9F ;SCAN KEYBOARD<br />
LDA<br />
POP<br />
CMP<br />
BEQ<br />
JMP<br />
$C5<br />
#$39<br />
PAUSE<br />
$EA31<br />
;LOOK AT KEYPRESS<br />
;CHECK FOR BACK-ARROW<br />
;PAUSE WHILE PRESSED<br />
;NORMAL IRQ ROUTINE<br />
This command discards a RETURN from <strong>the</strong> stack; this erases <strong>the</strong> effect of <strong>the</strong> pre<br />
vious GOSUB so that if RETURN is encountered, <strong>the</strong> address returned to will be that<br />
of <strong>the</strong> next-to-last GOSUB, or 7RETURN WITHOUT GOSUB will be signaled. This is<br />
useful in providing a premature escape from BASIC subroutines, which o<strong>the</strong>rwise<br />
causes problems. To explain, GOSUB causes <strong>the</strong> computer to store a return address<br />
on <strong>the</strong> stack. When <strong>the</strong> subroutine is finished, RETURN pulls this address off <strong>the</strong><br />
stack, using it to resume execution at <strong>the</strong> correct spot in your BASIC program. If you<br />
repeatedly exit a subroutine without performing a RETURN (using GOTO, for ex<br />
ample), <strong>the</strong> stack eventually fills up with unused return addresses, causing an ?OUT<br />
OF MEMORY ERROR. You can cure this problem and o<strong>the</strong>rs like it with Program 6-<br />
19 below.<br />
POP is relocatable, but this version starts at 830 and is called by SYS 830 from<br />
within a program. RUN and test with SYS 830 in direct mode; you should get a<br />
7RETURN WITHOUT GOSUB ERROR.<br />
Program 6-19.<br />
Pop<br />
10 DATA 104,104,169,255,133,74,32,138,163,201,141<br />
20 DATA 240,3,76,224,168,232,232,232,232,232,154,9<br />
6<br />
30 FOR J=830 TO 852:READ X:POKE J,X:NEXT<br />
183
Advanced BASIC<br />
POP mimics RETURN in all respects apart from <strong>the</strong> actual change in program<br />
control. With this routine in memory, use SYS 830 immediately before any pre<br />
mature exit from a subroutine.<br />
A more thorough POP, using a part of CLR, clears away all loops and sub<br />
routines within a program by resetting <strong>the</strong> stack pointer, thus deleting all evidence<br />
of FOR-NEXT and GOSUB. Variable values, DATA pointers, and so on are retained.<br />
On an abort or escape, this routine cuts through any tangle of loops and subroutines.<br />
With <strong>the</strong> <strong>64</strong>, machine language is required to perform this POP:<br />
PLA ; REMOVE SYS ADDRESS<br />
PLA<br />
JMP $A67E ; ENTER CLR TO RESET THE STACK<br />
In decimal, this looks like:<br />
10 DATA 104,104,76,126,166<br />
20 FOR J=830 TO 834: READ X: POKE J,X: NEXT: REM SYS 830 FOR THIS POP<br />
PRINT<br />
@<br />
This moves <strong>the</strong> cursor rapidly to any place on <strong>the</strong> screen, as specified by horizontal<br />
and vertical parameters (HTAB and VTAB are o<strong>the</strong>r forms of this command). Graph<br />
ics in BASIC can often be much improved with one of <strong>the</strong>se methods, in place of<br />
printing {HOME} and many cursor moves. <strong>The</strong> fastest versions contain <strong>the</strong>ir own<br />
ML routines and <strong>the</strong>refore require storage space. Slightly slower versions use ROM<br />
routines and are more convenient.<br />
To use <strong>the</strong> fast ML version below, type in <strong>the</strong> lines and run <strong>the</strong> program. SYS<br />
828,H,y takes in horizontal and vertical parameters and puts <strong>the</strong>m into <strong>the</strong> Kernal<br />
PLOT routine vectored at $FFF0.<br />
0 DATA 32,155,183,138,72,32,155,183,104,170,1<strong>64</strong>,101,24,76,240,255<br />
10 FOR J=828 TO 843: READ X: POKE J,X: NEXT<br />
Although it is no simpler to do <strong>the</strong> same routine with BASIC, POKE 781,V:<br />
POKE 782,H: POKE 783,0: SYS 65520: PRINT "HELLO!" will work.<br />
PRINT USING<br />
Some computer languages allow you to specify <strong>the</strong> format in which numbers are<br />
printed. This <strong>64</strong> program allows easy and fast output in a variety of formats,<br />
(rounded to two decimal places, or including a leading $ symbol, for example). <strong>The</strong><br />
overall length of <strong>the</strong> output (including leading spaces) is programmable, so lining up<br />
columns of figures is made simpler. Also, output can be directed to a printer.<br />
Programm 6-20. Print Using<br />
For mistake-proof ■proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
1,10,2,32,162,0,221,0,1,240,6,232,224,12,20<br />
>,24,96,169,69,32,-162 :rem 113<br />
176,90,173,-166,240,94,173,2,1,208,11,172,-<br />
l/TA AC% 1 CO O 1 1 OC • Y»ATT1 00\Q<br />
184
Advanced BASIC<br />
3 DATA 46,32,-162,172,-1<strong>64</strong>,232,136,208,252,236,-16<br />
5,176,33,172,-165,169,0 :rem 3<br />
4 DATA 153,1,1,189,0,1,201,32,208,3,169,32,234,153<br />
,0,1,202,16,6,173,-163,136 trem 114<br />
5 DATA 16,244,136,16,231,169,0,133,97,160,1,132,98<br />
,96,169,0,32,-162,144,240 :rem 106<br />
6 DATA 138,168,173,2,1,240,9,169,46,32,-162,144,2,<br />
138,168,152,170,202,16,181 :rem 156<br />
7 DATA 0,32,158,173,32,221,189,32,-148,32,30,171,9<br />
6 :rem 202<br />
30 T=49318 srem 253<br />
40 L=T-166 jrem 11<br />
50 FORJ=L TO T-l ,rem 116<br />
60 READ X%:IF X%9)*7<br />
):PRINTL$;:L=16*(L-L%):NEXT :rem 210<br />
510 RETURN :rem 118<br />
Once you enter, save, and run <strong>the</strong> program, <strong>the</strong> screen display should include this:<br />
49152 = DEC/INT FLAG<br />
49153 = OUTPUT LENGTH<br />
49154 = DEC. PLACES<br />
49155 = LEADING CHARACTERS<br />
49250 = +VE LEAD CHR<br />
SET UP NOW WITH LENGTH 11, 2 DEC. PLACES, & LEADING SPACES.<br />
185
Advanced BASIC<br />
Executing a SYS 49305(X) will print <strong>the</strong> current value of X, formatted (where<br />
possible) in accordance with <strong>the</strong> values in <strong>the</strong> five locations listed.<br />
Decimal/integer flag. A value of 0 in this location means <strong>the</strong> result will be<br />
treated as an integer (no decimal point symbol will be printed), while 1 means it is<br />
decimal.<br />
Output length. This location specifies <strong>the</strong> total length of <strong>the</strong> output string -1.<br />
It allows tables of numbers to be constructed easily.<br />
Decimal places. This controls <strong>the</strong> number of figures after <strong>the</strong> decimal point. If<br />
<strong>the</strong> number is an integer, this is ignored.<br />
Leading characters. This location holds <strong>the</strong> ASCII character printed before <strong>the</strong><br />
number begins. This enables printing in formats like ****100 or 000123,23. <strong>The</strong><br />
usual leading character is <strong>the</strong> space character (32).<br />
Positive symbol. Numerals are preceded by a space or minus sign with BASIC'S<br />
unmodified PRINT statement; this routine permits a substitute for <strong>the</strong> space charac<br />
ter to be printed (for example, $), so all positive numbers will appear preceded by<br />
<strong>the</strong> substitute character.<br />
Note that X is truncated; if you wish to round <strong>the</strong> output value to two decimal<br />
places, use SYS (7667) X + .005.<br />
Using PRINT USING. Program 6-21 prints formatted columns of figures. Lines<br />
20, 30-31, and 40 print <strong>the</strong> first, second, and third columns, respectively. Meaningful<br />
variable names should help to make <strong>the</strong> POKEs more understandable.<br />
Program 6-21. Print Using Demo<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader/' Appendix C.<br />
10 PRNT=49305:SWITCH=49152:LNGTH=49153:DECPTS=4915<br />
4:CHAR=49155:LDGCHR=49250<br />
15 FORJ=-10 TO 100 STEP 10:PRINT<br />
20 POKE SWITCH,0:POKE LNGTH,4:POKE CHAR,42:POKE LD<br />
GCHAR,42:SYS (PR) J<br />
30 POKE SWITCH,1:POKE LNGTH,7:POKE CHAR,32:POKE LD<br />
GCHAR,32<br />
31 POKE DECPTS,4: SYS (PR) l/(l+j)<br />
40 POKE LDGCHAR,ASC(W$M):POKE DECPTS,2: SYS (PR) 1<br />
00 + J<br />
50 NEXT<br />
100 REM SYS 49305 (400.00) ETC.<br />
<strong>The</strong> central piece of machine language code in this routine follows:<br />
JSR $AD9E ; INPUT AND EVALUATE A BASIC NUMERIC EXPRESSION<br />
JSR $BDDD ; CONVERT BYTE IN ACCUMULATOR 1 INTO A STRING<br />
JSR $C012 ; SPECIAL ROUTINE (ADDRESSING MAY VARY) TO PROCESS<br />
; NUMBER OUTPUT AT $100-$10C<br />
JSR $AB1E ; PRINT THE STRING USING A (LOW), Y (HIGH) POINTERS<br />
RTS ; RETURN TO BASIC WITHOUT ANY OTHER ACTION<br />
<strong>The</strong> idea is to print normally, except that <strong>the</strong> number, after being prepared for print<br />
ing as a string, is edited. Most of this is identical to <strong>the</strong> <strong>64</strong> ROM routines, but <strong>the</strong> in<br />
serted subroutine processes <strong>the</strong> number as it is held in memory just before being<br />
186
Advanced BASIC<br />
printed. <strong>The</strong> program is designed to allow relocation of ML by altering <strong>the</strong> parameter<br />
T; it can, for example, be stored at <strong>the</strong> top of BASIC. Remember to protect it from<br />
BASIC by lowering <strong>the</strong> top-of-BASIC pointer.<br />
RECONFIGURE<br />
Chapter 5 explains how BASIC configures itself on switch-on. However, <strong>the</strong>re are<br />
many ways memory can be allocated on <strong>the</strong> <strong>64</strong>. <strong>The</strong> pointers at 43 and 44, and 55<br />
and 56, show <strong>the</strong> entire BASIC area is normally $0800-$A000. To lower <strong>the</strong> top of<br />
BASIC memory to $8000, POKE 55,0: POKE 56,128. Now, CLR will reset all <strong>the</strong><br />
string pointers correctly, but stored variable values will be lost. POKE 51,0: POKE<br />
52,128: POKE 53,0: POKE 54,128: POKE 55,0: POKE 56,128 has <strong>the</strong> same effect, but<br />
retains variables, and is <strong>the</strong>refore sometimes better.<br />
Program 6-22 allows <strong>the</strong> start or end of BASIC (or both) to be changed, so that<br />
PRINT FRE(0) returns different values from usual. <strong>The</strong> screen RAM can also be<br />
moved, within IK boundaries; if it's moved to overlap BASIC, a program or its vari<br />
ables may be displayed in <strong>the</strong> screen, generally with odd side effects.<br />
Program protection methods sometimes make use of this feature. For example,<br />
you can move <strong>the</strong> screen to $C000 and write ML starting at <strong>the</strong> normal screen area<br />
of $0400. When <strong>the</strong> ML is loaded, <strong>the</strong> screen fills with what is apparently garbage,<br />
but which is necessary to run <strong>the</strong> program. This makes a program relatively safe<br />
from being copied.<br />
Ano<strong>the</strong>r use is to simulate o<strong>the</strong>r machines, mainly <strong>the</strong> VIC-20 and CBM/PET.<br />
For example, <strong>the</strong> CBM/PET simulator in <strong>the</strong> Appendices moves BASIC and <strong>the</strong><br />
screen to <strong>the</strong> CBM/PET positions and adds some o<strong>the</strong>r CBM-like features. All <strong>the</strong>se<br />
examples keep BASIC in ROM; Chapter 8 explains how BASIC in RAM can be used<br />
to reconfigure BASIC more fundamentally.<br />
Note that BASIC must have a zero byte at <strong>the</strong> position immediately before that<br />
indicated by <strong>the</strong> pointers 43 and 44. If it does not, NEW or RUN will cause a ?SYN-<br />
TAX ERROR.<br />
Program 6-22.<br />
Reconfigure<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
800 S=2049:INPUT " START OF BASIC" ;S :rem 24<br />
802 E=40960:INPUT "{3 SPACES}END OF BASIC";E<br />
:rem 123<br />
804 SC=1024:INPUT "START OF SCREEN";SC :rem 248<br />
999 VB=INT(SC/16384):VB=(NOT VB) AND 3 :rem 118<br />
1000 POKE <strong>64</strong>8,SC/256:POKE 53272,(PEEK(53272) AND 1<br />
5) OR ((SC/<strong>64</strong>) AND 240) :rem 150<br />
1002 POKE 56576,(PEEK(56576) AND 252)OR VBtrem 1<strong>64</strong><br />
1010 POKE 55,E-INT(E/256)*256 :rem 13<br />
1015 POKE 56,E/256 :rem 158<br />
1020 POKE 43,S-INT(S/256)*256 :rem 39<br />
1030 POKE 44,S/256 :rem 166<br />
1040 POKE S-1,0 :rem 1<br />
1050 PRINT "{CLR}" :rem 42<br />
187
Advanced BASIC<br />
To make BASIC start at $1200 and end at $1400, with <strong>the</strong> screen at $2000, run<br />
Program 6-22, enter 4609, 5120, and 8192 at <strong>the</strong> prompts, and <strong>the</strong>n enter NEW.<br />
Following this PRINT FRE(0) shows 509 free bytes, and POKE 8192,6: POKE<br />
55296,5 prints a green F at <strong>the</strong> home position, showing that <strong>the</strong> POKEs have worked<br />
correctly.<br />
Boots. <strong>The</strong> ability of a computer to load and run a program automatically is<br />
called booting. Some microsystems require <strong>the</strong> disk operating system (DOS) to be<br />
loaded in when <strong>the</strong> computer is turned on. <strong>The</strong> term came about because <strong>the</strong> com<br />
puter is said to be "pulling itself up by its own bootstraps." <strong>The</strong> <strong>Commodore</strong> 1541<br />
disk drive, however, uses proprietary software (<strong>the</strong> DOS is contained in <strong>the</strong> disk<br />
drive's ROM chips). And <strong>the</strong> 1541's built-in software does not autoboot. Never<strong>the</strong><br />
less, <strong>the</strong> keyboard buffer and input buffer can be used to solve this deficiency. For<br />
example, <strong>the</strong> commands can be printed to <strong>the</strong> screen and <strong>the</strong> keyboard can be filled<br />
with <strong>the</strong> characters needed to input <strong>the</strong>m; this, however, assumes that <strong>the</strong> position<br />
of screen memory doesn't change.<br />
Tape boot. When using tape, autobooting is easy. Simply press SHIFT-RUN/<br />
STOP. If you want to see how to do <strong>the</strong> same from within a program, add line 1045<br />
to Program 6-22:<br />
1045 POKE 631,131: POKE 198,1<br />
<strong>The</strong>se POKEs have <strong>the</strong> effect of typing SHIFT-RUN/STOP (<strong>the</strong> ASCII value is<br />
131). Since 198 holds <strong>the</strong> number of characters in <strong>the</strong> keyboard queue, POKEing a 1<br />
into that location simulates a single keypress. Now, <strong>the</strong> program reconfigures BASIC,<br />
loads <strong>the</strong> next tape program, and runs it.<br />
Disk boot. <strong>The</strong> keyboard queue can't easily hold much more than ten charac<br />
ters, which is insufficient to load a disk program since, unlike tape, a name is usually<br />
needed. LOAD"*",8:RUN in its short form just fits. One solution is to use <strong>the</strong> input<br />
buffer as in <strong>the</strong> following lines:<br />
61 CLR :REM NEW NOT NEEDED AT END (AS NEW PROGRAM IS TO BE LOADED)<br />
62 N$="LOAD" + CHR$(34) + "NAME" + CHR$(34) + ",8" + CHR$(0)<br />
63 FOR J = 1 TO LEN(N$): POKE 511+J, ASC (MID$(N$,J)): NEXT<br />
<strong>64</strong> POKE 198,3: POKE 631,82: POKE 632,213: POKE 633,13<br />
65 POKE 781,255: POKE 782,1 :REM POINTER TO $01FF<br />
66 SYS 42118 :REM INPUT LINE<br />
Line 62 sets up a string ending with a null byte; this exactly mimics a line input<br />
from <strong>the</strong> keyboard. Line 63 POKEs <strong>the</strong> characters into <strong>the</strong> input buffer at 512<br />
($0200). Line <strong>64</strong> puts R SHIFT-U RETURN in <strong>the</strong> keyboard buffer, to cause <strong>the</strong> pro<br />
gram to run after loading. Lines 65 and 66 process <strong>the</strong> line in <strong>the</strong> buffer, loading <strong>the</strong><br />
program called "NAME".<br />
REM<br />
REM is, of course, one of <strong>the</strong> <strong>64</strong>'s normal statements. It deserves a place here be<br />
cause of <strong>the</strong> unique status of REM statements outside <strong>the</strong> normally strict rules of<br />
BASIC syntax.<br />
REM with SHIFT and quotes. SHIFTed characters have <strong>the</strong>ir high bit set and<br />
are interpreted as tokens, so LIST converts <strong>the</strong>se into reserved words, expanding <strong>the</strong><br />
188
Advanced BASIC<br />
line. Cursor control characters, {CLR}, {HOME}, etc., can be inserted after an open<br />
ing quotation mark. {DEL} (delete) characters can be used by opening up space in<br />
side quotes with <strong>the</strong> {INST} (insert) key. A hidden line can be created by following<br />
it with :REM" ", expanding <strong>the</strong> space in quotes, and filling <strong>the</strong> space with {DEL}<br />
characters, though this maneuver won't hide <strong>the</strong> line when it's listed on a printer.<br />
You can use REM statements to produce colorful listings, too. For example, you<br />
could list <strong>the</strong> initialization section of <strong>the</strong> program in white, <strong>the</strong> main loop in yellow,<br />
and subroutines in o<strong>the</strong>r colors. This way you could find <strong>the</strong> section you wanted to<br />
view easily. To change <strong>the</strong> color of <strong>the</strong> listing, type REM " " and delete <strong>the</strong> second<br />
quotation mark, <strong>the</strong>n press {RVS} (CTRL-9) followed by SHIFT-M. Next, press<br />
{INST} (SHIFT-DEL) once, and select <strong>the</strong> color by pressing CTRL or <strong>Commodore</strong><br />
key and <strong>the</strong> correct numbered key. After this, press <strong>the</strong> RETURN key to enter <strong>the</strong><br />
line. REM stores some characters differently inside quotes than outside. Thus, util<br />
ities which search for strings may not find <strong>the</strong>m in REM statements.<br />
Inserting characters into REM statements. REM is tokenized as 143 in decimal.<br />
<strong>The</strong> following short routine puts two RETURN characters immediately after REM in<br />
a REM line, and also immediately before <strong>the</strong> end of <strong>the</strong> REM line, so 100 REM**<br />
REMINDER COMMENTS * will list remarks neatly onto new lines.<br />
63000 L=43<br />
63010 L=PEEK(L) + 256*PEEK(L+1): IF L=0 THEN ENDrREM SKIP THROUGH LINKS<br />
63020 IF PEEK(L+4)143 GOTO 63010:REM IF REM NOT FOUND TRY NEXT LINE<br />
63030 POKE L+5,13: POKE L+6,13:REM POKE TWO RETURNS<br />
63040 FOR J=L+5 TO 9E9: IF PEEK(P>0 THEN NEXT:REM FIND END-OF-LINE,<br />
63050 POKE J-1,13: GOTO 63010 :REM AND POKE ONE RETURN<br />
Inserting reverse SHIFT-M within quotes adds a SHIFT-RETURN character with<br />
a similar effect. O<strong>the</strong>r characters could include printer control characters to enhance<br />
REM statements, or color characters to list REMs in a different screen color.<br />
Using REMs to store ML. As Chapter 9 explains in detail, BASIC can hold ML<br />
within REM statements. <strong>The</strong> data can simply be POKEd in. This can be very ef<br />
ficient, but <strong>the</strong>re are two potential problems with <strong>the</strong> technique.<br />
Zeros should not be used, because <strong>the</strong>y will be treated as end-of-line markers if<br />
<strong>the</strong> program is edited, so <strong>the</strong> ML will be corrupted—a link address and line number<br />
will be inserted. This could be used, with care, as a security device. Generally, in<br />
stead of LDX #0, use LDX #1:DEX.<br />
<strong>The</strong> actual position in memory of <strong>the</strong> ML data must be known. <strong>The</strong> easiest<br />
method is to use a REM statement at <strong>the</strong> very start of <strong>the</strong> program, so <strong>the</strong> sixth byte<br />
from <strong>the</strong> initial zero byte is <strong>the</strong> start position. <strong>The</strong> ML routine must be relocatable to<br />
work with all BASIC configurations.<br />
RENUMBER<br />
Renumbering a BASIC program has some cosmetic advantages and is valuable<br />
where BASIC line numbers are too close to allow more BASIC to be added, or when<br />
a program is finished and you want to renumber by ones starting at line number 0<br />
(which causes <strong>the</strong> program to run slightly faster). Program 6-23 is a short BASIC<br />
subroutine that changes line numbers only, between a selected range, by POKEing in<br />
new values.<br />
189
Advanced BASIC<br />
Program 6-23.<br />
Renumber<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
60000 INPUT "RENUMBER FROM,TO M;L,H :rem 40<br />
60005 INPUT "START, INCREMENT ";S,I :rem 49<br />
60010 DIM L(600,2):A=2049:B=256:J=-1 :rem 94<br />
60100 J=J+l:L(J,0)=PEEK(A+2)+B*PEEK(A+3):L(J,2)=A+<br />
4 :rem 250<br />
60105 IF L(J,0)H THEN L(J,1)=L(J,0):G<br />
OTO 60120 :rem 31<br />
60110 L(J,1)=S+R*I:R=R+1:NL=L(J,1) :rem 136<br />
60115 POKE A+2,NL-INT(NL/B)*B:POKE A+3,NL/B<br />
:rem 175<br />
60120 A=PEEK(A)+B*PEEK(A+1):IF A>0 GOTO 60100<br />
:rem 128<br />
60200 FOR K=0 TO J-30:A=L(K,2) :rem 18<br />
60205 P=PEEK(A):SP=0:IF P=0 THEN NEXT K :END<br />
:rem 219<br />
60210 IFPO137ANDP138ANDPO141ANDP155ANDP167<br />
ANDP203THENA=A+1:GOTO 60205 :rem 184<br />
60300 N=0:A=A+1:P=PEEK(A):IF P=32 THEN SP=SP+1:GOT<br />
O 60300 :rem 188<br />
60305 IF P=1<strong>64</strong> GOTO 60300 :rem 231<br />
60310 IF P
v?Advanced BASIC<br />
One difficulty with renumbering is that line numbers within programs are stored<br />
as ASCII strings, so if a renumbered line is different in length, <strong>the</strong> program's length<br />
may have to be changed. Ano<strong>the</strong>r difficulty concerns syntax; Program 6-23 simply<br />
assumes correct syntax, mainly to use less space.<br />
To use "Renumber," RUN 60000. You may renumber lines 0-59999, but not<br />
above. Lines 60000-60120 of <strong>the</strong> program build an array; L(J,O) holds original line<br />
numbers, L(J,1) holds new numbers, and L(J,2) holds pointers to <strong>the</strong> start of each<br />
line. <strong>The</strong> numbers at <strong>the</strong> start of each line are renumbered at this stage. J counts <strong>the</strong><br />
number of lines in <strong>the</strong> program; not all <strong>the</strong>se are needed, of course, since RE<br />
NUMBER itself should be left alone.<br />
Lines 60200-60210 scan all <strong>the</strong> relevant program lines, searching for keyword<br />
tokens, which are processed by <strong>the</strong> lines that follow. Line 60305 looks for TO; this<br />
allows GO TO to be renumbered, not just GOTO. Spaces after a keyword are<br />
counted, allowing variation in <strong>the</strong> renumbered line number lengths. Line 60320<br />
searches for lines in <strong>the</strong> table and signals if <strong>the</strong>y're not found. Lines 60400-60415<br />
POKE in <strong>the</strong> new line number, where possible. And 60500 processes constructions<br />
like ON X GOTO 100,200 and LIST 10-30.<br />
RESET<br />
SYS <strong>64</strong>738 resets <strong>the</strong> <strong>64</strong>, giving a result similar to switching on <strong>the</strong> machine. RAM<br />
from $0 to $0400, except for <strong>the</strong> stack, is completely cleared out, and BASIC is in ef<br />
fect NEWed, but <strong>the</strong> rest of memory is untouched and BASIC can be recovered with<br />
OLD.<br />
SYS <strong>64</strong>738 is useful whenever <strong>the</strong> <strong>64</strong> has been reconfigured or pointers have<br />
been set in unusual ways. For example, after loading ML high in memory, RESET<br />
will leave it <strong>the</strong>re by return to <strong>the</strong> normal condition of BASIC on startup. When<br />
BASIC is in ROM, a hardware reset (see Chapter 5) has <strong>the</strong> same effect as this soft<br />
ware reset; o<strong>the</strong>r CBM machines behave similarly.<br />
However, if BASIC is in RAM, SYS <strong>64</strong>738 acts differently from a hardware reset<br />
and may show an unusually large number of bytes free, because <strong>the</strong> software SYS<br />
call, unlike hardware, doesn't necessarily switch BASIC into ROM, if <strong>the</strong> Kernal has<br />
been modified. Chapter 8 explains in depth.<br />
Note that some CBM publications contain a wrong SYS call for this feature.<br />
SEARCH<br />
Searching BASIC is reasonably straightforward, given an understanding of <strong>the</strong> way it<br />
is stored in memory. <strong>The</strong> following ML search hunts for a match with <strong>the</strong> contents<br />
of <strong>the</strong> first BASIC line.<br />
Program 6-24.<br />
Search<br />
Tor mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
5 FOR J=830 TO 921:READ X:POKE J,X:NEXT :rem 219<br />
10 DATA 166,43,165,44,134,251,133,252,160fl,134,25<br />
3,133,254,177,253,240 :rem 158<br />
11 DATA 73,72,136,177,253,72,160,4,132,142,132,143<br />
,177,251,201,34,208 :rem 60<br />
191
Advanced BASIC<br />
12 DATA 2,230,143,1<strong>64</strong>,143,177,251,240,28,72,1<strong>64</strong>,14<br />
2,177,253,240,15,104 :rem 107<br />
13 DATA 209,253,240,6,230,142,160,4,208,222,230,14<br />
2,208,226,104,104,170 :rem 136<br />
14 DATA 104,208,193,160,2,177,253,170,200,177,253,<br />
32,205,189,169,32,32 :rem 120<br />
15 DATA 210,255,201,0,208,231,96 :rem 25<br />
Run Program 6-24, <strong>the</strong>n enter 0 DATA and type SYS 830. All <strong>the</strong> line numbers<br />
of lines with DATA statements will list. You can search for lines containing <strong>the</strong><br />
number 240 with 0"240; 0"SYS will find SYS as a word, not as a BASIC keyword.<br />
<strong>The</strong> ML relocates, and can be moved to any free RAM area.<br />
SET<br />
SET (and UNSET) are graphics commands in some BASICs which allow a point or<br />
small square to be drawn at any specified positions on <strong>the</strong> screen. Chapter 12 has a<br />
lot of information on this, including a high-resolution plotting routine.<br />
SORT<br />
Sorting means arranging a list in order, usually alphabetically or numerically. Many<br />
sorting methods exist, but only three major ones are discussed here: two BASIC sorts<br />
and one ML sort, which includes a demonstration to illustrate <strong>the</strong> syntax. <strong>The</strong> ma<br />
chine language version is far faster than BASIC.<br />
BASIC sorts. <strong>The</strong> Shell-Metzner Sort is a fast sort, which is also easy to pro<br />
gram. <strong>The</strong> version given in Program 6-25 sorts items 1 to N of an array dimensioned<br />
with A$(N). <strong>The</strong> sort is written as a subroutine to be added to your programs, and it<br />
assumes that array A$ and number of elements N have both been established before<br />
you GOSUB to <strong>the</strong> routine. Upon return from <strong>the</strong> routine, <strong>the</strong> contents of array A$<br />
will be arranged in ascending order.<br />
Program 6-25.<br />
Shell-Metzner Sort<br />
59010 M=N<br />
59020 M=INT(M/2):IF M=0 THEN END<br />
59030 J=1:K=N-M<br />
59040 I=J<br />
59050 L=I+M<br />
59060 IP A$(I)>A$(L) THEN T$=A$ (I):A$(I)=A$(L):A$(<br />
L)=T$:I=I-M:IF I>0 THEN 59050<br />
59070 J=J+1:IF J>K THEN 59020<br />
59080 GOTO 59040<br />
<strong>The</strong> Tournament Sort, so called because it pairs toge<strong>the</strong>r items for comparison,<br />
starts to give answers almost immediately, ra<strong>the</strong>r than waiting for <strong>the</strong> entire array to<br />
be sorted. In addition, since numbers ra<strong>the</strong>r than strings are moved, garbage collec<br />
tion (which can o<strong>the</strong>rwise be a problem with BASIC) is not a factor.<br />
Program 6-26 illustrates <strong>the</strong> Tournament Sort. Lines 10 and 20 allow you to set<br />
up <strong>the</strong> array N$, which will be sorted. A numeric array, I, is also required, and it<br />
192
Advanced BASIC<br />
must be dimensioned for twice as many elements as N$. Lines 200-330 perform <strong>the</strong><br />
sort, printing each element as it is sorted into its proper position and ending when<br />
<strong>the</strong> sort is complete.<br />
Program 6-26.<br />
Tournament Sort<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader/' Appendix C.<br />
10 INPUT "SORT HOW MANY ITEMS";N:B=N-1:DIM N$(B),I<br />
(2*B) :rem 141<br />
20 FOR J=0 TO B:N$(J)=STR$(RND(1)*100):NEXT<br />
:rem 113<br />
30 PRINT "SORTING:-11 :rem 193<br />
2000 X=0:FOR J=0 TO B:I(J)=J:NEXT :rem 98<br />
2005 FOR J=0 TO 2*N-3 STEP 2:B=B+1 :rem 215<br />
2010 I(B)=I(J):IF N$(I(J+1))
Advanced BASIC<br />
6 DATA 104,165,99,133,106,165,100,133,107,240,224,<br />
240,114,24,165,106,105 :rem 198<br />
7 DATA 3,133,106,165,107,105,0,133,107,230,103,208<br />
,2,230,104,160,2,177,106 :rem 17<br />
8 DATA 153,109,0,136,16,248,160,5,177,106,153,109,<br />
0,136,192,2,208,246,170 :rem 7<br />
9 DATA 56,229,109,144,2,166,109,160,255,232,200,20<br />
2,208,8,165,112,197,109 :rem 16<br />
10 DATA 144,10,176,34,177,113,209,110,240,238,16,2<br />
6,160,2,185,112,0,145 :rem 142<br />
11 DATA 106,136,16,248,160,5,185,106,0,145,106,136<br />
,192,2,208,246,169,0,133 :rem 49<br />
12 DATA 105,165,101,197,103,208,152,165,102,197,10<br />
4,208,146,165,105,240,138,96 zrem 1<br />
100 FOR J=49152 TO 49394:READ X:POKE J,X:NEXT<br />
:rem 18<br />
110 PRINT "USE SYS 49152:X TO SORT ARRAY X$(), FOR<br />
EXAMPLE:-" :rem 163<br />
1000 INPUT "SIZE OF ARRAY";N :rem 109<br />
1010 DIM XY$(N) :rem 16<br />
1020 FOR J=l TO N: XY$(J)=STR$(RND(1)*100): NEXT<br />
:rem 66<br />
1030 PRINT "SORTING.••" :rem 69<br />
1040 SYS 49152:XY :rem 180<br />
1050 FOR J=0 TO N:PRINT XY$(J):NEXT :rem 5<br />
Program 6-27 is a version of <strong>the</strong> Bubble Sort, which operates on <strong>the</strong> pointers of<br />
string arrays and produces no garbage collection delays. It operates in direct or pro<br />
gram modes, but to save space it doesn't include a validation routine, so don't try to<br />
sort an array that does not exist.<br />
Speed is maximized if new items are added at <strong>the</strong> beginning of an array before<br />
sorting. Note that <strong>the</strong> zeroth element isn't sorted—it can hold a title if desired. If <strong>the</strong><br />
255 in line 9 is changed to 1, strings are sorted from <strong>the</strong> second position; if it is 2,<br />
sorting begins from <strong>the</strong> third, and so on.<br />
Provided spaces pad out <strong>the</strong> strings correctly, it's possible to resort an array in<br />
different ways. For an example, see <strong>the</strong> disk directory sorting program in Chapter 15,<br />
which sorts on <strong>the</strong> initial of each program or file.<br />
Strings are sorted in ASCII order. This can produce apparent anomalies: 12.3<br />
comes before 2.87, which comes before 29.67. HELLO! precedes HELLO; and strings<br />
0-25 emerge as 0, 1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 2, 20, 21, 22, 23, 24, 25,<br />
3, 4, 5, 6, 7, 8, 9. Computer sorting often produces effects like <strong>the</strong>se, but <strong>the</strong>y should<br />
not pose too much of a problem in practice.<br />
In fact, programming can often be simplified by careful choice of <strong>the</strong> way in<br />
which items to be sorted are arranged. For instance, a date held as YYMMDD auto<br />
matically sorts into <strong>the</strong> correct order. Similarly, <strong>the</strong> fact that <strong>the</strong> comma has a lower<br />
ASCII value than any letter insures that names held with commas sort correctly. Wil<br />
liams, P. will come before Williamson, A.<br />
Lines 1000-1050 in Program 6-27 provide a demonstration of <strong>the</strong> sort. Lines<br />
194
Advanced BASIC<br />
1000 and 1010 establish array XY$, and line 1020 fills <strong>the</strong> array with random nu<br />
meric characters. Line 1040 calls <strong>the</strong> ML sort routine, and 1050 prints <strong>the</strong> values to<br />
<strong>the</strong> screen. Note that you specify XY to sort array XY$—<strong>the</strong> $ is not used. If you<br />
wish to add this sorting routine to your own programs, lines 1000-1050 should not<br />
be included.<br />
TRACE with<br />
SINGLE STEP<br />
This version of TRACE displays <strong>the</strong> whole current BASIC line at <strong>the</strong> top of <strong>the</strong><br />
screen. <strong>The</strong> f 1 key toggles <strong>the</strong> trace on and off, f3 changes <strong>the</strong> speed of TRACE by<br />
accepting a number from 0 to 9 (fast), f5 executes a true single-step, and f7 traces as<br />
fast as possible through BASIC.<br />
Program 6-28.<br />
Trace<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
10 DATA 169,76,133,132,169,19,133,133,169,192,133,<br />
134,96,255,0,254,15 :rem 84<br />
11 DATA 0,252,72,138,72,152,72,173,136,2,141,148,1<br />
92,166,197,224,4,208 :rem 122<br />
12 DATA 12,228,197,240,252,173,13,192,73,255,141,1<br />
3,192,173,13,192,240 :rem 116<br />
13 DATA 38,224,5,208,61,228,197,240,252,160,0,140,<br />
14,192,132,198,32,66 :rem 117<br />
14 DATA 241,240,251,24,105,198,141,15,192,165,57,1<br />
<strong>64</strong>,58,205,16,192,208 :rem 126<br />
15 DATA 5,204,17,192,240,92,173,15,192,141,18,192,<br />
162,128,160,128,165 :rem 74<br />
16 DATA 197,201,3,240,22,201,5,240,200,173,14,192,<br />
208,162,208,74,202 :rem 250<br />
17 DATA 208,236,136,208,233,238,18,192,208,228,120<br />
,162,0,181,0,157,76 :rem 75<br />
18 DATA 193,202,208,248,162,79,169,160,157,0,4,202<br />
,208,250,32,102,229 :rem 71<br />
19 DATA 165,57,1<strong>64</strong>,58,141,16,192,140,17,192,133,20<br />
,132,21,32,207,192 :rem 18<br />
20 DATA 162,0,189,76,193,149,0,202,208,248,32,108,<br />
229,88,104,168,104 :rem 31<br />
21 DATA 170,104,76,179,227,224,6,208,137,142,14,19<br />
2,228,197,240,252,208 :rem 180<br />
22 DATA 180,32,19,166,160,1,132,15,177,95,240,67,3<br />
2,44,168,234,234,234 :rem 122<br />
23 DATA 200,177,95,170,200,177,95,197,21,208,4,228<br />
,20,240,2,176,44,132 :rem 117<br />
24 DATA 73,32,205,189,169,32,1<strong>64</strong>,73,41,127,32,71,1<br />
71,201,34,208,6,165 :rem 73<br />
25 DATA 15,73,255,133,15,200,240,17,177,95,208,16,<br />
168,177,95,170,200 :rem 30<br />
26 DATA 177,95,134,95,133,96,208,181,96,234,234,23<br />
4,234,234,16,215,201 :rem 141<br />
195
Advanced BASIC<br />
27 DATA 255,240,211,36,15,48,207,56,233,127,170,13<br />
2,73,160,255,202,240 :rem 111<br />
28 DATA 8,200,185,158,160,16,250,48,245,200,185,15<br />
8,160,48,178,32,71 srem 36<br />
29 DATA 171,208,245,96 :rem 70<br />
100 FOR J=49152 TO 49483:READ X:POKE J,X:NEXT<br />
:rem 17<br />
110 SYS 49152 :rem 150<br />
Program 6-28 puts <strong>the</strong> ML for TRACE into memory starting at $C000. Load or<br />
type in a BASIC program and run it. As stated above, whenever you press fl, <strong>the</strong><br />
trace begins; f7 traces fast, f5 single-steps, and f3 waits for a keypress from 0 to 9<br />
before continuing. At this stage f 1 will turn TRACE off, leaving BASIC running nor<br />
mally, but fl is still tested for, so tracing can be resumed at any time. SYS 58260<br />
NEWs BASIC and turns off TRACE completely; SYS 49152 reinstates it if desired.<br />
This combination of features offers maximum flexibility in examining BASIC<br />
programs.<br />
Programs with graphics may list illegibly, with some BASIC characters appear<br />
ing as graphics; and programs using <strong>the</strong> function keys, of course, may present prob<br />
lems. <strong>The</strong>se are typical difficulties in designing TRACE routines.<br />
TRACE works by wedging into BASIC. It performs various operations before<br />
returning to BASIC, which as far as possible is untouched. First, <strong>the</strong> key fl is<br />
checked, and if it's pressed, a flag is reversed. If this flag is off, <strong>the</strong> program control<br />
is returned to BASIC. If <strong>the</strong> trace flag is on, f3 is checked, and, if pressed, a number<br />
key from 0 to 9 is awaited. When <strong>the</strong> number is received, it is inserted into a delay<br />
loop. Also, f5 is tested, and if <strong>the</strong> single-step flag is on, <strong>the</strong> program loops indefi<br />
nitely waiting for f5. When this key is found, <strong>the</strong> program runs BASIC until it finds<br />
a new line number. <strong>The</strong> new line is listed on <strong>the</strong> screen and <strong>the</strong> indefinite loop reentered.<br />
If i7 is pressed, <strong>the</strong> delay loop is bypassed, so BASIC lines are listed as rap<br />
idly as possible. In this way, <strong>the</strong>re is maximum keyboard control over <strong>the</strong> trace.<br />
<strong>The</strong> program is not relocatable as it stands, but it isn't difficult for an experi<br />
enced ML programmer to move it. If you disassemble <strong>the</strong> routine, note <strong>the</strong> routine at<br />
$C083, which lists lines. This routine saves <strong>the</strong> entire zero page (so LIST can't cor<br />
rupt any locations), homes <strong>the</strong> cursor and blanks <strong>the</strong> first two lines of <strong>the</strong> screen,<br />
lists <strong>the</strong> line using a modification of LIST, and restores <strong>the</strong> zero page values and pre<br />
vious cursor position.<br />
UNLIST<br />
This system command prevents LISTing of BASIC to reduce <strong>the</strong> risk of unauthorized<br />
copying or modification. UNLISTing is successful in proportion to <strong>the</strong> difficulty of<br />
acquiring detailed knowledge of a system. No widely sold microcomputer yet has<br />
foolproof protection. Never<strong>the</strong>less, temporary and makeshift expedients may be bet<br />
ter than nothing. A collection of suggestions follows. Note that disabling<br />
RUN/STOP and RUN/STOP-RESTORE is dealt with earlier in this chapter.<br />
Machine language routine to run BASIC. This method is given first because it<br />
is usable by anyone, works with any memory configuration, saves normally, and is<br />
very puzzling to <strong>the</strong> uninitiated. It also disables RUN/STOP and RUN/STOP- RE-<br />
196
Advanced BASIC<br />
STORE, so if <strong>the</strong> program has no errors, no explicit or implicit END, and no STOP<br />
statement, it can't be stopped at all by a user with an unmodified <strong>64</strong>. BASIC runs<br />
normally but lists as 0 SYSPEEK(44)*256+23 without any fur<strong>the</strong>r lines. To use this<br />
routine, follow <strong>the</strong>se steps:<br />
1. Be sure that <strong>the</strong> program has no line numbered 0 or 1. Change <strong>the</strong> numbering if it<br />
does.<br />
2. Enter line 0, with no spaces, in exactly this way: 0SYSPEEK(44)*256+23<br />
3. Enter line 1 with exactly 21 asterisks (or any o<strong>the</strong>r character) and no spaces, like<br />
this*<br />
i*********************<br />
4. List lines 0-1 and check <strong>the</strong>m.<br />
5. Type in X=PEEK(44)*256+23. This is <strong>the</strong> starting address of <strong>the</strong> ML you will<br />
POKE in, usually 2071.<br />
6. Enter <strong>the</strong> following 24 POKEs. <strong>The</strong>y are written as a continuous string of POKEs,<br />
but only to save space. You should enter <strong>the</strong>m one by one. Check with PRINT<br />
PEEK(X) before you run. All of <strong>the</strong>m must be correct<br />
POKE X, 169: POKE X+1,45: POKE X+2,133: POKE X+3,43: POKE X+4,169<br />
POKE X+5,234: POKE X+6,141: POKE X+7,40: POKE X+8,3: POKE X+9,160<br />
POKE X+10,0: POKE X+11,169: POKE X+12,PEEK(X+22): POKE X+13,145<br />
POKE X+14,43: POKE X+15,32: POKE X+16,89: POKE X+17,166: POKE X+18,76<br />
POKE X+19,174: POKE X+20,167:POKE X-4,0: POKE X-3,0: POKE X+22,0.<br />
7. Save <strong>the</strong> program, list it, and run it to be sure that UNLIST is working correctly.<br />
Now show <strong>the</strong> result to a friendly hacker and see if he or she can list it.<br />
Simple ML run. Here's ano<strong>the</strong>r method, with an explanation of how it works.<br />
Enter a program with no line 0 or 1, and add 0SYS2063 and l********** (ten as<br />
terisks). Next, perform <strong>the</strong> following ten POKEs:<br />
POKE 2063,169: POKE 20<strong>64</strong>,26: POKE 2065,133: POKE 2066,43<br />
POKE 2067,32: POKE 2068,89: POKE 2069,166: POKE 2070,76<br />
POKE 2071,174: POKE 2072,167<br />
In addition to <strong>the</strong> above POKEs, POKE 2059,0: POKE 2060,0 to put end-of-program<br />
bytes after line 0. This lists as 0 SYS 2063. It should run as normal. <strong>The</strong> ten ML<br />
bytes disassemble in this way:<br />
$100F LDA #$1A<br />
$1011 STA $2B ; MOVES START-OF-BASIC TO THE TRUE START AFTER ML<br />
$1013 JSR $A659 ; CLR SETS POINTERS<br />
$1016 JMP $A7AE ; RUNS PROGRAM FROM START<br />
<strong>The</strong> effect is identical to POKE 43,31: RUN. All that's needed is to add some<br />
UNLIST features and disable RUN/STOP and RUN/STOP-RESTORE to get an<br />
effective UNLIST.<br />
Special characters in REM statements. Since characters in <strong>the</strong> same line after<br />
REM don't affect a program's performance, <strong>the</strong>re is plenty of scope for POKEing in<br />
or o<strong>the</strong>rwise entering confusing characters. See <strong>the</strong> discussion of REM earlier in this<br />
section for some simple ideas.<br />
Five leading tokens method. This method, once considered for commercial use,<br />
causes a program's line numbers to LIST, but nothing else. It is easy to use. Add five<br />
colons (or any five characters or tokens) at <strong>the</strong> start of every line of BASIC. <strong>The</strong>n<br />
197
Advanced BASIC<br />
add <strong>the</strong>se lines to <strong>the</strong> program, choosing your own line numbers if 50000 to 50002<br />
are already taken:<br />
50000:::::S=PEEK(43)+256*PEEK(44): FOR J=l TO 9999<br />
50001:::::IF PEEK(S+4)>0 THEN POKE S+4,0:S=PEEK(S)+256*PEEK(S+l): NEXT<br />
50002:::::END<br />
RUN 50000 will put null bytes into <strong>the</strong> start of each line. Upon trying to LIST, you<br />
should see a set of line numbers and nothing else—but <strong>the</strong> program should work<br />
fine. Next, simply delete lines 50000-50002 and <strong>the</strong> process is complete.<br />
<strong>The</strong> following lines can put <strong>the</strong> colons back, so <strong>the</strong> lines will LIST again:<br />
S=PEEK(43)+256*PEEK(44)<br />
FOR J=l TO 1E8: POKE S+4,58: S=PEEK(S)+256*PEEK(S+1): IF S THEN NEXT.<br />
With this method, about <strong>the</strong> best you can hope for is that users of your pro<br />
grams haven't read this book. You can also set traps, like using :::NEW: or ::::X<br />
before a variable, ra<strong>the</strong>r than five colons, before UNLISTing <strong>the</strong> program. If <strong>the</strong> pro<br />
gram is made listable again but <strong>the</strong>se entries pass unnoticed, <strong>the</strong> program will be<br />
NEWed on running, or variable A may be mysteriously converted into XA.<br />
Overlong lines. All of a line that is longer than about 250 characters cannot be<br />
LISTed. LIST expects each line to be pointed to by a single-byte pointer and will<br />
loop indefinitely if <strong>the</strong> line is longer. However, some o<strong>the</strong>r commands, like READ,<br />
also fail to work.<br />
To combine lines, replace <strong>the</strong> null byte at <strong>the</strong> end of each line with a colon (ex<br />
cept <strong>the</strong> last one in <strong>the</strong> group), <strong>the</strong>n move <strong>the</strong> lines down in memory to overwrite<br />
<strong>the</strong> link addresses and line numbers. <strong>The</strong> very first link of <strong>the</strong> series must be set to<br />
span <strong>the</strong> completed giant line, and all <strong>the</strong> later link addresses (which are now<br />
wrong) must be corrected.<br />
If <strong>the</strong> idea interests you, put <strong>the</strong> following routine at <strong>the</strong> beginning of a program<br />
and run it. Type in two line numbers; when <strong>the</strong> program has finished <strong>the</strong>y'll be<br />
joined toge<strong>the</strong>r. Each line number is printed as its line joins onto <strong>the</strong> first line se<br />
lected; this ends up as a composite line, so <strong>the</strong> lines listed on <strong>the</strong> screen disappear<br />
from <strong>the</strong> program.<br />
Program 6-29.<br />
Combine Lines<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C<br />
0 INPUT "COMBINE LINES";L,U trem 132<br />
1 DEP FN DEEK(C)=PEEK(C) + 256*PEEK(C+1):C=FN DEEK<br />
(43):E=FN DEEK(45)-4 :rem 225<br />
2 LT=FN DEEK(C+2) :rem 173<br />
3 IF LTL THEN PRINT "LINE NOT FOUND":END : rem 147<br />
5 S=C:C=C+4 :rem 100<br />
6 IF PEEK(C)>0 THEN C=C+l:GOTO 6 :rem 227<br />
7 IF PEEK(C+2)=0 GOTO 13 :rem 230<br />
8 LT=FN DEEK(C+3):IF LT
Advanced BASIC<br />
11 IF PEEK(C)>0 THEN C=C+l:GOTO 11 :rem 59<br />
12 IP PEEK(C+2)>0 GOTO 10 :rerii 16<br />
13 PRINTC+3:C=C+1:POKE S,C-INT(C/256)*256:POKE S+l<br />
#C/256:CLR:END :rem 212<br />
When this program is run, line numbers are printed, as is a value (see line 13)<br />
which is <strong>the</strong> new, lower end-of-BASIC. It isn't necessary to POKE this in, but if you<br />
wish to save memory, you can do so. If, for example, 4567 is printed, type in POKE<br />
45, 4567 AND 255: POKE 46,4567/256:CLR. Be sure to type it correctly; o<strong>the</strong>rwise,<br />
<strong>the</strong>re will be problems. Incorrectly linked BASIC benaves in odd ways and may<br />
refuse to accept new lines or delete old ones. Remember not to include lines ref<br />
erenced by GOTO or GOSUB, or lines with IF statements or REM statements, which<br />
will cause later parts of <strong>the</strong> newly joined line to be bypassed.<br />
Self-modifying BASIC. If a program has only a few GOTOs and GOSUBs, this<br />
is an excellent way to get simple list protection. LIST needs a correct link address for<br />
each line of <strong>the</strong> BASIC program. However, RUN doesn't, except to process GOSUB<br />
or to GOTO a lower destination line than <strong>the</strong> current one (10000 GOTO 100).<br />
You can make use of this to get ano<strong>the</strong>r type of UNLIST. Type in some lines of<br />
BASIC, PRINT PEEK(2049), and write down <strong>the</strong> value, <strong>the</strong>n POKE 2049,255 or some<br />
o<strong>the</strong>r random value. LIST will probably show garbage, but RUN should be satisfac<br />
tory. Before a GOTO or GOSUB of <strong>the</strong> sort just described, you'll need to POKE 2049<br />
with <strong>the</strong> correct value for <strong>the</strong> program, <strong>the</strong>n afterward POKE in <strong>the</strong> wrong value<br />
again.<br />
VARPTR<br />
VARPTR finds <strong>the</strong> location of any variable stored in RAM. Its main use is to investi<br />
gate variables, exactly as in <strong>the</strong> first part of this chapter. Program 6-30 loads a ma<br />
chine language routine which will find <strong>the</strong> starting location of a variable name,<br />
whe<strong>the</strong>r simple or subscripted. To be conveniently usable with BASIC, it uses ROM<br />
routines not only to find <strong>the</strong> variable, but (with LET) to assign <strong>the</strong> resulting address<br />
to ano<strong>the</strong>r variable.<br />
Program 6-30.<br />
VARPTR<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
100 DATA 32,115,0,32,139,176,1<strong>64</strong>,95,165,96,32<br />
:rem 168<br />
110 DATA 145,179,32,115,0,32,139,176,133,73,132<br />
:rem 2<br />
120 DATA 74,165,14,72,165,13,72,76,186,169 :rem 35<br />
130 FOR J=830 TO 861:READ X:POKE J,X:NEXT :rem 61<br />
After this is typed in and run, to put <strong>the</strong> ML into memory, <strong>the</strong> syntax SYS<br />
828:AB$:L (for example) assigns to variable L <strong>the</strong> value of <strong>the</strong> address where AB$'s<br />
seven-byte description starts in memory. Below is an example that finds and prints<br />
<strong>the</strong> value of X.<br />
199
Advanced BASIC<br />
200 N=123<br />
210 SYS 830:N:X<br />
220 FOR J=X to X+6: PRINT PEEK(J);: NEXT<br />
<strong>The</strong>se lines print <strong>the</strong> seven bytes which store X. In <strong>the</strong> same way, pointers or<br />
any string can be found, and so on. (Note that arrays move if new simple variables<br />
are defined; if you're investigating arrays, be sure not to add variables after VARPTR<br />
has found <strong>the</strong> current array position.)<br />
This routine can't find TI, TI$, or ST, which are not stored as conventional vari<br />
ables. <strong>The</strong> machine language for <strong>the</strong> VARPTR routine follows this flow:<br />
JSR<br />
JSR<br />
LDY<br />
LDA<br />
JSR<br />
JSR<br />
JSR<br />
STA<br />
STY<br />
LDA<br />
PHA<br />
LDA<br />
PHA<br />
$0073<br />
$B08B<br />
$5F<br />
$60<br />
$B391<br />
$0073<br />
$B08B<br />
$46<br />
$47<br />
$08<br />
$07<br />
; JSR CHRGET (IGNORES SEPARATING COLON)<br />
; WITH JSR PTRGET FINDS THE VARIABLE<br />
; CONVERTS POINTER BYTES TO FLOATING-POINT<br />
: IGNORES COLON<br />
; FINDS SECOND VARIABLE<br />
TWO ENTRIES ON STACK NEEDED<br />
TO ASSIGN VALUE TO VARIABLE<br />
JMP $A9BA ; EXIT THROUGH LET<br />
200
Chapter 7<br />
6510 Machine<br />
Language<br />
• Introduction to 6510 ML<br />
<strong>Programming</strong><br />
• Description of <strong>the</strong> 6510 Chip<br />
• 6510 ML Techniques<br />
• Monitors for <strong>the</strong> <strong>64</strong><br />
• Monitor Command Dictionary<br />
• Assemblers for <strong>the</strong> <strong>64</strong>
Chapter 7<br />
6510 Machine Language<br />
Machine language (ML) programming is often considered more difficult than<br />
programming in BASIC, but by <strong>the</strong> end of this chapter you should have a good<br />
grasp of ML techniques on <strong>the</strong> <strong>64</strong>. This chapter assumes familiarity with hexadeci<br />
mal notation (explained in Chapter 5) and that you have an ML monitor program<br />
available. Readers without a monitor may type in Supermon from <strong>the</strong> Appendix.<br />
Note that Chapter 10 is a complete reference guide to all 6510 commands and con<br />
tains examples which can help you write your own ML programs.<br />
This chapter contains actual examples to teach you <strong>the</strong> simple techniques, a<br />
description of <strong>the</strong> 6510 microprocessor, a list of problem-solving techniques, and dis<br />
cussions of monitors (notably Supermon) and assemblers.<br />
Introduction to 6510 ML <strong>Programming</strong><br />
This section presents some short ML programs, using only <strong>the</strong> simplest instructions.<br />
Each example should be entered with a monitor. Supermon uses a fairly standard for<br />
mat, and <strong>the</strong> examples presented here use <strong>the</strong> Supermon syntax. At this stage, only<br />
four monitor commands will be discussed: A (Assemble) for writing ML programs<br />
using <strong>the</strong> mnemonic instruction set; D (Disassemble) to decode ML bytes so <strong>the</strong>y ap<br />
pear as <strong>the</strong>y did during assembly; M (Memory display), which displays <strong>the</strong> contents<br />
of consecutive bytes; and G(Go) which executes <strong>the</strong> program, much as RUN exe<br />
cutes BASIC programs in memory. Additional monitor commands are discussed later<br />
in this chapter.<br />
Most of <strong>the</strong> demonstration ML routines end with BRK. This is fine with<br />
Supermon and o<strong>the</strong>r ML monitors, because <strong>the</strong> JBRKJinstruction returns control to <strong>the</strong><br />
monitor program. SYS calls from BASIC usuallyena with KlTT because RTS returns<br />
control to BASIC. <strong>The</strong>refore, if you call any of <strong>the</strong>se routines from BASIC, remember h<br />
replace BRK with RTS.<br />
<strong>The</strong>se programs put characters into <strong>the</strong> screen memory, so <strong>the</strong> effect of each<br />
program is instantly visible; direct feedback like this is helpful in learning. <strong>The</strong> <strong>64</strong><br />
has a movable screen memory, but <strong>the</strong>se programs assume that <strong>the</strong> normal $0400<br />
starting place applies. Color RAM starts at $D800.<br />
Example 1. POKEing a Single Character to <strong>the</strong> Screen<br />
Load and run Supermon (or your favorite monitor). <strong>The</strong> microprocessor's registers<br />
will be displayed (don't worry about <strong>the</strong>m for now) on <strong>the</strong> line above a period fol<br />
lowed by a cursor. <strong>The</strong> period is a prompt showing that <strong>the</strong> monitor is waiting for<br />
you to proceed.<br />
Type in <strong>the</strong> ML program below exactly as shown, using ei<strong>the</strong>r method. <strong>The</strong> two<br />
forms of <strong>the</strong> program are exact equivalents; <strong>the</strong>y are just different ways of showing<br />
<strong>the</strong> same information. For example, <strong>the</strong> byte $A9 is always treated as <strong>the</strong> LDA com<br />
mand by <strong>the</strong> 6510, and <strong>the</strong> D, or disassemble, command simply expands $A9 into<br />
LDA whenever it finds it in <strong>the</strong> right place, similar to <strong>the</strong> way that BASIC'S LIST ex<br />
pands one-byte tokens into keywords.<br />
<strong>The</strong> A command lets you enter <strong>the</strong> program using mnemonics. Simply type A<br />
followed by a space, <strong>the</strong>n <strong>the</strong> address where you want <strong>the</strong> program to start, and<br />
203
6510 Machine Language<br />
<strong>the</strong>n <strong>the</strong> first instruction. After you press RETURN, <strong>the</strong> monitor will print <strong>the</strong> next<br />
free memory location for you.<br />
.A C000 LDA #$00 *S<br />
.A C002 STA $0400 C~<br />
.A COOS BRK —><br />
Press RETURN twice after typing BRK, to return to <strong>the</strong> period prompt. You can enter<br />
<strong>the</strong> same program by typing a colon, followed by eight hexadecimal values.<br />
.:C000 A9 00 8D 00 04 00 —any—<br />
This puts <strong>the</strong> designated values into <strong>the</strong> eight memory locations from $C000 to<br />
C007. Ano<strong>the</strong>r way to do this is with <strong>the</strong> M command. Type M C000 C007 to dis<br />
play <strong>the</strong> contents of those addresses, <strong>the</strong>n cursor over and type in <strong>the</strong> new value for<br />
each byte.<br />
You'll find that .D C000 C005 disassembles <strong>the</strong> bytes, translating <strong>the</strong> contents of<br />
memory back into mnemonics. At left is <strong>the</strong> address where each instruction starts; to<br />
<strong>the</strong> right are <strong>the</strong> hexadecimal values which make up <strong>the</strong> instruction, and finally <strong>the</strong><br />
mnemonic. <strong>The</strong> Supermon D command always prints an entire screen of disassembly;<br />
o<strong>the</strong>r monitors may display only <strong>the</strong> specified range of addresses.<br />
Note that, looking at <strong>the</strong> six bytes of <strong>the</strong> program, <strong>the</strong> screen start $0400 is held<br />
with <strong>the</strong> low byte first and <strong>the</strong> high byte second—with 00 preceding 04. This feature<br />
is common to all three-byte commands of <strong>the</strong> 6510 and o<strong>the</strong>r 6500 series chips.<br />
.G C000 executes this short program, <strong>the</strong>n returns to Supermon. Its effect is to<br />
print an @ symbol in <strong>the</strong> top left of <strong>the</strong> screen, unless <strong>the</strong> screen scrolls and loses it<br />
or unless <strong>the</strong>re was no character <strong>the</strong>re already, so color RAM is <strong>the</strong> background<br />
color, making <strong>the</strong> @ invisible.<br />
This is an easy program to understand, since $0400 is <strong>the</strong> first screen position,<br />
and POKEing 0 to <strong>the</strong> screen generates <strong>the</strong> @ symbol. In fact, we can read <strong>the</strong> ML<br />
like this: Load <strong>the</strong> accumulator with 0 (<strong>the</strong> number zero), store <strong>the</strong> byte in <strong>the</strong> accu<br />
mulator in $0400, <strong>the</strong>n BRK (break) to return to Supermon. <strong>The</strong> accumulator is an<br />
eight-bit location, and it can be loaded with any value $00-$FF; essentially, it is a<br />
one-byte buffer. <strong>The</strong> above example, <strong>the</strong>refore, has <strong>the</strong> same effect as POKEing<br />
$0400 with 0, using <strong>the</strong> BASIC command POKE 1024,0.<br />
Here's ano<strong>the</strong>r idea. If we cursor-up and alter <strong>the</strong> first line to LDA #$01, <strong>the</strong>n G<br />
C000 has <strong>the</strong> effect of POKEing a 1 into <strong>the</strong> screen top, so <strong>the</strong> letter A appears. To<br />
make this change in <strong>the</strong> Supermon disassembly, type over <strong>the</strong> value 00 shown in <strong>the</strong><br />
middle of <strong>the</strong> screen, to <strong>the</strong> left of <strong>the</strong> mnemonic. O<strong>the</strong>r monitors may let you<br />
change <strong>the</strong> value in <strong>the</strong> mnemonic field. You can now put any character into any<br />
screen location, after a certain amount of calculation to determine <strong>the</strong> address, and<br />
with <strong>the</strong> screen POKE value from <strong>the</strong> Appendices.<br />
From BASIC, FOR J=49152 TO 49157 PRINT PEEK(J): NEXT prints <strong>the</strong> six<br />
bytes of ML in decimal form, much like Supermon's M command. ML programs can<br />
be POKEd into memory as well, and Chapter 9 includes a program which converts<br />
ML into BASIC DATA statements for that purpose.<br />
To illustrate <strong>the</strong> fact that BASIC can POKE in and use ML programs, enter:<br />
.A C005 RTS<br />
(press RETURN twice)<br />
.X<br />
204
6510 Machine Language<br />
<strong>The</strong> X command allows you to leave <strong>the</strong> monitor. Now that you are in BASIC, enter:<br />
FOR J=l to 255: POKE 49153J: SYS 49152: NEXT<br />
This prints all 256 characters in quick succession at <strong>the</strong> top left of <strong>the</strong> screen. Each<br />
loop alters <strong>the</strong> ML program, <strong>the</strong>n executes it in its new form. To disassemble <strong>the</strong><br />
program in its final form, enter:<br />
SYS 8<br />
.D C000<br />
and you will see this:<br />
C000 LDA #$FF<br />
C002 STA $0400<br />
C005 RTS<br />
This illustrates how <strong>the</strong> second byte of <strong>the</strong> six in <strong>the</strong> sequence contains $FF, or 255,<br />
<strong>the</strong> last value we POKEd in from BASIC. Note that your program is no longer <strong>the</strong><br />
same one that you first typed in. Beginners are ordinarily discouraged from writing<br />
self-modifying programs (which change <strong>the</strong>ir own instructions as <strong>the</strong>y run), because<br />
<strong>the</strong>y can be confusing and difficult to debug. Until you have gained more expe<br />
rience, it is probably best to avoid self-modifying code.<br />
Example 2. POKEing a Character with Its Color<br />
We'll now POKE a character to <strong>the</strong> screen, and also POKE a byte into <strong>the</strong><br />
corresponding position in color RAM. With Supermon, enter <strong>the</strong> following code (omit<br />
everything from <strong>the</strong> semicolon to <strong>the</strong> end of each line—<strong>the</strong>se are comments to help<br />
you understand <strong>the</strong> commands):<br />
.A C000 LDA #$00 ;LOAD ACCUMULATOR WITH 0<br />
.A C002 STA $0400 ;STORE ACCUMULATOR IN SCREEN<br />
.A C005 STA $D800 ;STORE ACCUMULATOR IN COLOR RAM<br />
.A C008 BRK ;BREAK, BACK TO SUPERMON<br />
This nine-byte program will disassemble with .D C000 C009 into exactly <strong>the</strong><br />
same form; try this to confirm that it was entered correctly. Entering .M C000 C009<br />
gives this (<strong>the</strong> hyphens represent bytes that don't matter):<br />
.:C000 A9 00 8D 00 04 8D 00 D8<br />
.:C008 00<br />
.G C000 executes <strong>the</strong> program; @ appears, at <strong>the</strong> top left; it is black because 0<br />
indicates black in <strong>the</strong> color RAM. Cursor up and replace LDA #$00 with LDA #$02.<br />
Now a red B will appear when you enter .G C000.<br />
Example 3.<br />
Using an Index<br />
This section introduces <strong>the</strong> X register and shows how to use it as an index. X is an<br />
eight-bit register like <strong>the</strong> accumulator (a one-byte buffer, of sorts), and <strong>the</strong> instruc<br />
tion TAX (Transfer Accumulator to <strong>the</strong> X register) simply copies <strong>the</strong> byte in A into X.<br />
<strong>The</strong> special notation:<br />
$0400,X<br />
refers not just to address $0400, but to address $0400 plus <strong>the</strong> value of <strong>the</strong> byte in <strong>the</strong><br />
205
6510 Machine Language<br />
X register. That is, <strong>the</strong> eight-bit value contained in X is added to <strong>the</strong> sixteen-bit ad<br />
dress $0400, and <strong>the</strong> result is <strong>the</strong> address used in <strong>the</strong> command. Since X has eight<br />
bits, <strong>the</strong> range of addresses must be within $0400 to $04FF in <strong>the</strong> example, with<br />
similar figures applying to <strong>the</strong> color RAM area.<br />
.A<br />
.A<br />
.A<br />
.A<br />
.A<br />
.A<br />
cooo<br />
C002<br />
C003<br />
C006<br />
C008<br />
C008<br />
LDA<br />
TAX<br />
STA<br />
LDA<br />
STA<br />
BRK<br />
#$00<br />
$0400,X<br />
#$00<br />
$D800,X<br />
;LOAD A WITH 0<br />
;TRANSFER A TO X<br />
;STORE ACCUMULATOR IN SCREEN<br />
;LOAD A WITH 0<br />
;STORE A IN COLOR RAM + X<br />
;BREAK<br />
Now .G COOO prints @ in black, exactly like <strong>the</strong> previous program. <strong>The</strong> dif<br />
ference only appears on cursoring up, and altering LDA #$00 to LDA #$05, for ex<br />
ample. Executing this prints E in black in <strong>the</strong> fifth screen position past <strong>the</strong> @<br />
symbol. And any value in place of $00 prints a character offset from <strong>the</strong> screen start.<br />
Change BRK to RTS, type X to exit to BASIC, and enter:<br />
FOR J=0 TO 255: POKE 49153J: SYS 49152: NEXT<br />
This prints all 256 characters consecutively in black, filling <strong>the</strong> top part of <strong>the</strong> screen<br />
and showing clearly how <strong>the</strong> index, X, operates. POKE 49159 with ano<strong>the</strong>r color<br />
value, say, 2 for red, to watch <strong>the</strong> effect of <strong>the</strong> ML at $C006.<br />
Example 4. Loops with ML<br />
We've just used BASIC to cause a FOR-NEXT loop and we can do <strong>the</strong> same in ML.<br />
Exactly as in BASIC, we need a counter to check <strong>the</strong> number of loops, plus a test for<br />
<strong>the</strong> end of <strong>the</strong> loop. <strong>The</strong> example shows a standard way of doing this with <strong>the</strong> 6510,<br />
which has increment, decrement, and branching instructions. Type in <strong>the</strong> following<br />
ML program using your monitor:<br />
+ X<br />
.A<br />
.A<br />
.A<br />
.A<br />
.A<br />
.A<br />
.A<br />
.A<br />
COOO<br />
C002<br />
C003<br />
C006<br />
C008<br />
C00B<br />
cooc<br />
C00E<br />
LDX<br />
TXA<br />
STA<br />
LDA<br />
STA<br />
INX<br />
BNE<br />
BRK<br />
#$00<br />
$0400,X<br />
#$02<br />
$D800,X<br />
$C002<br />
;LOAD X REGISTER WITH 0<br />
;TRANSFER X TO A (HAPPENS 256 TIMES IN<br />
;LOOP)<br />
;STORE A IN SCREEN START + OFFSET X<br />
;SET COLOR RED<br />
;STORE COLOR IN COLOR RAM + OFFSET X<br />
INCREMENT X REGISTER<br />
;BRANCH IF X NOT EQUAL TO 0<br />
;BREAK WHEN X CYCLES THROUGH TO 0<br />
With this ML in memory, .G COOO prints 256 characters in red in <strong>the</strong> top half of<br />
<strong>the</strong> screen; it does this far faster than <strong>the</strong> equivalent BASIC version in Example 3,<br />
taking about 1/200 second.<br />
First, X is loaded with 0 and this is copied into A. (<strong>The</strong> TXA transfer uses one<br />
fewer bytes than LDA #$00.) Using TXA insures that <strong>the</strong> offset X corresponds to <strong>the</strong><br />
character in A so that after <strong>the</strong> branch at $C00C, which is taken 255 times, <strong>the</strong> value<br />
in <strong>the</strong> accumulator depends on <strong>the</strong> value in <strong>the</strong> X register. This shortcut depends on<br />
<strong>the</strong> use of INX (INcrement X) to increase <strong>the</strong> value of <strong>the</strong> byte in <strong>the</strong> X register by<br />
one. Note that <strong>the</strong> accumulator (A) value stored in screen memory cycles through<br />
$00-$FF, but <strong>the</strong> A value stored in color RAM is always $02, so <strong>the</strong> color of each<br />
206
6510 Machine Language<br />
character stays constant. To understand this program fully, note <strong>the</strong> values in A and<br />
X at each stage of <strong>the</strong> program; X increases until it is as large as eight bits can con<br />
tain, at which point it increments from $FF to $00, while A alternates between <strong>the</strong><br />
identical, increasing, value of X and $02. (Incrementing an eight-bit register or mem<br />
ory location past $FF flips <strong>the</strong> value back to $00; similarly, decrementing below $00<br />
gives you $FF.)<br />
At <strong>the</strong> point that <strong>the</strong> X register holds a value of 0, <strong>the</strong> program stops looping<br />
back and executes <strong>the</strong> BRK instruction. This is because of <strong>the</strong> BNE (Branch if Not<br />
Equal to zero) instruction. As long as X contained a nonzero byte, <strong>the</strong> program<br />
branched back to <strong>the</strong> code at $C002. As soon as <strong>the</strong> value flips over to 0, no branch<br />
occurs and <strong>the</strong> next instruction is executed.<br />
Note that <strong>the</strong> branch command starting at $C00C occupies only two bytes, in<br />
spite of looking as though it would take three bytes. It uses relative addressing,<br />
meaning that if <strong>the</strong> branch is taken, execution resumes at <strong>the</strong> address of <strong>the</strong> follow<br />
ing command plus <strong>the</strong> byte just after <strong>the</strong> branch command. <strong>The</strong> example adds <strong>the</strong><br />
offset value of $F4 to <strong>the</strong> address $C00E (it treats $F4 as negative, or -$0C, since<br />
$F4 + $0C = $00 in a single-byte register). Since $C00E-$0C is $C002, it all<br />
works fine. Don't worry if this arithmetic looks confusing; <strong>the</strong> monitor will calculate<br />
<strong>the</strong> right offset value for you, as soon as you enter <strong>the</strong> destination address for <strong>the</strong><br />
branch. Note, however, that such branch commands can reach only 127 bytes for<br />
ward or 128 back.<br />
Example 5. Comparisons and Subroutines in ML<br />
Just as SYS calls can run an ML program as a subroutine, provided <strong>the</strong> RTS com<br />
mand ends <strong>the</strong> ML, you can call ML subroutines from your own ML using <strong>the</strong> JSR<br />
(Jump to SubRoutine) instruction. RTS is <strong>the</strong>refore analogous to RETURN in BASIC,<br />
and JSR is similar to GOSUB. Add <strong>the</strong> following program steps to Example 4:<br />
.A<br />
.A<br />
.A<br />
.A<br />
.A<br />
.A<br />
.A<br />
C00E<br />
C00F<br />
C012<br />
C015<br />
C018<br />
C01A<br />
C01C<br />
RTS<br />
JSR<br />
INC<br />
LDA<br />
CMP<br />
BNE<br />
BRK<br />
$cooo<br />
$C007<br />
$C007<br />
#$10<br />
$C00F<br />
;CHANGE BRK TO RTS<br />
;CALL LOOP IN EXAMPLE 4 AS A SUBROUTINE<br />
INCREMENT THE COLOR IN EXAMPLE 4<br />
;LOAD A WITH THE NEW COLOR<br />
;COMPARE THE NEW COLOR WITH 16<br />
;BRANCH IF NOT EQUAL TO 16<br />
;BREAK WHEN COLOR = 16<br />
Now, .G C00F runs Example 4, cycling through <strong>the</strong> colors until <strong>the</strong> last color<br />
(light gray) is reached. Because <strong>the</strong> subroutine is changed by this program, ,G C00F<br />
behaves differently <strong>the</strong> second time. However, <strong>the</strong> point is that, like BASIC, sub<br />
routines provide a powerful means of dividing programs into manageable chunks.<br />
CMP (CoMPare) tests <strong>the</strong> byte in <strong>the</strong> accumulator with $10 (decimal 16), and if <strong>the</strong><br />
two are equal, a special flag called <strong>the</strong> zero flag is set. <strong>The</strong> BNE that follows checks<br />
that flag, so if <strong>the</strong> value in <strong>the</strong> accumulator is not $10, <strong>the</strong> branch takes effect.<br />
Comparisons can be followed by o<strong>the</strong>r branches than BNE or BEQ (Branch if EQual<br />
to zero—if <strong>the</strong> zero flag is clear); <strong>the</strong> illustrations here are used for simplicity.<br />
Because of <strong>the</strong> speed of ML, <strong>the</strong> colors on <strong>the</strong> screen are changed too fast to be<br />
visible. As an exercise, you could add a delay loop after C00F JSR $C000, using up<br />
time without performing significant processing work. Use <strong>the</strong> X and Y registers; Y is<br />
207
6510 Machine Language<br />
ano<strong>the</strong>r eight-bit register in <strong>the</strong> 6510. Construct a loop within a loop, and use DEX<br />
(DEcrement X) and DEY (DEcrement Y), each followed by BNE, so that X decrements<br />
256 times for each decrement of Y. Remember that RUN/STOP-RESTORE generally<br />
returns you to BASIC if your program doesn't work.<br />
Description<br />
of <strong>the</strong> 6510 Chip<br />
This section describes <strong>the</strong> 6510 microprocessor by looking at addressing modes; <strong>the</strong><br />
statusjrgfflStelJU!^ jgfflrStelJU!^ <strong>the</strong> program pg counter, , zero page, pg, and ad<br />
stack; NMI, RESEX and IRQ vectors;ancfopcodes. tfd <strong>The</strong> opcodes d (hi (machine language<br />
instructions) are introduced last because <strong>the</strong>ir use depends on prior knowledge of <strong>the</strong><br />
o<strong>the</strong>r 6510 features. Chapter 10 has an annotated guide to all <strong>the</strong> opcodes; and <strong>the</strong><br />
Appendices have comprehensive tables, giving concise information on <strong>the</strong> 6510 for<br />
experienced ML programmers.<br />
<strong>The</strong> 6510 has 13 addressing modes. Most are easy to understand, but a few are more<br />
difficult. Disassembly treats a given byte in <strong>the</strong> same way every time, once it has<br />
determined <strong>the</strong> byte is an instruction; 8D rrt/y is always treated as STA yyxx. In<br />
o<strong>the</strong>r words, this is implicit in <strong>the</strong> chip? Whenever 8D is encountered as an instruc<br />
tion, <strong>the</strong> following pair of bytes is considered to be an address in low/high byte or<br />
der. A disassembler <strong>the</strong>refore prints STA in place of 8D and follows it with a 16-bit<br />
address.<br />
Most addressing modes process <strong>the</strong> contents of memory locations, ra<strong>the</strong>r than<br />
using explicit numeric values. This is invaluable in dealing with RAM and ROM<br />
where <strong>the</strong> processor often is mainly concerned with arranging blocks of RAM. For<br />
instance, in <strong>the</strong> short programs above, we changed <strong>the</strong> contents of memory locations<br />
beginning at $0400.<br />
All 6510 instr^tions are^ej<strong>the</strong>r oner two, qy thre^ bytes long. <strong>The</strong> following dis<br />
cussion examines each type.<br />
Single-byte instructions. Single-byte instructions cannot reference ei<strong>the</strong>r ad<br />
dress or data, and operate only on features within <strong>the</strong> 6510 chip itself. <strong>The</strong> phrase<br />
addressing mode doesn't really apply since <strong>the</strong>re is no address, but for consistency<br />
<strong>the</strong>se instructions are described as possessing implied addressing (<strong>the</strong> address can be<br />
thought of as an eight-bit location in <strong>the</strong> processor itself). Instructions which shift or<br />
rotate bits in <strong>the</strong> accumulator, like ASL (Arithmetic Shift Left), are sometimes said to<br />
use accumulator addressing. Never<strong>the</strong>less, you may encounter monitors which re<br />
quire ASL A ra<strong>the</strong>r than just ASL.<br />
Two-byte instructions. <strong>The</strong>se instructions consist of an instruction followed by<br />
a single byte. If this byte is treated as data, <strong>the</strong> instruction uses immediate mode.<br />
This is usually indicated by a number sign (#) before <strong>the</strong> data (see <strong>the</strong> examples<br />
above). Apart from loading <strong>the</strong> accumulator or X and Y registers with a value, this<br />
addressing mode is used in arithmetic operations, logical operations, and<br />
comparisons.<br />
All o<strong>the</strong>r two-byte instructions refer to addresses, not data. <strong>The</strong>re are six dif<br />
ferent types. You have already used one of <strong>the</strong>m, branches, in <strong>the</strong> previous section.<br />
That addressing mode is usually called relative, because <strong>the</strong> offset indicates a<br />
destination address relative to <strong>the</strong> current address.<br />
208
6510 Machine Language<br />
Zero page instructions. Five of <strong>the</strong> two-byte modes use zero page addressing. <strong>The</strong><br />
zero page is not a feature of <strong>the</strong> chip itself; it is <strong>the</strong> section of RAM in <strong>the</strong> <strong>64</strong> which<br />
is wired to addresses $0000-^00FR However, <strong>the</strong> chip has <strong>the</strong> facility of enabling<br />
<strong>the</strong> most significant byte"tobelgnored (since it is a zero anyway), so that LDA $34<br />
can be written in place of LDA $0034, for example. This saves a byte, which short<br />
ens programs and increases execution speed. For this reason, <strong>the</strong> first 256 bytes are<br />
usually in great demand in 6510 programs, and machine language routines which<br />
coexist with BASIC must be careful to take into account BASIC'S use of <strong>the</strong>se<br />
locations.<br />
In <strong>the</strong> simplest type, <strong>the</strong> second byte specifies <strong>the</strong> address in zero page. For ex<br />
ample, LDAJf^i^Q^^ Sf ^ddressi ,$O,QSfj^r l°cat|on<br />
$55may .hold anyvahieiftom^ $06l^"$F^Noj^ne ditterence"'betwe'^''mi{S and <strong>the</strong><br />
^h^LBA^S^^^ih loads Jbhe value j^j^^*<br />
monsburceToi programMffi^ Bugs tor beginners!<br />
- J.LPJ*8e indexed by X. LDA $A0,X loads into <strong>the</strong> accumulator <strong>the</strong> value in <strong>the</strong><br />
address' calculal:ed"by"an^ing $A0 to <strong>the</strong> contents of <strong>the</strong> X register. Note that <strong>the</strong> to<br />
tal of $A0+X is itself treated as a zero page address; if <strong>the</strong>re is overflow, it is ig<br />
nored. For example, if X holds $60, $A0+$60 is treated as $00, not $0100, and <strong>the</strong><br />
contents of address 0 are loaded into <strong>the</strong> accumulator.<br />
Zero ffflffg indexed fey y This is exactly analogous to <strong>the</strong> previous mode, but <strong>the</strong><br />
chip is designed so that only two instructions can use this mode (LDX and STX).<br />
LDX,Y is an example.<br />
Indexed indirect^An example of this type of instruction is LDA ($00,X). <strong>The</strong><br />
paren<strong>the</strong>ses indicate^that <strong>the</strong> accumulator is loaded from an indirect' address. That is,<br />
<strong>the</strong> quantity in paren<strong>the</strong>ses specifies <strong>the</strong> address of <strong>the</strong> first of two consecutive zero<br />
page bytes which form <strong>the</strong> address from which <strong>the</strong> data is taken. Let's assume for<br />
<strong>the</strong> moment that X contains 0, to simplify matters. In effect, LDA ($00,X) would <strong>the</strong>n<br />
be equivalent to LDA ($00), since zero plus zero equals zero.<br />
Suppose <strong>the</strong> first four bytes in zero page are 01 80 84 02. <strong>The</strong> instruction LDA<br />
($00) would be expected to load <strong>the</strong> accumulator from <strong>the</strong> address it finds in <strong>the</strong><br />
bytes in locations $00 and $01, in this case $8001. So <strong>the</strong> instruction, in this in<br />
stance, would have <strong>the</strong> same effect as LDA $8001.<br />
However, such pure zero page indirect addressing is not available on <strong>the</strong> 6510;<br />
you must use an index as well. Indexed indirect addressing, as <strong>the</strong> name implies, al<br />
lows indexing of <strong>the</strong> indirect address. Thus, if X has <strong>the</strong> value $02, <strong>the</strong>n LDA ($00,X)<br />
has <strong>the</strong> effect of loading <strong>the</strong> accumulator from <strong>the</strong> indirect address of $00 + $02, or<br />
($02). If <strong>the</strong> bytes in locations $02 and $03 are 84 02, <strong>the</strong> equivalent of LDA $0284<br />
is executed. <strong>The</strong> instruction is useful when X is set to $00, as pure indirect address<br />
ing of <strong>the</strong> zero page, or when you want to access a table of pointers in <strong>the</strong> zero<br />
page. <strong>The</strong> pointers to <strong>the</strong> start and end of BASIC program and variable space pro<br />
vide an example. This instruction is not uniform with respect to <strong>the</strong> X and Y reg<br />
isters; see STY in Chapter 10 for additional information.<br />
h^E^LB^^- An example of this type of addressing is LDA ($00),Y. As with<br />
<strong>the</strong> pre^ousmode, <strong>the</strong> address in paren<strong>the</strong>ses specifies <strong>the</strong> location of <strong>the</strong> first of<br />
two consecutive bytes which toge<strong>the</strong>r form an address. However, this mode is post-<br />
209
6510 Machine Language<br />
indexed by Y; that is, first <strong>the</strong> indirect address is calculated, <strong>the</strong>n <strong>the</strong> value in <strong>the</strong> Y<br />
register is added, and <strong>the</strong> resulting address is <strong>the</strong> object of <strong>the</strong> processing.<br />
To show how this works, suppose again that <strong>the</strong> four bytes at <strong>the</strong> very start of<br />
RAM contain 01 80 08 24. Now, LDA ($00),Y loads from $8001 + Y, so <strong>the</strong> 256<br />
bytes from $8001 to $8100 can all be accessed, depending on Y's value.<br />
Indirect indexing can be done only with <strong>the</strong> Y register. It's used for pure indirect<br />
addressing when Y is $00, for such purposes as following <strong>the</strong> link pointers from one<br />
BASIC line to <strong>the</strong> next; it is also used for processing blocks of data which aren't in<br />
<strong>the</strong> zero page.<br />
»<strong>The</strong> difference between indexed indirect and indirect indexed can be confusing<br />
affirst. Put simply, indexed, jndirect—LDA ($00,X)—is often used to access a vector<br />
table (a series of indirect addresses which point to special locations). By changing <strong>the</strong><br />
value of X, you can pick different two-byte addresses from <strong>the</strong> table, and use <strong>the</strong>m<br />
in processing.<br />
In^aggygdoced—LDA ($00),Y—is a far more useful addressing mode; it lets<br />
you access any memory location from $0000 to $FFFF. Typically, you will place <strong>the</strong><br />
desired base address in two free zero page locations, and index from <strong>the</strong>re. To use a<br />
common example, suppose that you have loaded locations $FB and $FC with 00 04.<br />
Your base address is $0400, <strong>the</strong> first byte of screen memory. When <strong>the</strong> Y register<br />
contains zero, LDA ($FB),Y loads <strong>the</strong> accumulator with <strong>the</strong> contents of $0400. If Y is<br />
$01, STA ($FB),Y stores tfie accumulator contents at $0401, and so on.<br />
Three-byte instructions. Three-byte instructions in <strong>the</strong> 6510 always consist of<br />
an instruction iolll6w5ci'By"a two-byte address. <strong>The</strong>re ar^mi^njgtjiyreta^nnsinf <strong>the</strong><br />
address: absolute,)absolute indexed by X| absolute indexed by Yf ancTabsolute<br />
This mode is a simple reference to a two-byte address, as in LDA<br />
$8000 or LDA $0012.<br />
V^^SimAm^^' ^e contents of X are added to <strong>the</strong> base address to give<br />
<strong>the</strong>" actual re^rencecfaddress. Thus, if X holds $50, LDA $8000,X loads <strong>the</strong> accu<br />
mulator with <strong>the</strong> contents of $8050. As with zero page indexing, <strong>the</strong> maximum value<br />
cannot exceed <strong>the</strong> legitimate address range of $0000-$FFFF, so LDA $FFF0,X—when<br />
X holds $11—loads <strong>the</strong> accumulator from $0001, not from <strong>the</strong> nonexistent $10001.<br />
lexed by Y. This is exactly analogous to <strong>the</strong> previous mode, except<br />
that iffTinH^eff^SrLDA $8000,Y is an example.<br />
Absolute iji^iect. <strong>The</strong> 6510 has only one instruction with this mode, namely,<br />
JMP (fuMP). An indirect jump transfers <strong>the</strong> program's flow of control to a new ad<br />
dress; this address is found from <strong>the</strong> contents of <strong>the</strong> address indicated by <strong>the</strong> indirect<br />
instruction. Suppose once more that <strong>the</strong> first four bytes in zero page contain <strong>the</strong> val<br />
ues 01 80 84 02. In that case, JMP ($0000) has <strong>the</strong> same effect as JMP $8001; JMP<br />
($0001) jumps to $8480; and so on. This instruction is useful when a table of ad<br />
dresses (like <strong>the</strong> three vectors at <strong>the</strong> top of memory) exists in a block. For example,<br />
<strong>the</strong> RESET vector at $FFFC-$FFFD can be called by JMP ($FFFC). This addressing<br />
mode is not often used, partly because of a bug in <strong>the</strong> 6502 series chips. If <strong>the</strong> in<br />
direct jump address is located on a page boundary—for example, JMP ($80FF)—pro<br />
gram flow will be transferred to an erroneous address.<br />
210
6510 Machine Language<br />
<strong>The</strong> Status Register<br />
<strong>The</strong> status register (or processor status register), denotedjs^SR in Supermon, is ano<strong>the</strong>r<br />
eight-bit register. It contains seven individuaTswtusDitsTorTT^Tanof which<br />
are automatically controlled by <strong>the</strong> 6510 chip as ML programs run. Bit 5 of <strong>the</strong> reg<br />
ister isn't used and is permanently set at 1. Table 7-1 lists all possible bit-patterns for<br />
<strong>the</strong> status register. Note that values of 0, 1, 4, 5, 8, 9, C, or D are not possible in <strong>the</strong><br />
high nybble, since bit 5 is always set to 1. This means that <strong>the</strong> value in <strong>the</strong> status<br />
register will always be at least $20 (32), even when all flags are clear. For example, if<br />
<strong>the</strong> register contains $32, <strong>the</strong>n B (<strong>the</strong> break flag) is set and Z (<strong>the</strong> zero result flag) is<br />
set. <strong>The</strong>se flags don't change unless altered by an instruction. For example, D (<strong>the</strong><br />
decimal mode flag) typically remains off through all BASIC programs.<br />
Table 7-1.<br />
<strong>64</strong> Status Register<br />
7<br />
6<br />
5<br />
4<br />
3<br />
2<br />
1<br />
0<br />
N.<br />
V<br />
1<br />
B<br />
D<br />
I<br />
Z<br />
c<br />
High Nybble<br />
Low Nybble<br />
2<br />
1<br />
0<br />
3<br />
1<br />
B<br />
1<br />
c<br />
6<br />
V<br />
1<br />
2<br />
Z<br />
7<br />
V<br />
1<br />
B<br />
3<br />
Z<br />
c<br />
A<br />
N<br />
1<br />
4<br />
I<br />
B<br />
N<br />
1<br />
B<br />
5<br />
I<br />
c<br />
E<br />
F<br />
N<br />
N<br />
V<br />
V<br />
1<br />
1<br />
B<br />
6<br />
7<br />
I<br />
I<br />
z<br />
z<br />
c<br />
8<br />
9<br />
D<br />
D<br />
c<br />
A<br />
D<br />
z<br />
B<br />
D<br />
z<br />
c<br />
C<br />
D<br />
I<br />
D<br />
D<br />
I<br />
c<br />
E<br />
D<br />
I<br />
z<br />
F<br />
D<br />
I<br />
z<br />
c<br />
211
6510 Machine Language<br />
Chapter 10 shows which flags are affected by each instruction. LDA, for in<br />
stance, affects <strong>the</strong> N (negative) and Z flags, but no o<strong>the</strong>rs. This process is automatic;<br />
it's part of LDA and happens even if you don't need it to. However, a few instruc<br />
tions are specifically for setting or clearing flags: CLC (CLear Carry) clears <strong>the</strong> C<br />
(carry) flag to 0, and SEC (SEt Carry) sets C to 1.<br />
<strong>The</strong> logic behind <strong>the</strong> use of flags can be difficult to follow at first. <strong>The</strong> V (over<br />
flow) and N flags are tricky, while Z and I (<strong>the</strong> interrupt disable flag) are much<br />
simpler. With practice, <strong>the</strong> programmer should find <strong>the</strong>m easy enough or at least be<br />
able to avoid <strong>the</strong> awkward ones. For instance, V is seldom used.<br />
<strong>The</strong> N, or negative, flag (bit 7 of SR) is a direct copy of bit 7 of <strong>the</strong> result of<br />
some o<strong>the</strong>r operation. Thus, LDA #$D3 loads $D3 into <strong>the</strong> accumulator, and since<br />
$D3 is hexadecimal shorthand for binary 1101 0011 (which has bit 7 high), N is<br />
turned on by this instruction. Some hardware ports are wired up to bit 7, so LDA<br />
from <strong>the</strong> location sets or clears N to reflect <strong>the</strong> status of bit 7. N is used along with<br />
BMI (Branch on Minus) or BPL (Branch on PLus), <strong>the</strong> branches being taken if N is 1<br />
or 0, respectively. This special concept of negative is part of twos complement<br />
arithmetic, which is discussed below.<br />
<strong>The</strong> V, or internal overflow, flag (bit 6 of SR) is seldom used. Like N, it's re<br />
lated to twos complement arithmetic and indicates typically that two numbers added<br />
toge<strong>the</strong>r give a result outside <strong>the</strong> acceptable range. See below.<br />
<strong>The</strong> 1 flag (bit 5 of SR) is unused. Since it is always set to 1, it is referred to in<br />
this book as <strong>the</strong> 1 flag.<br />
<strong>The</strong> B, or break, flag (bit 4 of SR) is usually set only when a BRK instruction is<br />
encountered. Its purpose is to enable a BRK instruction to be distinguished from an<br />
interrupt, since both jump to <strong>the</strong> same address. <strong>The</strong> address is fixed in ROM. This is<br />
a hardware feature of <strong>the</strong> 6510, discussed in greater detail later.<br />
<strong>The</strong> D, or decimal calculation mode, flag (bit 3 of SR) changes <strong>the</strong> way <strong>the</strong><br />
processor handles bytes in general and selects <strong>the</strong> 6510's binary coded decimal<br />
(BCD) mode of addition and subtraction, instead of <strong>the</strong> usual binary. <strong>The</strong> results re<br />
semble ordinary decimal arithmetic. This concept is not a simple one. As an illustra<br />
tion, consider adding 35 to 97. In hex, <strong>the</strong> result is $CC; in decimal mode, it is 32<br />
with <strong>the</strong> carry flag set, identical to <strong>the</strong> normal decimal outcome. <strong>The</strong> 6510 automati<br />
cally adds 6 to ei<strong>the</strong>r nybble if a result exceeds 9. For more on BCD representation<br />
of numbers, see Mapping <strong>the</strong> <strong>Commodore</strong> <strong>64</strong> and <strong>The</strong> Second Book of Machine Language<br />
from COMPUTE! Publications.<br />
<strong>The</strong> I, or interrupt disable, flag (bit 2 of SR), when set with SEI (SEt Interrupt<br />
flag), prevents any IRQ interrupts from taking place—remember, this is <strong>the</strong> interrupt<br />
disable flag. Chapter 8 explains <strong>the</strong>se interrupts, with examples, but due to <strong>the</strong>ir im<br />
portance in handling <strong>the</strong> keyboard, <strong>the</strong>y are mentioned in o<strong>the</strong>r places as well. <strong>The</strong><br />
main reason for disabling interrupts is to prevent <strong>the</strong>m from disturbing ML routines<br />
which won't work properly if interrupted; for example, you would not want an inter<br />
rupt to occur while you were changing <strong>the</strong> interrupt vector to point to your own ML<br />
routine. CLI clears this flag.<br />
<strong>The</strong> Z, or zero result, flag (bit 1 of SR) is set by most of <strong>the</strong> instructions which<br />
set N. To derive Z, all eight bits of a result are ORed toge<strong>the</strong>r; if this process gives a<br />
value of zero, <strong>the</strong> Z bit is set to show a zero result. O<strong>the</strong>rwise, when <strong>the</strong> result is non<br />
zero, Z is zero. <strong>The</strong> notes to BEQ and BNE in Chapter 10 expand on this.<br />
212
6510 Machine Language<br />
<strong>The</strong> C, or carry, flag (bit 0 of SR) is primarily of use in addition or subtraction,<br />
where its function is similar to <strong>the</strong> carry in addition, which denotes overflow from a<br />
column of figures to a more significant column (it is used as a borrow flag in subtrac<br />
tion). BCC, BCS, CLC, and SEC are o<strong>the</strong>r instructions involving this flag; <strong>the</strong>y often<br />
follow a comparison (CMP) instruction.<br />
<strong>The</strong> Program Counter, Zero Page, and Stack<br />
<strong>The</strong> program counter (PC) is a 16-bit register within <strong>the</strong> chip that records <strong>the</strong> ad<br />
dress of <strong>the</strong> current instruction being executed. <strong>The</strong> register can't be accessed di<br />
rectly. A BRK or interrupt causes <strong>the</strong> PC value to be saved on <strong>the</strong> stack, as does a<br />
JSR. Thus, <strong>the</strong> value of <strong>the</strong> PC can be determined after BRK, which is how Supermon<br />
records <strong>the</strong> PC. Machine branch and jump instructions operate by loading new val<br />
ues into <strong>the</strong> PC, <strong>the</strong>reby transferring control to some new program location.<br />
<strong>The</strong> zero page, as you have seen, is <strong>the</strong> section of memory from $00 to $FF. Be<br />
cause many 6510 instructions can use zero page addressing modes, which are faster<br />
and shorter than absolute addressing, this region is <strong>the</strong> most important area of RAM.<br />
A page is a section of 256 (28) bytes—<strong>the</strong> area that can be indexed by a single-byte<br />
reference—and <strong>the</strong> 6510 can address 256 pages.<br />
<strong>The</strong> stack is a part-RAM, part-hardware feature of <strong>the</strong> 6510. It uses page 1 of<br />
RAM, from $100 to $1FF, and it can be difficult to understand for several reasons.<br />
First, although page 1 is used by <strong>the</strong> processor as <strong>the</strong> stack, it also doubles as normal<br />
RAM. Second, instructions like PHA (PusH Accumulator onto stack) and its opposite<br />
PLA (PuLl a byte from <strong>the</strong> stack into <strong>the</strong> Accumulator), which are used for tem<br />
porary storage purposes, work in a fairly complex way, adding new bytes to <strong>the</strong><br />
lower end of <strong>the</strong> stack and recovering old bytes from <strong>the</strong> lower end, under <strong>the</strong> control<br />
of ano<strong>the</strong>r 8-bit register, <strong>the</strong> stack pointer. <strong>The</strong> process is explained in Chapter 10.<br />
Note that ano<strong>the</strong>r complementary pair of instructions, PHP and PLP, operates<br />
on <strong>the</strong> processor status register, allowing it to be stored and examined at will. Four<br />
o<strong>the</strong>r instructions use <strong>the</strong> stack: JSR flump to SubRoutine), its converse RTS (ReTurn<br />
from Subroutine), RTI (ReTurn from Interrupt), and BRK. <strong>The</strong> stack pointer can be<br />
read or reset by copying values to or from <strong>the</strong> X register, using <strong>the</strong> TSX (Transfer<br />
Stack pointer to X register) or TXS (Transfer X register to Stack pointer) instructions,<br />
respectively (see Chapter 10).<br />
NMI, RESET, and IRQ Vectors<br />
<strong>The</strong> 6510 has a group of reserved addresses, defined in hardware, at <strong>the</strong> top of its<br />
addressing area. <strong>The</strong> top of memory is <strong>the</strong>refore invariably ROM. Whenever <strong>the</strong><br />
NMI, RESET, or IRQ pin of <strong>the</strong> 6510 is grounded, <strong>the</strong> processor sets <strong>the</strong> program<br />
counter to <strong>the</strong> address in location $FFFA-$FFFB, $FFFC-$FFFD, or $FFFE-$FFFF,<br />
respectively. For example, when <strong>the</strong>"1531s turned on, after a short delay, <strong>the</strong> RESET<br />
line of its 6510 is held low, causing <strong>the</strong> processor to look at locations $FFFC and<br />
$FFFD for <strong>the</strong> address for <strong>the</strong> standard power-up sequence. If you check <strong>the</strong>se ad<br />
dresses with PRINT PEEK(65532)+256*PEEK(65533), you'll see that <strong>the</strong> <strong>the</strong> <strong>64</strong>'s<br />
ROM reset routine begins at location <strong>64</strong>738 ($FCE2), as discussed in Chapter 5.<br />
<strong>The</strong> RESTORE key uses a Non-Maskable Interrupt vector, so NMI can be pro<br />
grammed. RESET is valuable in program recovery, to restore programs which have<br />
213
6510 Machine Language<br />
crashed in o<strong>the</strong>rwise infinite loops. IRQ is used by <strong>the</strong> <strong>64</strong> to read <strong>the</strong> keyboard,<br />
among o<strong>the</strong>r things. Chapters 5, 6, and 8 discuss <strong>the</strong> software side of <strong>the</strong>se hardware<br />
features.<br />
6510 Instructions and Opcodes<br />
An opcode (operation code) is a single-byte value that instructs <strong>the</strong> microprocessor to<br />
perform a particular action. Since humans find it easier to deal with letters ra<strong>the</strong>r<br />
than binary digits, <strong>the</strong> opcodes are usually represented by mnemonics, character<br />
representations intended to make machine language relatively easy to read. All 6510<br />
opcodes are three letters long, which makes for neat assembly and disassembly<br />
listings.<br />
Although <strong>the</strong> mnemonics are standard, <strong>the</strong>re is nothing to stop you from coming<br />
up with your own. This may in fact be helpful as a learning aid, although it would<br />
be unorthodox.<br />
<strong>The</strong>re are 56 distinct instruction types (and hence 56 standard mnemonics),<br />
some with one addressing mode, some with as many as eight, for a total of 151 valid<br />
opcodes. <strong>The</strong>y can be grouped by function, as shown below.<br />
Add/subtract. ADC^ (ADd withn Carry) and SBC (SuBtr^ are<br />
<strong>the</strong> 6510's arithmetic hincfions. "iiotn""aaaition ancl subtrac&on are'cam^a'ouron all<br />
eight bits, using <strong>the</strong> carry flag (C) for overflow. Twos complement arithmetic is not<br />
used, but flags are present which enable it to be implemented. A binary coded decimaHBCD)<br />
arithmetic mode is also available.<br />
^jpf <strong>The</strong> 6510 has eight branch instructions, all conditional on <strong>the</strong> status<br />
ofa flag and all having a single-byte, twos-complement offset. <strong>The</strong> instructions are<br />
JJCCdJ£§ !&E and BEC^/BPL and BML BVC and BVS, and <strong>the</strong> branch is taken<br />
if <strong>the</strong> C, Z7N, orv flag is clear or set, respectively.<br />
a"SBBH—<br />
**#* Break. <strong>The</strong> BRK instruction causes an unconditional jump to <strong>the</strong> address in<br />
locations SFFFE-IH^FFF? having first saved both <strong>the</strong> program counter and <strong>the</strong> status<br />
register on <strong>the</strong> stack.<br />
^ Comparisons. CP)(fmfP\fi^^ £fyfff make it possible to compare <strong>the</strong> contents of<br />
X, Y, and A (<strong>the</strong> accuifltilator) with data or with bytes in memory. <strong>The</strong> byte is sub<br />
tracted from X, Y, or A, and flags are set, without changing <strong>the</strong> value in <strong>the</strong> register.<br />
N, Z, and C are set, so a comparison may be followed by any branch (except BVC or<br />
BVS) to test <strong>the</strong> comparison.<br />
Data transfers. Data can be loaded into <strong>the</strong> 6510 from RAM or ROM by LDA,<br />
LDX, or LDY; it can be stored in RAM by STA, STX, or STY. <strong>The</strong>se few instructions<br />
are extended in power by being equipped with a large number of addressing modes.<br />
Decrements/increments. <strong>The</strong>se alter X, Y, or memory locations by subtracting<br />
or adding one bit, setting N and Z according to <strong>the</strong> result. <strong>The</strong> instructions are DEX,<br />
DEY, DEC, and INX, INY, INC. <strong>The</strong>re is no instruction that directly increments or<br />
decrements <strong>the</strong> accumulator; however, you can use ADC or SBC to add or subtract a<br />
value from A.<br />
Flag clear/set. <strong>The</strong>se enable some status register flags to be altered at will.<br />
CLC, CLD, CLI, and CLV clear flags C, D, I, and V; SEC, SED, and SEI set flags C,<br />
D, and I.<br />
214
6510 Machine Language<br />
Jumps. JMP acts like GOTO in BASIC. JSR acts like GOSUB, with RTS <strong>the</strong><br />
equivalent of RETURN. JSR pushes <strong>the</strong> current address plus two onto <strong>the</strong> stack, for<br />
use when <strong>the</strong> subroutine is finished.<br />
Logical operations. AND, EOR (Exclusive-OR <strong>the</strong> byte in <strong>the</strong> accumulator), and<br />
ORA (OR <strong>the</strong> byte in <strong>the</strong> accumulator) perform binary logical operations on <strong>the</strong><br />
accumulator using immediate data or a byte in a specified memory location, retain<br />
ing <strong>the</strong> result in <strong>the</strong> accumulator, and setting <strong>the</strong> N and Z flags. <strong>The</strong> BIT instruction<br />
sets <strong>the</strong> Z flag just as AND would, but does not affect <strong>the</strong> contents of <strong>the</strong> accu<br />
mulator; it also copies <strong>the</strong> sixth and seventh bits of <strong>the</strong> tested value into <strong>the</strong> V and N<br />
flags.<br />
No operation. NOP does nothing but take space. It is useful for testing, because,<br />
for example, JSR instructions can be masked by inserting NOPs over <strong>the</strong> JSR and <strong>the</strong><br />
two subsequent address bytes.<br />
Return. RTS returns to <strong>the</strong> instruction following JSR by pulling <strong>the</strong> stored return<br />
address off <strong>the</strong> stack, and transferring program control to <strong>the</strong> next byte after <strong>the</strong> ad<br />
dress. This has <strong>the</strong> effect of jumping to <strong>the</strong> instruction which follows <strong>the</strong> two-byte<br />
address after JSR. RTI jumps to <strong>the</strong> address on <strong>the</strong> stack and also loads <strong>the</strong> status<br />
register from <strong>the</strong> stack.<br />
Rotate/shift. ROL (ROtate Left) and ROR (ROtate Right) act on <strong>the</strong> accumulator<br />
and <strong>the</strong> C (carry) flag (a nine-bit rotation). For example, an ROL causes all bits in <strong>the</strong><br />
accumulator to move one position to <strong>the</strong> left; <strong>the</strong> leftmost bit (bit 7) is pushed into<br />
<strong>the</strong> carry flag, and <strong>the</strong> old contents of <strong>the</strong> carry flag wrap around into <strong>the</strong> rightmost<br />
bit of <strong>the</strong> accumulator (bit 0). ASL (Arithmetic Shift Left) and LSR (Logical Shift<br />
Right) also involve <strong>the</strong> accumulator and C (but do not rotate C) so that bit 0 with<br />
ASL and bit 7 with LSR ^re always set to 0. Flags N, Z, and C are set.<br />
Stack operations. <strong>The</strong>se are PHA, PHP, PLA, and PLP and are explicit opera<br />
tions on <strong>the</strong> stack, but BRK, JSR, RTS, and RTI also use <strong>the</strong> stack. TSX and TXS<br />
allow <strong>the</strong> stack pointer to be found and set, respectively.<br />
Transfers between registers. Six instructions allow transfers between any two<br />
registers Y, A, X, and S. <strong>The</strong> opcodes are TYA. and TAY, TAX and TXA, and TXS and<br />
TSX.<br />
Note: Not all of a 6510 machine language program consists of instructions;<br />
tables of data are a common, and necessary, feature, and <strong>the</strong>se can usually be identi<br />
fied by <strong>the</strong> fact that <strong>the</strong>y don't disassemble sensibly. Chapter 5 explains about such<br />
tables. BASIC ROM starts with tables, including address tables (that is, tables of 16-<br />
bit numbers), BASIC keywords, and BASIC messages.<br />
Timing<br />
All opcodes take a precise number of 6510 clock cycles; <strong>the</strong> faster <strong>the</strong> clock, <strong>the</strong><br />
faster <strong>the</strong> ML executes. <strong>The</strong> <strong>64</strong>'s chip runs at about one million cycles per second.<br />
Table 7-2 summarizes timing in <strong>the</strong> 6510; most instructions are included in <strong>the</strong> first<br />
column, but a few exceptional instructions are listed in <strong>the</strong> o<strong>the</strong>r columns.<br />
215
6510 Machine Language<br />
Table 7-2.<br />
6510 Timing Reference Chart<br />
Stack PH=3, PL<br />
RTS=6 RTI=6<br />
BRK=7<br />
2 (if no branch)<br />
3 (if branch taken<br />
+ lover page)<br />
In practice, it is difficult to time long programs by timing individual instructions,<br />
since <strong>the</strong>re are too many instructions to count. But it's helpful in speeding up slow<br />
ML routines, when you're trying to optimize functions like updating a screen full of<br />
information.<br />
6510 ML Techniques<br />
This section uses assembler-style listings in <strong>the</strong> examples. See <strong>the</strong> section on assem<br />
blers for information. <strong>The</strong> following topics are discussed:<br />
• Two-Byte Operations<br />
• Testing <strong>the</strong> Range of a Byte<br />
• Loops<br />
• Shift and Rotate Instructions<br />
• Logical Instructions<br />
• Twos Complement Arithmetic<br />
• Decimal Arithmetic<br />
• Debugging ML Programs<br />
216
6510 Machine Language<br />
Two-Byte Operations<br />
Incrementing two bytes. <strong>The</strong> best method to increment two bytes is illustrated<br />
by <strong>the</strong> following routine:<br />
CONT ...<br />
INC LOBYTE<br />
BNE CONT ;BRANCH UNLESS $FF JUST BECAME $00<br />
INC HIBYTE ;ONLY NEEDED WHEN LOBYTE NOW IS $00<br />
Decrementing two bytes. This is not as simple as incrementing, since <strong>the</strong>re's no<br />
test for decrement from #$00 to #$FF. However, <strong>the</strong> following routine will do it:<br />
LDA LOBYTE<br />
BNE DECL ;BRANCH UNLESS LOBYTE IS 00<br />
DEC HIBYTE ;ONLY NEEDED WHEN LOBYTE WAS 00<br />
DECL DEC LOBYTE<br />
Adding two-byte pairs. <strong>The</strong> carry flag carries overflow from low to high bytes.<br />
CLC<br />
;START BY CLEARING CARRY<br />
LDA LO1 ;GET FIRST LOW BYTE ...<br />
ADC LO2 ;...ADD IT TO OTHER LOW BYTE<br />
STA LO2 ;AND STORE RESULT<br />
LDA HI1 ;GET FIRST HIGH BYTE ...<br />
ADC HI2 ;...ADD IT AND CARRY TO OTHER HIGH BYTE,<br />
STA HI2 ;AND STORE RESULT<br />
In this example, LO2 and HI2 end up with <strong>the</strong> contents of LO1 and HI1 added<br />
to <strong>the</strong>m. Chapter 10 has ano<strong>the</strong>r example.<br />
Subtracting two-byte pairs. <strong>The</strong> carry flag (C) is set before subtraction (if it is<br />
left clear, <strong>the</strong> result will be off by 1). If C is clear on exit, <strong>the</strong> result is negative—that<br />
is, <strong>the</strong> amount subtracted was larger than <strong>the</strong> original two-byte amount.<br />
SEC<br />
;SET CARRY FLAG<br />
LDA LO1 ;GET FIRST LOW BYTE...<br />
SBC LO2 SUBTRACT OTHER LOW BYTE<br />
STA LO2 ;STORE RESULTS LOW BYTE<br />
LDA HI1 ;GET FIRST HIGH BYTE...<br />
SBC HI2 SUBTRACT OTHER HIGH BYTE AND CARRY FLAG COMPLEMENT<br />
STA HI2 ;STORE HIGH BYTE OF RESULT.<br />
Multiplying two single bytes to give a two-byte result. Amazingly enough,<br />
<strong>the</strong> 6510 has no instructions specifically for multiplying and dividing. <strong>The</strong> example<br />
below, however, multiplies <strong>the</strong> contents of two zero page locations ($FC and $FD),<br />
leaving <strong>the</strong> result in <strong>the</strong> same two bytes. On average, about 6000 multiplications per<br />
second can be performed by this routine, which uses ROR (ROtate Right) to detect<br />
bits and to store <strong>the</strong> result in $FC (low byte) and $FD (high byte).<br />
C000 CLC<br />
C001 LDA #$00<br />
C003 LDX #$08<br />
C005 ROR<br />
C006 ROR $FC<br />
C008 BCC $C00D<br />
C00A CLC<br />
217
6510 Machine Language<br />
C00B ADC $FD<br />
C00D DEX<br />
COOE BPL $C005<br />
C010 STA $FD<br />
C012 RTS<br />
After exiting Supermon, <strong>the</strong> following BASIC line can be used to test <strong>the</strong> ML multiply<br />
routine.<br />
10 INPUT X,Y: POKE 252,X: POKE 253,Y: SYS 49152: PRINT PEEK(252)+256* PEEK(253)<br />
Division of a two-byte number by a single byte. <strong>The</strong> next routine is roughly<br />
<strong>the</strong> opposite of <strong>the</strong> previous one. A 16-bit (two-byte) number in locations $FC (low<br />
byte) and $FD (high byte) is divided by <strong>the</strong> contents of $FE, and <strong>the</strong> result (assumed<br />
to be in <strong>the</strong> range $00-$FF) is left in $FC, with <strong>the</strong> remainder in $FD. <strong>The</strong> identical<br />
addresses and locations need not be retained in actual programs, of course.<br />
C000<br />
C001<br />
C003<br />
C005<br />
C007<br />
C008<br />
C00A<br />
COOC<br />
COOE<br />
C010<br />
con<br />
C012<br />
C014<br />
C016<br />
C018<br />
CLC<br />
LDX<br />
LDA<br />
ROL<br />
ROL<br />
BCS<br />
CMP<br />
BCC<br />
SBC<br />
SEC<br />
DEX<br />
BNE<br />
ROL<br />
STA<br />
RTS<br />
#$08<br />
$FD<br />
$FC<br />
$C00E<br />
$FE<br />
$C011<br />
$FE<br />
$C005<br />
$FC<br />
$FD<br />
This can be tested from BASIC by POKEing locations 252 and 253 with low and<br />
high bytes of <strong>the</strong> numerator, POKEing 254 with <strong>the</strong> denominator, SYSing to 49152,<br />
and printing PEEK(252) and PEEK(253) for <strong>the</strong> solution and remainder.<br />
Comparing two-byte pairs. <strong>The</strong> trick is to avoid comparison instructions and<br />
use SBC instead, which retains results as well as setting flags. Use <strong>the</strong> following<br />
routine:<br />
SEC<br />
LDA LO1<br />
SBC LO2<br />
STA TEMP TEMPORARY STORE<br />
LDA HI1<br />
SBC HI2<br />
ORA TEMP ;RESULT 0 ONLY IF A AND TEMP BOTH 0<br />
Z is set if <strong>the</strong> contents of <strong>the</strong> first address equal those of <strong>the</strong> second; C is clear if<br />
<strong>the</strong> contents of <strong>the</strong> first are less than <strong>the</strong> second. <strong>The</strong>refore, BEQ, BCC, and BCS test<br />
for =, respectively.<br />
O<strong>the</strong>r two-byte operations. It's often possible to write compact ML using <strong>the</strong> X<br />
and Y registers to store two bytes. Suppose locations $FD and $FE contain an ad-<br />
218
6510 Machine Language<br />
dress to be decremented, <strong>the</strong>n stored in locations $0350 and $0351. You can use <strong>the</strong><br />
following routine:<br />
NO<br />
LDY<br />
LDX<br />
BNE<br />
DEY<br />
$FE<br />
$FD<br />
NO<br />
DEX<br />
STY $0351<br />
STX $0350<br />
Testing <strong>the</strong> Range of a Byte<br />
<strong>The</strong> following example tests whe<strong>the</strong>r <strong>the</strong> byte in <strong>the</strong> accumulator is within <strong>the</strong> range<br />
5-9. A whole sequence of CMP instructions, with <strong>the</strong>ir immediate mode bytes in<br />
increasing order, can be tested with a succession of BCC instructions. It's not nec<br />
essary that <strong>the</strong> second branch be BCS, as it is here.<br />
LDA TESTBYTE<br />
CMP #$05<br />
BCC SMALL ;BRANCH TAKEN IF A = 0,1,2,3, OR 4<br />
CMP #$0A<br />
BCS LARGE ;BRANCH TAKEN IF A = 0A,0B,0C,...,FF<br />
OK ... ;CONTINUE WITH A IN DESIRED RANGE<br />
Loops<br />
Loops generally use X or Y as a counter and often as an offset, too. <strong>The</strong>re's some<br />
room for timesaving in <strong>the</strong> design of loops. Also, it's worth checking over <strong>the</strong>ir logic.<br />
It's easy to write loops which aren't quite correct, perhaps missing one of <strong>the</strong> values<br />
at one end of <strong>the</strong> loop.<br />
<strong>The</strong> short loop below puts <strong>the</strong> five bytes for <strong>the</strong> letters of <strong>the</strong> word HELLO on<br />
<strong>the</strong> screen. <strong>The</strong>re are two versions:<br />
LOOP<br />
TABLE<br />
LDX<br />
LDA<br />
STA<br />
INX<br />
CPX<br />
BNE<br />
RTS<br />
.BYTE<br />
#0<br />
TABLE,X<br />
$lE00,X<br />
#5<br />
LOOP<br />
"HELLO"<br />
LDX #5<br />
LOOP LDA TABLE-1,X<br />
STA<br />
$lE00,X<br />
DEX<br />
BNE LOOP<br />
RTS<br />
TABLE .BYTE "HELLO"<br />
In <strong>the</strong> first version, X successively takes values 0, 1, 2, 3, and 4; in <strong>the</strong> second,<br />
<strong>the</strong> values taken are 5, 4, 3, 2, and 1. <strong>The</strong> second version is shorter; DEX counts<br />
down to 0, and CPX #$00 is redundant, since in effect <strong>the</strong> processor does this when<br />
it sets <strong>the</strong> Z flag of <strong>the</strong> status register. Decrements are often more efficient than in<br />
crements, because you can eliminate <strong>the</strong> two-byte comparison instruction. However,<br />
you should take note that <strong>the</strong> decrementing version prints OLLEH instead of<br />
HELLO. That is, <strong>the</strong> bytes are read from right to left with this version, since <strong>the</strong> loop<br />
starts with <strong>the</strong> highest value of X and indexes backward to zero. You'll need to take<br />
that into account when you set up your tables of data. Using decrements also adds<br />
219
6510 Machine Language<br />
an extra difficulty; <strong>the</strong> LDA instruction cannot be executed with an X value of 0. This<br />
explains why <strong>the</strong> decrementing version uses LDA TABLE—1,X instead of LDA<br />
TABLE,X; if this were not done, <strong>the</strong> program would print a garbage character fol<br />
lowed by OLLE.<br />
Note that LDX #$04 : ... : BPL LOOP counts X down from 4 to 0, and <strong>the</strong> accu<br />
mulator loads from <strong>the</strong> expected starting point. However, X values larger than $7F<br />
won't cause a branch on BPL (because bit 7 is used to indicate a negative number<br />
with <strong>the</strong> BPL and BMI instructions), so it's best to avoid BPL at first.<br />
Looking at longer loops, <strong>the</strong>re are again several possible methods. Suppose 512<br />
bytes are to be moved into color RAM from $C000. Different approaches are shown<br />
below:<br />
LOOP<br />
(A)<br />
LDA<br />
STA<br />
STA<br />
LDA<br />
STA<br />
LDA<br />
STA<br />
LDY<br />
LDA<br />
STA<br />
INY<br />
BNE<br />
INC<br />
INC<br />
LDA<br />
CMP<br />
BNE<br />
#$00<br />
$FB<br />
$FD<br />
#$C0<br />
$FC<br />
#$D8<br />
$FE<br />
$00<br />
6510 Machine Language<br />
from BASIC (usually, <strong>the</strong> top of BASIC is lowered). Use <strong>the</strong> following routine to<br />
save <strong>the</strong> area, where STORE is <strong>the</strong> first byte of your protected area:<br />
LDX #$00<br />
LOOP LDA $00,X ; LOAD CONTENTS OF ZERO PAGE LOCATION<br />
STA STORE,X ; STORE IN SAFE LOCATION<br />
INX<br />
BNE LOOP ; FINISH ALL 256 BYTES<br />
Use this routine to restore <strong>the</strong> area later:<br />
LDX #$00<br />
LOOP LDA STORE,X ; LOAD VALUE FROM STORAGE<br />
STA $00,X ; STORE BACK IN ZERO PAGE<br />
INX<br />
BNE LOOP ; FINISH<br />
Shift and Rotate Instructions<br />
Shifting instructions (ASL, LSR) and rotating instructions (ROL, ROR) are useful<br />
whenever individual bits are important. For example, an easy way to print a byte as<br />
eight 0's or l's is to shift <strong>the</strong> byte eight times, using BCC or BCS to determine<br />
whe<strong>the</strong>r 0 or 1 is correct. Parallel-to-serial interconversion, where a byte is ei<strong>the</strong>r<br />
sent as separate bits or put toge<strong>the</strong>r from bits, uses <strong>the</strong> same idea.<br />
Because rotations use nine bits, including C, <strong>the</strong>y can be used to hold intermedi<br />
ate results during processing. <strong>The</strong> multiply and divide routines presented earlier use<br />
rotations like this; division, for example, repeatedly doubles <strong>the</strong> denominator and<br />
compares it with <strong>the</strong> numerator (to see which is bigger) while collecting <strong>the</strong> result.<br />
Both types of commands are valuable in calculations, because <strong>the</strong>y multiply by 2.<br />
This example shows how to multiply by 40; at <strong>the</strong> start, location $FC holds a Y-<br />
coordinate from 0 to 24, and $FD holds 1.<br />
<strong>The</strong> ML points to <strong>the</strong> Xth column of <strong>the</strong> Yth row of <strong>the</strong> screen. This can be done<br />
with a lookup table (which would be faster), but this is shorter: it calculates<br />
$400+40*Y (pointing to <strong>the</strong> beginning of <strong>the</strong> line), putting <strong>the</strong> result in ($FC), so an<br />
instruction like LDY $FB followed by LDA ($FC),Y references <strong>the</strong> correct position on<br />
<strong>the</strong> screen.<br />
LDA $FC ;A HOLDS Y-COORDINATE<br />
ASL ;A HOLDS 2*Y-COORD (0-48)<br />
ASL ;A HOLDS 4*Y-COORD (0-96) C=0<br />
ADC $FC ;A HOLDS 5*Y-COORD (0-120)<br />
ASL ;A HOLDS 10*Y-COORD (0-240)<br />
ASL ;A = 20*Y-COORD (0-480); ANY OVERFLOW IN C<br />
ROL $FD ;$FC, $FD HOLDS $0200 + OVERFLOW<br />
ASL ;A = 40*Y-COORD (0-960); ANY OVERFLOW IN C<br />
ROL $FD ;$FD, $FD HOLDS $0400 + OVERFLOW<br />
STA $FC ;($FC) HOLDS $0400 + 40*Y-COORDINATE<br />
Logical Instructions<br />
AND and ORA act like BASIC'S AND and OR, except that only eight bits are in<br />
volved. EOR (Exclusive-OR) doesn't exist in BASIC; <strong>the</strong> nearest thing is (A OR B)<br />
221
6510 Machine Language<br />
AND NOT (A AND B). As Chapter 11 shows, AND is used to mask out bits, ORA is<br />
used to force bits high, and EOR is used to reverse bits. In each case, any combina<br />
tion of bits can be chosen.<br />
For example, assume you have a byte ($72) which you want to print as <strong>the</strong> digits<br />
7 <strong>the</strong>n 2. Store <strong>the</strong> byte, <strong>the</strong>n shift it right four times. <strong>The</strong>n, AND #$0F to mask off<br />
(erase) <strong>the</strong> leftmost bytes. ORA #$30 forces $30 into <strong>the</strong> byte to create <strong>the</strong> ASCII<br />
value of a numeral ready for printing. Recover <strong>the</strong> original byte and repeat. This<br />
way, both digits are correctly output.<br />
An example of EOR may be helpful, too. EOR combines bits in repeatable pat<br />
terns, and you can use this to generate a checksum for BASIC or ML programs,<br />
which is helpful in verifying if a program is correct. <strong>The</strong> following version prints a<br />
number 0-255 and will print <strong>the</strong> same number whenever <strong>the</strong> identical program<br />
loads into <strong>the</strong> identical memory area.<br />
LOOP<br />
NOINC<br />
LDA<br />
STA<br />
LDA<br />
STA<br />
LDY<br />
EOR<br />
INC<br />
BNE<br />
INC<br />
LDX<br />
CPX<br />
BNE<br />
LDX<br />
CPX<br />
BNE<br />
TAX<br />
LDA<br />
JMP<br />
$2B ;COPY START-OF-PROGRAM POINTER<br />
$FD<br />
; INTO FD AND FE<br />
$2C<br />
$FE<br />
#$00 ;SET Y TO 0<br />
6510 Machine Language<br />
Bit 7, <strong>the</strong> leftmost (highest) bit, can be regarded as a sign bit. It takes one of two<br />
values, with 0 designating a positive number and 1 representing a negative sign; <strong>the</strong><br />
seven lower bits in <strong>the</strong> byte indicate <strong>the</strong> actual value of <strong>the</strong> number. <strong>The</strong> N flag in<br />
<strong>the</strong> status register is wired to be consistent with this scheme; when N=l, <strong>the</strong> num<br />
ber is considered negative and BMI's branch is taken. If N=0, BPL is taken. <strong>The</strong>se<br />
branches operate whe<strong>the</strong>r or not you're using signed arithmetic.<br />
A number and its negative must add to 0. It follows that a pair of numbers (say,<br />
+ 7 and —7) can be represented by $07 and $F9, because <strong>the</strong>se add to $00, and be<br />
cause <strong>the</strong> second has its high bit set. <strong>The</strong> use of numbers like $F9 to represent neg<br />
atives is called twos complement arithmetic, and $F9 is <strong>the</strong> twos complement of $07.<br />
You'll find with experiment that <strong>the</strong> largest possible one-byte twos complement<br />
number (%0111 1111) is 127, and <strong>the</strong> smallest (%1000 0000) is -128. <strong>The</strong>se figures<br />
are identical to <strong>the</strong> range available to branch instructions and show how a branch's<br />
offset can be stored in just one byte.<br />
Subtraction from 256 gives <strong>the</strong> twos complement. Ano<strong>the</strong>r rule, which may be<br />
easier to use, is to flip all <strong>the</strong> bits in <strong>the</strong> byte, and add 1. So, <strong>the</strong> twos complement<br />
of %0101 0101 ($55) is %1010 1010 plus 1, or %1010 1011 ($AB). Again, $55 plus<br />
$AB adds to $00, ignoring <strong>the</strong> carry flag. Note that $00 is its own negative<br />
complement.<br />
You can generate twos complement numbers with <strong>the</strong> following routine:<br />
LDA NUMBER<br />
EOR #$FF<br />
CLC<br />
ADC #$01<br />
Since <strong>the</strong> sign can be stored elsewhere, this type of arithmetic isn't particularly<br />
popular; however, <strong>the</strong> <strong>64</strong>'s BASIC integer variables (for example, X%) use a 16-bit<br />
version of twos complement arithmetic in which <strong>the</strong> highest bit stores <strong>the</strong> sign, so<br />
integers may range from -32768 to +32767.<br />
<strong>The</strong> V flag is also associated with this type of arithmetic, showing that an over<br />
flow took place into <strong>the</strong> sign bit. Consider addition, for example, where V is affected<br />
by ADC. Numbers of opposite signs cannot overflow; even extreme values must fall<br />
in <strong>the</strong> correct range. But if <strong>the</strong> signs are <strong>the</strong> same, overflow is possible. For example,<br />
$44 + $33 gives %77, and V is clear, but $63 + $32 gives $95, which in twos com<br />
plement arithmetic is considered negative; this addition results in V being set. Simi<br />
larly, two negative numbers can appear to add to a positive result, and if this is <strong>the</strong><br />
case, V will also be set.<br />
<strong>The</strong> condition of <strong>the</strong> V flag is in fact determined internally, by <strong>the</strong> chip, by<br />
reversing <strong>the</strong> EOR of <strong>the</strong> sign bits (giving 0 if <strong>the</strong>y match, 1 o<strong>the</strong>rwise). V is set, in<br />
o<strong>the</strong>r words, when <strong>the</strong> signs are <strong>the</strong> same; <strong>the</strong> result (incorrectly) shows a different<br />
sign.<br />
To reiterate, twos complement is an interpretation. Many programmers may<br />
never use it, preferring to work in positive numbers. But you can't fully understand<br />
<strong>the</strong> N and V flags without grasping <strong>the</strong> idea of negative bytes.<br />
223
6510 Machine Language<br />
Decimal Arithmetic<br />
Decimal mode arithmetic, with <strong>the</strong> D flag set, packs two digits into each byte and<br />
adds or subtracts in decimal. This example adds a four-digit number in locations $8B<br />
(high byte), $8C (low byte) to a six-digit number in locations $8D (high byte), $8E<br />
(middle byte), $8F (low byte), leaving <strong>the</strong> result in <strong>the</strong> three-byte number. Scoring in<br />
games often uses such subroutines; a score is stored in <strong>the</strong> smaller location, <strong>the</strong> sub<br />
routine called to total, and <strong>the</strong> result printed.<br />
NOINC<br />
SED<br />
CLC<br />
LDA<br />
ADC<br />
STA<br />
LDA<br />
ADC<br />
STA<br />
BCC<br />
INC<br />
CLD<br />
;TURN ON BCD MODE<br />
;CLEAR CARRY<br />
$8C ;ADD LOW BYTES,<br />
$8F<br />
$8F ;STORE RESULT<br />
$8B ;ADD MID BYTES<br />
$8E<br />
$8E ;AND STORE<br />
NOINC<br />
$8D ;HIGH BYTE<br />
;RETURN TO NORMAL MODE<br />
<strong>The</strong> six-digit number can be printed by looping three times to select a byte, <strong>the</strong>n<br />
shifting it right, using AND #$0F followed by ORA #$30 to convert to ASCII,<br />
outputting with JSR $FFD2, and repeating with <strong>the</strong> same byte unshifted.<br />
It is often simpler to use individual bytes for totals of this sort. This example<br />
uses <strong>the</strong> first five locations of <strong>the</strong> <strong>64</strong>'s screen to print <strong>the</strong> score of a game. Start by<br />
putting <strong>the</strong> screen code for zero—$30(48)—into <strong>the</strong> first five screen locations. <strong>The</strong>n<br />
put <strong>the</strong> score into $8B through $8F as, for example, 00 00 01 00 00 (to represent<br />
100). <strong>The</strong> result appears directly on <strong>the</strong> screen.<br />
LDX #$04<br />
CLC<br />
LOOP LDA $0400,X<br />
ADC $8B,X<br />
CMP #$3A<br />
BCC CLEAR<br />
SBC #$0A<br />
CLEAR STA $0400/X<br />
DEX<br />
BPL<br />
LOOP<br />
;SET COUNTER FOR 4,3,2,1,0<br />
;CLEAR CARRY<br />
;LOAD BYTE FROM SCREEN<br />
;ADD CORRESPONDING BYTE<br />
;IS RESULT 10 OR MORE?<br />
;IF NOT, BRANCH,<br />
;IF SO, SUBTRACT 10, LEAVING CARRY SET<br />
;UPDATE SCREEN BYTE<br />
;COUNT DOWN TO NEXT BYTE<br />
;BRANCH UNTIL X IS FF<br />
This is fast and efficient. Change <strong>the</strong> value of X for more than or fewer than six<br />
digits. This method does not use BCD mode.<br />
Debugging ML Programs<br />
Listed here are many errors common in 6510 ML programming. Program design<br />
should be approached methodically, preferably from <strong>the</strong> top down, starting with <strong>the</strong><br />
writing or reusing of standard subroutines. Careful analysis of <strong>the</strong> code, perhaps<br />
with flow charting, and testing with typical and abnormal data should insure a<br />
sound program. Your program will be simpler to debug if you build it out of distinct<br />
modules or subroutines that each perform a clearly defined task. Thus, when bugs<br />
appear, you can locate <strong>the</strong> source of trouble by testing one routine at a time.<br />
224
6510 Machine Language<br />
Careless errors. Errors of oversight may remain undetected for a long time.<br />
Examples include transcription errors (entering 7038 for 703B) and immediate mode<br />
# errors (using LDA 00 instead of LDA #00). You might also use a wrong ROM ad<br />
dress, perhaps one for a different computer, or make branch errors, especially with<br />
simple assemblers where forward addresses must be reentered. Yet ano<strong>the</strong>r possibil<br />
ity is <strong>the</strong> use of a Kernal or o<strong>the</strong>r subroutine which alters A, X, or Y.<br />
Addressing mode errors. <strong>The</strong>se stem from confusing <strong>the</strong> order of low and high<br />
address bytes, failure to understand indirect addressing modes, or attempted use of<br />
indexed zero page addressing to extend above location $FF (LDA $AB,X always<br />
loads from zero page, for any value of X). Indirect jumps may also cause problems;<br />
JMP ($03FF) takes its address from $03FF and $0300, due to a bug in <strong>the</strong><br />
microprocessor.<br />
Calculation errors. With addition, subtraction, and so on, do not forget to use<br />
<strong>the</strong> proper flag instruction (CLC before adding, SEC before subtracting, and SED and<br />
CLD with BCD math). Remember, too, that LDA #$02 followed by ADC $FD adds<br />
<strong>the</strong> contents of location $FD to <strong>the</strong> 2 in <strong>the</strong> accumulator, but leaves $FD unchanged.<br />
It's easy to forget that only <strong>the</strong> accumulator holds <strong>the</strong> result, and STA $FD may be<br />
needed to return <strong>the</strong> answer to <strong>the</strong> desired location. Also, be careful to keep track of<br />
<strong>the</strong> carry bit with shifts and rotates. That can be tricky, since C is easy to overwrite.<br />
Status flag errors. <strong>The</strong> logic behind flags may cause difficulties for beginners,<br />
who may not realize (for example) that AND #$00 is identical to LDA #$00. In<br />
crementing from a value of #$7F to #$80 sets <strong>the</strong> negative flag. <strong>The</strong> following rou<br />
tine stores <strong>the</strong> contents of KEY in LOCN, but STA sets no flags:<br />
LDA<br />
CMP<br />
BNE<br />
STA<br />
KEY<br />
#$3A<br />
ERROR<br />
LOCN<br />
You might expect it to clear Z, but this is not <strong>the</strong> case. Z will remain set until cleared<br />
by <strong>the</strong> execution of some instruction which affects that flag.<br />
Stack errors. Generally, <strong>the</strong> number of stack pushes should equal <strong>the</strong> number of<br />
pulls, and <strong>the</strong> order should match. For example, PHA:TXA:PHA usually requires<br />
PLA:TAX:PLA to retrieve A and X. Stack errors frequently crash <strong>the</strong> computer by<br />
transferring program flow to an address that contains garbage. Advanced pro<br />
grammers often use <strong>the</strong> stack for temporary storage, but it is usually safer (and about<br />
as efficient) to use o<strong>the</strong>r RAM locations for that purpose. You may find that you can<br />
program for a long time without ever having to use <strong>the</strong> stack.<br />
Errors in which RAM is overwritten. Programs or <strong>the</strong>ir data can be overwrit<br />
ten by BASIC strings or variables, by tape activity, or by subroutines which happen<br />
to access <strong>the</strong> BASIC pointers (including utilities like Supermon), to name but a few.<br />
<strong>The</strong> program itself may be at fault: A loop may move some data it shouldn't, a<br />
pointer may be updated while still in use so that it points temporarily to a wrong ad<br />
dress, or a part of <strong>the</strong> stack may be used for storage but get filled by normal stack<br />
activity.<br />
225
6510 Machine Language<br />
Monitors for <strong>the</strong> <strong>64</strong><br />
Monitors are programs that allow individual bytes to be inspected and programmed.<br />
<strong>The</strong> simplest and least useful allow little more than a hexadecimal display of bytes;<br />
<strong>the</strong> best allow assembly and disassembly as well as facilities to examine and alter<br />
memory freely, to run ML programs in a controlled way, and to convert types of<br />
data.<br />
BASIC Monitors<br />
BASIC programs which use PEEK and POKE to program in ML are slow, but <strong>the</strong>y<br />
do have advantages, particularly for beginners. <strong>The</strong>y use familiar INPUT commands,<br />
and can be loaded, run, stopped, and listed without difficulty. As <strong>the</strong>y are BASIC,<br />
<strong>the</strong>y occupy <strong>the</strong> normal BASIC space in RAM, whereas ML monitors occupy un<br />
familiar areas. <strong>The</strong>y're easily modified; if you'd like decimal addresses with a dis<br />
assembly, or nonstandard opcodes, <strong>the</strong>se are easy to put in.<br />
Machine Language Monitors<br />
<strong>The</strong>se are fast and generally better than BASIC. We'll concentrate on Supermon, a<br />
public domain program which is <strong>the</strong> work of several people, including Jim<br />
Butterfield. Supermon is listed in an Appendix with instructions on saving it to tape<br />
or disk. If you don't have a monitor, but do have some free time, type it in and use it.<br />
CBM MON, supplied with <strong>Commodore</strong>'s assembler package, and "Micromon-<br />
<strong>64</strong>" (published in Computers First Book of <strong>Commodore</strong> <strong>64</strong>) are both better than<br />
Supermon, but are about twice as long. <strong>The</strong>y are typical good-quality monitors. Each<br />
is used in a fairly standard way; <strong>the</strong> alphabetic list of commands which follows de<br />
scribes typical commands, although <strong>the</strong>re are minor variations. For example, a Save<br />
command like .S "ML PROGRAM",08,C000,C100, which saves $C000-$C0FF to<br />
disk and names it ML PROGRAM on one monitor, must be entered .S "ML<br />
PROGRAM",C000,C100,08 with some monitors.<br />
Before discussing specific monitors, some potential difficulties are worth noting.<br />
First, new versions appear from time to time, and you may find that documentation<br />
lags behind. Second, <strong>the</strong>re are potential memory problems, because BASIC strings or<br />
POKEs may overwrite <strong>the</strong> monitor, or <strong>the</strong> monitor may use part of <strong>the</strong> memory<br />
needed for ML. <strong>The</strong> notes following <strong>the</strong>refore explain <strong>the</strong> monitor's memory po<br />
sitions. Finally, it can be quite important that a monitor is compatible with BASIC.<br />
One reason is that a SYS call often runs ML in a different way from a monitor's G<br />
command. For example, POKEing location 1 to switch out ROMs may not work<br />
properly using G, despite being correct in ML.<br />
Supermon is usually supplied with a BASIC loader, which loads like BASIC,<br />
and, when run, relocates Supermon into memory. Chapter 9 explains how this works.<br />
Supermon is put into <strong>the</strong> current top of BASIC, and <strong>the</strong> pointer ($37) is adjusted<br />
down so BASIC will not corrupt it. Its starting address is usually 38893. After exiting<br />
to BASIC with X, Supermon can be reentered by SYS 38893 or SYS 13, because 13<br />
normally holds a zero byte which acts as a BRK command.<br />
Supermon coexists well with BASIC. Also, it leaves <strong>the</strong> tape buffer from $033C<br />
unused, using page 2 locations for its own work. Supermon can be saved as an ML<br />
program, needing a forced load back into its correct area. <strong>The</strong>re's no timesaving in<br />
226
6510 Machine Language<br />
doing this, and if you wish to use BASIC including string processing, you must<br />
remember to lower <strong>the</strong> top of BASIC—POKE 56,150: NEW is fine. So <strong>the</strong> BASIC<br />
loader is often <strong>the</strong> most convenient. Obviously, since loading it will overwrite BASIC<br />
in memory, it's better to load Supermon at <strong>the</strong> start of a session. Like most BASIC<br />
loaders of its type, repeated running of <strong>the</strong> BASIC loader will put a series of working<br />
versions of <strong>the</strong> program next to each o<strong>the</strong>r in <strong>the</strong> top of memory, lowering <strong>the</strong><br />
pointer each time, and reducing memory available to BASIC.<br />
Supermon won't normally load into <strong>the</strong> area starting at $C000, so it's suitable<br />
when you wish to write ML into $C000, which is probably most of <strong>the</strong> time. It also<br />
locates itself below cartridges at $8000, and is <strong>the</strong>refore easy to use when examining<br />
ROM programs.<br />
Supermon has <strong>the</strong>se commands:<br />
A, D,F, G, H, L, M, Rf S, T,<br />
(Note: All <strong>the</strong> monitors discussed here have an ASCII table near <strong>the</strong> end that lists <strong>the</strong><br />
commands.)<br />
It does not have an intelligent relocater, and its memory display command<br />
doesn't print ASCII equivalents of memory, so you cannot scroll through a program<br />
looking for keywords, for example. Disassembly clears <strong>the</strong> screen, <strong>the</strong>n fills <strong>the</strong> page;<br />
<strong>the</strong> result is tidy, but tiresome to move through, particularly backward. But it's still a<br />
useful monitor.<br />
CBM MON is supplied with <strong>Commodore</strong>'s editor/assembler package in two<br />
versions; both use forced loads, one at $8000 and <strong>the</strong> o<strong>the</strong>r at $C000. A call to <strong>the</strong><br />
start address enters <strong>the</strong> monitor—SYS 8*4096 or SYS 12*4096, respectively. Two<br />
versions are supplied so ML can be written into ei<strong>the</strong>r of <strong>the</strong> major RAM areas; for<br />
example, <strong>the</strong> version starting at $8000 must be used when programming in $C000.<br />
POKE 56,128: POKE 55,0: NEW protects <strong>the</strong> $8000 version against corruption by<br />
BASIC strings.<br />
It's easy to relocate CBM MON. For example, you may use <strong>the</strong>se commands to<br />
move <strong>the</strong> $8000 version to $1000:<br />
.T 8000 9000 1000<br />
.N 1000 2000 9000<br />
.N 1E66 1E99 9000<br />
STRAIGHT TRANSFER OF 4K<br />
:ADJUST ADDRESSES BY ADDING 9000<br />
:ADJUST ADDRESS TABLE BY ADDING 9000<br />
CBM MON is based on VICMON, with a number of changes—references to loca<br />
tions 0 and 1 removed, screen altered from $lE00 to $0400, JMP (C002) replaced by<br />
JMP (A002). It fits 4K of memory, but <strong>the</strong> conversion to <strong>the</strong> <strong>64</strong> is not very satisfac<br />
tory, and CBM MON versions vary. In particular, <strong>the</strong> commands B, Q, and W (to set<br />
Breakpoints, Quick Trace, and Walk) have been written out, perhaps because <strong>the</strong>se<br />
didn't work properly on <strong>the</strong> <strong>64</strong>. As a result, because CBM MON freely uses zero page<br />
locations (<strong>the</strong> tape buffer isn't touched), BASIC won't work properly with CBM<br />
MON; <strong>the</strong> easiest way to recover BASIC in full seems to be to follow <strong>the</strong> exit com<br />
mand, X, with:<br />
POKE 43,1: POKE 44,8: POKE 45,A: POKE 46,B: CLR<br />
POKE 2,25: POKE 23,24: POKE 24,0<br />
where A and B are previous PEEK values of 45 and 46. <strong>The</strong>se POKEs reset BASIC<br />
and string handling.<br />
8000<br />
8000<br />
9000<br />
9000<br />
227
6510 Machine Language<br />
CBM MON has <strong>the</strong>se commands:<br />
A, C, D, F, G, H, I, L, M, N, R, S, T, and X<br />
It has backward and forward scrolling with D, I, and M, which is very handy.<br />
(Spacing is sometimes erratic; <strong>the</strong> current line-links are used. <strong>The</strong> RUN/STOP key<br />
isn't implemented during scrolls, which can be irritating.) Backward disassembly is<br />
inherently unreliable; CBM MON gives priority to longer opcodes, so results can be<br />
different from forward disassembly, but this is unavoidable. Unlike Supermon, a dis<br />
assembled portion of ML can be edited and immediately reassembled by typing over<br />
<strong>the</strong> mnemonic field of <strong>the</strong> disassembly. <strong>The</strong>re's a possible bug here, because ASCII<br />
tables which happen not to be opcodes disassemble as ???. Typing RETURN on such<br />
a line converts ??? into #2, <strong>the</strong> smallest non-opcode, irrespective of its original value.<br />
"Micromon" is very similar to CBM MON. It occupies 4K, typically<br />
$7000-$7FFF, and is force-loaded, typically by LOAD "MICROMON",8,1. POKE<br />
56,122: NEW (or POKE 56,122: CLR) protects a version at $7000, for example, from<br />
BASIC; SYS 7*4096 would enter it. Like CBM MON, this monitor is quite easy to re<br />
locate: See <strong>the</strong> example later in this chapter.<br />
Its major commands are identical to CBM MON's, except that it has a command<br />
to calculate branch offsets (O), and its memory display, in effect, includes I. Like<br />
CBM MON, commands to set Breakpoints, Quick Trace, and so on aren't im<br />
plemented. (It hangs on Q, for example.) However, Micromon has some valuable<br />
additional commands tagged on <strong>the</strong> end: $, #, and % convert hex, decimal, and bi<br />
nary numbers, " converts ASCII characters, + and — allow hex arithmetic, and <strong>the</strong><br />
ampersand (&) totals bytes between two addresses into a two-byte checksum.<br />
BASIC coexists successfully with Micromon, though it has a tendency to return<br />
to <strong>the</strong> monitor when <strong>the</strong> screen scrolls. Micromon uses <strong>the</strong> tape buffer, so don't try<br />
to assemble into <strong>the</strong> area around $033C.<br />
How to<br />
Use a Monitor<br />
Syntax. Supermon starts each line by printing a period, and commands are typed<br />
after this, appearing as .X, for example. O<strong>the</strong>r prompts are used internally by <strong>the</strong><br />
monitor—a colon when altering memory with .M, a comma when disassembling,<br />
and so on. <strong>The</strong>se are generally handled automatically by <strong>the</strong> monitor, but sometimes<br />
it's useful to alter <strong>the</strong>m manually.<br />
Commands generally consist of a single letter followed by some pattern of bytes<br />
relevant to <strong>the</strong> command. When RETURN is pressed, <strong>the</strong> table storing valid com<br />
mands is searched for <strong>the</strong> letter, and if it's found, a specific address is jumped to. At<br />
that point <strong>the</strong> parameters following <strong>the</strong> command are evaluated. In fact, experienced<br />
ML programmers can add extra commands by modifying <strong>the</strong> search loop.<br />
As an example, .T 1800 1850 033C transfers <strong>the</strong> bytes in 1800 through 1850<br />
into <strong>the</strong> area starting at 033C, <strong>the</strong>refore filling 033C through 038C. <strong>The</strong> pattern of<br />
addresses must be entered correctly; <strong>the</strong>re is no error indication if it isn't, but <strong>the</strong><br />
command is ignored. Some errors, like attempting to use an invalid command, do in<br />
dicate <strong>the</strong>re's been a mistake, usually by printing a question mark. Since <strong>the</strong> param<br />
eters to be input are accepted by absolute position, punctuation is irrelevant. Thus,<br />
.T 1800,1850,033C and .T 1800 1850 033C have <strong>the</strong> same effect. Most command in<br />
puts are accepted up to, but not beyond, a colon, so .A 033C CLC: GARBAGE is<br />
treated as .A 033C CLC and assembled correctly; this is often useful.<br />
228
6510 Machine Language<br />
Screen scrolling may be erratic. Monitors with this feature generally use what<br />
ever linked lines already exist, so spacing may be unpredictable, with occasional<br />
skips of a line.<br />
Interaction with BASIC. As we've seen, routines ending with BRK return to <strong>the</strong><br />
monitor after .G C000 or a similar command runs <strong>the</strong>m. A routine ending with RTS<br />
typically returns to BASIC. To run such a routine requires that you exit to BASIC<br />
with .X and <strong>the</strong>n enter SYS 12*4096 or something similar. You may find, in fact, that<br />
some ML routines run correctly when called with SYS calls, but don't work from<br />
within a monitor; JSR $BDCD (a ROM routine which outputs a number) has this ef<br />
fect, because <strong>the</strong> monitor uses zero page routines used by <strong>the</strong> ROM routine.<br />
After exit to BASIC, you may like to reset with SYS <strong>64</strong>738. This leaves your<br />
monitor in RAM, but completely resets pointers and <strong>the</strong> low part of memory, leaving<br />
everything in order. If you use <strong>the</strong> above SYS, POKEs to lower <strong>the</strong> top of memory<br />
will have to be reentered.<br />
What to do if ML crashes. When <strong>the</strong> computer is caught in a loop or will not<br />
respond for some unknown reason, try <strong>the</strong> following:<br />
• Try RUN/STOP-RESTORE. If this works, enter a SYS call back to <strong>the</strong> monitor.<br />
• If RUN/STOP-RESTORE fails (as it will with an X2 crash), <strong>the</strong> only recovery<br />
procedure is a hardware reset switch, not standard to <strong>the</strong> <strong>64</strong>, as explained in Chap<br />
ter 5. This erases ML from $0 to $102 and from $200 to $400, but leaves ML<br />
higher in RAM unaffected. If you have no reset switch, you'll have to turn <strong>the</strong> com<br />
puter off and start over.<br />
You may be able to avoid this problem more often if you fill RAM with zero<br />
bytes using <strong>the</strong> .F command, thus increasing <strong>the</strong> chance that a wrong command will<br />
end on BRK and return you safely to <strong>the</strong> monitor.<br />
Getting started. If you're an absolute beginner, load your monitor and enter it;<br />
for example, load, <strong>the</strong>n run Supermon. Try assembling <strong>the</strong> short demonstration pro<br />
grams at <strong>the</strong> start of this chapter, using <strong>the</strong> .A command, <strong>the</strong>n run <strong>the</strong>m with .G,<br />
and disassemble again with .D—<strong>the</strong> full syntax is explained in <strong>the</strong> following list of<br />
commands. You'll soon get <strong>the</strong> feel of it. Note that many monitors output <strong>the</strong>ir re<br />
sults using $ to indicate hex, but won't accept a $ as part of <strong>the</strong> command format.<br />
Don't be discouraged if your first ML programs crash <strong>the</strong> computer with<br />
distressing regularity. ML instructions are very powerful, and work without <strong>the</strong> auto<br />
matic error-checking that BASIC provides. Nearly every ML program contains a few<br />
bugs at first, and correcting <strong>the</strong>m is a normal part of <strong>the</strong> programming process, for<br />
experts as well as beginners.<br />
Monitor Command Dictionary<br />
Following is a list of commands commonly available when using a machine language<br />
monitor. Not all of <strong>the</strong>m are supported by Supermon. For more information, see <strong>the</strong><br />
article and program "Micromon-<strong>64</strong>" in COMPUTE'S First Book of <strong>Commodore</strong> <strong>64</strong>.<br />
/^Assemble)<br />
nfhe Assemble command converts 6510 mnemonics and data into <strong>the</strong> correct form,<br />
inferring <strong>the</strong> addressing mode from <strong>the</strong> command's format and storing ML bytes into<br />
229
6510 Machine Language<br />
memory. Labels and o<strong>the</strong>r features of true assemblers aren't accepted. <strong>The</strong>re's often<br />
a read-back check in case RAM isn't <strong>the</strong>re; try assembling at $A000 to see this.<br />
Typing RETURN with no ML instructions following <strong>the</strong> address allows you to exit<br />
<strong>the</strong> A mode. An example of <strong>the</strong> use of <strong>the</strong> A command follows:<br />
.A C000 LDA #$00<br />
.A C002 STA $0400<br />
.A C004 BRK<br />
.A C005<br />
After you enter a line, many monitors will expand it to show <strong>the</strong> actual hex bytes<br />
which make up <strong>the</strong> instruction. For instance, after entering <strong>the</strong> second line above,<br />
you would see:<br />
.A C002 8D 00 04 STA $0400<br />
Screen editing can be used to alter addresses, opcodes, and operands already on<br />
<strong>the</strong> screen. Cursor to <strong>the</strong> appropriate place, make <strong>the</strong> changes, and press RETURN.<br />
C (Compare Memory)<br />
Compare Memory reports any differences between two areas of memory. Syntax is<br />
identical to T.<br />
.C C000 C100 C800, for example, checks whe<strong>the</strong>r <strong>the</strong> bytes in $C000-$C100<br />
match bytes from $C800 to $C900, and prints <strong>the</strong> addresses of nonmatching bytes.<br />
^^Disassemble)<br />
xfie Disassemble command translates <strong>the</strong> contents of memory into standard 6510<br />
mnemonics, using $ and # to denote hex addresses and data. <strong>The</strong> format is compat<br />
ible with that of <strong>the</strong> assembler. It cannot produce labeled disassemblies and lacks<br />
some o<strong>the</strong>r features of true assemblers.<br />
Supermon always prints as much disassembly as will fit on <strong>the</strong> screen, whe<strong>the</strong>r<br />
you specify a single address or a range of addresses. <strong>The</strong> disassembly begins with<br />
<strong>the</strong> first specified address.<br />
On o<strong>the</strong>r monitors, .D A500 <strong>the</strong>n RETURN disassembles a single address; if<br />
your monitor allows scrolling, you'll be able to continue disassembly by moving <strong>the</strong><br />
cursor to <strong>the</strong> top or bottom of <strong>the</strong> screen.<br />
.D A46E A471 disassembles between <strong>the</strong> two limits, producing an output as<br />
follows:<br />
A46E C8 INY<br />
A46F F0 03 BEQ $A474<br />
A471 20 C2 BD JSR $BDC2<br />
Some monitors let you edit a disassembly by typing changes in <strong>the</strong> mnemonic<br />
field. Supermon disassemblies can only be edited by typing over <strong>the</strong> hex bytes be<br />
tween <strong>the</strong> address and <strong>the</strong> mnemonic.<br />
FjfFill Memory)<br />
Memory fills a region of RAM with identical bytes. For example, .F 033C 03FF<br />
00 fills <strong>the</strong> tape buffer with zero bytes. This has no syntax or read-back checking, so<br />
230
6510 Machine Language<br />
if you enter it wrongly, nothing will happen and you'll have no warning of this. $EA<br />
(NOP) is a useful space filler.<br />
runs ML from Supermon. <strong>The</strong> command .G C000, for example, transfers control<br />
to a program starting at location $C000. <strong>The</strong> G execution continues until a BRK<br />
occurs (which returns to <strong>the</strong> monitor) or some o<strong>the</strong>r irregular event takes place. For<br />
example, RTS may return execution to BASIC or <strong>the</strong> program may contain a mistake<br />
and crash. <strong>The</strong> G command can also be entered without an address, to transfer con<br />
trol to <strong>the</strong> current program counter address (see R command below).<br />
HflHunt Memory)<br />
This command reports all instances of a byte combination or string of characters be<br />
tween two addresses. For example:<br />
.H E000 FFFF 00 90<br />
prints all Kernal uses of $9000, and<br />
.H E000 FFFF "BASIC<br />
prints all Kernal uses of <strong>the</strong> word BASIC. H requires some care in interpretation. A<br />
Hunt for 20 E4 FF will certainly find all instances of JSR $FFE4, but a Hunt for 21<br />
DO, for example, may yield nothing, even though <strong>the</strong> address $D021 had been used,<br />
because D000,X may have been used to address it. And while JMP $C100 can be<br />
found with .H 4C 00 Cl, a branch command like BEQ $C100 cannot be located like<br />
this.<br />
I (Interpret Memory)<br />
Interpret Memory prints addresses followed by eight ASCII characters per row (or<br />
dots, where ASCII doesn't apply) and <strong>the</strong>ir hex equivalents. Some monitors include<br />
this as part of <strong>the</strong> M command.<br />
Tj(Load ML)<br />
Load ML uses this syntax for tape and disk loads, respectively:<br />
.L "NAME",01<br />
X "NAME",08<br />
Abbreviations are accepted, so .L " ",01 loads <strong>the</strong> next tape program, L "N*",08<br />
loads <strong>the</strong> first disk program beginning with N. <strong>The</strong> program or data is loaded as a<br />
block—after loading, it is not altered in any way.<br />
(M/(Memory Display)<br />
Memory Display prints addresses followed by eight hex bytes, including, with some<br />
monitors, ASCII characters in reverse. Monitors which scroll allow examination of<br />
large areas of ROM or RAM. For example, .M A09E A0EE displays 11 lines of BASIC<br />
keywords, as <strong>the</strong>y are stored in ROM. Readability is improved in lowercase mode.<br />
Nonprinting characters are displayed as reversed periods. <strong>The</strong> addresses and bytes<br />
can be altered, followed by RETURN to enter <strong>the</strong> new values.<br />
231
6510 Machine Language<br />
N (Number Adjuster)<br />
Number Adjuster is a command which adjusts absolute addresses, such as sub<br />
routine calls, within ML. It is usually used after moving ML as a block with T.<br />
"Micromon-<strong>64</strong>" supports <strong>the</strong> N command. For a detailed description, including<br />
instructions for relocating Micromon-<strong>64</strong>, see COMPUTERS First Book of <strong>Commodore</strong> <strong>64</strong>.<br />
P (Printer Disassembly)<br />
Disassembly to a printer is supported by some monitors. When using a monitor that<br />
does not support this command, enter OPEN 4,4: CMD 4 from BASIC, <strong>the</strong>n enter<br />
<strong>the</strong> monitor and type commands blind. <strong>The</strong> output from <strong>the</strong> monitor will be directed<br />
to <strong>the</strong> printer instead of <strong>the</strong> screen. This will also work if you wish to dump <strong>the</strong><br />
bytes in memory with M. To recover <strong>the</strong> screen display after printing, use X to exit<br />
<strong>the</strong> monitor, <strong>the</strong>n enter PRINT#4: CLOSE 4. If your printer omits <strong>the</strong> last instruc<br />
tion, specify an ending address a few bytes past <strong>the</strong> last byte you want to print.<br />
/ Display)<br />
displays <strong>the</strong> contents of <strong>the</strong> program counter (PC), IRQ vector, status register,<br />
bytes in <strong>the</strong> A, X, and Y registers, and <strong>the</strong> stack pointer as <strong>the</strong>y were on entry to <strong>the</strong><br />
monitor. Typically, any of <strong>the</strong>se can be changed. When .G runs <strong>the</strong> program, <strong>the</strong><br />
modified contents are loaded into PC, IRQ, and so on, before actual running. In this<br />
way, you can change IRQ to point to your own interrupt routine; try different values<br />
of A, X or Y; or experiment with different flag settings in <strong>the</strong> status register.<br />
(pSaveML)<br />
Save uses <strong>the</strong> following syntax for tape and disk, respectively:<br />
.S "ML",01,C000,C200<br />
.S "ML",08,C000,C200<br />
Unlike BASIC SAVEs, it's essential to specify <strong>the</strong> limits of memory to be saved.<br />
<strong>The</strong>se examples save memory from $C000 to $C1FF. <strong>The</strong> final byte is not saved, due<br />
to <strong>the</strong> way <strong>the</strong> pointers in <strong>the</strong> machine execute <strong>the</strong> SAVE command; saving from<br />
$E000 to $FFFF is <strong>the</strong>refore impossible. In some monitors, <strong>the</strong> device number de<br />
faults to 8 if not explicitly included. Note that <strong>the</strong>re may be no error message if a<br />
disk drive is off.<br />
6510 Machine Language<br />
6510 Machine Language<br />
<strong>The</strong> job of <strong>the</strong> assembler is to convert <strong>the</strong> source code intd object code—<strong>the</strong> ac<br />
tual numbers which make up machine language instructions. Note that <strong>the</strong> object<br />
code is a sequence of bytes identical to that produced by a simple disassembler. This<br />
is necessary, of course, since <strong>the</strong> 6510 has precise requirements which any utility<br />
program must respect. Object code is often stored on disk as an object file; this is a<br />
machine language program and can be loaded into a specific place in memory and<br />
run with a SYS call.<br />
<strong>The</strong> versatility of assemblers is illustrated by <strong>the</strong> pseudo-opcode (a special assem<br />
bler function) which assigns <strong>the</strong> starting address of <strong>the</strong> ML program. <strong>The</strong> starting ad<br />
dress pseudo-op is always used at <strong>the</strong> beginning of <strong>the</strong> source code. <strong>The</strong> command<br />
*=$2000 at <strong>the</strong> start of <strong>the</strong> source code causes <strong>the</strong> assembler to create <strong>the</strong> ML pro<br />
gram starting at $2000. Simply changing <strong>the</strong> command to *=$3000, followed by<br />
assembly, generates ML identical in its effect, but positioned to start at $3000. Object<br />
code, on <strong>the</strong> o<strong>the</strong>r hand, isn't usually relocatable without some effort.<br />
But <strong>the</strong> great advantage of source code is <strong>the</strong> fact that it can be edited. Inserting<br />
extra instructions in <strong>the</strong> middle of a program is easy, because assembly simply re<br />
calculates all <strong>the</strong> addresses and branches. In contrast, monitor users have to shift<br />
parts of <strong>the</strong> program, alter addresses, and generally rewrite and recheck.<br />
Assemblers also have <strong>the</strong> advantage of potentially giving ML a very readable<br />
format, provided <strong>the</strong> reader has a good grasp of ML. Symbols like GETCHR and la<br />
bels like FOUND make ML easier to follow than <strong>the</strong> object code; and comments<br />
allow <strong>the</strong> programmer room for thorough explanation of <strong>the</strong> program.<br />
Figure 7-1 is part of a routine which waits for A, B, or C to be pressed, <strong>the</strong>n<br />
jumps to a corresponding address, using <strong>the</strong> trick of pushing <strong>the</strong> destination address<br />
less 1 onto <strong>the</strong> stack, <strong>the</strong>n using RTS to jump. <strong>The</strong> column of comments helps in de<br />
ciphering <strong>the</strong> program. Object code is obviously harder to follow than source code,<br />
but <strong>the</strong>re are reverse assemblers available, which take ML and insert labels. Of course,<br />
it is impossible to reconstruct comments or <strong>the</strong> original labels.<br />
Overview of <strong>the</strong> Assembly Process<br />
A number of assembler packages are available for <strong>the</strong> <strong>64</strong>, many of which have been<br />
converted from PET/CBM programs. Converting source into object code is complex,<br />
and full-featured assemblers are naturally longer than those which are more re<br />
stricted. But <strong>the</strong>y all have some common characteristics. All use two passes or more<br />
(an assembler must look through <strong>the</strong> code at least twice), and all build up a symbol<br />
table, which is used on <strong>the</strong> second pass to fill in <strong>the</strong> forward addresses. To see why<br />
this is necessary, imagine you are assembling <strong>the</strong> code in Figure 7-1, and have ar<br />
rived at line 24 for <strong>the</strong> first time. CHRLIS hasn't yet been reached, so its value can<br />
be put into <strong>the</strong> object code and <strong>the</strong> symbol table only on <strong>the</strong> second pass. If <strong>the</strong> la<br />
bel CHRLIS is never found—it may have been misspelled, for example—<strong>the</strong> assem<br />
bler will print an error message.<br />
All assemblers have to build symbol tables, which means part of RAM must be<br />
allocated. <strong>The</strong> assembler itself and <strong>the</strong> file on which it's working have to coexist in<br />
memory. Since <strong>the</strong> source code is usually much longer than <strong>the</strong> output object code,<br />
programs amounting to only a few K when assembled can be difficult to fit in <strong>the</strong><br />
<strong>64</strong>'s memory. For this reason, many assemblers allow one source file to chain or link<br />
to ano<strong>the</strong>r, so a program is assembled in sections. However, <strong>the</strong> symbol table is<br />
234
6510 Machine Language<br />
usually kept intact. Symbols are often limited in length to conserve space in <strong>the</strong><br />
symbol table.<br />
If <strong>the</strong> assembler is directed to send <strong>the</strong> assembled ML to disk, clearly no RAM is<br />
used. Many assembler packages include a monitor to allow disassembly, running un<br />
der ML control, and o<strong>the</strong>r convenient functions. An assembler may also include a<br />
text editor to facilitate <strong>the</strong> task of writing source code. Special loader and relocater<br />
programs are helpful, too. One key to successful use of assembler packages, <strong>the</strong>re<br />
fore, is learning to manage <strong>the</strong> <strong>64</strong>'s memory efficiently.<br />
Assembler Features<br />
An assembler ei<strong>the</strong>r reads a source file into RAM or operates on source text already<br />
in RAM, converting it into object code on a command such as A, ASSEMBLE, or<br />
OPT OO. Assemblers vary in <strong>the</strong> way <strong>the</strong>y scan source code; some require precise<br />
alignment in columns and signal errors if <strong>the</strong>y don't find <strong>the</strong>m, while o<strong>the</strong>rs are<br />
more tolerant. Line 18, containing START, may be rejected, because it seems to con<br />
tain <strong>the</strong> opcode STA. Since standardization is limited, it makes sense to learn to use<br />
just one assembler.<br />
First-time assemblies without errors are rare. Unlike BASIC, which can run with<br />
SYNTAX ERRORS remaining in <strong>the</strong> unused portions of <strong>the</strong> code, assembling is not<br />
tolerant of errors. Often, removing errors becomes a goal in itself. <strong>The</strong> triumph of<br />
achieving a no-errors message may cause <strong>the</strong> programmer to fail to notice that <strong>the</strong><br />
program doesn't do what it should. Because repeated assemblies are <strong>the</strong> norm, it's<br />
desirable that assemblers and <strong>the</strong>ir source files should coexist in RAM; it saves disk<br />
access time. Likewise, you can speed up <strong>the</strong> process of testing and revision by<br />
assembling directly to RAM when possible, using <strong>the</strong> disk only to back up your<br />
work and save <strong>the</strong> final product.<br />
Assemblers for <strong>the</strong> 6510 typically allow for <strong>the</strong>se features:<br />
Labels. <strong>The</strong>se mark addresses to which branches, jumps, or subroutine calls are<br />
made. Often <strong>the</strong>re is a specified maximum length.<br />
Symbols. <strong>The</strong>se are values like GETIN in <strong>the</strong> example which are explicitly set.<br />
<strong>The</strong> assembler must be able to distinguish zero page symbols from o<strong>the</strong>rs. In prac<br />
tice, <strong>the</strong> terms label and symbol are often used synonymously.<br />
Opcodes. Standard 6510 opcodes, like LDA.<br />
Operands. <strong>The</strong>se are symbols or arithmetic values punctuated in a standard<br />
way. Line 22's LDX #2 could be written LDX #$2 (hex) or LDX #%00000010 (bi<br />
nary). Line 42 has a symbol used in indirect addressing, but also shows <strong>the</strong> use of<br />
simple arithmetic; many assemblers allow evaluations like this. Line 53 shows <strong>the</strong><br />
use of <strong>the</strong> quote to generate ASCII. LDA "A is equivalent to LDA #$41 on some<br />
assemblers and is often more convenient. <strong>The</strong> constructions LDA #ADDRESS, loading <strong>the</strong> low and high bytes of ADDRESS, respectively, are<br />
often used.<br />
Comments. Generally <strong>the</strong>se are signaled by a semicolon, which causes <strong>the</strong><br />
assembler to ignore <strong>the</strong> rest of <strong>the</strong> line.<br />
Pseudo-opcodes (directives). <strong>The</strong>se are important and, like symbols, essential to<br />
assemblers. Formats vary, so what follows may not apply to your assembler.<br />
Pseudo-ops are commands to <strong>the</strong> assembler, some of which have housekeeping<br />
functions, like diverting output to a printer ra<strong>the</strong>r than to <strong>the</strong> screen. O<strong>the</strong>rs ease<br />
235
6510 Machine Language<br />
programming, allowing, for example, easy entry of ASCII bytes. <strong>The</strong>y are called<br />
pseudo-opcodes because <strong>the</strong>y appear in <strong>the</strong> source code in <strong>the</strong> same place as<br />
opcodes; often <strong>the</strong>y begin with a period or some o<strong>the</strong>r special symbol, so <strong>the</strong> assem<br />
bler's parser looks ei<strong>the</strong>r for an opcode or a period or some o<strong>the</strong>r character on each<br />
line. <strong>The</strong>se are typical pseudo-ops:<br />
*= (sometimes .ORG, meaning origin) sets <strong>the</strong> current address at which object<br />
code should start. Line 12 in <strong>the</strong> example starts <strong>the</strong> assembly at $2000. Line 52 re<br />
serves one byte, by adding 1 to <strong>the</strong> current address. Similarly * = *+500 reserves 500<br />
bytes, and LDA #*—LABEL loads A with <strong>the</strong> difference between <strong>the</strong> <strong>the</strong>n-current<br />
address and an earlier label. <strong>The</strong> term star is often used to denote this symbol.<br />
= (sometimes .EQU, meaning equates) assigns values to symbols. Equates are<br />
usually collected at <strong>the</strong> start of source code, where <strong>the</strong>y can be easily checked.<br />
Usually, zero page equates must be at <strong>the</strong> start of <strong>the</strong> source code.<br />
.BYTE allows bytes to be assembled; this is necessary for any kind of data table.<br />
So .BYT 31,$EA,%00010001,"HELLO puts eight bytes, IF EA 11 48 45 4C 4C 4F,<br />
into <strong>the</strong> object code.<br />
.DISK NAME outputs object code to a disk file called NAME.<br />
.END marks <strong>the</strong> end of <strong>the</strong> source code. Anything later is treated as comment.<br />
.FILE NEXT instructs <strong>the</strong> assembler to load, <strong>the</strong>n assemble <strong>the</strong> source file called<br />
NEXT. This pseudo-op is essential for chaining <strong>the</strong> components of large ML<br />
programs.<br />
.LABELS causes a symbol table (a sorted table of all symbols with <strong>the</strong>ir values)<br />
to be printed after <strong>the</strong> assembly.<br />
.MACRO INC causes a macro to be inserted; see below.<br />
.OUTPUT instructs <strong>the</strong> assembler to put <strong>the</strong> object code into RAM as it is<br />
assembled so that it will be ready to run.<br />
.PRINTER diverts whatever output is requested to a printer.<br />
.SCREEN turns on output to screen. With .NS (for example) a part of <strong>the</strong> assem<br />
bly can be selected.<br />
.WORD puts two bytes into <strong>the</strong> object code, least significant byte first, to con<br />
form with 6510 convention. Line 54 has an example.<br />
Conditional assembly, library files, macros, and relocatable object files.<br />
<strong>The</strong>se are typical extras of good assemblers. Conditional assembly allows several ver<br />
sions of <strong>the</strong> object code to be prepared. A simple example follows:<br />
.IF TYPE-1 <<br />
*=$8000<br />
SCREEN=$0400 ><br />
.IF TYPE-2 <<br />
*=$C000<br />
SCREEN=$8000 ><br />
Depending on how <strong>the</strong> expression after IF evaluates, <strong>the</strong> source code is assembled at<br />
different locations.<br />
.LIB NAME loads and assembles a file, inserting it into <strong>the</strong> current assembly.<br />
This is not <strong>the</strong> same as chaining, but permits a source file to be built from a group of<br />
separate library source files.<br />
236
6510 Machine Language<br />
Macros allow space to be saved and readability improved by defining pseudoops<br />
which correspond to ML. For example:<br />
.MAC DBINC<br />
INC?1<br />
BNE XXX<br />
INC71+1<br />
XXX .MND<br />
defines a macro called DBINC, which performs a double-byte increment, on Com<br />
modore's assembler. So DBINC POINTR in <strong>the</strong> source code will cause <strong>the</strong> assembler<br />
to expand DBINC into three operations.<br />
As mentioned earlier, object files need not exist in immediately loadable form.<br />
<strong>The</strong>y can be stored in an intermediate state, without symbols or comments of any<br />
kind, but with sufficient extra information on <strong>the</strong> file to allow relocation in RAM by<br />
a loader. <strong>The</strong> process is analogous to <strong>the</strong> N command available on monitors. Ad<br />
dresses requiring relocation must be marked in <strong>the</strong> object file, <strong>the</strong>n <strong>the</strong> loader simply<br />
has to calculate <strong>the</strong> actual addresses depending on where in RAM it's asked to put<br />
<strong>the</strong> final ML.<br />
Assembler Packages<br />
Here's a brief look at <strong>the</strong> different types of assemblers that are available for <strong>the</strong> <strong>64</strong>.<br />
Assemblers written in BASIC. Everything described so far can be carried out<br />
by BASIC. For example, symbols can be stored in a string array, object files can be<br />
written straight to disk, one byte at a time, and pseudo-ops like * can be im<br />
plemented. <strong>The</strong> problem with using BASIC is that such assemblers are painfully<br />
slow, and take up more RAM than those written in ML.<br />
Assemblers with BASIC editing. Some cartridge-based assemblers use <strong>the</strong><br />
BASIC editor. When you turn <strong>the</strong> computer on with <strong>the</strong> cartridge in place, routines<br />
to intercept BASIC are set up, and commands like &A, &S, and &L are used to as<br />
semble, save, and load source files. It may not be possible to write <strong>the</strong> resulting ob<br />
ject code to disk, but a monitor with <strong>the</strong> S (Save) command may be included to save<br />
<strong>the</strong> RAM image as a normal ML program. <strong>The</strong> instant availability of <strong>the</strong> assembler is<br />
nice, and some edit features may be present, in fact, like AUTO line numbering. A<br />
programmer's aid utility may be compatible with <strong>the</strong> assembler, and this can help<br />
edit, though clearly it couldn't be expected to automatically format its output <strong>the</strong><br />
way <strong>the</strong> assembler would like.<br />
Ano<strong>the</strong>r example of an assembler that uses <strong>the</strong> BASIC editor is Richard Mans<br />
field's LADS assembler (published in <strong>The</strong> Second Book of Machine Language, from<br />
COMPUTE! Books). LADS loads into memory beginning at $2AF8 (11000). Its sym<br />
bol table is stored down from <strong>the</strong> start of its own code, and <strong>the</strong> BASIC source code is<br />
stored, loaded, and saved in <strong>the</strong> usual BASIC area from $0800. <strong>The</strong> area after $C000<br />
is free, and this could hold a monitor or RAM object code. <strong>The</strong> assembler recognizes<br />
and expands tokenized BASIC keywords if <strong>the</strong>se occur in <strong>the</strong> source file. <strong>The</strong> syntax<br />
requirements of <strong>the</strong> assembler are relatively strict—LOOP LDA #0 needs exactly one<br />
space between each item. LADS produces ML in nonrelocatable form which can<br />
ei<strong>the</strong>r remain in RAM or be saved to disk or tape as loadable ML. Of course, <strong>the</strong><br />
source code allows for locating <strong>the</strong> program at different places in memory, and <strong>the</strong><br />
text describes how to modify LADS, customizing it to your needs.<br />
237
6510 Machine Language<br />
Editor/Assemblers. <strong>Commodore</strong>'s ASM<strong>64</strong>40 program package has eight sepa<br />
rate programs on disk, but some are simply relocated versions—CBM MON can load<br />
at $8000 or $C000, for example. <strong>The</strong> assembler loads into <strong>the</strong> same area of RAM<br />
that <strong>the</strong> editor stores source code, and <strong>the</strong> package entails a lot of disk activity be<br />
cause of this. Source code must be saved to disk, <strong>the</strong>n <strong>the</strong> assembler loaded and run,<br />
and errors in <strong>the</strong> source code can be corrected only by reloading <strong>the</strong> source file,<br />
editing it, saving to disk, reloading and running <strong>the</strong> assembler, and so on.<br />
<strong>The</strong> editor is force-loaded into $C000-$C<strong>64</strong>A. SYS 49152 activates it. Tokenization<br />
is disabled. PUT and CPUT (which writes to disk omitting spare spaces) write<br />
<strong>the</strong> source to disk as a sequential file, and GET reads it back. AUTO and NUMBER<br />
handle line numbers, and FORMAT is similar to LIST, but formats <strong>the</strong> source into<br />
columns. KILL disables <strong>the</strong> editor, returning to normal BASIC, while DELETE allows<br />
block deletion. FIND and CHANGE allow symbols or o<strong>the</strong>r source code features to<br />
be altered and allow any delimiter to be used. For example, FIND /GETIN/ searches<br />
for GETIN, and CHANGE /LOOP/EXIT/ replaces <strong>the</strong> characters LOOP with <strong>the</strong><br />
characters EXIT. CHANGE ALOOPAEXITA has <strong>the</strong> same effect.<br />
<strong>The</strong> assembler loads like BASIC and includes a SYS call, so RUN is all that's<br />
needed to initiate assembly. Its output is an object file on disk, written in a relocat<br />
able format. This must be loaded into RAM with one of <strong>the</strong> loaders supplied, <strong>the</strong>n<br />
saved with <strong>the</strong> monitor; this process is more cumbersome than using an assembler<br />
that puts pure object code directly into RAM.<br />
Two loaders are supplied; LOLOADER fits <strong>the</strong> BASIC space and can be run.<br />
HILOADER is force-loaded into $C800 and run with SYS 51200. <strong>The</strong>se loaders in<br />
clude a checksum test and read-back check to insure that <strong>the</strong> ML goes to RAM.<br />
Yet ano<strong>the</strong>r program included is a DOS wedge, located at $CC00 (52224), which<br />
simplifies program LOADs from disk.<br />
Part of <strong>the</strong> reason for this patchwork of programs is its origin as a PET assem<br />
bler; several programs have been left so <strong>the</strong>y overwrite each o<strong>the</strong>r.<br />
Ano<strong>the</strong>r assembler is <strong>the</strong> MAE Editor/Assembler by Carl Moser of Eastern House<br />
Software. Also based on PET, <strong>the</strong> default memory management is well designed. <strong>The</strong><br />
editor, assembler, monitor, assembled ML at $C000, and BASIC all coexist in RAM,<br />
allowing writing and testing of ML programs to be very efficient. O<strong>the</strong>r programs in<br />
clude a screen scroller, a disk wedge, and relocaters, which operate on relocatable<br />
object files if <strong>the</strong>se are chosen. This assembler supports all <strong>the</strong> previously explained<br />
features.<br />
238
Chapter 8<br />
ML Methods<br />
Specific<br />
<strong>the</strong> <strong>64</strong><br />
to<br />
• Kernal Routines<br />
• BASIC ROM Routines<br />
• Using RAM Under ROM<br />
• Modifying BASIC<br />
• Vectors<br />
• Interrupts
Chapter 8<br />
ML Methods Specific to <strong>the</strong> <strong>64</strong><br />
This chapter is a reference to <strong>the</strong> ROM of <strong>the</strong> <strong>64</strong>, and a guide to using <strong>the</strong> vectors<br />
that point to that ROM effectively. You can include your own ML routines that<br />
wedge into <strong>the</strong> normal operations of <strong>the</strong> computer if you like, and this chapter will<br />
show you how. O<strong>the</strong>r chapters that include specific ML material are Chapter 6 (key<br />
board, screen, etc.), Chapter 12 (graphics), and Chapters 13-17 (sound, tape, disks,<br />
peripherals).<br />
Kernal<br />
Routines<br />
<strong>The</strong> Kernal is <strong>the</strong> essential core of ML routines that <strong>the</strong> <strong>64</strong> uses during normal op<br />
eration, and it has a jump table pointing to <strong>the</strong> routines. While <strong>the</strong> specific addresses<br />
of <strong>the</strong> routines may differ from one computer to <strong>the</strong> next—like in <strong>the</strong> VIC and <strong>64</strong>—<br />
<strong>the</strong> addresses of <strong>the</strong> jump table are supposed to remain constant between machines.<br />
In <strong>the</strong>ory, this will allow programs to work on several ROM versions of <strong>the</strong> same<br />
machine and even on different <strong>Commodore</strong> computer models. In practice, consis<br />
tency among different models is achievable only to a small extent, because so many<br />
hardware and software differences exist between machines. Still, it does serve a use<br />
ful purpose.<br />
<strong>Commodore</strong> has upgraded <strong>the</strong> <strong>64</strong>'s Kernal ROM in <strong>the</strong> past, and more changes<br />
are possible (see "Upgrading ROMs" below). ML programs that access Kernal<br />
routines only through <strong>the</strong> jump table are likely to work correctly on machines with<br />
updated ROMs; programs that jump into ROM routines at o<strong>the</strong>r entry points might<br />
work differently (or not at all) after a ROM upgrade.<br />
<strong>The</strong> jump table listed below is arranged in ascending order by memory location.<br />
<strong>The</strong> Kernal appears less formidable if you note that more than half is concerned with<br />
opening and closing files and input/output of characters. Table 8-1 lists input/out<br />
put errors that may be returned by Kernal routines.<br />
Note that values shown in paren<strong>the</strong>ses in "<strong>The</strong> Kernal Jump Table" (below) are<br />
two-byte vectors which contain addresses in standard 6510 low/high byte form. <strong>The</strong><br />
value ($0281) can be read as "<strong>the</strong> vector at $0281/'<br />
Table 8-1. Kernal Routine I/O Errors<br />
JMPto<br />
F77E<br />
F780<br />
F783<br />
F786<br />
F789<br />
F78C<br />
F78F<br />
F792<br />
F795<br />
ERROR#<br />
1<br />
2<br />
3<br />
4<br />
5<br />
6<br />
7<br />
8<br />
9<br />
Description<br />
Example<br />
OPEN when ten files open already<br />
OPEN 1,3: OPEN 1,4<br />
PRINT#5 without OPEN 5<br />
LOAD "NONEXISTENF',8<br />
OPEN 11,11: PRINT#11<br />
OPEN ^M/'SEQ^W": GET#8,X$<br />
OPEN 1,0: PRINT#1<br />
LOAD "",8<br />
LOAD "PROGRAM",3<br />
Kernal routines with error-trapping return 0-9 in <strong>the</strong> accumulator. To see <strong>the</strong> Kernal<br />
error messages at work, run this program:<br />
10 POKE 157,<strong>64</strong>:PRINT#55,X$<br />
TOO MANY FILES<br />
FILE OPEN<br />
FILE NOT OPEN<br />
RLE NOT FOUND<br />
DEVICE NOT PRESENT<br />
NOT INPUT FILE<br />
NOT OUTPUT FILE<br />
MISSING FILENAME<br />
ILLEGAL DEVICE NO.<br />
241
ML Methods Specific to <strong>the</strong> <strong>64</strong><br />
<strong>The</strong> Kernal message I/O ERROR #3 will be printed, as will <strong>the</strong> BASIC message ?FILE<br />
NOT OPEN ERROR IN 10. Location 157 controls <strong>the</strong> printing of error messages; in<br />
normal BASIC operation it contains 0, which suppresses Kernal messages.<br />
<strong>The</strong> Kernal<br />
Jump Table<br />
Kernal Routine<br />
Address Location Name Description<br />
FF81 FF5B PCINT Initialize Screen and Keyboard<br />
Sets VIC chip; sets keyboard buffer to 10; sets light blue<br />
foreground; clears screen; homes cursor. CIA timer 1 (reg<br />
isters $D804-$D805) set to generate 60 Hz interrupts both<br />
with PAL and NTSC TVs.<br />
FF84 FDA3 IOINIT Initialize I/O Devices<br />
Sets CIA 1 for keyboard scan, CIA 2 for serial device<br />
input/output, and <strong>the</strong> port at address 1 for standard BASIC<br />
memory map and for tape handling. Turns off SID chip<br />
volume but does not clear o<strong>the</strong>r SID registers.<br />
FF87 FD50 RAMTAS Set and Check RAM<br />
Clears RAM up to $03FF, excluding <strong>the</strong> stack. Sets tape<br />
buffer to start at $033C, sets ($0281) to start of BASIC<br />
RAM, $0800, and ($0283) to end of BASIC RAM (varies if<br />
plug-in cartridge is present). Sets screen to start at $0400.<br />
FF8A FD15 RESTOR Set Default Vectors<br />
Sets 16 vectors in ($0314-$0333) from a ROM table; used<br />
on power-up and reset. Alters A, X, Y, and SR. No error<br />
FF8D<br />
FD1A<br />
VECTOR<br />
returns.<br />
Save/Set User Vectors<br />
If C flag is set: Moves table from $0314-$0334 to X (low),<br />
Y (high) address, saving current vectors. If C is clear:<br />
Moves table from X (low), Y (high) back to $0314-$0334.<br />
Alters A, Y, and SR. No error returns.<br />
FF90 FE18 SETMSG Control Screen Messages<br />
Puts A into $9D to control messages. A has bit 7 set for di<br />
rect mode, off for program mode. Bit 6 (not used by <strong>64</strong>)<br />
causes I/O errors to appear, as <strong>the</strong> table above shows. Al<br />
ters A and SP. No error returns.<br />
FF93 EDB9 SECOND Send Secondary Address After LISTEN<br />
Can be used to send a secondary address to <strong>the</strong> serial bus<br />
after LISTEN; A holds <strong>the</strong> address, which is used un<br />
changed, and <strong>the</strong>refore needs to be ORAd with $60. After<br />
this subroutine, ATN is brought low so that data output<br />
from <strong>the</strong> <strong>64</strong> can begin (see Chapter 17). Alters A, SR, and<br />
probably X,Y. Errors returned in ST byte at $90.<br />
FF96 EDC7 TKSA Send Secondary Address After TALK<br />
Can be used to send a secondary address after TALK on<br />
<strong>the</strong> serial bus; A needs to be ORAd with $60. <strong>The</strong> routine<br />
checks for a return clock pulse. Alters A, and probably X<br />
and Y, and sets C flag. Errors returned in ST byte at $90.<br />
242
ML Methods Specific to <strong>the</strong> <strong>64</strong><br />
FF99 FE25 MEMTOP BASIC RAM Top<br />
If C flag set: Loads X (low), Y (high) from ($0283). If C<br />
clear: Stores X (low), Y (high) into ($0283). Note that<br />
($0283) is not <strong>the</strong> normal top of memory, which is ($37),<br />
but that it holds <strong>the</strong> top of memory as detected by <strong>the</strong> <strong>64</strong><br />
when power is applied. Alters X, Y, and SR. No error<br />
returns.<br />
FF9C FE34 MEMBOT BASIC RAM Bottom<br />
Identical to MEMTOP, except that ($0281) is <strong>the</strong> relevant<br />
address.<br />
FF9F EA87 SCNKEY Read Keyboard<br />
Reads <strong>the</strong> keyboard and puts key, if any, into <strong>the</strong> keyboard<br />
buffer, where GETIN can recover it. <strong>The</strong> normal keyboard<br />
locations are used, too, so $028D holds <strong>the</strong> SHIFT key in<br />
dicator (see Chapter 6). Normally executed at each inter<br />
rupt, this subroutine is useful for interrupt-driven routines<br />
where IRQ is moved or when <strong>the</strong> interrupt is disabled. Al<br />
ters A, X, Y, and SR. C set on return means <strong>the</strong> buffer was<br />
full and <strong>the</strong> character wasn't accepted.<br />
FFA2 FE21 SETTMO Set Time-out<br />
Not used by <strong>the</strong> <strong>64</strong>. Stores A into $0285, but this location<br />
is never used. Intention is to set a time-out value, after<br />
which a serial device is assumed not present.<br />
FFA5<br />
FFA8<br />
EE13<br />
EDDD<br />
ACPTR<br />
CIOUT<br />
Input a Character from Serial Bus<br />
Gets a byte from device number 4 or higher, typically disk.<br />
A file must be opened or <strong>the</strong> device made to talk. This rou<br />
tine is virtually identical to CHRIN, FFE4; <strong>the</strong> reason it has<br />
a Kernal address at all is because it allows GET from a de<br />
vice without a file necessarily being open. <strong>The</strong> character re<br />
turns in A. Errors are returned in <strong>the</strong> status byte $90. Alters<br />
A, X, and SR.<br />
Output a Character to Serial Bus<br />
Exactly analogous to ACPTR, this routine transmits <strong>the</strong><br />
contents of A to device number 4 or higher, provided a file<br />
is open and ready or <strong>the</strong> device is a listener. CHROUT,<br />
FFD2, calls this routine. Errors return in <strong>the</strong> status byte<br />
$90. Alters A and SR.<br />
FFAB EDEF UNTALK Untalk Serial Devices<br />
Untalks devices on <strong>the</strong> serial bus, sending IEEE standard<br />
UNTALK command. Alters A, X, SR, and probably Y. Er<br />
rors return in status byte $90.<br />
FFAE EDFE UNLSN Unlisten Serial Devices<br />
Exactly analogous to UNTALK, this command unlistens de<br />
vices numbered 4 or higher. Alters A, X, SR, and probably<br />
Y. Errors return in status byte $90.<br />
FFB1 ED0C LISTEN Make Device Listen<br />
Converts a device on <strong>the</strong> serial bus to a listener. Register A<br />
holds <strong>the</strong> device number (4-30). ATN is held low to send<br />
<strong>the</strong> command byte, which is <strong>the</strong> device number ORA #$20.<br />
Alters A, X, SR, and probably Y. Errors return in status<br />
byte $90.<br />
243
ML Methods Specific to <strong>the</strong> <strong>64</strong><br />
FFB4 ED09 TALK Make Device Talk<br />
Exactly analogous to LISTEN, this converts a device into a<br />
talker. <strong>The</strong> device number in A is ORAd with $40. Alters<br />
A, X, R, and probably Y. Errors return in status byte $90.<br />
FFB7 FE07 READST Read a Status Byte<br />
Reads <strong>the</strong> status byte into A. Serial bus devices have $90,<br />
and RS-232 devices have $0297 for <strong>the</strong>ir respective status<br />
bytes. Note that this routine clears $0297 to zero after read<br />
ing it.<br />
FFBA FE00 SETLFS Set Logical (File Number), First (Device), Secondary<br />
Address<br />
This and <strong>the</strong> following routine are preliminaries to opening<br />
a file. <strong>The</strong>y are, in effect, used by all OPEN statements.<br />
<strong>The</strong>re are three routines because <strong>the</strong> 6510 has only A, X,<br />
and Y registers.<br />
SETLFS puts <strong>the</strong> contents of A into file number stor<br />
age in RAM, X into device number, and Y into secondary<br />
address. To mimic OPEN 1,4 in ML, load A, X, and Y with<br />
1, 4, and 0, respectively, <strong>the</strong>n JSR $FFBA. No error returns.<br />
FFBD FDF9<br />
SETNAM Set Filename<br />
A is <strong>the</strong> length of <strong>the</strong> filename; X (low), Y (high) points to<br />
<strong>the</strong> start of <strong>the</strong> name. If A is 0 (acceptable for tape), X and<br />
Y become irrelevant. No error returns.<br />
FFC0 (031A)<br />
Usually<br />
F34A<br />
OPEN Open a File<br />
Opens a file, assuming that <strong>the</strong> filename and o<strong>the</strong>r param<br />
eters have been set by using SETNAM and SETLFS. Entry<br />
values of A, X, and Y are thus irrelevant. On exit, carry set<br />
indicates an error; <strong>the</strong> error number 1, 2, 4, 5, or 8 (see<br />
Table 8-1) returns in A. Alters A, X, Y, and SR.<br />
FFC3 (031C)<br />
Usually<br />
F291<br />
CLOSE Close a File<br />
A holds <strong>the</strong> file number on entry to this routine, which<br />
closes that file only, deleting its parameters from <strong>the</strong> file<br />
tables and decrementing <strong>the</strong> number-of-files-open location.<br />
On exit, C is clear. No errors are reported. Alters A, X, Y,<br />
and SR.<br />
FFC6 (031E)<br />
Usually<br />
F20E<br />
CHKIN Prepare Open File for Input<br />
Prepares an open channel to receive input, in <strong>the</strong> way<br />
GET# is used. Load X with <strong>the</strong> file number, call CHKIN<br />
with JSR $FFC6. <strong>The</strong>n you can use GETIN, FFE4, to get<br />
characters. After <strong>the</strong> characters are read, CLRCHN returns<br />
files and devices to normal, untalking <strong>the</strong>m. On return<br />
from CHKIN, C set indicates an error; A holds <strong>the</strong> error<br />
number (3, 5, or 6). Alters A, X, Y, and SR.<br />
FFC9 (0320) CHKOUT Prepare Open File for Output<br />
Usually Exactly analogous to CHKIN, this prepares output to be di-<br />
F250 rected to a file specified by CHKOUT, in <strong>the</strong> same way<br />
PRINT# commands operate. Load X with <strong>the</strong> file number,<br />
call CHKOUT with JSR $FFC9, output characters with<br />
CHROUT, <strong>the</strong>n close files with CLOSE, for example. An<br />
error is indicated if C is set on return from CHKOUT; A<br />
holds <strong>the</strong> error number (3, 5, or 7). Alters A, X, Y, and SR.<br />
244
ML Methods Specific to <strong>the</strong> <strong>64</strong><br />
FFCC (0322)<br />
Usually<br />
F333<br />
FFCF (0324)<br />
Usually<br />
F157<br />
FFD2 (0326)<br />
Usually<br />
F1CA<br />
FFD5<br />
F49E<br />
(0330)<br />
Usually<br />
F4A5<br />
CLRCHN Set I/O Devices to Normal<br />
Sets output device to screen and input device to keyboard,<br />
and unlistens or untalks active devices. Leaves open files<br />
open, so CHKIN and CHKOUT still operate when wanted<br />
without fur<strong>the</strong>r OPENs being needed. Compare CLALL,<br />
which is virtually identical but also closes all files. JSR<br />
$FFCC is all that's needed. Alters A, X, and SR. No error<br />
returns. Note that $9A holds current output device number;<br />
$99 holds input device number.<br />
CHRIN Input a Character<br />
Gets a single byte from <strong>the</strong> current input device (indicated<br />
in $99). This routine is identical to GETIN, $FFE4, except<br />
for two factors. For keyboard characters, CHRIN is de<br />
signed for use with INPUT statements and gets characters<br />
from <strong>the</strong> screen even when <strong>the</strong> keyboard is <strong>the</strong> nominal in<br />
put device. Second, CHRIN with RS-232 loops until a nonnull<br />
character is found. In all o<strong>the</strong>r cases (tape, disk),<br />
CHRIN and GETIN are identical. See <strong>the</strong> examples for use<br />
of CHRIN. JSR CHRIN returns <strong>the</strong> byte in A. Alters A, X,<br />
Y, and SR. Errors returned in ST byte $90.<br />
CHROUT Output a Character<br />
Outputs a single character to <strong>the</strong> current output devices. A<br />
character may be sent to tape, RS-232, screen, or <strong>the</strong> serial<br />
bus, where any listener will receive <strong>the</strong> character. Gen<br />
erally, <strong>the</strong>re is only one listener. To use CHROUT, load A<br />
with <strong>the</strong> character, <strong>the</strong>n JSR $FFD2 to output it. Register A<br />
retains its entry value, X and Y are unaltered. Errors return<br />
in status byte $90.<br />
LOAD Load to RAM<br />
Kernal LOAD is used by BASIC LOAD to load from tape or<br />
disk into RAM. <strong>The</strong> result is not relinked as it is with<br />
BASIC LOAD. Thus, this routine loads RAM from <strong>the</strong> de<br />
vice without any changes. Keyboard, RS-232, and screen<br />
return ILLEGAL DEVICE.<br />
Since commands like LOAD "filename"',1,1 use a de<br />
vice number and name, SETLFS and SETNAM or <strong>the</strong><br />
equivalent POKEs have to be used before LOAD.<br />
Before entering LOAD, A holds 0 for LOAD, 1 (or<br />
some nonzero value) for VERIFY. LOAD and BASIC'S VER<br />
IFY use almost identical routines, except that VERIFY com<br />
pares bytes ra<strong>the</strong>r than storing <strong>the</strong>m in memory. Provided<br />
that <strong>the</strong> secondary address is 0, X (low) and Y (high) point<br />
to <strong>the</strong> address at which LOAD will start. If it is nonzero,<br />
<strong>the</strong> program will be loaded at <strong>the</strong> start address stored with<br />
<strong>the</strong> file to be loaded.<br />
LOAD uses a vector after X and Y are stored. <strong>The</strong><br />
routine branches to $F4B8 (disk LOAD) or $F533 (tape<br />
LOAD).<br />
On exit, C set denotes an error. Register A holds <strong>the</strong><br />
error number (4, 5, 8, or 9); A, X, Y, and SR are all altered<br />
by LOAD. X (low) and Y (high) point to <strong>the</strong> end address<br />
plus one following LOAD.<br />
245
ML Methods Specific to <strong>the</strong> <strong>64</strong><br />
FFD8 F5DD SAVE<br />
(0332)<br />
Usually<br />
F5ED<br />
FFDB F6E4 SETTIM<br />
FFDE F6DD RDTIM<br />
FFE1 (0328) STOP<br />
Usually<br />
F6ED<br />
FFE4 (032A) GETIN<br />
Usually<br />
F13E<br />
Save to Device<br />
Kernal SAVE is similar to LOAD. It dumps memory un<br />
changed to tape or disk, has <strong>the</strong> same illegal devices, and<br />
requires SETLFS and SETNAM or <strong>the</strong>ir equivalents to be<br />
called first. <strong>The</strong>re is no equivalent to a LOAD/VERIFY flag.<br />
But SAVE has to specify two addresses, <strong>the</strong> start and end<br />
addresses; it uses A, X, and Y for this. X (low) and Y (high)<br />
define <strong>the</strong> end address (one byte past <strong>the</strong> end of <strong>the</strong> block<br />
to be saved). Register A is used as a pointer to a zero page<br />
vector that contains <strong>the</strong> start address. If A holds $2A, for<br />
instance, <strong>the</strong> contents of $2A (low) and $2B (high) define<br />
<strong>the</strong> start address.<br />
SAVE uses a vector after <strong>the</strong> addresses are stored in<br />
($C1) and ($AE). After this, <strong>the</strong> routine branches to $F5FA<br />
(disk) or $F659 (tape).<br />
On exit, C set denotes an error. In this case, A holds<br />
5, 8, or 9. However, with disks <strong>the</strong>re may be a disk error<br />
which has to be read from <strong>the</strong> disk drive error channel. Al<br />
ters A, X, Y, and SR.<br />
Set Jiffy Clock<br />
Stores Y (highest), X (high), A (low) into three RAM loca<br />
tions which store <strong>the</strong> jiffy clock. If TI$ is greater than<br />
240000, <strong>the</strong> next interrupt resets to a normal time range.<br />
This is not <strong>the</strong> most useful routine since <strong>the</strong> CIA clocks are<br />
generally more reliable.<br />
Read Jiffy Clock<br />
<strong>The</strong> converse of <strong>the</strong> previous routine, RDTIM loads Y<br />
(highest), X (high), and A (low) from <strong>the</strong> TI clock's bytes.<br />
<strong>The</strong> result usually needs some conversion to be useful.<br />
Test RUN/STOP Key<br />
This is an easy way to check if RUN/STOP is pressed, so<br />
RUN/STOP can be used to break into ML programs as an<br />
exit mechanism. JSR $FFE1:BEQ will branch if <strong>the</strong><br />
RUN/STOP key is pressed. Note that seven o<strong>the</strong>r keys—Q,<br />
<strong>Commodore</strong> key, space, 2, CTRL, left-arrow, and 1 return<br />
unique values in A (for example, $EF is space). If none of<br />
<strong>the</strong>se eight keys is pressed, A returns with $FF.<br />
If RUN/STOP is pressed, CLRCHN is called. If you<br />
don't want this, use LDA $91:CMP #$7F, which will test<br />
for <strong>the</strong> RUN/STOP key.<br />
Alters A and SR and, if CLRCHN is called, Y. No er<br />
rors returned.<br />
Get a Character<br />
Almost identical to CHRIN, except that keyboard input is<br />
taken directly from <strong>the</strong> keyboard buffer, like BASIC GET.<br />
Character is returned in A. Zero byte means no character<br />
found in keyboard, RETURN means no more disk charac<br />
ters, and space means no more tape characters. <strong>The</strong> o<strong>the</strong>r<br />
alternatives apply only when <strong>the</strong> input device number in<br />
$99 is changed from 0. Alters A, X, Y, and SR. No errors<br />
returned if keyboard GETIN; o<strong>the</strong>rwise, errors returned in<br />
status byte $90.<br />
246
ML Methods Specific to <strong>the</strong> <strong>64</strong><br />
FFE7<br />
(032C)<br />
Usually<br />
F32F<br />
CLALL<br />
FFEA F69B UDTIM<br />
FFED E505 SCREEN<br />
FFFO E50A PLOT<br />
FFF3 E500 BASE<br />
(FFFA)<br />
(FFFC)<br />
(FFFE)<br />
FE43<br />
FCE2<br />
FF48<br />
NMI<br />
RESET<br />
IRQ<br />
Abort AH I/O<br />
Sets number of open files to 0 and unlistens and untalks all<br />
devices, but does not close files. Compare to CLRCHN,<br />
which is almost identical. Alters A, X, and SR. No error re<br />
turns. Note: Because files aren't closed, this command may<br />
give problems with write files. In simple terms, CLALL<br />
makes all files appear closed to <strong>the</strong> computer, but <strong>the</strong> disk<br />
drive may still treat a file as open and create an unclosed<br />
file on <strong>the</strong> disk (see Chapter 15). It is always best to close<br />
each file individually with CLOSE (FFC3).<br />
Update Timer, Read RUN/STOP Key<br />
Increments TI clock; if <strong>the</strong> result is 24 hours, returns to 0.<br />
(To keep correct time, increments must be made regularly<br />
by interrupt.) Location $91 is updated to hold <strong>the</strong><br />
RUN/STOP key register, so <strong>the</strong> Kernal STOP routine can<br />
be used after this. Alters A, X, and SR. No error returns.<br />
Check Screen Format<br />
SCREEN returns 22 in X and 23 in Y for VIC, and 40 in X<br />
and 25 in Y for <strong>the</strong> <strong>64</strong>, regardless of <strong>the</strong> true screen dimen<br />
sions (which can change). It can be used in programs that<br />
work on <strong>the</strong> VIC and <strong>64</strong> to determine which computer is in<br />
use. For an example, see "SpeedScript Customizer" in COM-<br />
PUTEI's <strong>Commodore</strong> Collection, Volume 2. Alters X, Y, and<br />
SR. No errors.<br />
Cursor Position<br />
If C flag set: Reads $D6 into X and $D3 into Y, cursor po<br />
sitions down (0-24) and across (0-39), respectively. If C<br />
flag clear: Sets X (down), Y (across). An example of its use<br />
is given below. PLOT adjusts <strong>the</strong> screen links. Alters A, X,<br />
and SR. No errors.<br />
Return $DC00<br />
Loads X (low), Y (high) with $DC00, <strong>the</strong> start of CIA 1.<br />
Non-Maskable Interrupt<br />
Reset<br />
Interrupt Request and Break<br />
Using <strong>the</strong><br />
Kernal<br />
Using CHROUT to print to screen. CHROUT ($FFD2) prints to screen some<br />
what like PRINT does, using <strong>the</strong> same characters. This makes it an easy command to<br />
use, and it produces a notable increase over BASIC'S speed. Typically, a table of<br />
characters beginning with 147 ($93, which is {CLR}) and ending with 0 (to mark <strong>the</strong><br />
end) is set up, including color and cursor characters; an ML loop prints <strong>the</strong>se far<br />
faster than BASIC. Chapter 12 includes several graphics routines using CHROUT.<br />
Try <strong>the</strong> following:<br />
033C LDX #$00<br />
LOOP 033E LDA TABLE,X ;TABLE COULD START AT $034A<br />
0341 BEQ EXIT<br />
0343 JSR $FFD2 ;CHROUT DOESN'T AFFECT X<br />
0346<br />
0347<br />
INX<br />
BNE LOOP<br />
EXIT 0349 RTS (or BRK)<br />
247
ML Methods Specific to <strong>the</strong> <strong>64</strong><br />
Using PLOT to position cursor. <strong>The</strong> following example is typical. It positions<br />
<strong>the</strong> cursor with PLOT, and <strong>the</strong>n prints <strong>the</strong> letter A with CHROUT.<br />
CLC<br />
LDX #$09<br />
LDY #$03<br />
JSR $FFF0<br />
LDA #$41<br />
JSR $FFD2<br />
RTS or BRK<br />
: SET CURSOR. EXAMPLE VALUES:<br />
: TENTH LINE DOWN<br />
: FOURTH COLUMN ACROSS<br />
PLOT SETS PARAMETERS<br />
ASCn LETTER A<br />
CHROUT PRINTS THE CHARACTER<br />
Using GETIN to fetch keyboard characters. <strong>The</strong> short example below shows a<br />
method for echoing keypresses to <strong>the</strong> screen. In practice, more constructive uses are<br />
likely. Note <strong>the</strong> loop branching back to JSR $FFE4; this is similar to <strong>the</strong> GET loop<br />
waiting for a character. Note also <strong>the</strong> test for <strong>the</strong> * key, which allows an exit from<br />
<strong>the</strong> loop.<br />
LOOP<br />
EXIT<br />
JSR<br />
BEQ<br />
CMP<br />
BEQ<br />
JSR<br />
$FFE4<br />
LOOP<br />
#$2A<br />
EXIT<br />
$FFD2<br />
BNE LOOP<br />
RTS or BRK<br />
;AWAITKEY<br />
;A HOLDS BYTE. COMPARE WITH ♦<br />
;EXIT ON *<br />
;CHROUT PRINTS TO SCREEN<br />
;BRANCHES ALWAYS<br />
GETIN alters X and Y registers, unlike CHROUT. Thus, while you can use X or Y in<br />
a loop with CHROUT alone, you must use a temporary storage location as <strong>the</strong><br />
counter when using GETIN and CHROUT toge<strong>the</strong>r.<br />
Using CHRIN to fetch characters. <strong>The</strong> routine below shows how a loop inputs<br />
successive characters using CHRIN. If you precede this short program by <strong>the</strong> cursor<br />
position routine, you can simulate INPUT. <strong>The</strong> cursor will flash at <strong>the</strong> selected po<br />
sition onscreen. <strong>The</strong> program prints <strong>the</strong> characters at <strong>the</strong> top of <strong>the</strong> screen to show<br />
how CHRIN works. Note how ANDing <strong>the</strong> accumulator contents with $3F converts<br />
<strong>the</strong> ASCII value into <strong>the</strong> correct screen display code. $FE is used as a temporary<br />
store for <strong>the</strong> current offset, since X or Y can be altered by CHRIN. As with BASIC<br />
INPUT, if you wish to validate a string being typed, GETIN is best, but CHRIN is<br />
easier to use.<br />
LOOP<br />
EXIT<br />
LDA<br />
STA<br />
JSR<br />
CMP<br />
BEQ<br />
LDX<br />
#$00<br />
$FE<br />
$FFCF<br />
#$0D<br />
EXIT<br />
$FE<br />
$FE<br />
#$3F<br />
$0400,X<br />
STA<br />
LDA<br />
STA<br />
BEQ<br />
RTS or BRK<br />
#$00<br />
$D800,X<br />
LOOP<br />
;POSITION CURSOR BEFOREHAND<br />
;COUNTER<br />
;CHRIN<br />
;RETURN IS LAST CHARACTER<br />
;BUMP COUNTER UP<br />
;CONVERT ASCII TO POKE VALUE<br />
;STORE CHARACTER TO SCREEN<br />
;SET COLOR RAM<br />
248
ML Methods Specific to <strong>the</strong> <strong>64</strong><br />
Using LOAD and SAVE. Examples are in Chapter 6 (BLOCK LOAD and<br />
SAVE) and in <strong>the</strong> chapters on disk and tape. If <strong>the</strong> precise mechanism of <strong>the</strong>se com<br />
mands interests you, disassemble <strong>the</strong> routines, following <strong>the</strong> branches to tape or<br />
disk. Tape LOAD at $F533 prints SEARCHING, loads a header, computes <strong>the</strong> start<br />
and end addresses, prints LOADING, and continues with <strong>the</strong> data load. Disk LOAD<br />
reads <strong>the</strong> first two bytes for its LOAD address.<br />
Using OPEN and CLOSE. Chapter 15 contains disk examples.<br />
Using READST. JSR $FFB7 loads A with <strong>the</strong> status byte, ei<strong>the</strong>r RS-232 or<br />
o<strong>the</strong>rwise, depending on which device is used. This simple routine saves you <strong>the</strong><br />
trouble of remembering ST's RAM address.<br />
Using SCNKEY. Chapter 6's PAUSE is an example of how this can be used.<br />
<strong>The</strong> IRQ vector is redirected by altering ($0314) to point to some routine o<strong>the</strong>r than<br />
$EA31, its usual destination. <strong>The</strong> new routine sets <strong>the</strong> interrupt disable flag (SEI), so<br />
no fur<strong>the</strong>r interrupts are allowed, and repeatedly reads <strong>the</strong> keyboard until some<br />
predetermined keypress occurs. At that time, interrupts are enabled (CLI), <strong>the</strong>n JMP<br />
$EA31 carries on as though nothing had happened.<br />
Using STOP. JSR $FFE1 <strong>the</strong>n BEQ EXIT is an easy way to stop ML from <strong>the</strong><br />
keyboard. Without it, <strong>the</strong> RUN/STOP key is generally inactive. STOP is called after<br />
each BASIC statement is executed in a normal RUN, which is why STOP works with<br />
BASIC.<br />
Using SETTIM and RDTIM. Both <strong>the</strong>se commands are very simple. What's<br />
usually more important is converting <strong>the</strong> result into a readable form. This ML rou<br />
tine (non-Kernal) converts <strong>the</strong> clock's contents into a form exactly like TI$ (a string<br />
of exactly six numerals, with leading zeros where needed) so that a quarter after<br />
seven is 071500. <strong>The</strong> string is left in locations $00FF-$0104, as <strong>the</strong> demo shows by<br />
storing it to <strong>the</strong> screen top. <strong>The</strong> six bytes can, of course, be edited and printed (for<br />
example) as 07:15:00.<br />
LOOP<br />
JSR<br />
STY<br />
DEY<br />
STY<br />
LDY<br />
STY<br />
LDY<br />
JSR<br />
LDX<br />
LDA<br />
STA<br />
LDA<br />
STA<br />
DEX<br />
BPL<br />
RTS<br />
$AF84<br />
$5E<br />
$71<br />
#$06<br />
$5D<br />
#$24<br />
$BE68<br />
#$05<br />
$00FF,X<br />
$0400,X<br />
#$00<br />
$D800,X<br />
LOOP<br />
orBRK<br />
;READ/SET CLOCK<br />
; NOW TI$ IS SET UP IN 00FF-0104<br />
; POKE SIX BYTES INTO SCREEN<br />
; NOT $FF,X<br />
; STORE TO SCREEN<br />
; COLOR RAM<br />
249
ML Methods Specific to <strong>the</strong> <strong>64</strong><br />
BASIC ROM Routines<br />
BASIC obviously has an enormous number of built-in routines, many of <strong>the</strong>m hav<br />
ing a recognizably BASIC feel about <strong>the</strong>m. This section will show you how RUN can<br />
be performed from ML and will give you an easy way to input data from <strong>the</strong> screen.<br />
You'll see how numbers and strings can be input by ML. Finally, you'll look at cal<br />
culation in ML, which is not as difficult as it might seem. Examples include <strong>the</strong> USR<br />
function, a hex-to-decimal converter, and a random number generator.<br />
Executing RUN from ML<br />
When a BASIC program is in memory, JMP $A871 (or SYS 43121) will run <strong>the</strong> pro<br />
gram, provided it has a line 0, or generate an 7UNDEFV STATEMENT ERROR if line<br />
0 does not exist. Any line of BASIC can be run from ML with this equivalent of<br />
RUN:<br />
JSR $A660 ;CLR<br />
LDA #$LO ;LOW BYTE OF LINE NUMBER<br />
STA $14<br />
LDA #$HI ;HIGH BYTE OF LINE NUMBER<br />
STA $15<br />
JSR $A8A3 ;FIND LINE<br />
JMP $A7AE ;GOTOLINE<br />
This can be useful when ML calls BASIC; see UNLIST in Chapter 6 for an ex<br />
ample. Remember that it's sometimes easier to include some BASIC along with ML,<br />
particularly with tricky programming involving arrays or file handling, which can be<br />
more trouble to convert to ML than <strong>the</strong>y're worth.<br />
Receiving Lines from <strong>the</strong> Keyboard<br />
JSR $A560 prints a flashing cursor, <strong>the</strong>n transfers <strong>the</strong> screen line into <strong>the</strong> 88-byte in<br />
put buffer starting at $200. This is easier to use than <strong>the</strong> Kernal CHRIN routine. <strong>The</strong><br />
end of line is marked by a zero byte (replacing <strong>the</strong> carriage return character actually<br />
entered). Once input, <strong>the</strong> line can be processed in any way you want; normally, <strong>the</strong><br />
<strong>64</strong> tokenizes <strong>the</strong> buffer and treats it as BASIC. To get <strong>the</strong> feel of this, load and out<br />
put characters from $200 onward with CHROUT.<br />
Processing BASIC Variables<br />
VARPTR (Chapter 6) uses JSR $B08B to input a variable name and search for it in<br />
BASIC RAM. <strong>The</strong> address returns in Y and A.<br />
Printing strings and numerals. A cluster of routines around $AB1E outputs<br />
strings without <strong>the</strong> need to repeatedly call $FFD2 to print individual characters. For<br />
example, JSR $BDDD, <strong>the</strong>n JSR $AB1E prints <strong>the</strong> contents of FAC1. JSR $BDDD<br />
converts <strong>the</strong> accumulator to an ASCII string, setting pointers ready for JSR $AB1E to<br />
print.<br />
$AB1E is generally useful and will print any ASCII string up to a zero byte (or<br />
double quotation mark), providqfj A (low) and Y (high) were set correctly on entry.<br />
Inputting parameters for SYS calls from BASIC. PRINT@ and Computed<br />
GOTO in Chapter 6 are examples which take in numbers, in <strong>the</strong> first case in <strong>the</strong><br />
range 0-255, and in <strong>the</strong> second in <strong>the</strong> two-byte range 0-65535. <strong>The</strong> entire range<br />
250
ML Methods Specific to <strong>the</strong> <strong>64</strong><br />
isn't used in ei<strong>the</strong>r example, of course. JSR $B79B and JSR $AD8A fetch <strong>the</strong> num<br />
bers. <strong>The</strong>re's generally a choice of registers and memory locations for use in transfer<br />
ring data between ROM routines. $B79B returns <strong>the</strong> value in both $65 and X;<br />
$AD8A evaluates numeric expressions (for instance, VAL(X$)+6*X) and leaves <strong>the</strong><br />
result in FAC1, so <strong>the</strong>re's less choice with this. Computed GOTO shows one<br />
continuation with FAC1, namely conversion to integer format using only two bytes.<br />
Calculations<br />
This section explains how to carry out calculations in ML. With <strong>the</strong> help of Chapter<br />
11, it will be clear that useful results are relatively easy to achieve, so you should<br />
not be held back by problems requiring arithmetic.<br />
Floating-Point Accumulator 1 (FAC1 for short) is a major location for number<br />
work. Occupying six bytes from $61 to $66, <strong>the</strong> format is slightly different from <strong>the</strong><br />
five-byte variable storage of BASIC. Conversion from FAC1 to <strong>the</strong> memory format<br />
(MFLPT, for short) rounds off <strong>the</strong> extra bit.<br />
FAC storage can be si^jnarize^J^EMMMMS, having an exponent byte, four<br />
bytes of data (mantissa), and jLsign. If E is set to 0, <strong>the</strong> number is treated as 0<br />
regardless_pf M's contents. "<br />
Some math routines (like negation) operate only on FAC1. However, many use<br />
EAC2^ including all <strong>the</strong> binary operations. For example, when adding, FAC1 and<br />
FAC2 are each loaded with a value; when <strong>the</strong> addition subroutine is called, <strong>the</strong><br />
numbers are totaled and <strong>the</strong> result left in FAC1.<br />
FAC1 can be stored in RAM ei<strong>the</strong>r by copying <strong>the</strong> six bytes for later use or by<br />
using one of <strong>the</strong> routines around $BBC7. You'll see an example in <strong>the</strong> ML hex-todecimal<br />
converter later on.<br />
Storing FAC1 in MFLPT format is, of course, part of BASIC, and many of Chap<br />
ter 11 's routines are relevant to BASIC. As an example, $BD7E adds <strong>the</strong> contents of<br />
A to FAC1, and $BAE2 multiplies FAC1 by 10. Between <strong>the</strong>m, <strong>the</strong>se routines allow<br />
ordinary decimal numbers to be input and stored in FAC1 as each digit is entered.<br />
<strong>The</strong> ROM routine at $B391 is an easy way to put integers from —32768 to<br />
+32767 into FAC1 as floating-point numbers. <strong>The</strong> following routine loads 1 into<br />
FAC1; A holds <strong>the</strong> high byte and Y holds <strong>the</strong> low byte of <strong>the</strong> number, in 16-bit<br />
signed integer format.<br />
LDY #1<br />
/LDA #0<br />
/JSR $B391<br />
<strong>The</strong> USR Function<br />
USR is helpful with ML calculation programming. It is less often used with BASIC,<br />
because function definitions are much easier to write than USR. However, USR is a<br />
function which is always followed by a value in paren<strong>the</strong>ses, like PEEK. USR lets<br />
you pass a value from BASIC to ML by enclosing <strong>the</strong> value in paren<strong>the</strong>ses after <strong>the</strong><br />
keyword. You can pass a value from ML back to BASIC by assigning a variable to<br />
<strong>the</strong> function.<br />
For example, consider <strong>the</strong> statement A = USR(6). When BASIC finds this, <strong>the</strong><br />
value in paren<strong>the</strong>ses is computed, and <strong>the</strong> value is put into FAC1. <strong>The</strong>n BASIC<br />
251
ML Methods Specific to <strong>the</strong> <strong>64</strong><br />
executes JMP $0310. Locations $0310-0312 (784-786) act as a user-defined jump<br />
vector, just like <strong>the</strong> Kernal jump tables at <strong>the</strong> top of memory. $0310 contains a JMP<br />
instruction, and you are responsible for loading <strong>the</strong> next two bytes with a destination<br />
address. If this vector contains 0310 JMP $C000, for example, program flow is trans<br />
ferred to <strong>the</strong> routine at $C000, where you may process <strong>the</strong> value in FAC1. When <strong>the</strong><br />
ML routine ends with RTS, <strong>the</strong> value <strong>the</strong>n contained in FAC1 is assigned to <strong>the</strong><br />
BASIC variable A.<br />
Thus, POKE 784,96 puts RTS at $0310, so USR returns without any alteration to<br />
FAC1. PRINT USR(6) is 6.<br />
Program 8-1 is a more elaborate example. Load and run <strong>the</strong> program; <strong>the</strong>n enter<br />
any number in <strong>the</strong> legal range and five bytes will be output in MFLPT format.<br />
Program 8-1.<br />
USR Demonstration<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader/' Appendix C.<br />
10 FOR J=828 TO 841:READ X:POKE J,X:NEXT :rem 15<br />
20 POKE 784,76:POKE 785,60:POKE 786,3 :rem 217<br />
30 INPUT X :rem 75<br />
40 X=USR(X) :rem 156<br />
50 FOR J=842 TO 846:PRINT PEEK(J);:NEXT :rem 241<br />
100 DATA 32,199,187,162,4,181,92 :rem 45<br />
110 DATA 157,74,3,202,16,248,96 :rem 247<br />
120 PRINT:GOTO 30 :rem 246<br />
<strong>The</strong> first byte controls <strong>the</strong> magnitude of <strong>the</strong> number. <strong>The</strong> o<strong>the</strong>rs determine its<br />
value, except for <strong>the</strong> high bit of <strong>the</strong> first data byte, which handles <strong>the</strong> sign. This is<br />
handy if you wish to store floating-point numbers in memory. <strong>The</strong> program works<br />
by directing USR to <strong>the</strong> following:<br />
033C<br />
033F<br />
0341<br />
0343<br />
0346<br />
0347<br />
0349<br />
JSR<br />
LDX<br />
LDA<br />
STA<br />
DEX<br />
BPL<br />
RTS<br />
$BBC7<br />
#$04<br />
$5C,X<br />
$034A,X<br />
$0341<br />
;FAC1 INTO MFLPT FORMAT AT $5C<br />
;MOVE TO MORE PERMANENT RAM AREA<br />
;WHERE PEEKS CAN RECOVER<br />
;BACK TO BASIC AFTER USR<br />
Line 20's POKEs direct USR to $033C. Line 40 executes a USR command. First,<br />
whatever number was input is converted to FAC1 format. <strong>The</strong>n BASIC jumps to<br />
$0310, where it finds JMP $033C. Here, FAC1 is rearranged in RAM, and its five<br />
bytes are moved from <strong>the</strong>ir temporary storage (which would soon be overwritten)<br />
into <strong>the</strong> tape buffer. After RTS, BASIC resumes and MFLPT can be PEEKed.<br />
For example, suppose you want to evaluate — 10*X*X. Enter <strong>the</strong> following at<br />
$033C:<br />
033C<br />
033F<br />
0342<br />
0345<br />
0348<br />
JSR<br />
JSR<br />
JSR<br />
JSR<br />
RTS<br />
$BC0C<br />
$BA30<br />
$BFB4<br />
$BAE2<br />
;COPY FAC1 INTO FAC2<br />
;MULTIPLY FAC1 BY FAC2; RESULT IN FAC1<br />
;NEGATE FAC1<br />
;MULTIPLY FAC1 BY 10; RESULT IN FAC1<br />
252
ML Methods Specific to <strong>the</strong> <strong>64</strong><br />
Return to BASIC, <strong>the</strong>n POKE 784,76:POKE 785,60:POKE 786,3 and PRINT USR(8).<br />
You'll get —<strong>64</strong>0, and so on. If you have no ML monitor, POKE <strong>the</strong> following num<br />
bers using BASIC to locations 828-839: 32, 12, 220, 32, 48, 218, 32, 180, 223, 76,<br />
226, and 218.<br />
Routines can be strung toge<strong>the</strong>r like this in many ways, though it's helpful to<br />
know ML well enough to appreciate potential problems. For instance, add JSR<br />
$BFED to calculate EXP of FAC1. Alternatively, use temporary storage areas. For in<br />
stance, <strong>the</strong> following routine puts FAC1 into MFLPT form beginning at $57, <strong>the</strong>n<br />
multiplies FAC1 by <strong>the</strong> MFLPT number it finds starting at $57. In effect, it is simply<br />
ano<strong>the</strong>r way of multiplying a number by itself.<br />
JSR $BBCA<br />
LDA #$57<br />
LDY #$00<br />
JSR $BA28<br />
USR is not a very important function, but as <strong>the</strong>se examples show, it can be<br />
useful in testing ML calculation routines.<br />
Hex-to-Decimal Conversion<br />
<strong>The</strong> program below is a longer program example using ML arithmetic that illustrates<br />
several points. INIT sets FAC1 to 0 and stores 16 in MFLPT form in spare RAM (in<br />
fact, in <strong>the</strong> random number storage area). GET not only fetches an individual charac<br />
ter, but also flashes <strong>the</strong> cursor and tests for <strong>the</strong> RUN/STOP key. PROC is <strong>the</strong><br />
processing part; each digit is converted from ASCII ($30 to <strong>the</strong> character 0, for ex<br />
ample), added to FAC1, and, if a fur<strong>the</strong>r digit is wanted, multiplied by 16. PRINT<br />
outputs <strong>the</strong> result.<br />
INIT<br />
GET<br />
EXIT<br />
PROC<br />
033C<br />
033E<br />
0340<br />
0342<br />
0334<br />
0346<br />
0348<br />
0349<br />
034B<br />
034D<br />
034F<br />
0351<br />
0353<br />
0355<br />
0358<br />
035A<br />
035C<br />
035E<br />
035F<br />
0362<br />
03<strong>64</strong><br />
0365<br />
0368<br />
LDA<br />
STA<br />
LDA<br />
STA<br />
LDX<br />
STA<br />
DEX<br />
BNE<br />
LDA<br />
STA<br />
LDA<br />
STA<br />
STA<br />
JSR<br />
BNE<br />
LDA<br />
STA<br />
RTS<br />
JSR<br />
BEQ<br />
PHA<br />
JSR<br />
PLA<br />
#$04<br />
$FE<br />
#$00<br />
$61<br />
#$04<br />
$8B,X<br />
$0346<br />
#$85<br />
$8B<br />
#$00<br />
$cc<br />
$CF<br />
$FFE1<br />
$035F<br />
#$01<br />
$cc<br />
$FFE4<br />
$0355<br />
$FFD2<br />
;COUNT FOUR DIGITS<br />
;FAC1 NOW ZERO<br />
;LOOP PUTS 16 IN<br />
;MFLPT FORM INTO<br />
;8B-8F (RND AREA)<br />
;FOR REPEATED USE<br />
;CONTROL CURSOR<br />
;TEST RUN/STOP KEY<br />
;IF RUN/STOP PRESSED,<br />
;FLASH CURSOR &<br />
;RETURN<br />
;GET CHARACTER FROM KEYBD<br />
;WAIT FOR NON-NULL CHR<br />
;SAVE CHARACTER ...<br />
;ECHO TO SCREEN...<br />
;RECOVER<br />
253
ML Methods Specific to <strong>the</strong> <strong>64</strong><br />
036B<br />
036D<br />
036F<br />
0371<br />
0374<br />
0376<br />
0378<br />
037A<br />
037C<br />
037F<br />
0381<br />
PRINT 0383<br />
0369 CMP #$41 ;COMPARE WITH "A"<br />
;BRANCH IF LESS THAN "A"<br />
0386<br />
0389<br />
038B<br />
038E<br />
0390<br />
BCC<br />
SBC<br />
SBC<br />
JSR<br />
DEC<br />
BEQ<br />
LDA<br />
LDY<br />
JSR<br />
BEQ<br />
BNE<br />
JSR<br />
JSR<br />
LDA<br />
JSR<br />
BEQ<br />
BNE<br />
$036F<br />
#$08<br />
#$2F<br />
$BD7E<br />
$FE<br />
$0383<br />
#$8B<br />
#$00<br />
$BA28<br />
$0355<br />
$0355<br />
$BDDD<br />
$AB1E<br />
#$0D<br />
$FFD2<br />
$033C<br />
$033C<br />
;CONVERT ASC 0-F TO 0-15<br />
;ADD A TO FAC1<br />
;REDUCE COUNTER<br />
;EXIT AFTER FOUR DIGITS<br />
;SET POINTERS TO $8B<br />
;MULTIPLY FAC1 BY MFLPT AT $8B (IE BY 16)<br />
;BRANCH BACK (RELOCATABLE) FOR<br />
;NEXT DIGITS<br />
;CONVERT FAC1 INTO STRING AT $100<br />
;PRINT STRING<br />
;PRINT RETURN TO GO TO<br />
;NEXT LINE<br />
;BRANCH BACK (RELOCATABLE) FOR<br />
;NEXT HEX NUMBER<br />
SYS828 accepts four-digit hex numbers and continues until <strong>the</strong> RUN/STOP key<br />
is pressed. <strong>The</strong> routine is relocatable. For binary-to-decimal conversion, POKE 829,8:<br />
POKE 844,130 after running.<br />
Random Numbers<br />
Random numbers are used in simulations and in games. From ML, <strong>the</strong> easiest<br />
method is to call ROM routines, which have <strong>the</strong> advantage of being repeatable if<br />
you want <strong>the</strong>m to be. JSR $E0D3 is equivalent to RND(—X) and seeds <strong>the</strong> random<br />
number storage area with a value dependent on FAC1. <strong>The</strong> reason RND of negative<br />
integers is always very small is that <strong>the</strong> FLPT bytes are simply switched around.<br />
$E0D3 can be used to seed a constant value. However, with ML it's quicker to<br />
store your own seed value directly in $8B-$8F. JSR $E0BE uses a formula to cal<br />
culate a new random number from <strong>the</strong> previous one, leaving <strong>the</strong> result in both FAC1<br />
and $8B-$8F. <strong>The</strong> sequence is completely predictable.<br />
JSR $E09E uses CIA timers to generate a true random number, except in <strong>the</strong><br />
sense that very short ML loops may start to show regularities.<br />
Typically, during testing, a seed is chosen. <strong>The</strong>n $E0BE is used to give a repeatable<br />
sequence (this eases debugging). <strong>The</strong> seed is replaced by $E09E for use.<br />
One- or Two-Byte Random Numbers<br />
<strong>The</strong>se are often more useful in ML. You could use <strong>the</strong> following routine, which uses<br />
all four bytes, excluding <strong>the</strong> exponent, presumably increasing <strong>the</strong> result's<br />
randomness.<br />
JSR $E0BE ;NEW RND NUMBER FROM OLD<br />
LDA $8C<br />
EOR $8D ;COMBINE DATA BYTES<br />
EOR $8E ;INTO COMPOSITE BYTE<br />
EOR $8F<br />
Suppose you want something to happen 10 times in every 256. All you need is<br />
CMP #$0A, <strong>the</strong>n BCC to branch when <strong>the</strong> accumulator holds 0-9.<br />
254
ML Methods Specific to <strong>the</strong> <strong>64</strong><br />
If you need a random number in ML within a fixed range, say, 0-20, <strong>the</strong> easiest<br />
method is to use repeated subtraction (ra<strong>the</strong>r than to get a decimal, multiply by 20,<br />
take an integer, and add 1):<br />
RANGE CMP #$15 ;COMPARE WITH DECIMAL 21<br />
BCC FOUND ;NUMBER IN RANGE 0-20<br />
SBC #$15 SUBTRACT DECIMAL 21<br />
JMP RANGE ;COMPARE AGAIN<br />
FOUND CONTINUE ;A HOLDS 0-20 DECIMAL<br />
Note that a random number from 48 to 57 is simply 48 plus a random number<br />
from 0 to 9. Ano<strong>the</strong>r easy, but slow method is to check a resultnancTgo back if it's<br />
not in range.<br />
If you need random numbers in quantity, it's faster to generate your own. All<br />
you need is one RAM location (or two for a 16-bit number). <strong>The</strong> following routine<br />
uses a single byte, LO (for example, $FB):<br />
LDA<br />
LO<br />
ASL<br />
ASL<br />
CLC<br />
ADC #$odd number<br />
ADC LO<br />
STA LO<br />
Any odd number can be selected ($81, for example). <strong>The</strong> contents of LO now<br />
cycle through 256 different values in sequence. <strong>The</strong> method uses 5 times <strong>the</strong> pre<br />
vious value plus an odd number, ignoring overflow above 255; in o<strong>the</strong>r words, x be<br />
comes 5x+c (mod 256). Five is easy to program, but 9, 11, 21, or o<strong>the</strong>r numbers can<br />
also be used.<br />
Each call to this routine loads A with <strong>the</strong> next number; this is not necessarily<br />
suitable as a random number, since <strong>the</strong> series repeats, but EOR with a timer (for ex<br />
ample, EOR $DC04) will scramble LO into an unpredictable form.<br />
For a two-byte random number, use <strong>the</strong> following:<br />
CLC<br />
LDA<br />
ADC<br />
STA<br />
CLC<br />
LDA<br />
ADC<br />
STA<br />
LDA<br />
ADC<br />
STA<br />
LO<br />
HI<br />
HI<br />
#$odd number<br />
LO<br />
LO<br />
#$any number<br />
HI<br />
HI<br />
In this case, x becomes 257*X+c (mod 65536) where c is odd. Any series gen<br />
erated from this repeats at 65,536 cycles. Sequences generated by this method al<br />
ways produce alternate odd and even values, and internal subseries are common, so<br />
<strong>the</strong> guarantee of a very long repeat interval doesn't insure success in any actual<br />
application.<br />
255
ML Methods Specific to <strong>the</strong> <strong>64</strong><br />
Series Calculations<br />
All of <strong>the</strong> <strong>64</strong>'s ma<strong>the</strong>matical functions are evaluated by series summation. Briefly,<br />
<strong>the</strong> value to be converted is first put into a smaller range. Trigonometric functions,<br />
for instance, repeat regularly, so <strong>the</strong>ir input values can be reduced (if large) by<br />
subtracting multiples of pi. <strong>The</strong>n a series evaluation works out <strong>the</strong> function's value,<br />
and finally an allowance is made for <strong>the</strong> initial scaling-down process.<br />
In <strong>the</strong> <strong>64</strong>, <strong>the</strong> ROM routine at $E059 sums <strong>the</strong> series. <strong>The</strong> following short ex<br />
ample shows how:<br />
LDY #$03<br />
LDA #$40<br />
JSR $E059<br />
RTS<br />
Location $0340 must contain 2, and locations $0341-$0345, $0346-$034A, and<br />
$034B-$034F each must contain a number in MFLPT format. If we designate <strong>the</strong>se<br />
Nl, N2, and N3, calling <strong>the</strong> routine replaces FACl's value with N3 + N2*X +<br />
N1*X*X. Working out <strong>the</strong> actual series parameters is beyond this book's scope.<br />
Integer to Floating-Point Conversion and Powers of Two<br />
Although conversion of two-byte integers into floating-point form is often useful, <strong>the</strong><br />
standard ROM routine at $B391 converts A (high) and Y (low) into <strong>the</strong> range from<br />
—32768 to 32767, <strong>the</strong> range of integer variables.<br />
<strong>The</strong> following routine puts A (high) and Y (low) into FAC1 in <strong>the</strong> range 0-<br />
32767. Note that <strong>the</strong> <strong>64</strong> has vectors near <strong>the</strong> start of RAM which can be changed to<br />
allow for just such modifications.<br />
LDX #$00<br />
STX $0D<br />
STA $62 ;HIGH<br />
STY $63 ;LOW (NOTE REVERSE ORDER)<br />
LDX #$90 ;EXPONENT (#$91 DOUBLES; #$94 MULTIPLIES BY 16)<br />
SEC<br />
JSR $BC49 ;CONVERT TO FAC1<br />
Using<br />
RAM Under ROM<br />
As we saw in Chapter 5, <strong>the</strong> normal operating system is stored in two ROMs, one at<br />
$A000-$BFFF (BASIC ROM), <strong>the</strong> o<strong>the</strong>r at $E000-$FFFF (Kernal ROM), which work<br />
toge<strong>the</strong>r as <strong>the</strong> familiar BASIC language. <strong>The</strong>se ROMs can be switched out ei<strong>the</strong>r by<br />
hardware—when an external cartridge is sensed by <strong>the</strong> <strong>64</strong>—or by software. With<br />
cartridges, <strong>the</strong> software controlled lines HIRAM and LORAM have higher priority<br />
than <strong>the</strong> EXROM and GAME lines which control <strong>the</strong> <strong>64</strong>'s sensing of plug-in ROMs,<br />
so <strong>the</strong> methods in this section will actually apply to <strong>64</strong>s with or without a cartridge.<br />
For example, a language like Forth or Logo could exist on a cartridge from $8000 to<br />
$BFFF, but RAM BASIC could still be switched in to replace it, though <strong>the</strong>re might<br />
be complications. For example, <strong>the</strong> new language might have no equivalent to<br />
POKE, or it could use RAM from $A000 to $BFFF itself. For simplicity, most of this<br />
section assumes that your <strong>64</strong> has no plug-in cartridges present.<br />
256
ML Methods Specific to <strong>the</strong> <strong>64</strong><br />
Moving BASIC into RAM<br />
<strong>The</strong> <strong>64</strong>'s software control allows both BASIC and Kernal ROMs to be switched out<br />
in favor of RAM. This means <strong>the</strong> entire BASIC language can be stored in RAM,<br />
where it can be modified. This feature alone gives <strong>the</strong> <strong>64</strong> possibilities that many<br />
computers don't have. Of course, <strong>the</strong>re is a potential problem: Programs which write<br />
into <strong>the</strong>se RAM areas will corrupt <strong>the</strong>m, something impossible with a ROM-based<br />
language.<br />
<strong>The</strong> process is simple enough. First, note that writing to <strong>the</strong> ROM area, whe<strong>the</strong>r<br />
or not RAM is selected, always writes to RAM. Second, HIRAM and LORAM are bits<br />
1 and 0, respectively, in <strong>the</strong> control port. Thus, POKE 1,55 selects ROM BASIC,<br />
while POKE 1,54 switches out <strong>the</strong> BASIC ROM, and POKE 1,53 switches out both<br />
BASIC and Kernal ROMs. Note that bits 0 and 1 of location 0 must both be set for<br />
this process to work. Normally, this is automatic, but location 0 can sometimes be<br />
corrupted—see POKE in Chapter 3. From now on assume POKE 1,53 will allow us<br />
to modify ei<strong>the</strong>r of <strong>the</strong> two ROMs.<br />
If POKE 1,53 is done without preparation, BASIC will disappear as far as <strong>the</strong> <strong>64</strong><br />
is concerned, and any BASIC will immediately crash.This program copies BASIC and<br />
<strong>the</strong> Kernal into RAM:<br />
3 FOR J=40960 TO 49151: POKE J,PEEK(P: NEXT :REM MOVE BASIC TO RAM<br />
4 FOR J=57344 TO 65535: POKE J,PEEK(J): NEXT :REM MOVE KERNAL TO RAM<br />
5 POKE 1,53 :REM SWITCH TO RAM<br />
A single long loop can't be used, because it sets <strong>the</strong> VIC chip wrongly. <strong>The</strong> pro<br />
gram exploits <strong>the</strong> fact that POKE puts a byte into <strong>the</strong> RAM underlying ROM, even<br />
when ROM is selected. It's quite slow because of <strong>the</strong> slowness of BASIC, taking<br />
more than a minute to perform 16384 PEEK and POKE combinations. Program 8-2<br />
uses ML to speed things up:<br />
Program 8-2.<br />
ROM RAM<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader/' Appendix C.<br />
2 DATA 120,165,1,72,169,55,133,1,169,160 :rem 170<br />
3 DATA 133,3,160,0,132,2,177,2,145,2,136 :rem 149<br />
4 DATA 208,249,230,3,165,3,240,8,201,192,208<br />
:rem 109<br />
5 DATA 239,169,224,208,229,104,133,1,88,96 :rem 36<br />
10 FOR J=49152 TO 49193:READ X:POKE J,X:NEXT<br />
:rem 223<br />
20 SYS 49152:POKE 1,53 :rem 148<br />
At this stage, POKE 1,55 and POKE 1,53 can be alternated with absolutely no<br />
visible effect, since <strong>the</strong> two versions of BASIC are identical.<br />
Making Modifications to RAM BASIC<br />
Small modifications. It makes sense to signal that modified BASIC is present.<br />
Type in POKE 41853,33. This replaces READY, with READY! so whenever you see<br />
READY! you'll be sure RAM BASIC is in use. Enter POKE 1,55 and POKE 1,53 alter<br />
nately, to flip from one version of BASIC to <strong>the</strong> o<strong>the</strong>r. Generally, tables are <strong>the</strong><br />
257
ML Methods Specific to <strong>the</strong> <strong>64</strong><br />
easiest features of BASIC to alter. For example, BASIC keywords are stored from<br />
41118, starting with END, and <strong>the</strong>se can be changed. It's easiest to keep keywords<br />
<strong>the</strong> same length as <strong>the</strong>ir original form, although it's possible (say) to redefine BASIC<br />
with single-letter and o<strong>the</strong>r short keywords, allowing very long (but hard to read!)<br />
lines of BASIC to be entered. Ano<strong>the</strong>r example is <strong>the</strong> power-up message; a later prograin<br />
shows how this can be altered as far as is possible.<br />
Larger modifications. Significant adjustments to BASIC require some ML<br />
knowledge and <strong>the</strong> information on BASIC'S structure given in Chapter 11. At <strong>the</strong><br />
simplest level, we can alter locations like $EAEA (delay between repeats) and $EB1D<br />
(cursor countdown) to alter cursor control. At a more advanced level, Chapter 6<br />
shows how Computed GOTO and MERGE can be introduced into BASIC, and how<br />
<strong>the</strong> keyboard's tables can be redefined. <strong>The</strong>se small adjustments are known as<br />
patches; $E4E0 (Filename) and $E4EC (Color) are two patches added to more recent<br />
<strong>64</strong> Kernal ROMs, correcting a tape name bug and a screen color effect. As ano<strong>the</strong>r<br />
example, we can modify RUN to eliminate <strong>the</strong> test for RUN/STOP, <strong>the</strong> CONT line<br />
updates, and <strong>the</strong> end-of-program test (so END becomes necessary) quite easily, with<br />
a small speed increase of 3-1/2 percent. With BASIC in RAM, use <strong>the</strong>se POKEs:<br />
POKE 42960,160: POKE 42961,0: POKE 42962,177: POKE 42963,122: POKE 429<strong>64</strong>,208<br />
POKE 42965,49: POKE 42966,24: POKE 42967,169: POKE 42968,4: POKE 42987,208<br />
Upgrading ROMs<br />
Earlier CBM computers had to have ROMs changed, at some expense, when<br />
improvements were made to BASIC. With <strong>the</strong> <strong>64</strong> this is no longer necessary. New<br />
versions of BASIC can be used as <strong>the</strong>y become available. If <strong>the</strong> changes aren't too<br />
great, a program with ML to move BASIC to RAM, and a series of values to POKE<br />
into RAM to upgrade BASIC will be faster than loading <strong>the</strong> entire 16K from disk<br />
or tape.<br />
Many <strong>64</strong>s have a version number 0; PRINT PEEK(65408) in <strong>the</strong> Kernal to see<br />
this. Newer ROMs return 3. <strong>The</strong>se have a few improvements: INPUT with a long<br />
prompt string works correctly with wraparound to <strong>the</strong> next line, and <strong>the</strong> screen edit<br />
bug is removed (where BASIC lines overrunning <strong>the</strong> bottom screen line, <strong>the</strong>n back<br />
spaced, crash). Also, like very early <strong>64</strong>s, POKEs to <strong>the</strong> screen are visible after {CLR}<br />
without needing color RAM POKEs.<br />
If you'd like a different version of <strong>64</strong> ROM, an easy way to compare ROMs is<br />
with Program 8-3 or a similar comparison routine:<br />
Program 8-3.<br />
Compare ROM<br />
10 OPEN 1,8,4,"KERNAL 02"<br />
20 OPEN 2#8,5#"KERNAL 03"<br />
30 FOR J=14*4096 TO 65535<br />
40 GET#1,X$:GET#2,Y$<br />
50 IF X$OY$ THEN PRINT J; "NEW=" ASC(Y$+CHR$ (0) )<br />
60 NEXT:CLOSE 1:CLOSE 2<br />
258
ML Methods Specific to <strong>the</strong> <strong>64</strong><br />
Program 8-3 compares Kernals, assuming <strong>the</strong>se to have been saved with a mon<br />
itor, and commands like .S "KERNAL",08,E000,FFFF, but BASIC ROMs can be com<br />
pared, too. Program 8-4 can be used at <strong>the</strong> start of a session; it converts version 0 to<br />
version 3 (<strong>the</strong>re are no BASIC ROM differences):<br />
Program 8-4.<br />
ROM Upgrade<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
20 FOR 1=40960 TO 49151:POKE I,PEEK(I):NEXT<br />
:rem 143<br />
30 FOR 1=57334 TO 65535:POKE I,PEEK(I):NEXT<br />
:rem 151<br />
40 POKE 1,53 :rem 88<br />
50 READ X:IF X>255 THEN A=X:GOTO 50<br />
rem 177<br />
60 IF X
ML Methods Specific to <strong>the</strong> <strong>64</strong><br />
RUN/STOP-RESTORE and RAM BASIC<br />
RUN/STOP-RESTORE calls <strong>the</strong> IOINIT routine, which unless changed sets location<br />
1 to 55. This would automatically convert RAM BASIC back to ROM. <strong>The</strong> easiest<br />
way to prevent this is to POKE <strong>64</strong>982,53 which changes IOINIT in RAM. To see<br />
how this might work, POKE <strong>the</strong>se values as well:<br />
POKE 60634,1: POKE 60633,1: REM BACKGROUND AND BORDER WHITE<br />
POKE 58677,0 : REM CHARACTERS TO BLACK<br />
<strong>The</strong> first pair alters <strong>the</strong> <strong>64</strong>'s default screen color values; <strong>the</strong> last changes <strong>the</strong> color in<br />
CINT. Now press RUN/STOP-RESTORE; RAM BASIC is retained and <strong>the</strong> new col<br />
ors appear. RAM BASIC is made completely secure against RESTORE in this way.<br />
RESET and RAM BASIC<br />
Reset with SYS <strong>64</strong>738 uses IOINIT like RUN/STOP-RESTORE. But with RAM<br />
BASIC as we've developed it so far, it prints 51,216 bytes free! This happens because<br />
ano<strong>the</strong>r routine, RAMTAS, which checks <strong>64</strong>'s RAM, detects <strong>the</strong> first ROM location at<br />
$D000, since RAM does in fact now exist up to <strong>the</strong>re. To avoid this (<strong>the</strong> top-of-<br />
BASIC is set too high) RAMTAS must be modified; <strong>the</strong> easiest method is just to put<br />
in <strong>the</strong> desired top-of-BASIC. Thus, RAM BASIC can be made secure against a soft<br />
ware reset.<br />
Hardware resets are different. A reset switch sets <strong>the</strong> <strong>64</strong> for ROM BASIC, <strong>the</strong>n<br />
goes through <strong>the</strong> usual ROM reset routines, including putting 55 into location 1. You<br />
may expect POKE 1,53 to revert to RAM BASIC without any problems, since reset<br />
ting leaves <strong>the</strong> area alone, but <strong>the</strong>re's one subtle effect of reset: It tests RAM by<br />
POKEing in a value, <strong>the</strong>n reading it back, so it will detect BASIC ROM at $A000.<br />
However, it will also corrupt location $A000, POKEing in $55. POKE 40960,<br />
PEEK(40960) after any hardware reset so $A000 is correct.<br />
Protection against ei<strong>the</strong>r type of reset can also be arranged by putting bytes 195,<br />
194, 205, 56, and 48 sequentially from $8004 on, with ($8000) holding <strong>the</strong> address<br />
to be jumped to on reset ($E37B to warm start BASIC, for example). <strong>The</strong> indirect<br />
vector ($8002) should point to RUN/STOP-RESTORE's destination (perhaps $FEBC,<br />
which returns from <strong>the</strong> interrupt generated by RUN/STOP-RESTORE).<br />
Full Example of Modified BASIC in RAM<br />
Program 8-5 shows a variety of features demonstrating everything we've seen so far.<br />
It sets RAM top to $8000 without checking RAM (as a result <strong>the</strong>re's no delay before<br />
<strong>the</strong> sign-on message); it moves <strong>the</strong> start-of-screen to $8000, which means several<br />
adjustments to <strong>the</strong> VIC chip; BASIC starts at $0400; RUN/STOP-RESTORE and<br />
both resets are nullified as far as possible. Green characters on black are selected. Be<br />
cause this configuration simulates <strong>the</strong> PET/CBM, many programs for PET/CBM will<br />
run, provided <strong>the</strong>y don't use ML routines which are too machine-specific.<br />
Program 8-5. PET Your <strong>64</strong><br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
1 PRINTM{CLR}{2 DOWN}SET <strong>64</strong> TO SIMULATE PET/CBM BA<br />
SIC" :rem 96<br />
3 PRINT"LINES 400-600 CONTROL COLOR SCHEME":rem 43<br />
260
ML Methods Specific to <strong>the</strong> <strong>64</strong><br />
5 PRINT"LINE 1200 SETS BASIC TOP; YOU CANM:rem 143<br />
6 PRINT"SELECT OTHER VALUES, E.G. $7000." :rem 70<br />
8 PRINT"LINES 1300-1400 SET BASIC & SCREEN11<br />
:rem 110<br />
9 PRINT"STARTS; YOU CAN USE OTHER VALUES" :rem 52<br />
11 PRINT"LINES 1500-1600 SELECT VIC BANK; ":rem 97<br />
12 PRINT"OFTEN THIS WON'T BE NEEDED." :rem 177<br />
14 PRINT"LINE 1700 PERSONALIZES THE SIGNON"<br />
:rem 116<br />
16 PRINT"NEWER <strong>64</strong>S DON'T NEED LINE 1800" :rem 223<br />
18 PRINT"NOTE: IF YOU USE A HARDWARE RESET":rem 53<br />
19 PRINT"SWITCH, RESETTING LEAVES MODIFIED"<br />
:rem 239<br />
20 PRINT"BASIC INTACT IN RAM, EXCEPT FOR" :rem 171<br />
21 PRINT"LOCATION $A0001 POKE 40960,148" :rem 158<br />
22 PRINT"CORRECTS THIS." :rem 0<br />
25 PRINT "{3 DOWN}{RVS}PRESS RETURN TO CONTINUE<br />
{OFF}" :rem 132<br />
26 GET R$:IF R$="" THEN 26 :rem 21<br />
27 IF R$CHR$(13) THEN 26 :rem 53<br />
100 FOR J=49152 TO 49193:READ X:POKE J,X:NEXT:SYS<br />
{SPACE}49152 :rem 77<br />
200 POKE 1,53 :rem 134<br />
300 POKE 41853,33 :rem 89<br />
400 POKE <strong>64</strong>982,53 :rem 100<br />
500 POKE 60634,0 :rem 35<br />
600 POKE 60633,0 :rem 35<br />
700 POKE 58677,5 :rem 56<br />
1000 POKE <strong>64</strong>887,240 :rem 195<br />
1100 POKE <strong>64</strong>904,162:POKE <strong>64</strong>905,0 :rem 138<br />
1200 POKE <strong>64</strong>906,160:POKE <strong>64</strong>907,128 :rem 248<br />
1300 POKE <strong>64</strong>913,4 :rem 90<br />
1400 POKE <strong>64</strong>918,128 :rem 199<br />
1500 POKE <strong>64</strong>972,5 :rem 98<br />
1600 POKE 60625,4 :rem 89<br />
1700 FOR J=58494 TO 58505:POKE J,ASC(MID$("REVISED<br />
{2 SPACES}CBM",J-58493)):NEXT :rem 149<br />
1800 POKE 58587,134:POKE 58588,2 :rem 166<br />
1900 SYS <strong>64</strong>738 :rem 213<br />
5000 DATA 120,165,1,72,169,55,133,1,169 :rem 122<br />
5010 DATA 160,133,3,160,0,132,2,177,2 :rem 1<br />
5020 DATA 145,2,136,208,249,230,3,165,3 :rem 119<br />
5030 DATA 240,8,201,192,208,239,169,224 :rem 131<br />
5040 DATA 208,229,104,133,1,88,96 :rem 94<br />
In <strong>the</strong> Appendices is a short PET reconfiguration program, which keeps BASIC<br />
in ROM. RUN/STOP-RESTORE and reset <strong>the</strong>refore destroy <strong>the</strong> configuration. This<br />
is partly cured by disabling RESTORE. <strong>The</strong>se two programs show <strong>the</strong> greater<br />
versatility of RAM over ROM BASIC.<br />
261
ML Methods Specific to <strong>the</strong> <strong>64</strong><br />
<strong>Programming</strong> RAM Under BASIC ROM<br />
RAM under BASIC can store programs unrelated to BASIC, as well as graphics infor<br />
mation which <strong>the</strong> VIC chip can access. Graphics can be POKEd in, and <strong>the</strong> VIC chip<br />
reset appropriately. But programs under ROM require <strong>the</strong> use of ML in some form to<br />
be usable. However, this needn't be particularly complex. For example, suppose<br />
some ML programs are stored from $A000 on. All that's needed to access <strong>the</strong>m is<br />
this set of POKEs in RAM, perhaps located at 830 onward:<br />
169, 54, 133,1, 32, 0,160,169, 55,133,1, 96<br />
Perhaps <strong>the</strong> easiest method would be to use READ and DATA statements to<br />
POKE <strong>the</strong> bytes in. After this, SYS 830 will carry out <strong>the</strong> ML routine at $A000<br />
(assuming it ends with an RTS) by putting 54 into 1, jumping to $A000, and putting<br />
55 into 1 on return. In this version, <strong>the</strong> ML mustn't call BASIC routines, of course.<br />
<strong>The</strong> digits 0 and 160 correspond to $A000 in low/high byte form. A routine at<br />
$B055, for example, would need 85, 176 instead of 0, 160. All this, including POKEs<br />
or LOADs of ML under BASIC, can be done from BASIC.<br />
<strong>The</strong> following lines of ML allow access to RAM under BASIC ROM while keep<br />
ing <strong>the</strong> Kernal in ROM:<br />
LDA #$36<br />
STA $01<br />
LDA #$37<br />
STA $01<br />
6RK<br />
; PERFORM PROCESSING HERE<br />
You'll find that some ML monitors will allow you to assemble in <strong>the</strong> BASIC area<br />
after $A000 if you use M 0001 0001 and enter 36, to turn off BASIC ROM. Don't try<br />
to exit to BASIC without BASIC present—it'll crash!—and don't turn off <strong>the</strong> Kernal<br />
ROM by putting $35 into $0001, or <strong>the</strong> keyboard and screen handling will crash.<br />
Modifying BASIC<br />
<strong>64</strong> BASIC has a large number of vectors in RAM; <strong>the</strong>se are addresses which BASIC<br />
uses as it runs. If <strong>the</strong>se vectors are altered, BASIC can be intercepted and new com<br />
mands tested for and executed. Alternatively, old commands can be modified slightly<br />
(or completely) as required.<br />
<strong>The</strong> techniques are simple, but plenty of small problems await <strong>the</strong> programmer.<br />
In particular, when you alter BASIC, any errors in <strong>the</strong> added ML are likely to pre<br />
vent BASIC from working normally. Thus, in order to avoid tedious retyping, it's im<br />
portant to save programs as <strong>the</strong>y are written or to use a reset switch for emergency<br />
recovery. All <strong>the</strong> methods use ML, but this need not stop you, since <strong>the</strong> <strong>64</strong> can do<br />
most of <strong>the</strong> work.<br />
Vectors and Wedge Methods<br />
RAM contains blocks of vectors as indirect addresses. LIST, for example, has a com<br />
mand JMP ($0308) within it, so <strong>the</strong> contents of ($0308-$0309) determine where LIST<br />
executes.<br />
262
ML Methods Specific to <strong>the</strong> <strong>64</strong><br />
<strong>The</strong>re are over 20 such vectors. In addition, <strong>the</strong> CHRGET routine at $0073<br />
(which fetches BASIC characters) is accessible for programming. As you'll see, this<br />
allows access to BASIC as it runs, so new commands can be added.<br />
Alterations to CHRGET or to vectors called from BASIC are semipermanent.<br />
Once in place, SYS <strong>64</strong>738 or switching off and on will remove <strong>the</strong>m, tilt o<strong>the</strong>rwise<br />
even RUN/STOP-RESTORE leaves <strong>the</strong>m untouched. This is intentioiial. On <strong>the</strong><br />
o<strong>the</strong>r hand, RUN/STOP-RESTORE sets vectors used by <strong>the</strong> Kernal routines to <strong>the</strong><br />
default values.<br />
First, consider examples involving vector alterations and use of wedges. Gen<br />
erally, wedges are probably easier to program; <strong>the</strong>re's only one subroutine to worry<br />
about, and commands can be added almost indefinitely. However, because<br />
tokenization isn't possible, short commands like @X or @Y are generally used.<br />
BASIC vectors allow some effects to be achieved which aren't possible with<br />
wedges—for example, modified LIST. Such large-scale modifications take prior plan<br />
ning and are not for inexperienced programmers. Kernal vectors are easier to deal<br />
with, in <strong>the</strong> sense that <strong>the</strong>y give convenient access to commands but are not often<br />
used. <strong>The</strong>re are not that many occasions when you would want to reprogram OPEN,<br />
LOAD, or SAVE. Again, Kernal vectors, like <strong>the</strong> three interrupt vectors, are all set to<br />
normal by RUN/STOP-RESTORE.<br />
<strong>The</strong> Wedge<br />
To understand <strong>the</strong> wedge, first look at CHRGET, <strong>the</strong> RAM routine starting at $0073,<br />
which fetches every BASIC character while BASIC runs:<br />
CHRGET 0073 INC $7A ;ADDS 1 TO CURRENT ADDRESS<br />
0075 BNE $0079 ;ADDS 1 TO CURRENT ADDRESS<br />
0077 INC $7B INCREMENT<br />
CHRGOT 0079 LDA CURRENT<br />
007C CMP #$3A ;COLON (OR GREATER) EXITS<br />
007E BCS $008A<br />
0080 CMP #$20 ;SKIPS SPACE CHARACTERS<br />
0082 BEQ $0073<br />
0084 SEC ;ANYTHING FROM $30 to $39<br />
0085 SBC #$30 ;CLEARS C FLAG;<br />
0087 SEC ;ELSE C IS SET<br />
0088 SBC #$D0<br />
008A RTS<br />
CHRGET is stored in ROM at $E3A2; SYS 58303 from BAStC moves it back to<br />
RAM. This may be useful if you've altered CHRGET, but note that it NEWs BASIC.<br />
A call to CHRGET returns with A holding <strong>the</strong> next BASIC character, C clear if an<br />
ASCII numeral was found, and <strong>the</strong> zero flag set if ei<strong>the</strong>r a colon or null byte was<br />
found. JSR $0073 followed by BCC or BEQ is common in ROM, and BCC applies<br />
when a line number (made of ASCII numerals) is read from a GOTO or GOSUB<br />
statement.<br />
ROM also uses JMP $0073. In this case, RTS uses <strong>the</strong> address it finds on <strong>the</strong><br />
stack, and in fact BASIC keywords are executed in this way. <strong>The</strong> 6510 requires that<br />
<strong>the</strong> return address -1 be pushed on <strong>the</strong> stack. .<br />
263
ML Methods Specific to <strong>the</strong> <strong>64</strong><br />
CHRGET can be changed. Try <strong>the</strong> following POKE129,234:POKE128,234-<br />
POKE131,234:POKE130,234 (without spaces between POKEs). This deletes <strong>the</strong> test<br />
for space characters, replacing <strong>the</strong> ML by NOP commands, and BASIC runs exactly<br />
as normal except that spaces outside quotes cause 7SYNTAX ERROR. <strong>The</strong> first SEC<br />
becomes redundant, and SBC #$2F corrects for it. CHRGET shortened like this runs<br />
BASIC faster than normal, as expected; but only by 0.5 percent.<br />
Before seeing how to insert a wedge, note <strong>the</strong> difference between CHRGET and<br />
CHRGOT. CHRGET always increases <strong>the</strong> current address; it's normally called only<br />
once per character. CHRGOT rereads <strong>the</strong> current BASIC character and sets <strong>the</strong> rele<br />
vant flags; <strong>the</strong>refore, whenever processing loses track of <strong>the</strong> current BASIC character<br />
in some way, CHRGOT is always available to recover it.<br />
Wedge Demonstration<br />
If you replace $0073 INC $7A with $0073 JMP $C000, or some o<strong>the</strong>r jump address,<br />
all ROM calls to BASIC characters can be intercepted before <strong>the</strong>y are executed. This<br />
allows us to test for and use new commands in BASIC. A wedge, once inserted, is<br />
quite durable. As mentioned above, RUN/STOP-RESTORE, for example, leaves it<br />
unaltered. That can be important. If your routine has an error, it may be impossible<br />
to POKE in <strong>the</strong> correct values or enter a SYS call to replace <strong>the</strong> wedge, since BASIC<br />
itself is behaving differently from usual.<br />
Many utility programs use wedges. This example puts a JMP at $0073; note that<br />
$0073 or subsequent addresses can be used and are sometimes better, since <strong>the</strong>y<br />
may allow ano<strong>the</strong>r wedge to be used simultaneously. Some wedges test for JMP at<br />
$0073 and allow for <strong>the</strong>m. <strong>The</strong>y also allow zero page RAM (typically $007F-$008A)<br />
to be used in programs.<br />
Program 8-6 adds <strong>the</strong> single command ! to BASIC. When it executes, <strong>the</strong> screen<br />
colors are changed. When naming new commands, it's easiest to use a character that<br />
doesn't appear in ordinary BASIC (like !, @, or &) as an identifier. If desired, it is<br />
easy to add fur<strong>the</strong>r commands, such as !R or !P (with specific functions of <strong>the</strong>ir<br />
own), by getting <strong>the</strong> following BASIC character with JSR $0073 whenever ! is found.<br />
However, to keep <strong>the</strong> example shorter, it adds only a single command.<br />
Program 8-6.<br />
BASIC Wedge Demonstration<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader/'Appendix G<br />
4 FOR J=49152 TO 49203:READ X:POKE J,X:NEXT:SYS 49<br />
152 :rem 232<br />
5 I:GOTO 5 :rem 254<br />
10 DATA 169,76,133,115,169,13,133,116,169,192,133<br />
:rem 116<br />
11 DATA 117,96,230,122,208,2,230,123,32,121,0,201<br />
:rem 69<br />
12 DATA 33,240,3,76,121,0,165,123,201,2,240,247,15<br />
2 :rem 175<br />
13 DATA 72,138,72,238,32,208,238,33,208,104,170,10<br />
4 :rem 203<br />
14 DATA 168,76,115,0 srem 214<br />
2<strong>64</strong>
ML Methods Specific to <strong>the</strong> <strong>64</strong><br />
Note that! is accepted only in program mode. A line like 100! works perfectly,<br />
but! on its own is an error. This is deliberate. It avoids commands being executed<br />
while a program is being written, when <strong>the</strong>y may not be wanted, although you<br />
could obviously create a wedge (like <strong>the</strong> DOS 5.1 wedge) that only works in direct<br />
mode. <strong>The</strong>re are several tests for direct mode. TSX <strong>the</strong>n LDA $0102,X to recover <strong>the</strong><br />
return address is one. Ano<strong>the</strong>r is location $9D, which usually holds $80 in direct<br />
mode. <strong>The</strong> test in <strong>the</strong> example simply checks <strong>the</strong> current address used by CHRGOT;<br />
if it's around $0200, it must be a direct mode line.<br />
<strong>The</strong> only peculiarity of BASIC syntax with wedges is <strong>the</strong> IF statement. IF X=l<br />
THEN: PRINT "ONE" is correct as far as BASIC is concerned, but <strong>the</strong> colon can be<br />
omitted. With wedges, <strong>the</strong> colon can't be left out.<br />
How <strong>the</strong> wedge works. Program 8-6 loads <strong>the</strong> following ML into <strong>the</strong> <strong>64</strong>:<br />
SETUP LDA #$4C ;PUTS JMP $C00D INTO CHRGET<br />
STA $73<br />
LDA #$0D<br />
STA $74<br />
LDA #$C0<br />
STA $75<br />
RTS<br />
WEDGE INC $7A<br />
BNE INC<br />
INC $7B<br />
INC JSR $0079<br />
CMP #$21<br />
BEQ YES<br />
NO JMP $0079<br />
YES LDA $7B<br />
CMP #$02<br />
BEQ NO<br />
TYA<br />
PHA<br />
TXA<br />
PHA<br />
INC $D020<br />
INC $D021<br />
PLA<br />
TAX<br />
PLA<br />
TAY<br />
JMP $0073 ;CONTINUE BASIC<br />
SYS 49152 activates <strong>the</strong> wedge. Note that <strong>the</strong> entire routine is relocatable, apart<br />
from <strong>the</strong> address in SETUP. Long routines that won't fit <strong>the</strong> tape buffer can also be<br />
put at <strong>the</strong> top of BASIC memory.<br />
$C021 jumps to CHRGOT, not CHRGET. This means that! in direct mode is<br />
treated as normal, generating 7SYNTAX ERROR if entered as a command, but in<br />
cluded in BASIC o<strong>the</strong>rwise. If $C021 jumps to CHRGET, <strong>the</strong>re are no SYNTAX ER-<br />
RORs, but <strong>the</strong> command becomes difficult to include in BASIC. Note as well that<br />
$C031 jumps to $0073. Of course, it immediately jumps back to $C00D, but $0073<br />
always relocates.<br />
;MIMIC CHRGET<br />
;A NOW HOLDS BASIC CHARACTER<br />
;IS IT '!' ?<br />
;NO—JMP BACK TO CHRGOT. WEDGE UNUSED<br />
;YES—CHECK FOR DIRECT MODE<br />
;DIRECT MODE—DON'T USE WEDGE<br />
;PROGRAM MODE. USE WEDGE—<br />
;SAVE X AND Y<br />
;EXECUTE '!' COMMAND; HERE WE<br />
INCREMENT BORDER AND GROUND COLORS.<br />
PROCESSING OVER. RECOVER A AND X.<br />
265
ML Methods Specific to <strong>the</strong> <strong>64</strong><br />
Vectors<br />
<strong>The</strong> main block of vectors starts at $300. ($0300) through ($030A) are vectors to<br />
BASIC. ($0314), ($0316), and ($0318) are vectors to IRQ, BRK, and NMI. ($031A)<br />
through ($0332) are vectors to Kernal routines, except ($032E) is unused.<br />
Earlier RAM has a sprinkling of vectors, including ($028F), used by SCNKEY,<br />
<strong>the</strong> keyboard-reading routine, which allows keys to be intercepted—see <strong>the</strong> "Func<br />
tion Keys" definition and o<strong>the</strong>r keyboard redefinition programs in Chapter 6. Vectors<br />
($0003) and ($0005) point to floating-to-fixed (and vice versa) number conversion<br />
routines, but nei<strong>the</strong>r is called by ROM—ei<strong>the</strong>r through oversight or perhaps because<br />
<strong>the</strong> intention was to allow JMP ($0003) and JMP ($0005) to work on both <strong>the</strong> VIC<br />
and <strong>64</strong> and possibly future machines.<br />
BASIC Vectors<br />
<strong>The</strong>re are six BASIC vectors, each called from <strong>the</strong> address three bytes before <strong>the</strong> vec<br />
tor's normal destination. For example, $A437 JMPs to ($0300), which is set normally<br />
to $A43A. Although this seems like useless extra execution time, it is <strong>the</strong> basis for<br />
<strong>the</strong> wedging technique. <strong>The</strong> vectors are listed here in order:<br />
($300) IERROR: Vector to Error Message Routine<br />
X holds <strong>the</strong> number of <strong>the</strong> error; for instance, decimal 10 means NEXT WITHOUT<br />
FOR. Unless altered, this prints an error message, <strong>the</strong>n READY. See Chapter 6 for<br />
ONERR-GOTO, allowing an error routine to be specified at some line number.<br />
($302) IMAIN: Vector to Main BASIC Loop<br />
This usually points to $A483, and is called just after READY prints, before input<br />
from <strong>the</strong> keyboard. Try POKEing <strong>the</strong>se values into 828 and <strong>the</strong> following addresses:<br />
169, 42, 32, 210, 255, 76,131,1<strong>64</strong><br />
Now POKE 770,60: POKE 771,3. <strong>The</strong> effect is to move <strong>the</strong> vector to point to <strong>the</strong>se<br />
instructions:<br />
LDA #$2A ;LOADWITH*<br />
JSR $FFD2 ;OUTPUTIT<br />
JMP $A483 ;CONTINUE AS USUAL<br />
Now <strong>the</strong> cursor expecting input is preceded by *. In fact, you can tell when <strong>the</strong> rou<br />
tine is called by <strong>the</strong> presence of <strong>the</strong> asterisk. IMAIN allows several possibilities:<br />
automatic BASIC line numbering, output of some message or prompt, and automatic<br />
LOAD and RUN, as Chapter 14 shows.<br />
($304) ICRNCH: Vector to Tokenize Keywords Routine<br />
This tokenizes BASIC, which is scanned while in <strong>the</strong> BASIC line input buffer at<br />
$200, converting keywords to tokens. This vector could be diverted, so new<br />
keywords could be recognized and converted into tokens. If this is done, ($308) and<br />
($306) have to be altered, too.<br />
($306) IQPLOP: Vector to Untokenize Keywords Routine<br />
POKE <strong>the</strong>se values from 828 and subsequent addresses:<br />
266
ML Methods Specific to <strong>the</strong> <strong>64</strong><br />
72, 201, 58, 208, 10,169,13, 32, 210, 255, 169, 32, 32, 210, 104, 76, 26, 167<br />
Now POKE 774,60: POKE 775,3. This simple routine compares <strong>the</strong> character to be<br />
listed with <strong>the</strong> colon; if it finds one, it starts a new line and prints a space, so LIST<br />
now puts every statement on a new line. (It doesn't test for colons within strings.)<br />
This sort of thing is useful with printers and could include a test for output device<br />
number 4.<br />
($308) IGONE: Vector to Execute Next BASIC Program Token<br />
Routine<br />
This is somewhat like CHRGET, but points only at tokens—it's used just before a<br />
statement is executed. (If <strong>the</strong>re's no token, LET is assumed.) Bytes outside <strong>the</strong> range<br />
of valid tokens trigger a 7SYNTAX ERROR. We can intercept <strong>the</strong> routine and process<br />
our own tokens or, as in <strong>the</strong> next example, redefine a standard token.<br />
Program 8-7. LET Vector Demo<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
10 DATA 32,115,0,201,136,240,6,32,121,0 :rem 85<br />
11 DATA 76,231,167,32,155,183,142,33,208 ;rem 176<br />
12 DATA 76,234,167 2rem 121<br />
20 DATA 169,3,141,9,3,169,60,141,8,3,96 :rem 127<br />
100 FOR J=828 TO 860:READ X:POKE J,X:NEXT :rem <strong>64</strong><br />
110 SYS 850 :rem 46<br />
Note that a SYS call is needed to alter <strong>the</strong> vector, because POKE is processed<br />
using this and gets confused if one byte of <strong>the</strong> vector changes.<br />
After RUN, LET is redefined so that LET 13, for example, sets <strong>the</strong> background<br />
light green. LET 13 now, in effect, POKEs $D021 of <strong>the</strong> <strong>64</strong> with 13, but does this<br />
much faster than POKE. FOR J=0 TO 15 :LET J :NEXT cycles <strong>the</strong> colors at great<br />
speed. <strong>The</strong> extra ML is this:<br />
HERE<br />
LETFND<br />
JSR<br />
CMP<br />
BEQ<br />
JSR<br />
JMP<br />
JSR<br />
STX<br />
JMP<br />
$0073<br />
#$88<br />
LETFND<br />
$0079<br />
$A7E7<br />
$B79B<br />
$D021<br />
$A7EA<br />
;FETCH NEXT BASIC CHR<br />
;LOOK FOR LET TOKEN<br />
;BRANCH IF FOUND<br />
;GOTCHR SETS FLAGS<br />
;CONTINUE NORMALLY<br />
;CALCULATE 1-BYTE BASIC PARAMETER<br />
;PUT IT IN VIC REGISTER (BACKGROUND COLOR)<br />
;CONTINUE, AFTER EXECUTION POINT<br />
LET (and GO) and many rarely used ma<strong>the</strong>matical functions lend <strong>the</strong>mselves to<br />
this treatment, and may be helpful in dealing with some of <strong>the</strong> more tiresome com<br />
mands needing POKEs and PEEKs, such as when controlling <strong>the</strong> SID sound chip.<br />
ML Methods Specific to <strong>the</strong> <strong>64</strong><br />
inclusion in <strong>the</strong> vector table is to allow nonstandard terms, ei<strong>the</strong>r string or numeric,<br />
to be defined. Thus, hex numbers beginning with $ can be introduced into BASIC, or<br />
binary numbers beginning with %. Examine <strong>the</strong> following assembly-style listing:<br />
HERE<br />
YES<br />
JSR<br />
CMP<br />
BEQ<br />
LDA<br />
STA<br />
JSR<br />
JMP<br />
JSR<br />
$0073<br />
#$24<br />
YES<br />
#$00<br />
$0D<br />
$0079<br />
$AE8D<br />
$0073<br />
JMP $0073<br />
;GET FIRST CHR OF TERM<br />
;IS IT $?<br />
;IF YES, BRANCH<br />
;IF NO, SIMULATE<br />
;NORMAL BEHAVIOR<br />
;GET FIRST CHR AFTER AND<br />
;PROCESS. PUT IN FAC1<br />
Set ($30A), 778 and 779, to point to <strong>the</strong> address of HERE.<br />
Program 8-8, is a BASIC program that uses <strong>the</strong> principles just explained to add<br />
hex numbers to BASIC. For example, POKE $D020,l works correctly.<br />
Program 8-8.<br />
Add $ Commands<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
10 DATA 169,71,141,10,3,169, 3,141,11,3 :rem 55<br />
11 DATA 96,169,0,133,13,32,115,0,201,36 : rem 106<br />
12 DATA 240,6,32,121,0,76,141,174,162,2 :rem 104<br />
13 DATA 32,115,0,201,<strong>64</strong>,144,2,105,8,10 :rem 45<br />
14 DATA 10,10,10,133,254,32,115,0,201,<strong>64</strong> :rem 137<br />
15 DATA 144,2,105,8,41,15,5,254,72,202 :rem 61<br />
16 DATA 208,224,104,168,104,133,98,132 :rem 78<br />
17 DATA 99,162,144,56,32,73,188,76,115,0 :rem 193<br />
100 FOR J=828 TO 905:READ X:POKE J,X:NEXT :rem <strong>64</strong><br />
110 SYS 828 :rem 51<br />
To compute results in <strong>the</strong> range 0-65535, a modified FXFLPT routine has to be used,<br />
as explained earlier.<br />
Kernal<br />
Vectors<br />
Twelve Kernal routines are vectored through RAM addressed from ($31 A) through<br />
($332); all, except ILOAD and ISAVE, are called immediately after entering <strong>the</strong><br />
Kernal jump table. Open and close routines, routines to fetch and output characters,<br />
and <strong>the</strong> RUN/STOP key test all have indirection and allow modifications to be<br />
made to <strong>the</strong> Kernal even in ROM. LOAD and SAVE store address parameters before<br />
entering <strong>the</strong>ir vectors. A short list of <strong>the</strong> vectors is given below:<br />
($31A)<br />
($31C)<br />
($31E)<br />
($320)<br />
($322)<br />
($324)<br />
($326)<br />
IOPEN:<br />
ICLOSE:<br />
ICHKIN:<br />
ICKOUT:<br />
ICLRCH:<br />
IBASIN:<br />
IBSOUT:<br />
Vector to Kernal OPEN Routine<br />
Vector to Kernal CLOSE Routine<br />
Vector to Kernal CHKIN Routine<br />
Vector to Kernal CKOUT Routine<br />
Vector to Kernal CLRCHN Routine<br />
Vector to Kernal CHRIN Routine<br />
Vector to Kernal CHROUT Routine<br />
268
ML Methods Specific to <strong>the</strong> <strong>64</strong><br />
($328)<br />
($32A)<br />
($32C)<br />
($32E)<br />
($330)<br />
($332)<br />
ISTOP:<br />
IGETIN:<br />
ICLALL:<br />
IUSRCMD:<br />
ILOAD:<br />
ISAVE:<br />
Vector to Kernal STOP Routine<br />
Vector to Kernal GETIN Routine<br />
Vector to Kernal CLALL Routine<br />
Vector to Kernal User-Defined Routine<br />
Vector to Kernal LOAD Routine<br />
Vector to Kernal SAVE Routine<br />
As a fairly simple example, using an asterisk as before, <strong>the</strong> routine below inter<br />
cepts CHROUT and causes it to output an extra asterisk. POKE <strong>the</strong>se values into 828<br />
and <strong>the</strong> following:<br />
72,169, 42, 32, 202, 241,104, 76, 202, 241<br />
Now POKE 806,60: POKE 807,3 to alter <strong>the</strong> vector to $033C. <strong>The</strong> ML is listed<br />
below:<br />
PHA ; SAVE OUTPUT CHARACTER<br />
LDA #$2A ; LOAD ASTERISK<br />
JSR $F1CA ; OUTPUT ASTERISK, NOT WITH FFD2, OF COURSE<br />
PLA ; RECOVER CHARACTER<br />
JMP $F1CA ; OUTPUT IT, CONTINUE<br />
Following SYS 828, any use of CHROUT prints an asterisk—including all BASIC<br />
messages like READY, which appears as R*E*A*D*Y*.<br />
Interrupts<br />
Defaults for NMI, BRK, and IRQ vectors (Non-Maskable Interrupt, BReaK, and Inter<br />
rupt ReQuest vectors) are permanently stored in <strong>the</strong> top six bytes of <strong>the</strong> 6510's<br />
memory. With <strong>the</strong> <strong>64</strong>, NMI jumps to $FE43, BRK to $FE66, and IRQ to $FF43. Hard<br />
ware reset (see Chapter 5) has no indirect vectors apart from <strong>the</strong> optional cartridge's<br />
start address.<br />
($318) NMINV: Vector to NMI Routine<br />
NMI processing is complex. One reason is that both RS-232 and <strong>the</strong> RESTORE key<br />
are handled by it. From $FE43, NMI goes through this sequence:<br />
$FE43 Vectors through ($318), normally to $FE47<br />
$FE47 Saves A, X, Y with PHA:TAX:PHA:TAY:PHA<br />
$FE4C Disables NMIs<br />
$FE51 Checks for RESTORE key or for RS-232 or o<strong>the</strong>r CIA interrupts<br />
$FE56 RESTORE key; checks for cartridge; if found JMP ($8002)<br />
$FE5E Tests for RUN/STOP key; if found, restore BASIC<br />
$FE72 RS-232 or CIA interrupts; processes RS-232<br />
$FEBC Restores Y, X, A with PLA:TAY:PLA:TAX:PLA<br />
$FEC1 Exits with RTI<br />
To use NMI interrupts successfully requires that ($0318) be redirected with a correct<br />
exit, and that <strong>the</strong> CIA timers be set properly.<br />
A simple example is to redirect ($0318) to $FEC1; RESTORE always now im<br />
mediately goes to RTI, so RUN/STOP-RESTORE cannot work. Chapter 5 has an ex<br />
ample showing how RESTORE generates an interrupt.<br />
269
ML Methods Specific to <strong>the</strong> <strong>64</strong><br />
($314) CINV: Vector to IRQ Routine<br />
($316) CBINV: Vector to BRK Routine<br />
<strong>The</strong> IRQ and BRK instruction interrupts are conceptually tricky, though <strong>the</strong>ir<br />
processing is fairly straightforward. Both an IRQ interrupt from CIA 1 (see Chapter<br />
5) and a BRK instruction jump to $FF43, provided interrupts aren't masked by SEI,<br />
and assuming ($FFFE) hasn't been changed in RAM. After $FF43, A, X, and Y are<br />
saved on <strong>the</strong> stack; <strong>the</strong>n <strong>the</strong> two types of interrupt are separated by checking for <strong>the</strong><br />
BRK flag in <strong>the</strong> status register.<br />
BRK. This type of interrupt vectors through ($316), normally set to <strong>the</strong> restore<br />
sequence in NMI, so SYS to a location holding a zero byte typically has <strong>the</strong> same ef<br />
fect as RUN/STOP-RESTORE. Monitors often change ($316) to point to <strong>the</strong>ir own<br />
start address, so BRK at <strong>the</strong> end of <strong>the</strong> ML returns control to <strong>the</strong> monitor.<br />
IRQ. Interrupts of this type are vectored through ($314), normally to $EA31.<br />
From $EA31, IRQ interrupts go through this process:<br />
$EA31 Update clock<br />
$EA34 Process cursor, tape motor; scan keyboard<br />
$EA7E Clear interrupt flags<br />
$EA81 Restore A, X, Y with PLA:TAY:PLA:TAX:PLA<br />
$EA86 Exit with RTI.<br />
IRQ interrupts usually occur 60 times every second, unless turned off, or during<br />
tape and disk access; CIA 1 controls <strong>the</strong> frequency. Adding extra ML, to be pro<br />
cessed immediately before <strong>the</strong> usual interrupt ML, must allow for this. Successful<br />
use of <strong>the</strong> vector requires preexisting ML, exiting typically with JMP $EA31, so <strong>the</strong><br />
TI clock and keyboard and so on work normally. Also, a routine to alter <strong>the</strong> vector<br />
in ($0314) to point to <strong>the</strong> new ML is needed. Since interrupts are already taking<br />
place, <strong>the</strong>y must ei<strong>the</strong>r be stopped or an ML routine must be used to be sure that <strong>the</strong><br />
address is changed before ano<strong>the</strong>r interrupt occurs.<br />
Interrupt-Driven<br />
Background Programs<br />
A background program runs with <strong>the</strong> main program. Interrupts allow programming<br />
feats which are o<strong>the</strong>rwise impossible; <strong>the</strong>ir common feature is periodicity. Playing<br />
music, updating graphics, and printing <strong>the</strong> current time are examples of background<br />
programs which interrupts allow on <strong>the</strong> <strong>64</strong>. Features can be added at will, giving<br />
long, complex background programs, if this is required.<br />
NMI-driven background programs. NMI interrupts aren't maskable; <strong>the</strong>y offer<br />
interrupts with near-perfect regularity. CIA 2 generates NMIs. <strong>The</strong> CIA's timers can<br />
be used toge<strong>the</strong>r, allowing repeats to occur over a time range from a few micro<br />
seconds to an hour or so without special counters for <strong>the</strong> purpose. Also, <strong>the</strong> NMI<br />
vector is simple to move, and it is easy to turn off NMIs. Disk and tape cannot work<br />
with <strong>the</strong>se routines, however. <strong>The</strong>se devices go through <strong>the</strong> motions, but <strong>the</strong> NMIs<br />
throw <strong>the</strong>ir timing off; IRQ interrupts, on <strong>the</strong> o<strong>the</strong>r hand, pause while <strong>the</strong>se opera<br />
tions take place, but are automatically reinstated afterward.<br />
Program 8-9 uses NMIs to drive a short program, which simply changes <strong>the</strong><br />
background color. <strong>The</strong> interrupt frequency is chosen to produce narrow bands of<br />
color on <strong>the</strong> screen; BASIC runs completely normally over this. Alter <strong>the</strong> timer<br />
parameters to see <strong>the</strong> effect of changing <strong>the</strong> rate at which NMIs are generated:<br />
270
ML Methods Specific to <strong>the</strong> <strong>64</strong><br />
Program 8-9.<br />
NMI Demo<br />
1 REM USING NON-MASKABLE INTERRUPTS WITH BASIC<br />
:rem 177<br />
2 REM TO PROVIDE COMPLETELY.REGULAR TIMING:rem 170<br />
3 REM E.G. TO PLAY MUSIC NOTES# DISPLAY THE :rem 5<br />
4 REM TIMtU DISPLAY GAME SCORES ETC. :rem 124<br />
6 REM THIS VERSION USES BOTH TIMERS OF CIA#2,<br />
:rem 156<br />
7 REM SO DELAYS CAN VARY FROM A FEW MILLIONTHS<br />
:rem 237<br />
8 REM OF A SECOND UP TO AN HOUR OR MORE. :rem 197<br />
10 REM THIS DEMO INCREMENTS SCREEN COLOR FAST,<br />
:rem 51<br />
11 REM PRODUCING NORMALLY IMPOSSIBLE STRIPES<br />
:rem 128<br />
100 FOR J=49152 TO 49162: READ X: POKE J,X: NEXT<br />
:rem 11<br />
110 POKE 792,0: POKE 793,192: REM NMI VECTOR TO C0<br />
00 :rem 222<br />
120 POKE 56589,127:{2 SPACES}REM ALL NMIS OFF<br />
, , :rem 162<br />
130 POKE 56589,130:{2 SPACESjREM TIMER B NMI ENABL<br />
ED :rem 68<br />
140 POKE 56580,99: POKE 56581,1: REM SET TIMER A<br />
:rem 5<br />
149 REM 16+1 IN TIMER A (1) SETS LATCH, (2) STARTS<br />
TIMER A:- :rem 132<br />
150 POKE 56590,17:{3 SPACES}REM START TIMER A<br />
:rem 208<br />
160 POKE 56582,2: POKE 56583,0:{5 SPACES}REM SET T<br />
IMER B :rem 203<br />
168 REM <strong>64</strong>+16+1 IN TIMER B (1) COUNTS TIMER A,<br />
:rem 129<br />
169 REM (2) SETS LATCH, (3) STARTS TIMER B:-<br />
:rem 109<br />
170 POKE 56591,81:{3 SPACES}REM START TIMER B<br />
:rem 213<br />
180 DATA 72,138,72,152,72,238,33,208,76,81,254<br />
:rem 232<br />
181 REM PHA/TXA/PHA/TYA/PHA/INC D021/JMP FE51<br />
:rem 135<br />
182 REM OR TRY EG. 0,4 IN PLACE OF 33,208 :*rem 38<br />
183 REM TO INCREMENT TOP LEFT SCREEN CHARACTER<br />
:rem 51<br />
RUN/STOP-RESTORE will return <strong>the</strong> system to normal.<br />
IRQ-driven background programs. IRQ programs are quite popular because<br />
early CBM machines couldn't use NMIs. <strong>The</strong>y are a little more complex than NMI<br />
programs, but need no special CIA knowledge. Chapter 13's music-playing program<br />
271
ML Methods Specific to <strong>the</strong> <strong>64</strong><br />
is an example; it plays a three-part composition while BASIC runs normally.<br />
To create an IRQ-driven program, you should first choose an area of RAM to<br />
store <strong>the</strong> ML—$C000 onward is <strong>the</strong> obvious choice on <strong>the</strong> <strong>64</strong>, but <strong>the</strong>re are o<strong>the</strong>r<br />
locations. Next, you must write <strong>the</strong> initialization routine, typically something similar<br />
to <strong>the</strong> following, which sets <strong>the</strong> vector to $C00D:<br />
$C000 SEI<br />
$C001 LDA #$C0<br />
$C003 STA $0315<br />
$C006 LDA #$0D<br />
$C008 STA $0314<br />
$C00B CLI<br />
$C00C RTS<br />
$C00D (background program starts here)<br />
Note that you must disable all interrupts with SEI before changing <strong>the</strong> vector,<br />
and <strong>the</strong>n reenable <strong>the</strong>m with CLI. It's also possible to change ($0314) from BASIC.<br />
For example, this line sets <strong>the</strong> IRQ vector to $C000:<br />
POKE 56334,0: POKE 788,0: POKE 789,192: POKE 56334,1<br />
<strong>The</strong> next step is to write <strong>the</strong> background ML, which in our example will start at<br />
$C00D and typically exit with JMP $EA31, <strong>the</strong> normal interrupt routine. Here are a<br />
few things to keep in mind when programming with interrupts.<br />
• A, X, and Y can be used independently of BASIC. Because <strong>the</strong> BASIC values at <strong>the</strong><br />
time <strong>the</strong> interrupt happens are saved automatically and restored on return from<br />
interrupt, your ML is self-contained.<br />
• <strong>The</strong> simplest termination of an IRQ routine is JMP $EA31. This is <strong>the</strong> usual address<br />
in ($0314), and exit to it means that BASIC behaves exactly as normal apart from<br />
<strong>the</strong> introduced ML. All exits must return properly, or BASIC will crash. It's not<br />
essential to exit via $EA31, though. Note that RTI restores <strong>the</strong> status register to its<br />
pre-interrupt value, so CLI isn't necessary.<br />
• Keep in mind <strong>the</strong> effects of repeats. Time dependency is a little hard to get used to.<br />
A command like DEC $FE in normal programs decrements <strong>the</strong> contents of $FE just<br />
once, from (for example) 9 to 8. But in an interrupt-driven program, this command<br />
decrements whenever interrupts occur, typically 60 times per second. This is how<br />
<strong>the</strong> TI clock works (except that it increments). Clearly, this is a valuable feature.<br />
• Polling means that, during each interrupt, locations are PEEKed to see if action is<br />
required. Chapter 16 has an interrupt-driven program to read joysticks; at regular<br />
intervals, <strong>the</strong> joystick hardware addresses are read in ML and transferred to a<br />
convenient location. Processing is far faster than <strong>the</strong> BASIC equivalent. Ano<strong>the</strong>r ex<br />
ample is reading location $C5 (197) to see if a key is held down; unlike GET's once<br />
only action, this allows keys to act as long as <strong>the</strong>y are held down.<br />
• POKEs into <strong>the</strong> background program can be a useful control if a program has sev<br />
eral functions. To illustrate, here is a simple example:<br />
LDA#0<br />
BNE INTML<br />
JMP $EA31<br />
272
ML Methods Specific to <strong>the</strong> <strong>64</strong><br />
If this is <strong>the</strong> first ML of an IRQ program, <strong>the</strong> branch will never be taken and<br />
<strong>the</strong> effect is negligible. But POKEing a value o<strong>the</strong>r than 0 into <strong>the</strong> location follow<br />
ing <strong>the</strong> LDA instruction will activate whatever ML has been put in at <strong>the</strong> location<br />
labeled INTML by <strong>the</strong> assembler. So completely invisible ML interrupt programs<br />
can be activated from BASIC.<br />
• <strong>The</strong> speed of background programs can, of course, vary. Suppose we have a rel<br />
atively slow routine, perhaps to fill a screen with graphics. Is <strong>the</strong>re a chance that<br />
<strong>the</strong> interrupt might itself be interrupted and <strong>the</strong> program crash? Since an IRQ inter<br />
rupt, in effect, performs SEI whenever it occurs, background programs driven by<br />
IRQ can be slower than 1/60 second. If every repetition is as slow as this, <strong>the</strong> nor<br />
mal program will hardly get a chance to run.<br />
NMIs have no disable flag; if interrupts occur faster than <strong>the</strong> background pro<br />
gram takes to execute, <strong>the</strong> <strong>64</strong> will lock up.<br />
273
Chapter 9<br />
Mixing<br />
with<br />
BASIC<br />
Machine<br />
Language<br />
• RAM Available for ML Routines<br />
• Combining BASIC and ML<br />
• Relocating ML
Chapter 9<br />
Mixing BASIC with<br />
Language<br />
Machine<br />
While most programmers are happy to use BASIC, machine language (ML) offers in<br />
creased speed and power. This short chapter explains how ML programs and data<br />
can be incorporated into BASIC, and how BASIC can be used to load in and relocate<br />
your ML routines.<br />
RAM Available for<br />
ML Routines<br />
<strong>The</strong> <strong>64</strong> has plenty of RAM; <strong>the</strong> only problem likely to arise is making all of your<br />
routines compatible with each o<strong>the</strong>r. <strong>The</strong> best way to achieve this is with <strong>the</strong> relocat<br />
ing loader technique, discussed later in this chapter. However, with proper planning,<br />
<strong>the</strong>re is plenty of room in <strong>the</strong> <strong>64</strong> for your ML routines and BASIC programs, too.<br />
<strong>The</strong> <strong>64</strong> has several areas of RAM that are particularly suited for storing ML, <strong>the</strong><br />
largest of which is $C000-$CFFF (49152-53247), which allows 4K to be stored, and<br />
is isolated from BASIC. This is generally <strong>the</strong> best choice, except in <strong>the</strong> sense that it is<br />
<strong>the</strong> favorite of most ML programmers using <strong>the</strong> <strong>64</strong>, so careful partitioning will be re<br />
quired to fit in more than one routine. You can calculate <strong>the</strong> length of your ML, sub<br />
tract that from <strong>the</strong> top of this block, and use <strong>the</strong> result as <strong>the</strong> LOAD address of your<br />
routine. Sometimes this will help you stay out of <strong>the</strong> way of o<strong>the</strong>r programs that use<br />
this area.<br />
<strong>The</strong> RAM under <strong>the</strong> BASIC ROM ($A000-$BFFF) is an 8K block, which requires<br />
<strong>the</strong> methods explained in Chapter 8 to be usable. This area is relatively unpopular,<br />
but access isn't really difficult. ML in this area cannot generally use BASIC sub<br />
routines, though. Note that <strong>the</strong> 8K under <strong>the</strong> Kernal is also usable, but liable to more<br />
difficulties than BASIC, since <strong>the</strong> machine's input and output operations are con<br />
trolled by <strong>the</strong> Kernal.<br />
<strong>The</strong> RAM normally used for BASIC program storage can usually be reduced<br />
without affecting BASIC programs. Ei<strong>the</strong>r <strong>the</strong> start of BASIC can be moved up, or<br />
<strong>the</strong> top moved down, or both. <strong>The</strong> top is often used, since any CBM machine can<br />
use this area, while not all of <strong>the</strong>m can raise <strong>the</strong> bottom of BASIC as easily.<br />
Supermon is stored at <strong>the</strong> top of BASIC, and resets end-of-BASIC pointers below it<br />
self so BASIC strings won't corrupt it. Autostart plug-in cartridges occupy<br />
$8000-$9FFF of this area.<br />
Smaller blocks of RAM are sometimes useful when writing autorun programs<br />
(see Chapter 15) or converting VIC-20 subroutines for <strong>the</strong> <strong>64</strong>. <strong>The</strong> main areas are<br />
$2A7-$2FF (679-767) and $334-$3FF (820-1023); part of <strong>the</strong> latter is used by tape<br />
and is secure if tape isn't used after BASIC is loaded. Even if you use tape,<br />
$334-$33B (820-827) is free to be used for vectors or flags. <strong>The</strong> stack, $100-$lFF<br />
(256-511), is partly usable—<strong>the</strong> low end, and only <strong>the</strong> low end, is safe as long as<br />
<strong>the</strong>re aren't many GOSUB or FOR-NEXT calls. Free zero page RAM includes loca<br />
tions 2-6 and $FB-$FE (251-254). Assuming certain ML math calls and RS-232<br />
communications aren't used, $F7-$FE (247-254) is free.<br />
277
Mixing BASIC with Machine Language<br />
Combining BASIC and ML<br />
DATA statements/<strong>The</strong> easiest way to combine ML with BASIC is to store <strong>the</strong> ML<br />
as BASIC DATA. When <strong>the</strong> BASIC loader is run, <strong>the</strong> numbers are read and POKEd<br />
into memory, for use by SYS or USR. Chapter 6 has examples of this technique. This<br />
type of program is self-contained and can be loaded and saved like any o<strong>the</strong>r BASIC<br />
program, and <strong>the</strong>re are generally no problems. <strong>The</strong> drawback is that each ML byte is<br />
stored on average in about four BASIC bytes. For instance, $EA is held as 234 with a<br />
comma. <strong>The</strong>refore, it is better to store long ML programs ano<strong>the</strong>r way, usually as ob<br />
ject code.<br />
If you've written some ML that works correctly, it's convenient to have a pro<br />
gram to read it from memory and write it as DATA statements. Program 9-1 will do<br />
this for you, so you will not have to do it by hand.<br />
Program 9-1.<br />
DATA Maker<br />
100 INPUT "START";A<br />
110 INPUT "{2 SPACES}END";E<br />
120 INPUT "FIRST LINE#";L<br />
130 INPUT "LINE LENGTH";LL<br />
140 PRINT "{CLR}<br />
150 PRINT "{HOME}" L "DATA "7<br />
160 PRINT MID$(STR$(PEEK(A)),2) ","7<br />
170 A=A+1:IF A>E THEN END<br />
180 IF POS(0)
Mixing BASIC with Machine Language<br />
This increments <strong>the</strong> character in <strong>the</strong> second column of <strong>the</strong> first row of <strong>the</strong><br />
screen. A complication is that <strong>the</strong> ML must not contain any null bytes, or on editing<br />
<strong>the</strong>se will be treated as ends of lines and spoil <strong>the</strong> ML. (You could use LDX #1:DEX<br />
in place of LDX #0.) Ano<strong>the</strong>r complication is that BASIC lines normally have a<br />
maximum length of 80 characters. By adjusting <strong>the</strong> link addresses <strong>the</strong> limitation is<br />
easy to overcome. A line treated in this way LISTs oddly, and mustn't be edited.<br />
Using quotes after REM and starting at 2055 will cause <strong>the</strong> program to LIST without<br />
keywords.<br />
Strings. ML can also be stored as a string. <strong>The</strong> line ML$="4C48D2AAD191D3"<br />
illustrates an alternative tq DATA that's sometimes encountered. It requires<br />
modifications to <strong>the</strong> DATA writing program to find and print separate hex bytes.<br />
This method saves space compared with ML stored as numbers, but is slower to de<br />
cipher and POKE back in. Since <strong>the</strong> <strong>64</strong> has a lot of RAM, <strong>the</strong> earlier method is used<br />
more often.<br />
Block LOADs. Chapter 6 explains how to load a block of ML. For an example,<br />
see Chapter 12's character editor which allows user-defined characters to be saved to<br />
disk for use later. To bypass <strong>the</strong> <strong>64</strong>'s attempt to GOTO <strong>the</strong> first line after a programmode<br />
LOAD, you'll need something like <strong>the</strong> following line, which loads <strong>the</strong> ML file<br />
only once:<br />
0 IF X=0 THEN X=l: LOAD "ML",8,1<br />
As we've seen, block LOADs save time compared with DATA READs and<br />
POKEs, so this technique (or one of those following) is desirable with ML of any<br />
substantial size.<br />
Consolidated BASIC and ML. <strong>The</strong>se programs, sometimes called hybrids, con<br />
tain ML immediately after BASIC. BASIC LISTs normally, but since <strong>the</strong> three null<br />
bytes marking its end are earlier than <strong>the</strong> end-of-BASIC pointer, <strong>the</strong>re's space for ex<br />
tra ML which doesn't show on listing. Programs like this can't be edited, or <strong>the</strong> ML<br />
will be moved in memory and probably will not function properly.<br />
An example of this method is <strong>64</strong> Term. <strong>The</strong> ML needed to run <strong>the</strong> <strong>64</strong> modem is<br />
loaded as BASIC, but <strong>the</strong> BASIC is reduced to a single SYS command. In this ex<br />
treme case, <strong>the</strong> only use of <strong>the</strong> BASIC is to run <strong>the</strong> ML, saving <strong>the</strong> user from having<br />
to force-load and <strong>the</strong>n use a SYS call. Some games include <strong>the</strong>ir graphics definitions<br />
after BASIC and are hybrids in ano<strong>the</strong>r sense.<br />
Here is an explanation of how to alter an ML program so that it can be loaded<br />
and run. Suppose <strong>the</strong> ML starts at $0810, just after <strong>the</strong> start of BASIC. If you have<br />
<strong>the</strong> source code, you can reassemble <strong>the</strong> program at $0810, and some monitors have<br />
an .N relocation feature. We want BASIC to LIST as 0 SYS 20<strong>64</strong>, our ML to start at<br />
$0810 (20<strong>64</strong>), and both to be loadable simultaneously. <strong>The</strong> process is shown below<br />
(a simple ML routine which changes <strong>the</strong> screen color serves as an illustration).<br />
Step 1. With a monitor, like Supermon, load <strong>the</strong> ML which is located at $0810.<br />
<strong>The</strong> sample program is shown after being loaded and examined with <strong>the</strong> M<br />
command:<br />
.M 0810 0810<br />
.: 0810 EE 21 DO 60 00 00 00 00<br />
This is equivalent to INC $D021 / RTS.<br />
279
Mixing BASIC with Machine Language<br />
Step 2. Add <strong>the</strong> SYS call. Here well put 0 SYS20<strong>64</strong>:<br />
.M 0800 0808<br />
.: 0800 00 0A 08 00 00 9E 32 30<br />
.: 0808 36 34 00 00 00 00 00 00<br />
Reading from $0800, this holds a null byte; <strong>the</strong> link address in low/high form,<br />
$080A; <strong>the</strong> line number 0; SYS20<strong>64</strong> (9E is <strong>the</strong> tokenized form of SYS and it's fol<br />
lowed by ASCII numbers); and three end-of-program zero bytes and a few more<br />
filler bytes before $0810.<br />
Step 3. Save to disk or tape. <strong>The</strong> trick is not to save <strong>the</strong> starting null byte. If you<br />
do, you'll need to force-load <strong>the</strong> program to make it work. So use something like<br />
this (using your monitor), with <strong>the</strong> ending address plus one for <strong>the</strong> final parameter:<br />
.S "ML AS BASIC',08,0801,0830<br />
Step 4. Test <strong>the</strong> result. It should load normally, LIST as 0 SYS20<strong>64</strong>, and run<br />
correctly.<br />
In practice, it's common to find o<strong>the</strong>r characters after SYS. For example, a colon,<br />
<strong>the</strong>n backspaces, and a copyright message will make <strong>the</strong> SYS command invisible<br />
when listed to screen.<br />
Relocating<br />
ML<br />
Moving relocatable ML. Where several utilities might be required in RAM, it makes<br />
sense to write <strong>the</strong>m in such a way that <strong>the</strong>y detect and fill <strong>the</strong> next highest available<br />
space. Supermon and some of <strong>the</strong> utilities in Chapter 6 are written like this; <strong>the</strong>y're<br />
not dependent on being put into a fixed place in RAM. <strong>The</strong> techniques that follow<br />
put ML into <strong>the</strong> top of BASIC, after first lowering <strong>the</strong> end-of-BASIC pointer by <strong>the</strong><br />
correct amount. <strong>The</strong>se techniques work with any CBM machine. Since <strong>the</strong> <strong>64</strong> has<br />
RAM available at o<strong>the</strong>r places than top of BASIC, it's possible to modify <strong>the</strong> method,<br />
for example, to allocate $C000-$CFFF to ML routines, but some pointer o<strong>the</strong>r than<br />
top of BASIC has to be chosen to locate <strong>the</strong> individual ML routines correctly. Load<br />
ers to put utility ML into <strong>the</strong> top of BASIC can start with this:<br />
100 T=PEEK(55) + 256*PEEK(56): REM T=ORIGINAL TOP OF BASIC<br />
110 S=T-N: : REM EG S=T-50 LOWERS BY 50 BYTES<br />
120 POKE 56,S/256: POKE 55,S-INT(S/256)*256: CLR: REM SET NEW TOP<br />
130 S=PEEK(55) + 256* PEEK(56): REM RECOVER VALUE OF S=START OF ML<br />
which lowers BASIC'S top by an amount specified in line 110 and sets S equal to <strong>the</strong><br />
start of <strong>the</strong> new area, ready for a loop of <strong>the</strong> type:<br />
FOR J=S TO S+49: READ X: POKE J,X: NEXT<br />
which reads 50 bytes of ML and puts it into protected RAM.<br />
This is fine for ML which is relocatable, using only branches and calls to Kernal<br />
or BASIC routines. But ML like this for <strong>the</strong> 6510 is difficult to write; for example, a<br />
block of ML with internal subroutine calls, like C000 JSR C100, cannot work if <strong>the</strong><br />
ML is simply shifted in RAM. Just as some assemblers (see Chapter 7) use special<br />
loader programs to put ML anywhere in RAM, we can write loaders which POKE<br />
ML anywhere, modifying it where necessary.<br />
280
Mixing BASIC with Machine Language<br />
Relocating ML with BASIC. <strong>The</strong> following technique involves a lot of work,<br />
but <strong>the</strong> versatility of <strong>the</strong> resulting utility makes it well worthwhile. If you wish to<br />
write ML subroutines to be as versatile as possible, bear in mind that it's always<br />
simpler for <strong>the</strong> user if ML is freely relocatable. We'll use <strong>the</strong> following loader:<br />
100 T=PEEK(55)+256*PEEK(56) :REM TOP OF MEMORY<br />
110 L=T-N :REM N=NUMBER OF BYTES OF CODE; L=LOWERED MEM.TOP<br />
120 FOR J=L TO T-l: READ X% :REM ML HELD IN DATA STATEMENTS<br />
130 IF X%
Mixing BASIC with Machine Language<br />
<strong>The</strong> number of bytes in <strong>the</strong> program is 17, so line 110 becomes:<br />
110 L=T-17<br />
After relocation, <strong>the</strong> new starting address of <strong>the</strong> ML can be found with:<br />
PRINT PEEK(55)+PEEK(56)*256<br />
and <strong>the</strong> ML can be started with:<br />
SYS PEEK(55)+PEEK(56)*256<br />
Relocating ML with ML. This technique is similar and much faster. Supermon<br />
uses this method. ML of course has no out-of-range values in <strong>the</strong> way BASIC has<br />
negatives; instead use null bytes as markers.<br />
First, mark <strong>the</strong> absolute addresses needing relocation. <strong>The</strong>n add a zero byte im<br />
mediately after each such address, and also after every genuine zero byte. This is<br />
much easier to do with an assembler.<br />
Third, replace <strong>the</strong> addresses by <strong>the</strong>ir displacement from <strong>the</strong> end of <strong>the</strong> program.<br />
That is, replace <strong>the</strong> absolute addresses with <strong>the</strong> twos complement of <strong>the</strong> number of<br />
bytes from <strong>the</strong> end of <strong>the</strong> program. (See Chapter 7 for information on calculating <strong>the</strong><br />
twos complement.) In <strong>the</strong> example above, we found that $02AB was 13 ($000D)<br />
bytes from <strong>the</strong> end of <strong>the</strong> program. Using <strong>the</strong> twos complement of $000D, you<br />
would replace <strong>the</strong> address with JSR $FFF3. Address $02B7 has a displacement of 1<br />
byte, so we replace LDA $02B7 with LDA $FFFF.<br />
Finally, put in a BASIC call (as shown earlier in this chapter) to <strong>the</strong> relocator<br />
program, <strong>the</strong> relocator (from Program 9-2, below), and <strong>the</strong> ML you wish to relocate<br />
(preceded by a unique marker byte not found anywhere in <strong>the</strong> ML to be relocated)<br />
toge<strong>the</strong>r in RAM, and save.<br />
<strong>The</strong> relocator program works by starting at <strong>the</strong> end of <strong>the</strong> ML to be relocated<br />
and working backward, moving <strong>the</strong> bytes one by one to <strong>the</strong> top of available memory<br />
(as indicated by <strong>the</strong> pointer in locations 55 and 56). If a zero byte is found, <strong>the</strong><br />
relocator examines <strong>the</strong> next byte. If it is also a zero, <strong>the</strong>n a zero byte is moved. But if<br />
it is not a zero, <strong>the</strong>n an additional byte is retrieved and <strong>the</strong>se two bytes (<strong>the</strong><br />
displacement you calculated) are added to <strong>the</strong> top-of-memory address to compute<br />
<strong>the</strong> proper absolute address for <strong>the</strong> relocated ML.<br />
This continues until <strong>the</strong> marker byte—which separates <strong>the</strong> relocator from <strong>the</strong><br />
ML being relocated—is encountered. As presented in Program 9-2, 222 is used as<br />
<strong>the</strong> marker. If <strong>the</strong> ML you wish to relocate contains <strong>the</strong> byte $DE (222), you'll need<br />
to change this marker to some o<strong>the</strong>r value not found in your code. You can do this<br />
by changing <strong>the</strong> 222 in line 16 to <strong>the</strong> desired value.<br />
Finally, <strong>the</strong> relocator program lowers <strong>the</strong> value in <strong>the</strong> pointers to <strong>the</strong> top of<br />
BASIC program storage (locations 55 and 56) and string storage (locations 51 and<br />
52) to protect <strong>the</strong> relocated ML from BASIC. It <strong>the</strong>n executes <strong>the</strong> ML by jumping to<br />
<strong>the</strong> first byte of <strong>the</strong> relocated code. As with <strong>the</strong> BASIC relocator, <strong>the</strong> starting address<br />
of <strong>the</strong> relocated code can be found with:<br />
PRINT PEEK(55)+PEEK(56)*256<br />
and <strong>the</strong> ML can be restarted with:<br />
SYS PEEK(55)+PEEK(56)*256<br />
282
Mixing BASIC with Machine Language<br />
Program 9-2.<br />
ML Relocator<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader/' Appendix C.<br />
I REM 222 IN LINE 16 IS MARKER VALUE FOR START OF<br />
{SPACE}ML :rem 79<br />
10 DATA 165,45,133,34,165,46,133,35,165,55,133,36,<br />
165,56,133 :rem 143<br />
II DATA 37,160,0,165,34,208,2,198,35,198,34,177,34<br />
,208,60 :rem 253<br />
12 DATA 165,34,208,2,198,35,198,34,177,34,240,33,1<br />
33,38,165 :rem 103<br />
13 DATA 34,208,2,198,35,198,34,177,34,24,101,36,17<br />
0,165,38 :rem 50<br />
14 DATA 101,37,72,165,55,208,2,198,56,198,55,104,1<br />
45,55,138 :rem 108<br />
15 DATA 72,165,55,208,2,198,56,198,55,104,145,55,5<br />
6,176,184 :rem 123<br />
16 DATA 201,222,208,237,165,55,133,51,165,56,133,5<br />
2,108,55,0 :rem 131<br />
For an example of how to use this relocator program, add <strong>the</strong> lines shown in<br />
Program 9-3 to Program 9-2. This will create a machine-language relocated version<br />
of <strong>the</strong> example routine from Figure 9-1. Line 30 creates a program file called RE<br />
LOCATE TEST directly on <strong>the</strong> disk. Line 40 writes out <strong>the</strong> data for a BASIC SYS call<br />
from line 5, line 50 writes out <strong>the</strong> ML relocator program from lines 10-16, and line<br />
60 writes <strong>the</strong> byte that separates <strong>the</strong> relocator from <strong>the</strong> code to be relocated. Line 70<br />
reads <strong>the</strong> ML to be relocated from <strong>the</strong> DATA statement in line 20. Notice how this<br />
data differs from that created for <strong>the</strong> BASIC relocator in <strong>the</strong> previous section. When<br />
you run <strong>the</strong> program, it creates a program on disk called RELOCATE TEST, which<br />
you can load and run like a BASIC program. <strong>The</strong> RELOCATE TEST program will<br />
move <strong>the</strong> routine to <strong>the</strong> top of available memory—adjusting addresses in <strong>the</strong> pro<br />
cess—<strong>the</strong>n lower <strong>the</strong> top-of-memory pointer and execute <strong>the</strong> routine.<br />
To use this program for your own ML, replace line 20 with DATA statements<br />
containing your ML (modified for relocation as described above), and change <strong>the</strong><br />
value of L (line 25) to reflect <strong>the</strong> number of items in your DATA statements.<br />
Program 9-3.<br />
Relocating Program Generator<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
5 DATA 11,8,0,0,158,50,48,54,49,0,0,0 :rem 2<br />
20 DATA 32,243,255,0,96,160,0,0,173,255,255,0,153,<br />
0,0,4,200,208,250,96,32 :rem 227<br />
25 L=21 :rem 83<br />
30 OPEN 1,8,8,"0:RELOCATE TEST,P,W":PRINT*1,CHR$(1<br />
);CHR$(8)? :rem 192<br />
40 FOR 1=1 TO 12:READ X:PRINT#1,CHR$(X);:NEXT : RE<br />
M BASIC SYS CALL (5) :rem 124<br />
283
Mixing BASIC with Machine Language<br />
50 FOR 1=1 TO 105:READ X:PRINT#1,CHR$(X)7:NEXT : R<br />
EM ML FOR RELOCATOR (10-16) :rem 30<br />
60 PRINT#1,CHR$(222);: REM SEPARATOR CHARACTER<br />
:rem 242<br />
70 FOR 1=1 TO L:READ X:PRINT#1,CHR$(X)7:NEXT : REM<br />
ML TO BE RELOCATED (20) :rem 110<br />
80 CLOSE 1 :rem 15<br />
284
Chapter 10<br />
Vocabulary of<br />
<strong>the</strong> 6510 Chip<br />
• <strong>The</strong> 6510 instruction Set
Chapter 10<br />
Vocabulary of <strong>the</strong> 6510 Chip<br />
This chapter lists each opcode with full details and helpful examples. <strong>The</strong> following<br />
conventions have been used:<br />
Read as "becomes." For example, A:= X means that <strong>the</strong> value in A becomes that<br />
currently in X.<br />
x, 0, and 1<br />
Show <strong>the</strong> effect of an opcode on <strong>the</strong> status flags. An x means that <strong>the</strong> flag depends<br />
on <strong>the</strong> operation's result; 0 and 1 represent flags which an opcode always sets to 0<br />
or 1, respectively. All o<strong>the</strong>r flags are left unchanged.<br />
$ and %<br />
Prefix hexadecimal and binary numbers; where <strong>the</strong>se are omitted, a number is<br />
decimal.<br />
A, X, and Y<br />
<strong>The</strong> accumulator and <strong>the</strong> two index registers, X and Y.<br />
M<br />
Memory. This may be ROM in <strong>the</strong> case of 6510 load instructions (like LDA). Note<br />
that immediate addressing mode (#) loads from <strong>the</strong> byte immediately following <strong>the</strong><br />
opcode in memory. All o<strong>the</strong>r addressing modes load from elsewhere in memory.<br />
PSR (or SR)<br />
<strong>The</strong> processor status register. Each bit of <strong>the</strong> register serves as an indicator (flag) for<br />
a different condition:<br />
bit 7: Negative (N) flag. Matches bit 7 of <strong>the</strong> result of <strong>the</strong> operation just com<br />
pleted, which indicates negative numbers in twos complement arithmetic.<br />
bit 6: Overflow (V) flag. Indicates an overflow (result too large for one byte) in<br />
twos complement operations.<br />
bit 5: Unused; always set.<br />
bit 4: Break (B) flag. A BRK instruction was encountered.<br />
bit 3: Decimal (D) flag. When set, all math is performed in decimal (BCD) mode.<br />
bit 2: Interrupt disable (I) flag. When set, interrupts are ignored.<br />
bit 1: Zero (Z) flag. Indicates that all bits are zero in <strong>the</strong> result of <strong>the</strong> operation<br />
just<br />
completed.<br />
bit 0: Carry (C) flag. Holds <strong>the</strong> carry bit for addition, or borrow for subtraction.<br />
S<br />
<strong>The</strong> location within <strong>the</strong> processor stack (locations $0100-$01FF) currently referenced<br />
by <strong>the</strong> stack pointer.<br />
SP<br />
<strong>The</strong> stack pointer.<br />
PC<br />
<strong>The</strong> program counter; this is composed of two eight-bit registers, PCL (program<br />
counter.low byte) and PCH (program counter high byte).<br />
287
Vocabulary of <strong>the</strong> 6510 Chip<br />
<strong>The</strong> 6510 Instruction<br />
Set<br />
ADC<br />
Add memory plus carry to <strong>the</strong> accumulator. A:= A+M+C<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$61<br />
$65<br />
$69<br />
$6D<br />
$71<br />
$75<br />
$79<br />
$7D<br />
( 97 %0110<br />
(101 %0110<br />
(105 %0110<br />
(109 %0110<br />
(113 %0111<br />
(117 %0111<br />
(121 %0111<br />
(125 %0111<br />
0001)<br />
0101)<br />
1001)<br />
1101)<br />
0001)<br />
0101)<br />
1001)<br />
1101)<br />
ADC (zero page, X)<br />
ADC zero page<br />
ADC # immediate<br />
ADC absolute<br />
ADC (zero page),Y<br />
ADC zero page,X<br />
ADC absolute,Y<br />
ADC absolute,X<br />
2<br />
2<br />
2<br />
3<br />
2<br />
2<br />
3<br />
3<br />
6<br />
3<br />
2<br />
4<br />
5*<br />
4<br />
4*<br />
4*<br />
*Add 1 if page boundary crossed.<br />
Flags:<br />
N<br />
V —<br />
B D I Z<br />
C<br />
X<br />
X<br />
X<br />
X<br />
Operation: Adds toge<strong>the</strong>r <strong>the</strong> current contents of <strong>the</strong> accumulator, <strong>the</strong> byte ref<br />
erenced by <strong>the</strong> opcode, and <strong>the</strong> carry bit. If <strong>the</strong> result is too large for a single byte, C<br />
is set to 1. If A holds 0 (each bit equals zero), <strong>the</strong> Z flag is set to 1; o<strong>the</strong>rwise, it is 0.<br />
If bit 7 in A is 1, <strong>the</strong> N flag is also set 1, to denote a negative value in A.<br />
Uses:<br />
1. Single-, double-, and multiple-byte additions. <strong>The</strong> carry bit automatically provides<br />
for overflow from one byte to <strong>the</strong> next. For example:<br />
CLC<br />
LDA $4A<br />
ADC #$0A<br />
STA $4A<br />
LDA $4B<br />
ADC #$00<br />
STA $4B<br />
INSURES CARRY BIT IS 0<br />
WE WISH TO ADD #$0A (10 DECIMAL) TO THE CONTENTS<br />
OF ($4A), I.E., THE DO0BLE-BYTE ADDRESS WHERE $4A<br />
IS THE LOW BYTE AND $4B THE HIGH BYTE<br />
ADDS THE CARRY BIT WHERE APPLICABLE<br />
RESULT MUST BE STORED, ELSE IT WILL REMAIN ONLY IN A<br />
2. Increasing or decreasing <strong>the</strong> accumulator. <strong>The</strong>re is no INC A opcode.<br />
CLC<br />
ADC #$01 ; INCREMENTS A; FF BECOMES 0.<br />
3. In binary-coded decimal mode, obtained by setting D to 1, each nybble represents<br />
0-9 and addition is corrected on this basis. This example adds 123 (decimal) to <strong>the</strong><br />
contents of locations 2 and 3, which are assumed to contain, in ascending order,<br />
four binary-coded digits.<br />
288
Vocabulary of <strong>the</strong> 6510 Chip<br />
SED<br />
CLC<br />
LDA $03<br />
ADC #$23<br />
STA<br />
LDA<br />
$03<br />
$02<br />
ADC #$01<br />
STA $02<br />
CLD<br />
; SET THE DECIMAL FLAG<br />
; CLEAR CARRY FLAG<br />
; WE'VE ASSUMED THE BCD DATA IS STORED IN NORMAL ORDER<br />
; WITH LOW BYTES FOLLOWING HIGHER ONES, NOT 6510 ORDER<br />
; ADD 23 DECIMAL<br />
; ADD 01 DECIMAL PLUS POSSIBLY CARRY BIT EQUIVALENT TO 100<br />
; CLEAR THE DECIMAL BIT, UNLESS MORE DECIMAL MATH<br />
NEEDED<br />
Notes: In decimal mode, <strong>the</strong> zero flag doesn't operate normally with ADC because<br />
of <strong>the</strong> automatic correction (adding 6) which <strong>the</strong> 6510 carries out. Testing for a zero<br />
result requires (for example) CMP #$00/ BEQ—which is an extra step not required<br />
in hexadecimal arithmetic.<br />
<strong>The</strong> V flag is important if <strong>the</strong> twos complement convention is in use, and is set<br />
if <strong>the</strong> apparent sign of <strong>the</strong> result (bit 7) is not <strong>the</strong> true sign. In decimal mode, V is<br />
not used.<br />
AND<br />
Logical AND of memory with <strong>the</strong> accumulator. A:= A AND M<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$21 (33 %0010 0001)<br />
$25 (37 %0010 0101)<br />
$29 (41 %0010 1001)<br />
$2D (45 %0010 1101)<br />
$31 (49 %0011 0001)<br />
$35 (53 %0011 0101)<br />
$39 (57 %0011 1001)<br />
$3D (61 %0011 1101)<br />
AND (zero page, X)<br />
AND zero page<br />
AND # immediate<br />
AND absolute<br />
AND (zero page),Y<br />
AND zero page,X<br />
AND absolute,Y<br />
AND absolute,X<br />
2<br />
2<br />
2<br />
3<br />
2<br />
2<br />
3<br />
3<br />
6<br />
3<br />
2<br />
4<br />
5<br />
4<br />
4*<br />
4*<br />
*Add 1 if page boundary crossed.<br />
Flags:<br />
N<br />
V — B D I Z<br />
C<br />
X<br />
X<br />
Operation: Performs logical AND of <strong>the</strong> eight bits currently in <strong>the</strong> accumulator and<br />
<strong>the</strong> eight bits referenced by <strong>the</strong> opcode. When both bits are 1, <strong>the</strong> result is 1, but if<br />
ei<strong>the</strong>r or both bits are 0, <strong>the</strong> result is 0. <strong>The</strong> resulting byte is stored in A. If A now<br />
holds 0—that is, all its bits are 0—<strong>the</strong> Z flag is set to 1; and if <strong>the</strong> high bit is set (bit<br />
7 is 1), <strong>the</strong> negative flag N is set to 1. O<strong>the</strong>rwise, <strong>the</strong> flag is 0.<br />
289
Vocabulary of <strong>the</strong> 6510 Chip<br />
Uses:<br />
1. Masking off unwanted bits, typically to test for <strong>the</strong> existence of a few high bits, or<br />
to test that some bits are 0:<br />
LDA $E081,X ; LOADS ACCUMULATOR FROM A TABLE OF CODED VALUES<br />
AND #$3F ; TURNS OFF BITS 6 AND 7, LEAVING ALPHABETIC ASCII.<br />
2. AND #$FF resets flags as though LDA had just occurred.<br />
AND #$00 has <strong>the</strong> same effect as LDA #$00.<br />
ASL<br />
Shift memory or accumulator left one bit.<br />
—| 76543210 [<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$06 ( 6 %0000 0110)<br />
$0A (10 %0000 1010)<br />
$0E (14 %0000 1110)<br />
$16 (22 %0001 0110)<br />
$1E (30 %0001 1110)<br />
ASL zero page<br />
ASL accumulator<br />
ASL absolute<br />
ASL zero page,X<br />
ASL absolute,X<br />
2<br />
1<br />
3<br />
2<br />
3<br />
5<br />
2<br />
6.<br />
6<br />
7<br />
Flags:<br />
N V -- B D I Z<br />
C<br />
X<br />
X<br />
X<br />
Operation: Moves <strong>the</strong> contents of memory or <strong>the</strong> accumulator left by one bit po<br />
sition, moving 0 into <strong>the</strong> low bit, and <strong>the</strong> high bit into <strong>the</strong> carry flag. <strong>The</strong> carry bit<br />
<strong>the</strong>refore is set to 0 or 1 depending on bit 7 previously being 0 or 1. Z and N are set<br />
according to <strong>the</strong> result; thus, Z can be true (that is, 1) only if <strong>the</strong> location or A held<br />
$00 or $80 before ASL. <strong>The</strong> N bit can be set true if bit 6 was previously 1.<br />
Uses:<br />
1. Doubles a byte (though not in decimal mode). If signed arithmetic is not being<br />
used, <strong>the</strong> result can safely reach values not exceeding 254, after which <strong>the</strong> carry<br />
must be taken into account, often with ROL. This example uses A from 0 to 127<br />
to load two bytes from a table of address pointers and store <strong>the</strong>m on <strong>the</strong> stack:<br />
ASL<br />
TAY<br />
LDA<br />
PHA<br />
LDA<br />
PHA<br />
A<br />
ADDHI,Y<br />
ADDLO,Y<br />
290
Vocabulary of <strong>the</strong> 6510 Chip<br />
<strong>The</strong> following example multiplies <strong>the</strong> contents of location $20 by 3, provided<br />
that <strong>the</strong> value it originally held was no greater than 85 decimal. In this case, <strong>the</strong><br />
carry bit is automatically cleared by <strong>the</strong> shift:<br />
LDA $20<br />
ASL A<br />
ADC $20<br />
2. Tests a bit by moving it into C or N, to be followed by an appropriate branch.<br />
Note that four ASLs move <strong>the</strong> low nybble into <strong>the</strong> high nybble.<br />
BCC<br />
Branch if <strong>the</strong> carry bit is 0. PC:= PC + offset if C=0 *<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$90 (144 %1001 0000)<br />
BCC relative<br />
2<br />
2*<br />
*Add 1 if branch occurs; add 1 more if <strong>the</strong> branch crosses a page.<br />
Flags:<br />
N V — B D I Z C<br />
Operation: If C holds 0, <strong>the</strong> byte following <strong>the</strong> opcode is added to PC to calculate<br />
<strong>the</strong> address of <strong>the</strong> next opcode. If C holds 1, <strong>the</strong> program counter is unaffected. <strong>The</strong><br />
effect is to cause a jump to <strong>the</strong> offset address when C is clear.<br />
Uses:<br />
1. As "branch always." If <strong>the</strong> carry bit is known to be clear, this command becomes<br />
effectively a "branch always" instruction. <strong>The</strong> flag may be set in a purely signal<br />
ing sense, with no significance o<strong>the</strong>r than to show that one of two conditions ap<br />
plies. Many Kernal routines return with C clear if <strong>the</strong>re were no errors, allowing<br />
JSR KERNAL/BCC OK followed by error-handling routines.<br />
2. After previous operations. Usually <strong>the</strong> test is concerned with <strong>the</strong> result of a pre<br />
vious operation which may or may not set <strong>the</strong> carry flag. This compare routine is<br />
an example:<br />
JSR GETCHAR<br />
CMP #$0A<br />
BCC LOW<br />
LOAD THE ACCUMULATOR WITH SOME VALUE, THEN<br />
COMPARE IT WITH DECIMAL 10.<br />
BRANCH TO PROCESS VALUES 0-9,<br />
CONTINUE HERE WITH VALUES, 10-225<br />
After any comparison, C is clear if <strong>the</strong> value compared was smaller, but is set with<br />
an equal or greater value. Bit 7 is irrelevant.<br />
291
Vocabulary of <strong>the</strong> 6510 Chip<br />
BCS<br />
Branch if <strong>the</strong> carry bit is 1. PC:= PC + offset if C=l<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$B0 (176 %1011 0000)<br />
BCS relative<br />
2<br />
2*<br />
*Add 1 if branch occurs; add 1 more if branch crosses a page.<br />
Flags:<br />
|n V — B D I Z C<br />
Operation: Identical to BCC, except that <strong>the</strong> branch is taken if C=l and not C=0.<br />
Uses: Identical to BCC. <strong>The</strong> choice between BCC and BCS at a branch point depends<br />
on convenience. For example, suppose a hardware port is to be read until bit 0 is set<br />
to 0. This routine:<br />
LOOP LDA PORT ; READ LOCATION UNTIL XXXXXXX0<br />
LSR A<br />
BCS LOOP<br />
is obviously tidier than:<br />
LOOP LDA<br />
LSR<br />
BCC<br />
BCS<br />
PORT<br />
A<br />
NEXT<br />
LOOP<br />
Similarly, JSR KERNAL/BCS ERROR followed by <strong>the</strong> normal processing path is<br />
probably preferable to <strong>the</strong> BCC version.<br />
BEQ<br />
Branch if zero flag is 1. PC:= PC + offset if Z=l<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$F0 (240 %1111 0000)<br />
BEQ relative<br />
2<br />
2*<br />
*Add 1 if branch occurs; add 1 more if branch crosses a page.<br />
Flags:<br />
NV-BD I Z c|<br />
Operation: If Z=l, <strong>the</strong> byte following <strong>the</strong> opcode is added, in twos complement<br />
arithmetic, to <strong>the</strong> program counter, which currently points to <strong>the</strong> next opcode. <strong>The</strong><br />
effect is to cause a jump, forward or backward, up to a maximum of +127 or -128<br />
locations if <strong>the</strong> zero flag is set. If Z=0, <strong>the</strong> branch is ignored.<br />
292
Vocabulary of <strong>the</strong> 6510 Chip<br />
Uses:<br />
1. Common as an unconditional branch. It may be used to make routines relocatable,<br />
where <strong>the</strong> branch command isn't wide-ranging enough to span <strong>the</strong> program with<br />
out an intermediate hop. <strong>The</strong> example inserts a couple of branches at a point<br />
where <strong>the</strong>y will never be taken by <strong>the</strong> ML immediately before, and so are avail<br />
able as long branches.<br />
LDA<br />
BEQ<br />
BEQ<br />
#$F5<br />
BACK<br />
FWRD<br />
: NONZERO VALUE<br />
; THESE TWO BRANCHES<br />
RELY ON Z=l<br />
2. To end a loop, ei<strong>the</strong>r when a counter is decremented to zero, or because a zero<br />
byte is deliberately used as a terminator:<br />
LOOP LDA TABLE,X ; LOAD A WITH THE NEXT CHARACTER<br />
BEQ EXIT ; EXIT LOOP WHEN ZERO BYTE FOUND<br />
... CONTINUE, E.G., STA OUTPUT,X/ INX/ BNE LOOP<br />
3. After comparisons. BEQ is popular after comparisons because it's easy to use. For<br />
example, JSR GETCHR/ CMP #$2C/ BEQ COMMA looks for a comma in BASIC.<br />
Notes: When a result is 0, <strong>the</strong> zero flag Z is made true (1). This point can be confus<br />
ing. BEQ is usually read "branch if equal to zero," but when comparisons are being<br />
made it could read "branch if equal." <strong>The</strong> zero flag cannot be set directly (<strong>the</strong>re is<br />
no SEZ instruction), but can be set only as <strong>the</strong> result of a location, register, or dif<br />
ference becoming zero.<br />
BIT<br />
Test memory bits. Z flag set according to A AND M; N flag:= M7; V flag:= M6<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$24 (36 %0010 0100)<br />
$2C (44 %0010 1100)<br />
BIT zero page<br />
BIT absolute<br />
2<br />
3<br />
3<br />
4<br />
Flags<br />
N<br />
V<br />
— B D I Z<br />
C<br />
M7<br />
M6<br />
X<br />
Operation: BIT affects only three flags, leaving registers and data unchanged. Z is<br />
set as if A AND M had been performed. If no bit position is 1 in both <strong>the</strong> memory<br />
location and A, <strong>the</strong>n A AND M is 0 and Z=l. Also, bits 6 and 7 are copied from<br />
memory to <strong>the</strong> V and N flags.<br />
Uses:<br />
1 Multiple entry points for subroutines. <strong>The</strong> three-byte absolute address BIT is <strong>the</strong><br />
only instruction regularly used to provide alternative entry points for a routine.<br />
<strong>The</strong> example loads A with RETURN, space, or a cursor-right depending on <strong>the</strong> en<br />
try point into <strong>the</strong> routine.<br />
293
Vocabulary of <strong>the</strong> 6510 Chip<br />
033C LDA #$0D A9 0D<br />
033E BIT $20A9 2C A9 20<br />
0341 BIT $1DA9 2C A9 ID<br />
LDA #$0D<br />
LDA #$20<br />
LDA #$1D<br />
If <strong>the</strong> routine is entered with JSR $033C, <strong>the</strong> accumulator is loaded with $0D and<br />
<strong>the</strong> two BIT operations are <strong>the</strong>n performed. <strong>The</strong>se will change <strong>the</strong> settings of <strong>the</strong><br />
status register flags, but will not affect <strong>the</strong> contents of <strong>the</strong> accumulator. If <strong>the</strong> rou<br />
tine is entered with JSR $033F, <strong>the</strong> routine begins with <strong>the</strong> A9 20 (LDA #$20) op<br />
eration, and <strong>the</strong> contents of <strong>the</strong> accumulator will not be affected by <strong>the</strong> following<br />
BIT operation. A JSR $0342 will leave $1D in <strong>the</strong> accumulator.<br />
This is a compact way to load values into A (or X or Y). BIT $18, in <strong>the</strong> same<br />
way, alters three flags, but if entered at <strong>the</strong> $18 byte clears <strong>the</strong> carry flag. Both<br />
constructions are common in <strong>Commodore</strong> ROM, which explains why you may fre<br />
quently see BIT instructions when you disassemble ROM.<br />
2. Testing bits 7 and 6. BIT followed by BMI/BPL or BVC/BVS tests bits 7 and 6.<br />
BIT $0D<br />
BMI ERR<br />
This example tests location $0D, with a branch taken if it holds a negative twos<br />
complement value. Location $0D is in fact used to check for type mismatches. A<br />
value of $FF <strong>the</strong>re denotes a string, $00 a numeric variable, so BMI occurs with<br />
strings.<br />
3. Used as AND without affecting <strong>the</strong> accumulator. <strong>The</strong> following example shows<br />
<strong>the</strong> AND feature in use. CHRFLG holds 0 if no character is to be output, and $FF<br />
o<strong>the</strong>rwise. Assuming <strong>the</strong> accumulator holds a nonzero value, BIT tests whe<strong>the</strong>r to<br />
branch past <strong>the</strong> output routine, while retaining A's value.<br />
LDA<br />
BIT<br />
BEQ<br />
VALUE<br />
CHRFLG<br />
NOTOUT<br />
BMI<br />
Branch if <strong>the</strong> N flag is 1. PC:= PC + offset if N=l<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$30 (48 %0011 0000)<br />
BMI relative<br />
2<br />
2*<br />
*Add 1 if branch occurs; add 1 more if branch crosses a page.<br />
Flags:<br />
N V — B D I Z c|<br />
Operation: If <strong>the</strong> N flag is set, <strong>the</strong> byte following <strong>the</strong> opcode is added to <strong>the</strong> pro<br />
gram counter in twos complement form. <strong>The</strong> effect is to force a jump to <strong>the</strong> new ad<br />
dress. <strong>The</strong> maximum range of a branch is -128 to +127 locations. When N is clear<br />
<strong>the</strong> branch command is ignored.<br />
294
Vocabulary of <strong>the</strong> 6510 Chip<br />
Uses:<br />
1. Testing bit 7 of a location. For example:<br />
LOOP BIT<br />
PORT ;TEST BITS OF A HARDWARE PORT (PRESERVING VALUE<br />
; IN A)<br />
BMI LOOP ;WAIT UNTIL BIT 7 OF THE PORT IS 0<br />
2. Conventional use. Like <strong>the</strong> o<strong>the</strong>r flags, N may be used in a purely conventional<br />
sense. As an example, consider BASIC'S keyword tokens. All have values, in deci<br />
mal, of 128 or more, which keeps keywords logically separate from o<strong>the</strong>r BASIC<br />
and also permits instructions like this:<br />
LDA NEXT ; LOAD NEXT CHARACTER INTO ACCUMULATOR<br />
BMI TOKEN; BRANCH TO PROCESS A KEYWORD<br />
; OTHERWISE, PROCESS DATA AND EXPRESSIONS<br />
Notes:<br />
1. It's important to realize that <strong>the</strong> minus in BMI (Branch if Minus) refers only to <strong>the</strong><br />
use of bit 7 to denote a negative number in twos complement arithmetic.<br />
Comparisons (for example, with CMP) followed by BMI implicitly use bit 7.<br />
Mostly, it is easier to think of this operation as "branch if <strong>the</strong> highest bit is set."<br />
2. BPL is exactly <strong>the</strong> opposite of BMI. Where one branches, <strong>the</strong> o<strong>the</strong>r does not.<br />
Branch if Z is 0. PC:= PC + offset if Z= 0<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$D0 (208<br />
%1101 0000)<br />
BNE relative<br />
2<br />
2*<br />
*Add 1 if branch occurs; add 1 more if branch crosses a page.<br />
Flags:<br />
NV —<br />
BDIZC<br />
Operation: BNE operates exactly like BEQ, except that <strong>the</strong> condition is opposite. If<br />
Z=0, <strong>the</strong> offset contained in <strong>the</strong> byte after BNE is added to <strong>the</strong> program counter, so<br />
<strong>the</strong> branch takes place. If Z=l, <strong>the</strong> branch is ignored.<br />
Uses:<br />
1. In unconditional branches. BNE may be used in unconditional branches in circum<br />
stances like those which apply to BEQ.<br />
2. In a loop, where a counter is being decremented. BNE is very often used in a loop<br />
in which a counter is being decremented. This is probably <strong>the</strong> easiest type of loop<br />
to write. Watch <strong>the</strong> data's starting address, as offset 0 isn't executed by a loop like<br />
this. <strong>The</strong> example prints ten characters from a table, <strong>the</strong>ir offsets being 10, 9, 8, ...<br />
2,1.<br />
295
Vocabulary of <strong>the</strong> 6510 Chip<br />
LDX<br />
LOOP LDA<br />
JSR<br />
DEX<br />
BNE<br />
#$0A<br />
TABLED<br />
OUTPUT<br />
LOOP<br />
3. After comparisons. BNE, like BEQ, is popular after comparisons:<br />
B4C0 LDA $C1 ;CHECK CONTENTS OF $C1<br />
B4C2 CMP #$42 ;IS IT B?<br />
B4C4 BNE $B4C9 ;BRANCH IF NOT<br />
Notes: When a result is nonzero, <strong>the</strong> zero flag, Z, is made false (set to 0). This can<br />
be confusing. BNE is usually read "branch if not equal to zero." <strong>The</strong> result of a<br />
comparison is zero if both bytes are identical, because one is subtracted from <strong>the</strong><br />
o<strong>the</strong>r, so "branch if not equal" is an optional alternative.<br />
BPL<br />
Branch if <strong>the</strong> N flag is 0. PC:= PC + offset if N=0<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$10 (16 %0001 0000)<br />
BPL relative<br />
2<br />
2*<br />
*Add 1 if branch occurs; add 1 more if branch crosses a page.<br />
Flags:<br />
|N V — B D I Z C<br />
Operation: BPL operates exactly like BMI, except that <strong>the</strong> condition is opposite. <strong>The</strong><br />
branch is taken to <strong>the</strong> new address given by program counter plus offset if N=0.<br />
This means that if <strong>the</strong> result is positive or zero, <strong>the</strong> branch is taken.<br />
Uses:<br />
1. In testing bit 7 of a memory location. This code, for example waits until <strong>the</strong> accu<br />
mulator holds a byte with bit 7 on. Such a location must be interrupt- or<br />
hardware-controlled, not just RAM.<br />
LOOP LDA<br />
BPL<br />
TESTLOCN<br />
LOOP<br />
2. Testing for <strong>the</strong> end of a loop. Where a counter is being decremented, and <strong>the</strong><br />
counter's value 0 is needed, this command can be useful. This simple loop prints<br />
ten bytes to screen:<br />
LDX #$09<br />
LOOP LDA BASE,X<br />
STA $0400,X<br />
DEX<br />
BPL LOOP<br />
;X REGISTER WILL COUNT 9,8,7,... 1,0<br />
;//BASE// IS THE STARTING ADDRESS OF THE 10 BYTES<br />
;START OF SCREEN (<strong>64</strong>)<br />
; DECREMENT X<br />
; BRANCH WHEN POSITIVE OR ZERO<br />
296
Vocabulary of <strong>the</strong> 6510 Chip<br />
Force break. S:= PCH, SP:= SP-1, S:= PCL, SP:= SP-1, S:= PSR, SP:= SP-1,<br />
PCL:= $FE, PCH:= $FF<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$00 (0 %0000 0000)<br />
BRK implied<br />
1<br />
7<br />
Flags:<br />
N V — B<br />
D<br />
I<br />
Z<br />
C<br />
1<br />
1<br />
Operation: BRK is a forced interrupt, which saves <strong>the</strong> current program counter and<br />
status register values and jumps to a standard address. Note that <strong>the</strong> value saved for<br />
<strong>the</strong> program counter points to <strong>the</strong> BRK byte plus two (like a branch) and that <strong>the</strong><br />
processor status register on <strong>the</strong> stack has flag B set to 1.<br />
<strong>The</strong> IRQ service routine behaves like BRK. <strong>The</strong> break flag is a sort of designer's<br />
patch so that BRK can be recognized as different from IRQ interrupts.<br />
Uses:<br />
1. BRK is mainly used with ML monitors. <strong>The</strong> ML stops when BRK is encountered,<br />
and <strong>the</strong> vector points back to <strong>the</strong> monitor, typically printing <strong>the</strong> current values of<br />
<strong>the</strong> program counter, flags' status register, A, X, Y, stack pointer, and possibly<br />
o<strong>the</strong>r ML variables.<br />
Whenever <strong>the</strong> 6510 encounters a BRK, it looks to locations $FFFE and $FFFF<br />
for <strong>the</strong> address of <strong>the</strong> next instruction. In <strong>the</strong> <strong>64</strong>'s ROM, locations $FFFE and<br />
$FFFF point to a routine beginning at $FF48. If <strong>the</strong> B flag is set, a jump is made<br />
through a vector at location $0316, so <strong>the</strong> BRK handling routine can be modified<br />
by changing <strong>the</strong> values in $0316 and $0317. Altering <strong>the</strong>se locations to point to<br />
<strong>the</strong> monitor is a function of initialization of <strong>the</strong> monitor; it isn't inherent in <strong>the</strong><br />
system that BRK behaves like that. BRK is valuable when developing ML<br />
programs.<br />
^T^<br />
2. Monitors can be entered from BASIC if $0316-$0317 points to <strong>the</strong>ir start. POKE<br />
790,0: POKE 791,96, for example, points this vector .to $6000, and SYS 13 (or a<br />
SYS to any location containing a zero byte) enters a monitor <strong>the</strong>re. Usually,<br />
$0316-$0317 points to a ROM routine used by RUN/STOP-RESTORE which re<br />
sets I/O and Kernal pointers. BRK is not widely used in ML that must interact di<br />
rectly with BASIC.<br />
297
Vocabulary of <strong>the</strong> 6510 Chip<br />
BVC<br />
Branch if <strong>the</strong> internal overflow flag (V) is 0. PC:= PC + offset if V=0<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$50 (80 %0101 0000)<br />
BVC relative<br />
2<br />
2*<br />
*Add 1 if branch occurs; add 1 more if branch crosses a page.<br />
Flags:<br />
|N V - B D I Z c]<br />
Operation: If V is clear, <strong>the</strong> byte following <strong>the</strong> opcode is added, as a twos com<br />
plement number, to <strong>the</strong> program counter, set to point at <strong>the</strong> following instruction.<br />
<strong>The</strong> effect is to jump to a new address. If V=l, <strong>the</strong> next instruction is processed and<br />
<strong>the</strong> branch ignored.<br />
Uses:<br />
1. As a "branch always" instruction. For instance:<br />
CLV<br />
BVC<br />
LOAD<br />
2. With signed arithmetic, to detect overflow from bit 6 into bit 7, giving a spurious<br />
negative bit. This is rarely used since <strong>the</strong> sign of a number can be held elsewhere<br />
so that ordinary arithmetic can be used without <strong>the</strong> complication of <strong>the</strong> V bit.<br />
<strong>The</strong> following routine adds two numbers in twos complement form; <strong>the</strong><br />
numbers must <strong>the</strong>refore be in <strong>the</strong> range -128 to +127. CLC is necessary; o<strong>the</strong>r<br />
wise, it may add 1 to <strong>the</strong> result. Overflow will occur if <strong>the</strong> total exceeds 127 or is<br />
less than —128.<br />
LDA ADD1<br />
CLC<br />
ADC ADD2<br />
BVC OK<br />
JMP OVERFL<br />
3. Testing bit 6. BIT copies bit 6 of <strong>the</strong> specified location into <strong>the</strong> V flag of <strong>the</strong><br />
processor status register, so BVC or BVS can be used to test bit 6. For example,<br />
<strong>the</strong> following routine waits until <strong>the</strong> hardware sets bit 6 of hardware location<br />
PORT to 1.<br />
F103<br />
F106<br />
BIT PORT<br />
BVC $F103<br />
298
Vocabulary of <strong>the</strong> 6510 Chip<br />
BVS<br />
Branch if <strong>the</strong> internal overflow flag (V) is 1. PC:= PC + offset if V=l<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$70 (112 %0111 0000)<br />
BVS relative<br />
2<br />
2*<br />
*Add 1 if branch occurs; add 1 more if branch crosses a page.<br />
Flags:<br />
|N V - B D I Z C<br />
Operation: This branch is identical to BVC except that <strong>the</strong> test logic to decide<br />
whe<strong>the</strong>r <strong>the</strong> branch is taken is opposite.<br />
CLC<br />
Clear <strong>the</strong> carry flag. C:= 0<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$18 (24 %0001 1000)<br />
CLC implied<br />
. 1<br />
2<br />
Flags:<br />
N V — B D I Z c<br />
0<br />
Operation: <strong>The</strong> carry flag is set to 0. All o<strong>the</strong>r flags are unchanged.<br />
Uses: <strong>The</strong> carry bit is automatically included in add and subtract commands (ADC<br />
and SBC) so that accurate calculations require <strong>the</strong> flag to be in a known state. CLC is<br />
<strong>the</strong> usual preliminary to additions:<br />
CLC<br />
LDA #$02<br />
ADC #$02<br />
JSR PRINT<br />
After CLC, this routine adds 2 and 2 and prints <strong>the</strong> resulting byte 4. In multiplebyte<br />
additions, C is cleared at <strong>the</strong> start, but is subsequently used to carry through <strong>the</strong><br />
overflows if <strong>the</strong>y exist.<br />
299
Vocabulary of <strong>the</strong> 6510 Chip<br />
CLD<br />
Clear <strong>the</strong> decimal flag. D:= 0<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$D8 (216 %1101 1000)<br />
CLD implied<br />
1<br />
2<br />
Flags:<br />
N V — 6 D<br />
I Z c<br />
0<br />
Operation: <strong>The</strong> decimal flag is set to 0; all o<strong>the</strong>r flags are unchanged.<br />
Uses: Resets <strong>the</strong> mode for ADC and SBC so that hexadecimal arithmetic is per<br />
formed, not binary coded decimal. Typically, SED precedes some decimal calculation,<br />
with CLD following when this is finished.<br />
Notes: BASIC uses no decimal mode calculations; when <strong>the</strong> machine is switched on,<br />
CLD is executed and <strong>the</strong> flag is left off. ML monitors clear <strong>the</strong> flag on entry, too.<br />
CLI<br />
Clear <strong>the</strong> interrupt disable flag. I:= 0<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$58 (88 %0101 1000)<br />
CLI implied<br />
1<br />
2<br />
Flags:<br />
N y B D I<br />
0<br />
z<br />
c<br />
Operation: <strong>The</strong> interrupt disable flag is set to 0. From now on, IRQ interrupts will<br />
take place and be processed by <strong>the</strong> system.<br />
Notes:<br />
1. Interrupts through <strong>the</strong> NMI line (non-maskable interrupts) take place irrespective<br />
of <strong>the</strong> I flag.<br />
2. Typically, CLI is used after SEI plus changes to interrupt vectors. Often, CLI isn't<br />
needed when used with BASIC, as a number of BASIC routines <strong>the</strong>mselves use<br />
CLI.<br />
300
Vocabulary of <strong>the</strong> 6510 Chip<br />
CLV<br />
Clear <strong>the</strong> internal overflow flag. V: = 0<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$B8 (184 %1011 1000)<br />
CLV implied<br />
1<br />
2<br />
Flags:<br />
N V —<br />
B D I Z C<br />
0<br />
Operation: Sets V to 0.<br />
Notes: CLV is used in "branch always" instructions, for example, CLV/BVC. Unlike<br />
C, V isn't added to results, so clearing is not necessary before calculations.<br />
CMP<br />
Compare memory with <strong>the</strong> contents of <strong>the</strong> accumulator. PSR set by A—M<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$C1 (193 %1100 0001)<br />
$C5 (197 %1100 0101)<br />
$C9 (201 %1100 1001)<br />
$CD (205 %1100 1101)<br />
$D1 (209 %1101 0001)<br />
$D5 (213 %1101 0101)<br />
$D9 (217 %1101 1001)<br />
$DD (221 %1101 1101)<br />
CMP (zero page,X)<br />
CMP zero page<br />
CMP # immediate<br />
CMP absolute<br />
CMP (zero page),Y<br />
CMP zero page,X<br />
CMP absolute,Y<br />
CMP absolute,X<br />
2<br />
2<br />
2<br />
3<br />
2<br />
2<br />
3<br />
3<br />
6<br />
3<br />
2<br />
4<br />
5*<br />
4<br />
4*<br />
4*<br />
*Add 1 if page boundary crossed.<br />
Flags:<br />
N<br />
V — B D I Z<br />
C<br />
X<br />
X<br />
X<br />
Operation: CMP affects three flags only, leaving registers and data intact. <strong>The</strong> accu<br />
mulator is not changed. <strong>The</strong> byte at <strong>the</strong> address specified by <strong>the</strong> opcode is subtracted<br />
from A, and <strong>the</strong> three flags N, Z, and C are set depending on <strong>the</strong> result. Thus, if <strong>the</strong><br />
accumulator holds <strong>the</strong> same value as <strong>the</strong> memory location, <strong>the</strong> result is zero and <strong>the</strong><br />
zero flag is set.<br />
Within <strong>the</strong> chip, what happens is that <strong>the</strong> value in <strong>the</strong> accumulator is added to<br />
<strong>the</strong> twos complement of <strong>the</strong> data. <strong>The</strong> result of this determines how <strong>the</strong> flags are set.<br />
301
Vocabulary of <strong>the</strong> 6510 Chip<br />
Uses:<br />
1. With <strong>the</strong> zero flag, Z. This is <strong>the</strong> easiest flag to use with CMP. Z=0 after a CMP<br />
means <strong>the</strong> two values were equal.<br />
FF22<br />
FF25<br />
FF27<br />
FF29<br />
FF2B<br />
FF2D<br />
JSR<br />
CMP<br />
BEQ<br />
CMP<br />
BEQ<br />
CMP<br />
$FFCF<br />
#$20<br />
$FF22<br />
#$OD<br />
$FF47<br />
#$22<br />
;INPUT A CHARACTER<br />
;IS IT A SPACE?<br />
;YES. INPUT AGAIN<br />
;IS IT RETURN?<br />
;YES. BRANCH ...<br />
;..NO. IS IT QUOTES? ETC.<br />
This is part of a ROM routine to search through BASIC lines from <strong>the</strong> keyboard<br />
buffer for particular characters, such as spaces, RETURNS, and quotes, which re<br />
quire special handling.<br />
2. With <strong>the</strong> carry flag, C. If <strong>the</strong> value of <strong>the</strong> byte is less than A or equal to A, <strong>the</strong><br />
carry flag is set; that is, C=0 (tested with BCC) after a CMP means that A
Vocabulary of <strong>the</strong> 6510 Chip<br />
CPX<br />
Compare memory with <strong>the</strong> contents of <strong>the</strong> X register. PSR set by X—M<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$E0 (224 %1110 0000)<br />
$E4 (228 %1110 0100)<br />
$EC (236 %1110 1100)<br />
CPX # immediate<br />
CPX zero page<br />
CPX absolute<br />
2<br />
2<br />
3<br />
2<br />
3<br />
4<br />
Flag*<br />
N V -<br />
B D I Z<br />
C<br />
X<br />
X<br />
X<br />
Operation: CPX affects three flags only, leaving <strong>the</strong> registers and data intact. <strong>The</strong><br />
byte referenced by <strong>the</strong> opcode is subtracted from <strong>the</strong> contents of <strong>the</strong> X register, and<br />
<strong>the</strong> flags N, Z, and C are set depending on <strong>the</strong> result. <strong>The</strong> value in X is not affected.<br />
Within <strong>the</strong> chip, X is added to <strong>the</strong> twos complement of <strong>the</strong> data, and <strong>the</strong> result<br />
determines how <strong>the</strong> flags are set.<br />
Uses:<br />
1. With <strong>the</strong> zero flag, Z. This flag tests equality.<br />
LDX #$00<br />
LOOP LDA $0278,X<br />
STA $0277,X<br />
INX<br />
CPX $C6<br />
BNE LOOP<br />
<strong>The</strong> loop in this example is part of <strong>the</strong> keyboard buffer processing, showing<br />
how <strong>the</strong> contents of <strong>the</strong> buffer are shifted one character at a time. Thus, $C6 is a<br />
zero page location, updated whenever a new character is keyed in, which holds<br />
<strong>the</strong> current number of characters in <strong>the</strong> buffer. <strong>The</strong> comparison provides a test to<br />
end <strong>the</strong> loop.<br />
2. With <strong>the</strong> carry flag, C. This flag tests for Xs>M and X39 (#$27)<br />
<strong>The</strong> test routine is part of a graphics plot program; location $FE holds <strong>the</strong><br />
horizontal coordinate, which is to be in <strong>the</strong> range 0-39 to fit <strong>the</strong> screen. <strong>The</strong><br />
comparison causes exit, without plotting, when X holds 40-255.<br />
3. With <strong>the</strong> negative flag, N. When X and <strong>the</strong> data have <strong>the</strong> same sign (both are 0-<br />
127 or 128-255), <strong>the</strong>n BMI has <strong>the</strong> same effect as BCC, and vibe versa. When <strong>the</strong><br />
signs are opposite, <strong>the</strong> process is complicated by <strong>the</strong> possibility of overflow into<br />
bit 7. For example, 78 compared with 225 sets N=0, but 127 compared with 255<br />
sets N=l. (Note that 225 = —31 as a twos complement number; thus,<br />
78+31 = 109 with N=0, but 127+31 = 158 with N=l.)<br />
303
Vocabulary of <strong>the</strong> 6510 Chip<br />
CPY<br />
Compare memory with <strong>the</strong> contents of <strong>the</strong> Y register. PSR set by Y—M.<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$C0 (192 %1100 0000)<br />
$C4 (196 %1100 0100)<br />
$CC (204 %1100 1100)<br />
CPY # immediate<br />
CPY zero page<br />
CPY absolute<br />
2<br />
2<br />
3<br />
2<br />
3<br />
4<br />
Flags:<br />
N V — B D I Z C<br />
X<br />
XX<br />
Operation: CPY affects three flags only, leaving <strong>the</strong> registers and data intact. <strong>The</strong><br />
byte referenced by <strong>the</strong> opcode is subtracted from Y, and <strong>the</strong> flags N, Z, and C are set<br />
depending on <strong>the</strong> result. Apart from <strong>the</strong> use of Y in place of X, this opcode is identi<br />
cal in its effects to CPX.<br />
Notes: <strong>The</strong> major difference in addressing between X and Y is <strong>the</strong> fact that postindexing<br />
of indirect addresses is available only with Y. This type of construction, in<br />
which a set of consecutive bytes (perhaps a string in RAM or an error message) is<br />
processed up to some known length, tends to use <strong>the</strong> Y register.<br />
LDY #$00<br />
LOOP LDA (PTR),Y<br />
JSR OUTPUT<br />
INY<br />
CPY<br />
BNE<br />
LENGTH<br />
LOOP<br />
DEC<br />
Decrement contents of memory location. M:= M—1<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$C6<br />
$CE<br />
$D6<br />
$DE<br />
(198 %1100 0110)<br />
(206 %1100 1110)<br />
(214 %1101 0110)<br />
(222 %1101 1110)<br />
DEC zero page<br />
DEC absolute<br />
DEC zero page,X<br />
DEC absolute,X<br />
2<br />
3<br />
2<br />
3<br />
5<br />
6<br />
6<br />
7<br />
Flags:<br />
N<br />
V<br />
— B D I<br />
z<br />
c<br />
X<br />
X<br />
304
Vocabulary of <strong>the</strong> 6510 Chip<br />
Operation: <strong>The</strong> byte referenced by <strong>the</strong> addressing mode is decremented by 1,<br />
conditioning <strong>the</strong> N flag and <strong>the</strong> Z flag. If <strong>the</strong> byte contains a value from $81 to $00<br />
after DEC, <strong>the</strong> N flag will be set. <strong>The</strong> Z flag will be 0 except for <strong>the</strong> one case where<br />
<strong>the</strong> location held $01 before <strong>the</strong> decrement. DEC is performed within <strong>the</strong> chip itself<br />
by adding $FF to <strong>the</strong> contents of <strong>the</strong> specified location, setting N and Z according to<br />
<strong>the</strong> result.<br />
<strong>The</strong> carry bit is unchanged regardless of <strong>the</strong> outcome of DEC.<br />
Uses:<br />
1. To decrement a double-byte value.<br />
LDA $93<br />
BNE +2<br />
DEC $94<br />
DEC $93<br />
This short routine shows an efficient method to decrement a zero page<br />
pointer or any o<strong>the</strong>r double-byte value. It uses <strong>the</strong> fact that <strong>the</strong> high byte must be<br />
decremented only if <strong>the</strong> low byte is exactly zero.<br />
2. Implementing o<strong>the</strong>r counters. Counters o<strong>the</strong>r than <strong>the</strong> X register and Y register<br />
can easily be implemented with this command (or INC). Such counters must be in<br />
RAM. DEC cannot be used to decrement <strong>the</strong> contents of <strong>the</strong> accumulator. This<br />
simple delay loop which decrements locations $FB and $FC shows an example:<br />
LOOP<br />
AND<br />
STA<br />
STA<br />
DEC<br />
BNE<br />
DEC<br />
BNE<br />
#$00<br />
$FB<br />
$FC<br />
$FB<br />
LOOP<br />
$FC<br />
LOOP<br />
;FOR A CHANGE<br />
;SET THESE BOTH<br />
;TO0<br />
;255 LOOPS...<br />
;... BY 255<br />
A zero page decrement takes five clock cycles to carry out; a successful<br />
branch takes three (assuming a page boundary isn't crossed). <strong>The</strong> inside loop<br />
<strong>the</strong>refore takes 8*255 cycles to complete, and <strong>the</strong> whole loop requires a little more<br />
than 8*255*255 cycles. Divide this by a million to get <strong>the</strong> actual time in seconds,<br />
which is about half a second.<br />
DEX<br />
Decrement <strong>the</strong> contents of <strong>the</strong> X register. X:= X—1<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$CA (202 %1100 1010)<br />
DEX implied<br />
1<br />
2<br />
Flags:<br />
N V —<br />
B D I Z<br />
C<br />
X<br />
X<br />
305
Vocabulary of <strong>the</strong> 6510 Chip<br />
Operation: <strong>The</strong> value in <strong>the</strong> X register is decremented by 1, setting <strong>the</strong> N flag if <strong>the</strong><br />
result has bit 7 set, and setting <strong>the</strong> Z flag if <strong>the</strong> result is 0. As with DEC, <strong>the</strong> carry<br />
bit is unaltered.<br />
Uses: To count X in a loop. DEX is almost exclusively used to count X in a loop.<br />
Since its maximum range, 255 bytes, is often insufficient, several loops may be nec<br />
essary. This routine moves 28 bytes from ROM to RAM, including <strong>the</strong> CHRGET<br />
routine.<br />
LDX #$1C<br />
NEXT LDA $E3A2,X<br />
STA $73,X<br />
DEX<br />
BNE NEXT<br />
DEY<br />
Decrement <strong>the</strong> contents of <strong>the</strong> Y register. Y:= Y—1<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$88 (136 %1000 1000)<br />
DEY implied<br />
1<br />
2<br />
Flags:<br />
N V —<br />
B D I Z<br />
C<br />
X<br />
X<br />
Operation: <strong>The</strong> value in <strong>the</strong> Y register is decremented by 1, setting <strong>the</strong> N flag if <strong>the</strong><br />
result has bit 7 set (that is, is greater than 127), and setting <strong>the</strong> Z flag if <strong>the</strong> result is<br />
0. As with DEC, <strong>the</strong> carry bit is unaltered.<br />
Uses: Counting within loops. DEY, like DEX, is almost exclusively used to count<br />
within loops. <strong>The</strong>re are more opcodes which have indexing by X than by Y, so X is<br />
more popular for this purpose. <strong>The</strong> example uses Y to count from 2 to 0.<br />
LDY #$02<br />
LDA (PTR),Y ;LOAD SECOND BYTE<br />
DEY<br />
ORA (PTR),Y ;ORA WITH FIRST BYTE<br />
DEY<br />
ORA (PTR),Y ;ORA WITH ZEROTH BYTE<br />
BNE CONT ;ENDIFZERO<br />
This inclusively ORs toge<strong>the</strong>r three adjacent bytes; if <strong>the</strong> result is 0, each of <strong>the</strong><br />
three must have been a zero.<br />
306
Vocabulary of <strong>the</strong> 6510 Chip<br />
EOR<br />
<strong>The</strong> byte in <strong>the</strong> accumulator is Exclusive ORed bitwise with <strong>the</strong> contents of memory.<br />
A:=<br />
AEORM<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$41 (65 %0100 0001)<br />
$45 (69 %0100 0101)<br />
$49 (73 %0100 1001)<br />
$4D (77 %0100 1101)<br />
$51 (81 %0101 0001)<br />
$55 (85 %0101 0101)<br />
$59 (89 %0101 1001)<br />
$5D (93 %0101 1101)<br />
EOR (zero page,X)<br />
EOR zero page<br />
EOR # immediate<br />
EOR absolute<br />
EOR (zero page),Y<br />
EOR zero page,X<br />
EOR absolute,Y<br />
EOR absolute,X<br />
2<br />
2<br />
2<br />
3<br />
2<br />
2<br />
3<br />
3<br />
6<br />
3<br />
2<br />
4<br />
5*<br />
4<br />
4*<br />
4*<br />
*Add 1 if page boundary crossed.<br />
Flags:<br />
N<br />
V — B D I Z<br />
C<br />
X<br />
X<br />
Operation: An Exclusive OR (see ORA for a description of an inclusive OR) is a logi<br />
cal operation in which bits are compared, and EOR is considered to be true if A or<br />
B—but not both or nei<strong>the</strong>r—is true. For example, consider $AB EOR $5R <strong>The</strong> byte<br />
$AB is %1010 1011, and $5F is %0101 1111. So <strong>the</strong> EOR of <strong>the</strong>se two is %1111<br />
0100, or $F4. You get this result by a process of bit comparisons, where bit 7 is 0<br />
EOR 1 = 1, and so on.<br />
Uses:<br />
1. Reversing a bit. EORing a bit with 0 leaves <strong>the</strong> bit unaffected; EORing a bit with 1<br />
flips <strong>the</strong> bit.<br />
LDA LOCN<br />
EOR #$02 ;FLIPSBIT1<br />
STA LOCN<br />
<strong>The</strong> example shows how a single bit can be reversed. To reverse an entire<br />
byte, use EOR #$FF; to reverse bit 7, use EOR #$80.<br />
2. In hash totals and encryption algorithms. Hash totals and encryption algorithms<br />
often use EOR. For example, if you have a message you wish to conceal, you can<br />
EOR each byte with a section of ROM or with bytes generated by some repeatable<br />
process. <strong>The</strong> message is recoverable with <strong>the</strong> same EOR sequence.<br />
307
Vocabulary of <strong>the</strong> 6510 Chip<br />
INC<br />
Increment contents of memory location. M:= M+l<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$E6<br />
$EE<br />
$F6<br />
$FE<br />
(230 %1110 0110)<br />
(238 %1110 1110)<br />
(246 %1111 0110)<br />
(254 %1111 1110)<br />
INC zero page<br />
INC absolute<br />
INC zero page,X<br />
INC absolute,X<br />
2<br />
3<br />
2<br />
3<br />
5<br />
6<br />
6<br />
7<br />
Flags:<br />
N<br />
V<br />
— B D I<br />
z<br />
c<br />
X<br />
X<br />
Operation: <strong>The</strong> byte referenced by <strong>the</strong> addressing mode is incremented by 1, pos<br />
sibly affecting <strong>the</strong> N flag and <strong>the</strong> Z flag. <strong>The</strong> N flag will be 1 if <strong>the</strong> high bit of <strong>the</strong><br />
byte is 1 after <strong>the</strong> INC, and o<strong>the</strong>rwise 0. <strong>The</strong> Z flag will be 1 only if <strong>the</strong> location<br />
held $FF before <strong>the</strong> INC. <strong>The</strong> carry bit is unchanged.<br />
Uses:<br />
1. Incrementing a double-byte value. This short routine shows an efficient method to<br />
increment a zero page pointer or any o<strong>the</strong>r double-byte value. <strong>The</strong> high byte is in<br />
cremented only when <strong>the</strong> low byte changes from $FF to $00.<br />
INC<br />
BNE<br />
INC<br />
CONT ...<br />
$FB<br />
CONT<br />
$FC<br />
2. Implementing counters in RAM. INC may be used to implement counters in RAM<br />
where <strong>the</strong> X and Y registers are insufficient. Suppose we use <strong>the</strong> IRQ interrupt<br />
servicing to change a tune regularly.<br />
IRQ INC $FE<br />
BEQ +3<br />
JMP IRQCONT<br />
LDA #20<br />
STA $FE<br />
Where IRQCONT is <strong>the</strong> interrupts usual routine, this allows some periodic rou<br />
tine to be performed. Here, <strong>the</strong> zero page location $FE is used to count from $20<br />
up to $FF and $00, so <strong>the</strong> processing occurs every 255—32=223 jiffies—about<br />
every 3.7 seconds.<br />
Notes:<br />
1. <strong>The</strong> accumulator can't be incremented with INC. Ei<strong>the</strong>r CLC/ADC #$01 or SEC/<br />
ADC #$00 must be used; TAX/ INX/ TXA or some o<strong>the</strong>r variation may also be<br />
used.<br />
2. Remember that INC doesn't load <strong>the</strong> contents of <strong>the</strong> location to be incremented<br />
into any of <strong>the</strong> registers. If <strong>the</strong> incremented value is wanted in A, X, or Y, <strong>the</strong>n<br />
INC $C6 must be followed by LDA $C6, LDX $C6, or LDY $C6.<br />
308
Vocabulary of <strong>the</strong> 6510 Chip<br />
INX<br />
Increment <strong>the</strong> contents of <strong>the</strong> X register. X:= X+l<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$E8 (232 %1110 1000)<br />
INX implied<br />
1<br />
2<br />
Flags:<br />
N V —<br />
B D I Z<br />
C<br />
X<br />
X<br />
Operation: <strong>The</strong> byte in <strong>the</strong> X register is incremented by 1, setting <strong>the</strong> N flag if <strong>the</strong><br />
result has bit 7 set, and <strong>the</strong> Z flag if <strong>the</strong> result is 0. <strong>The</strong>se flags may both be 0, or<br />
one of <strong>the</strong>m may be 1; it is impossible for both to be set to 1 by this command. <strong>The</strong><br />
carry bit is unchanged.<br />
Uses: As a loop variable. INX is common as a loop variable. It is also often used to<br />
set miscellaneous values which happen to be near each o<strong>the</strong>r, for example:<br />
LDX #$00<br />
STX $033A<br />
STX $033C<br />
INX<br />
STX $10<br />
Stack-pointer processing tends to be connected with <strong>the</strong> use of <strong>the</strong> X register,<br />
because TXS and TSX are <strong>the</strong> only ways of accessing SP.<br />
INY<br />
Increment <strong>the</strong> contents of <strong>the</strong> Y register. Y: = Y+l<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$C8 (200 %1100 1000)<br />
INY implied<br />
1<br />
2<br />
Flags:<br />
N V —<br />
B D I Z<br />
C<br />
X<br />
X<br />
Operation: <strong>The</strong> byte in <strong>the</strong> Y register is incremented by 1, setting N=l if <strong>the</strong> result<br />
has bit 7=1 and setting Z=l if <strong>the</strong> result is 0. A zero result is obtained by in<br />
crementing $FF. Note that <strong>the</strong> carry bit is unchanged in all cases.<br />
Uses: To control loops. Like DEX, DEY, and INX, this command is often used to con<br />
trol loops. It is often followed by a comparison, CPY, to check whe<strong>the</strong>r its exit value<br />
has been reached.<br />
309
Vocabulary of <strong>the</strong> 6510 Chip<br />
JMP<br />
Jump to a new location anywhere in memory. PC:= M<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$4C ( 76 %0100 1100)<br />
$6C (108 %0110 1100)<br />
JMP absolute<br />
JMP (absolute)<br />
Flags:<br />
N V — B D I Z C<br />
CO<br />
3<br />
5<br />
Operation: JMP is <strong>the</strong> 6510 equivalent of a GOTO, transferring control to some<br />
o<strong>the</strong>r part of <strong>the</strong> program. An absolute JMP, opcode $4C, transfers <strong>the</strong> next byte to<br />
<strong>the</strong> low byte of PC, and <strong>the</strong> one after to highest byte of PC, causing an uncondi<br />
tional jump.<br />
<strong>The</strong> indirect absolute jump, opcode $6C, is more elaborate and takes longer.<br />
PCL and PCH are loaded from <strong>the</strong> contents of two consecutive locations beginning<br />
at <strong>the</strong> address specified by <strong>the</strong> two bytes following <strong>the</strong> JMP opcode. This is <strong>the</strong> only<br />
absolute indirect command available on <strong>the</strong> 6510.<br />
Uses: JMP, unlike JSR, keeps no record of its present position; control is just shifted<br />
to ano<strong>the</strong>r part of a program. Branch instructions are preferable to jumps if ML is re<br />
quired to work even when moved around in memory, except for JMPs to fixed loca<br />
tions like ROM.<br />
CMP #$2C ; IS IT COMMA?<br />
BEQ +3<br />
JMP ERROR<br />
<strong>The</strong> example is part of a subroutine which checks for a comma in a BASIC line; if<br />
<strong>the</strong> comma has been omitted, an error message is printed.<br />
Notes:<br />
1. Indirect addressing. This is a three-byte command that takes <strong>the</strong> form JMP ($0072)<br />
or JMP ($7FF0). A concrete example is <strong>the</strong> IRQ vector. When a hardware interrupt<br />
occurs, an indirect jump to ($0314) takes place. A look at this region of RAM with<br />
a monitor reveals something like this:<br />
0314 31 EA 97 FF 47 FE<br />
So JMP ($0314) is equivalent to JMP $EA31 in this instance. Pairs of bytes can be<br />
collected toge<strong>the</strong>r to form an indirect jump table. Note that this instruction has a<br />
bug; JMP ($02FF) takes its new address from $02FF and $0200, not $0300.<br />
2. A subroutine call followed by a return is exactly identical to a jump, except that<br />
<strong>the</strong> stack use is less and <strong>the</strong> timing is shorter. Replacing JSR CHECK/ RTS by<br />
JMP CHECK is a common trick.<br />
310
Vocabulary of <strong>the</strong> 6510 Chip<br />
JSR<br />
Jump to a new memory location, saving <strong>the</strong> return address. S:= PC+2 H, SP:<br />
SP-1, S:= PC+2 L, SP:= SP-1, PC:= M<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$20 (32 %0010 0000)<br />
JSR absolute<br />
3<br />
6<br />
Flags:<br />
N V — B D I Z C<br />
Operation: JSR is <strong>the</strong> 6510 equivalent of a GOSUB, transferring control to ano<strong>the</strong>r<br />
part of <strong>the</strong> program until an RTS is found, which has an effect like RETURN. Like<br />
BRK, this instruction saves PC+2 on <strong>the</strong> stack, which points to <strong>the</strong> last byte of <strong>the</strong><br />
JSR command. RTS <strong>the</strong>refore has to increment <strong>the</strong> stored value in order to execute a<br />
correct return. Note that no flags are changed by JSR. RTS also leaves flags un<br />
altered, making JSR $FFC0/ BCC, for example, feasible.<br />
Uses:<br />
1. Breaking programs into subroutines. JSR allows programs to be separated into<br />
subroutines, which is a very valuable feature. <strong>The</strong> Kernal commands, all of which<br />
are called as subroutines by JSR, illustrate <strong>the</strong> convenience which subroutines<br />
bring to programming. Nei<strong>the</strong>r JSR nor RTS sets flags, so LDA #$0D/ JSR $FFD2<br />
(Kernal output routine) successfully transfers <strong>the</strong> accumulator contents—in this<br />
case, a RETURN character—since <strong>the</strong> carry flag status is transferred back after<br />
RTS.<br />
LOOP JSR $FFE4 ;GET RETURNS A=0<br />
BEQ LOOP ;IF NO KEY IS PRESSED<br />
STA BUFFER ;WE HAVE A KEY: PROCESS IT<br />
<strong>The</strong> example uses a Kernal subroutine which gets a character, usually from<br />
<strong>the</strong> keyboard. <strong>The</strong> subroutine is a self-contained unit. Chapter 8 has examples in<br />
which several JSR calls follow each o<strong>the</strong>r, performing a series of operations be<br />
tween <strong>the</strong>m.<br />
2. O<strong>the</strong>r applications. See RTS for <strong>the</strong> PLA/ PLA construction which pops one sub<br />
routine return address from <strong>the</strong> stack. RTS also explains <strong>the</strong> special construction in<br />
which an address (minus 1) is pushed onto <strong>the</strong> stack, generating a jump when<br />
RTS occurs. Finally, see JMP for a note on <strong>the</strong> way in which JSR/RTS may be re<br />
placed by JMP.<br />
311
Vocabulary of <strong>the</strong> 6510 Chip<br />
LDA<br />
Load <strong>the</strong> accumulator with a byte from memory. A:= M<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$A1 (161 %1010 0001)<br />
$A5 (165 %1010 0101)<br />
$A9 (169 %1010 1001)<br />
$AD (173 %1010 1101)<br />
$B1 (177 %1011 0001)<br />
$B5 (181 %1011 0101)<br />
$B9 (185 %1011 1001)<br />
$BD (189 %1011 1101)<br />
LDA (zero page,X)<br />
LDA zero page<br />
LDA # immediate<br />
LDA absolute<br />
LDA (zero page),Y<br />
LDA zero page,X<br />
LDA absolute,Y<br />
LDA absolute,X<br />
2<br />
2<br />
2<br />
3<br />
2<br />
3<br />
3<br />
6<br />
3<br />
2<br />
4<br />
5*<br />
4<br />
4*<br />
4*<br />
*Add 1 if page boundary crossed.<br />
Flags:<br />
N<br />
V — B D I Z<br />
C<br />
X<br />
X<br />
Operation: Loads <strong>the</strong> accumulator with <strong>the</strong> contents of <strong>the</strong> specified memory loca<br />
tion. <strong>The</strong> zero flag, Z, is set to 1 if <strong>the</strong> accumulator now holds 0 (all bits loaded are<br />
0's). Bit 7 is copied into <strong>the</strong> N (negative) flag. No o<strong>the</strong>r flags are altered.<br />
Uses:<br />
1. General transfer of data from one part of memory to ano<strong>the</strong>r. Such transfer needs<br />
a temporary intermediate-storage location, which A (or X or Y) can be. As an ex<br />
ample, this program transfers 256 consecutive bytes of data beginning at $7000 to<br />
an area beginning at $8000. <strong>The</strong> accumulator is alternately loaded with data and<br />
written to memory.<br />
LDX #$00<br />
LDA $7000,X<br />
STA $8000,X<br />
DEX<br />
BNE -9<br />
2. Binary operations. Some binary operations use <strong>the</strong> accumulator. ADC, SBC, and<br />
CMP all require A to be loaded before adding, subtracting, or comparing. <strong>The</strong><br />
addition (or whatever) can't be made directly between two RAM locations, so<br />
LDA is essential.<br />
LDA $C5<br />
CMP #$40<br />
BNE KEY<br />
; WHICH KEY?<br />
; PERHAPS NONE?<br />
: BRANCH IF KEY<br />
3. Setting chip registers. Sometimes a chip register is set by reading from it; this ex<br />
plains some LDA commands in initialization routines with no apparent purpose.<br />
312
Vocabulary of <strong>the</strong> 6510 Chip<br />
LDX<br />
Load <strong>the</strong> X register with a byte from memory. X: = M<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$A2 (162 %1010 0001)<br />
$A6 (166 %1010 0101)<br />
$AE (174 %1010 1110)<br />
$B6 (182 %1011 0101)<br />
$BE (190 %1011 1110)<br />
LDX # immediate<br />
LDX zero page<br />
LDX absolute<br />
LDX zero page,Y<br />
LDX absolute,Y<br />
2<br />
2<br />
3<br />
2<br />
3<br />
2<br />
3<br />
4<br />
4<br />
4*<br />
*Add 1 if page boundary crossed.<br />
Flags:<br />
N<br />
V — B D I Z<br />
C<br />
X<br />
X<br />
Operation: Loads X from memory and sets Z=l if X holds 0. Bit 7 from <strong>the</strong> memory<br />
is also copied into N. No o<strong>the</strong>r flags are altered.<br />
Uses:<br />
1. Transfer of data and holding temporary values. <strong>The</strong>se applications closely re<br />
semble LDA.<br />
2. Offset with indexed addressing. Register X has two characteristics which distin<br />
guish it from A: It is in direct communication with <strong>the</strong> stack pointer, and it can be<br />
used as an offset with indexed addressing. <strong>The</strong>re are o<strong>the</strong>r differences, too.<br />
Constructions like LDX #$FF/ TXS and LDX #$00/.../ DEX/ BNE are common.<br />
LDY<br />
Load <strong>the</strong> Y register with a byte from memory. Y:= M<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$A0 (160 %1010 0000)<br />
$A4 (1<strong>64</strong> %1010 0100)<br />
$AC (172 %1010 1100)<br />
$B4 (180 %1011 0100)<br />
$BC (188 %1011 1100)<br />
LDY # immediate<br />
LDY zero page<br />
LDY absolute<br />
LDY zero page,X<br />
LDY absolute,X<br />
2<br />
2<br />
3<br />
2<br />
3<br />
2<br />
3<br />
4<br />
4<br />
4*<br />
*Add 1 if page boundary crossed.<br />
Flags:<br />
N<br />
V — B D I Z<br />
C<br />
X<br />
X<br />
Operation: Loads Y from memory and sets Z=l if Y now holds 0. Bit 7 from mem<br />
ory is copied into N. No o<strong>the</strong>r flags are altered.<br />
313
Vocabulary of <strong>the</strong> 6510 Chip<br />
Uses:<br />
1. Transfer of data and storage of temporary values.<br />
2. Loops. Since Y can be used as an index and can be incremented or decremented<br />
easily, it is often used in loops. However, X generally has more combinations of<br />
addressing modes in which it is used as an index. <strong>The</strong>refore, X is usually reserved<br />
for indexing, while A and Y between <strong>the</strong>m process o<strong>the</strong>r parameters. When in<br />
direct addressing is used, this preference is reversed, since LDA (addr,X) is gen<br />
erally less useful than LDA (addr),Y.<br />
LOOP<br />
LDY<br />
DEX<br />
BEQ<br />
LDA<br />
JSR<br />
CMP<br />
BEQ<br />
BNE<br />
#$00 ;X HOLDS LENGTH<br />
DECREMENT IT<br />
EXIT<br />
(PTR),Y<br />
PRINT<br />
#$0D<br />
EXIT<br />
Loqp<br />
;EXIT WHEN 0<br />
;LOAD ACCUMULATOR<br />
;PRINT SINGLE CHR<br />
;EXIT IF<br />
; RETURN<br />
;CONTINUE LOOP<br />
This admittedly unexciting example shows how A, X, and Y have distinct<br />
roles. <strong>The</strong> ROM routine to print <strong>the</strong> character is assumed to return <strong>the</strong> original X<br />
and Y values, as in fact it does.<br />
LSR<br />
Shift memory or accumulator right one bit.<br />
6 5 4 3210<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$46 (70 %0100 0110)<br />
$4A (74 %0100 1010)<br />
$4E (78 %0100 1110)<br />
$56 (86 %0101 0110)<br />
$5E (94 %0101 1110)<br />
LSR zero page<br />
LSR accumulator<br />
LSR absolute<br />
LSR zero page,X<br />
LSR absolute,X<br />
2<br />
1<br />
3<br />
2<br />
3<br />
5<br />
2<br />
6<br />
6<br />
7<br />
Flags:<br />
N V -<br />
0<br />
B D I Z<br />
X<br />
C<br />
X<br />
Operation: Moves <strong>the</strong> contents of a memory location or <strong>the</strong> accumulator right by<br />
one bit position, putting 0 into bit 7 and <strong>the</strong> N (negative) flag and moving <strong>the</strong><br />
rightmost bit, bit 0, into <strong>the</strong> carry flag. <strong>The</strong> Z flag is set to 1 if <strong>the</strong> result is 0, and<br />
cleared if not. <strong>The</strong>refore, Z can become 1 only if <strong>the</strong> location held ei<strong>the</strong>r $00 or $01<br />
before LSR.<br />
314
Vocabulary of <strong>the</strong> 6510 Chip<br />
Uses:<br />
1. Similar to ASL. This might well have been called arithmetic shift right. A byte is<br />
halved by this instruction (unless D is set), and its remainder is moved into <strong>the</strong><br />
carry flag. With ASL, ROL, ROR, ADC, and SBC, this command is often used in<br />
ML calculations.<br />
2. O<strong>the</strong>r applications. LSR/ LSR/ LSR/ LSR moves a high nybble into a low nybble;<br />
LSR/ BCC tests bit 0 and branches if it was not set to 1. In addition, LSR turns off<br />
bit 7, giving an easy way to convert a negative number into its positive equivalent,<br />
when <strong>the</strong> sign byte is stored apart from <strong>the</strong> number's absolute value.<br />
NOP<br />
No operation.<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$EA (234 %1110 1010)<br />
NOP implied<br />
1<br />
2<br />
Flags:<br />
N V — B D I Z C<br />
Operation: Does nothing, except to increment <strong>the</strong> program counter and continue<br />
with <strong>the</strong> next opcode.<br />
Uses:<br />
1. Filling unused portions of program. This is useful with hand assembly and o<strong>the</strong>r<br />
methods where calculation of branch addresses cannot be done easily.<br />
2. When writing machine code. A large block of NOPs (or an occasional sprinkling<br />
of <strong>the</strong>m) can simplify <strong>the</strong> task of editing <strong>the</strong> code and inserting corrections. NOP<br />
can also be used as part of a timing loop.<br />
ORA<br />
Logical inclusive OR of memory with <strong>the</strong> accumulator A:= A OR M<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$01<br />
$05<br />
$09<br />
$0D<br />
$11<br />
$15<br />
$19<br />
$1D<br />
( 1 %0000 0001)<br />
( 5 %0000 0101)<br />
( 9 %0000 1001)<br />
(13 %0000 1101)<br />
(17 %0001 0001)<br />
(21 %0001 0101)<br />
(25 %0001 1001)<br />
(29 %0001 1101)<br />
ORA (zero page,X)<br />
ORA zero page<br />
ORA # immediate<br />
ORA absolute<br />
ORA (zero page),Y<br />
ORA zero page,X<br />
ORA absolute,Y<br />
ORA absolute,X<br />
2<br />
2<br />
2<br />
3<br />
2<br />
2<br />
3<br />
3<br />
6<br />
3<br />
2<br />
4<br />
5<br />
4<br />
4*<br />
4*<br />
*Add 1 if page boundary crossed.<br />
315
Vocabulary of <strong>the</strong> 6510 Chip<br />
Flags:<br />
N V —<br />
B D I Z<br />
C<br />
X<br />
X<br />
Operation: Performs <strong>the</strong> inclusive OR of <strong>the</strong> eight bits currently in <strong>the</strong> accumulator<br />
with <strong>the</strong> eight bits referenced by <strong>the</strong> opcode. <strong>The</strong> result is stored in A. If ei<strong>the</strong>r bit is<br />
1, <strong>the</strong> resulting bit is set to 1, so that, for example, %0011 0101 ORA %0000 1111 is<br />
%0011 1111. <strong>The</strong> negative flag, N, is set or cleared depending on bit 7 of <strong>the</strong> result.<br />
<strong>The</strong> Z (zero) flag is set if <strong>the</strong> result is zero, and clear o<strong>the</strong>rwise.<br />
Uses:<br />
1. Setting a bit or bits. This is <strong>the</strong> opposite of masking out bits, as described under<br />
AND.<br />
LDA #ERROR<br />
ORA $90<br />
STA $90<br />
<strong>The</strong> example shows <strong>the</strong> method by which an error code of 1, 2, 4, or whatever,<br />
held in A, is flagged into <strong>the</strong> <strong>64</strong>'s BASIC I/O status byte, ST, stored in location<br />
$90, without losing <strong>the</strong> value currently in that location. For example, if ERROR is<br />
4 and <strong>the</strong> current contents of ST is <strong>64</strong>, <strong>the</strong>n ORA $90 is equivalent to $04 OR<br />
$40, which gives $44. If ERROR is 0, <strong>the</strong>n ORA $90 leaves <strong>the</strong> current value from<br />
location $90 unchanged. Note <strong>the</strong> necessity for STA $90; without it, only A holds<br />
<strong>the</strong> correct value of ST.<br />
2. O<strong>the</strong>r uses. <strong>The</strong>se include <strong>the</strong> testing of several bytes for conditions which are in<br />
tended to be true for each of <strong>the</strong>m—for instance, that three consecutive bytes are<br />
all zero or that several bytes all have bit 7 equal to zero. LDY #00/ LDA (PTR),Y/<br />
INY/ ORA (PTR),Y/ INY/ ORA (PTR),Y/ BNE ... branches if one or more bytes<br />
contains a nonzero value.<br />
PHA<br />
Push <strong>the</strong> accumulator's contents onto <strong>the</strong> stack. S:= A, SP:= SP—1<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$48 (72 %0100 1000)<br />
PHA implied<br />
1<br />
3<br />
Flags:<br />
|N V — B PI Z C|<br />
Operation: <strong>The</strong> value in <strong>the</strong> accumulator is placed into <strong>the</strong> stack at <strong>the</strong> position cur<br />
rently pointed to by <strong>the</strong> stack pointer; <strong>the</strong> stack pointer is <strong>the</strong>n decremented. Figure<br />
10-1 illustrates <strong>the</strong> position before and after <strong>the</strong> push:<br />
316
Vocabulary of <strong>the</strong> 6510 Chip<br />
Figure 10-1. Effect of PHA<br />
$0100 $01FF<br />
h<br />
1 |t|A<br />
STACK IN USE<br />
SP (STACK POINTER)<br />
STACK IN USE<br />
SP (STACK POINTER)<br />
1<br />
1<br />
Uses: This instruction is used for temporary storage of bytes. It may be used to hold<br />
intermediate values of calculations produced during <strong>the</strong> parsing of numeric ex<br />
pressions, to temporarily store values for later recovery while A is used for o<strong>the</strong>r<br />
processing, for storage when swapping bytes, and for storage of A, X, and Y registers<br />
at <strong>the</strong> start of a subroutine.<br />
<strong>The</strong> example shows a printout routine which is designed to end when <strong>the</strong> high<br />
bit of a letter in <strong>the</strong> table is 1. <strong>The</strong> output requires <strong>the</strong> high bit to be set to 0; but <strong>the</strong><br />
original value is recoverable from <strong>the</strong> stack and may be used in a test for <strong>the</strong><br />
terminator at <strong>the</strong> end of message.<br />
LOOP JSR GETC ;GET NEXT CHARACTER<br />
PHA<br />
;STORE ON STACK<br />
AND #$7F ;REMOVE BIT 7<br />
JSR PRINT ;OUTPUT A CHARACTER<br />
PLA ;RECOVER WITH BIT 7 INTACT<br />
BPL LOOP ;CONTINUE IF BIT 7=0<br />
PHP<br />
Push <strong>the</strong> processor status register's contents onto <strong>the</strong> <strong>the</strong> stack. S:= PSR, SP:<br />
SP-1<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$08 (8 %0000 1000)<br />
PHP implied<br />
1<br />
3<br />
Flags:<br />
V — B D I Z c<br />
Operation: <strong>The</strong> operation is similar to PHA, except that <strong>the</strong> processor status register<br />
is put in <strong>the</strong> stack. <strong>The</strong> PSR is unchanged by <strong>the</strong> push.<br />
Uses: Stores <strong>the</strong> entire set of flags, usually ei<strong>the</strong>r to be recovered later and displayed<br />
by a monitor program or for recovery followed by a branch. PHP/ PLA leaves <strong>the</strong><br />
stack in <strong>the</strong> condition it was found; it also loads A with <strong>the</strong> flag register, SR, so <strong>the</strong><br />
flags' states can be stored for use later.<br />
317
Vocabulary of <strong>the</strong> 6510 Chip<br />
PLA<br />
Pull <strong>the</strong> stack into <strong>the</strong> accumulator. SP:= SP+1, A:= S<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$68 (104 %0110 1000)<br />
PLA implied<br />
1<br />
4<br />
Flags:<br />
N V -<br />
B D I Z<br />
C<br />
X<br />
X<br />
Operation: <strong>The</strong> stack pointer is incremented, <strong>the</strong>n <strong>the</strong> RAM address to which it<br />
points is read and loaded into A, setting <strong>the</strong> N and Z flags accordingly. <strong>The</strong> effect is<br />
similar to LDA. Figure 10-2 illustrates <strong>the</strong> position before and after <strong>the</strong> pull:<br />
Figure 10-2. Effect of PLA<br />
$0100 $01FF<br />
t | A I STACK IN USE [<br />
SP (STACK POINTER)<br />
1 t | STACK IN USE<br />
SP (STACK POINTER)<br />
Uses:<br />
1. PLA is <strong>the</strong> converse of PHA. It retrieves values put on <strong>the</strong> stack by PHA, in <strong>the</strong><br />
reverse order. PLA/ PHA leaves <strong>the</strong> stack unchanged, but leaves A holding <strong>the</strong><br />
contents of <strong>the</strong> current top of <strong>the</strong> stack. Flags N and Z are set as though by LDA.<br />
2. To remove <strong>the</strong> top two bytes of <strong>the</strong> stack. This is a frequent use of PLA; it is<br />
equivalent to adding 2 to <strong>the</strong> stack pointer. This is done to "pop" a return address<br />
from <strong>the</strong> stack; in this way, <strong>the</strong> next RTS which is encountered will not return to<br />
<strong>the</strong> previous JSR, but to <strong>the</strong> one before it (assuming that <strong>the</strong> stack has not been<br />
added to since <strong>the</strong> JSR).<br />
PLA<br />
PLA<br />
RTS<br />
;DISCARD ADDRESS STORED<br />
;BYJSR<br />
;RETURN TO EARLIER SUBROUTINE CALL<br />
PLP<br />
Pull <strong>the</strong> stack into <strong>the</strong> processor status register. SP:= SP+1, PSR:= S<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$28 (40 %0010 1000)<br />
PLP implied<br />
1<br />
4<br />
318
Vocabulary of <strong>the</strong> 6510 Chip<br />
Flags:<br />
N V -<br />
B<br />
D<br />
I<br />
Z<br />
C<br />
X<br />
X<br />
X<br />
X<br />
X<br />
X<br />
X<br />
Operation: <strong>The</strong> operation of PLP is similar to that of PLA, except that <strong>the</strong> processor<br />
status register, not <strong>the</strong> accumulator, is loaded from <strong>the</strong> stack.<br />
Uses: Recovers previously stored flags with which to test or branch. See <strong>the</strong> notes on<br />
PHP. This can also be used to experiment with <strong>the</strong> flags—to set V, for example.<br />
ROL<br />
Rotate memory or accumulator and <strong>the</strong> carry flag left one bit.<br />
76543210<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$26 (38 %0010 0110)<br />
$2A (42 %0010 1010)<br />
$2E (46 %0010 1110)<br />
$36 (54 %0011 0110)<br />
$3E (62 %0011 1110)<br />
ROL zero page<br />
ROL accumulator<br />
ROL absolute<br />
ROL zero page,X<br />
ROL absolute,X<br />
2<br />
1<br />
3<br />
2<br />
3<br />
5<br />
2<br />
6<br />
6<br />
7<br />
Flags:<br />
N V —<br />
B D I Z<br />
C<br />
X<br />
X<br />
X<br />
Operation: Nine bits, consisting of <strong>the</strong> contents of <strong>the</strong> memory location referenced<br />
by <strong>the</strong> instruction (or of <strong>the</strong> accumulator) and <strong>the</strong> carry bit, are rotated as <strong>the</strong> di<br />
agram shows. In <strong>the</strong> process, C is changed to what was bit 7, bit 0 takes on <strong>the</strong> pre<br />
vious value of C, and <strong>the</strong> negative flag becomes <strong>the</strong> previous bit 6. In addition, Z is<br />
set or cleared, depending on <strong>the</strong> new memory contents.<br />
Uses:<br />
1. Doubles <strong>the</strong> contents of <strong>the</strong> byte that it references. In this way, ROL operates like<br />
ASL, but in addition <strong>the</strong> carry bit may be used to propagate <strong>the</strong> overflow from<br />
such a doubling. Multiplication and division routines take advantage of this prop<br />
erty where a chain of consecutive bytes has to be moved one bit leftward. ROR is<br />
used where <strong>the</strong> direction of movement is rightward.<br />
ASL $4000/ ROL $4001/ ROL $4002 moves <strong>the</strong> entire 24 bits of<br />
$4000-$4002 over by one bit, introducing 0 into <strong>the</strong> rightmost bit. If <strong>the</strong>re is a<br />
carry, <strong>the</strong> carry flag will be 1.<br />
2. Like ASL, ROL may be used before testing N, Z, or C, especially N.<br />
ROL A ;ROTATE 1 BIT LEFTWARD<br />
BMI BRANCH ;BRANCHES IF BIT 6 WAS ON<br />
319
Vocabulary of <strong>the</strong> 6510 Chip<br />
ROR<br />
Rotate memory or accumulator and <strong>the</strong> carry flag right one bit.<br />
76543210 n<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$66 (102 %0110 0110)<br />
$6A (106 %0110 1010)<br />
$6E (110 %0110 1110)<br />
$76 (118 %0111 0110)<br />
$7E (126 %0111 1110)<br />
ROR zero page<br />
ROR accumulator<br />
ROR absolute<br />
ROR zero page,X<br />
ROR absolute,X<br />
2<br />
1<br />
3<br />
2<br />
3<br />
5<br />
2<br />
6<br />
6<br />
7<br />
Flags:<br />
N V —<br />
B D I Z<br />
C<br />
X<br />
X<br />
X<br />
Operation: Nine bits, consisting of <strong>the</strong> contents of memory referenced by <strong>the</strong><br />
instruction and <strong>the</strong> carry bit, are rotated as <strong>the</strong> diagram shows. C becomes what was<br />
bit 0, bit 7 and <strong>the</strong> N flag take on <strong>the</strong> previous value of C, and Z is set or cleared,<br />
depending on <strong>the</strong> byte's current contents. For applications, see ROL.<br />
RTI<br />
Return from interrupt. SP:= SP+1, PSR:= S, SP:= SP+1, PCL:= S, SP:= SP+1,<br />
PCH:= S<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$40 (<strong>64</strong> %0100 0000)<br />
RTI implied<br />
1<br />
6<br />
Flags:<br />
N V -<br />
B<br />
D<br />
I<br />
Z<br />
C<br />
X<br />
X<br />
X<br />
X<br />
X<br />
X<br />
X<br />
Operation: RTI takes three bytes from <strong>the</strong> stack, deposited <strong>the</strong>re by <strong>the</strong> processor it<br />
self when <strong>the</strong> hardware triggered <strong>the</strong> interrupt. <strong>The</strong> processor status flags are re<br />
covered as <strong>the</strong>y were when <strong>the</strong> interrupt occurred, and <strong>the</strong> program counter is<br />
restored so that <strong>the</strong> program resumes operation at <strong>the</strong> byte at which it was inter<br />
rupted. Note that <strong>the</strong> contents of A, X, and Y are not saved or recovered automati<br />
cally in this way, but must be saved by <strong>the</strong> interrupt processing and restored<br />
immediately before RTI. If you follow <strong>the</strong> vector stored in ROM at $FFFE-$FFFF, you<br />
will see how this works.<br />
320
Vocabulary of <strong>the</strong> 6510 Chip<br />
Uses:<br />
1. To resume after an interrupt. <strong>The</strong> techniques presented in Chapter 8 use <strong>the</strong><br />
interrupt-processing routine in ROM, which is <strong>the</strong> simplest approach; it's not nec<br />
essary even to understand RTI. <strong>The</strong> routines invariably end PLA/ TAY/ PLA/<br />
TAX/ PLA/ RTI because <strong>the</strong> contents of A, X, and Y are pushed on <strong>the</strong> stack in<br />
A, X, Y order by CBM ROMs when interrupt processing begins.<br />
2. To execute a jump. It is possible, as with RTS, to exploit <strong>the</strong> automatic nature of<br />
this command to execute a jump by pushing three bytes onto <strong>the</strong> stack, imitating<br />
an interrupt, <strong>the</strong>n using RTI to pop <strong>the</strong> addresses and processor status. By<br />
simulating <strong>the</strong> stack contents left by an interrupt, <strong>the</strong> following routine jumps to<br />
256*HI + LO with its processor flags equal to whatever was pushed on <strong>the</strong> stack<br />
as PSR.<br />
LDA<br />
PHA<br />
LDA<br />
PHA<br />
LDA<br />
PHA<br />
RTI<br />
HI<br />
LO<br />
PSR<br />
RTS<br />
Return from subroutine. SP:= SP+1, PCL:= S, SP:= SP+1, PCH:= S, PC:<br />
PC+1<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$60 (96 %0110 0000)<br />
RTS implied<br />
1<br />
6<br />
Flags:<br />
|N y — b D I Z c<br />
Operation: RTS takes two bytes from <strong>the</strong> stack, increments <strong>the</strong> result, and jumps to<br />
<strong>the</strong> address found by putting <strong>the</strong> calculated value into <strong>the</strong> program counter. It is<br />
similar to RTI but does not change <strong>the</strong> processor flags, since an important feature of<br />
subroutines is that, on return, flags should be usable. Also, unlike RTI in which <strong>the</strong><br />
address saved is <strong>the</strong> address to return to, RTS must increment <strong>the</strong> address it fetches<br />
from <strong>the</strong> stack, which points to <strong>the</strong> second byte after a JSR.<br />
Uses:<br />
1. Return after a subroutine. This is straightforward; a batch of ML to be callable by<br />
JSR is simply ended or exited from with RTS. This also applies to ML routines<br />
callable from BASIC with SYS calls; in this case <strong>the</strong> return address to <strong>the</strong> loop<br />
which executes BASIC is put on <strong>the</strong> stack first by <strong>the</strong> system.<br />
2. As a form of jump. RTS is used as a form of jump which takes up no RAM space<br />
and can be loaded from a table. For example, <strong>the</strong> following routine jumps to <strong>the</strong><br />
address $HILO+1, so put <strong>the</strong> desired address —1 on <strong>the</strong> stack.<br />
321
Vocabulary of <strong>the</strong> 6510 Chip<br />
LDA<br />
PHA<br />
LDA<br />
PHA<br />
RTS<br />
#$HI<br />
#$LO<br />
Notes: See PLA for <strong>the</strong> technique of discarding (popping) return addresses. JSR<br />
SUB/ RTS is identical in effect to JMP SUB, since SUB must end with an RTS. This<br />
point can puzzle programmers.<br />
SBC<br />
Subtract memory with borrow from accumulator. A:= A—M—(1 —C)<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$E1 (225 %1110 0001)<br />
$E5 (229 %1110 0101)<br />
$E9 (233 %1110 1001)<br />
$ED (237 %1110 1101)<br />
$F1 (241 %1111 0001)<br />
$F5 (245 %1111 0101)<br />
$F9 (249 %1111 1001)<br />
$FD (253 %1111 1101)<br />
SBC (zero page,X)<br />
SBC zero page<br />
SBC # immediate<br />
SBC absolute<br />
SBC (zero page),Y<br />
SBC zero page,X<br />
SBC absolute,Y<br />
SBC absolute,X<br />
2<br />
2<br />
2<br />
3<br />
2<br />
2<br />
3<br />
3<br />
6<br />
3<br />
2<br />
4<br />
5*<br />
4<br />
4*<br />
4*<br />
•Add 1 if page boundary crossed.<br />
Flags:<br />
N<br />
V -<br />
B D I Z<br />
C<br />
X<br />
X<br />
X<br />
X<br />
Operation: It is usual to set <strong>the</strong> carry bit before this operation or to precede it by an<br />
operation which is known to leave <strong>the</strong> carry bit set. <strong>The</strong>n SBC appears to subtract<br />
from <strong>the</strong> accumulator <strong>the</strong> data referenced by <strong>the</strong> addressing mode. If <strong>the</strong> carry flag is<br />
still set, this indicates that <strong>the</strong> result did not borrow (that is, that <strong>the</strong> accumulator's<br />
value is greater than or equal to <strong>the</strong> data). When C is clear, <strong>the</strong> data exceeded <strong>the</strong><br />
accumulator's contents; C shows that a borrow is needed. Within <strong>the</strong> chip, A is<br />
added to <strong>the</strong> twos complement of <strong>the</strong> data and to <strong>the</strong> complement of C; this con<br />
ditions <strong>the</strong> N, V, Z, and C flags.<br />
Uses:<br />
1. Single-byte subtraction. <strong>The</strong> following example is a detail from PRINT. When<br />
processing <strong>the</strong> comma in a PRINT statement, <strong>the</strong> cursor is moved to position 0,<br />
10, 20, etc. Suppose <strong>the</strong> cursor is at 17 horizontally; subtract 10's until <strong>the</strong> carry<br />
flag is clear, when A will hold —3. <strong>The</strong> twos complement is 3, so three spaces or<br />
cursor-rights take you to <strong>the</strong> correct position on <strong>the</strong> screen. Note that ADC #$01<br />
adds 1 only; <strong>the</strong> carry flag is known to be 0 by that stage.<br />
322
Vocabulary of <strong>the</strong> 6510 Chip<br />
LDA HORIZ ;LOAD CURRENT CURSOR POSN<br />
SEC<br />
LOOP SBC #$0A<br />
BCS LOOP<br />
EOR #$FF<br />
ADC #$01<br />
;CARRY FLAG SET DURING LOOP<br />
;SUBTRACT 10 UNTIL CARRY...<br />
;...IS CLEAR (A IS NEG)<br />
;FLIP BITS AND ADD 1 TO<br />
;CONVERT TO POSITIVE.<br />
Double-byte subtraction. <strong>The</strong> point about subtracting one 16-bit number from an<br />
o<strong>the</strong>r is that <strong>the</strong> borrow is performed automatically by SBC. <strong>The</strong> C flag is first set<br />
to 1; <strong>the</strong>n <strong>the</strong> low byte is subtracted; <strong>the</strong>n <strong>the</strong> high byte is subtracted, with borrow<br />
if <strong>the</strong> low bytes make this necessary.<br />
In <strong>the</strong> following example, $026A is subtracted from <strong>the</strong> contents of addresses<br />
(or data) LO and HI. <strong>The</strong> result is replaced in LO and HI. Note that SEC is per<br />
formed only once. In this way, borrowing is performed properly. For example,<br />
suppose <strong>the</strong> address from which $026A is to be subtracted holds $1234. When<br />
$6A is subtracted from $34, <strong>the</strong> carry flag is cleared, so that $02 and 1 is sub<br />
tracted from <strong>the</strong> high byte $12.<br />
SEC<br />
LDA<br />
SBC<br />
STA<br />
LO<br />
#$6A<br />
LO<br />
LDA HI<br />
SBC #$02<br />
STA HI<br />
SEC<br />
Set <strong>the</strong> carry flag to 1. C: = 1<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$38 (56 %0011 1000)<br />
SEC implied<br />
1<br />
2<br />
Flags:<br />
N V — B D I Z c<br />
1<br />
Operation: Sets <strong>the</strong> carry flag. This is <strong>the</strong> opposite of CLC, which clears it.<br />
Uses: Used whenever <strong>the</strong> carry flag has to be put into a known state; usually SEC is<br />
performed before subtraction (SBQ and CLC before addition (ADC) since <strong>the</strong> nu<br />
meric values used are <strong>the</strong> same as in ordinary arithmetic. Some Kernal routines re<br />
quire C to be cleared or set, giving different effects accordingly. SEC/BCS is<br />
sometimes used as a "branch always" command.<br />
323
Vocabulary of <strong>the</strong> 6510 Chip<br />
SED<br />
Set <strong>the</strong> decimal mode flag to 1. D:= 1<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$F8 (248 %1111 1000)<br />
SED implied<br />
1<br />
2<br />
Flags:<br />
N V — B D<br />
I Z c<br />
1<br />
Operation: Sets <strong>the</strong> decimal flag. This is <strong>the</strong> opposite of CLD, which clears it.<br />
Uses: Sets <strong>the</strong> mode to BCD (binary coded decimal) arithmetic, in which each nybble<br />
holds a decimal numeral. For example, ten is held as 10 and ninety as 90. Two thou<br />
sand four hundred fifteen is 2415 in two bytes. ADC and SBC are designed to op<br />
erate in this mode as well as in binary, but <strong>the</strong> flags no longer have <strong>the</strong> same<br />
meaning, except C. <strong>The</strong> result is not much different from arithmetic using individual<br />
bytes for each digit 0-9, but it takes up only half <strong>the</strong> space and is faster.<br />
SEI<br />
Set <strong>the</strong> interrupt disable flag to 1. I:= 1<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$78 (120 %0111 1000)<br />
SEI implied<br />
1<br />
2<br />
Flags:<br />
N V — B D I<br />
1<br />
Z<br />
c<br />
Operation: Sets <strong>the</strong> interrupt disable flag. This is <strong>the</strong> opposite of CLI, which clears it.<br />
Uses: When this flag has been set, no interrupts are processed by <strong>the</strong> chip, except<br />
non-maskable interrupts (which have higher priority), BRK, and RESET. IRQ inter<br />
rupts are processed by a routine vectored through locations $FFFE-$FFFF, like BRK.<br />
If <strong>the</strong> vector in <strong>the</strong> very top locations of ROM is followed, <strong>the</strong> interrupt servicing<br />
routines can be found. In <strong>the</strong> <strong>64</strong>, <strong>the</strong>se are not all in ROM: <strong>The</strong> vectors use an ad<br />
dress in RAM before jumping back to ROM.<br />
<strong>The</strong> example here is a typical initialization routine to redirect <strong>the</strong> <strong>64</strong>'s RAM IRQ<br />
vector into <strong>the</strong> user's own program at $C00D (where it may play a musical tone or<br />
whatever).<br />
324
Vocabulary of <strong>the</strong> 6510 Chip<br />
C000 SEI<br />
C001 LDA #$C0<br />
C003 STA $0315<br />
C006 LDA #$0D<br />
C008 STA $0314<br />
C00B CLI<br />
C00C RTS<br />
STA<br />
Store <strong>the</strong> contents of <strong>the</strong> accumulator into memory. M:= A<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$81 (129 %1000 0001)<br />
$85 (133 %1000 0101)<br />
$8D (141 %1000 1101)<br />
$91 (145 %1001 0001)<br />
$95 (149 %1001 0101)<br />
$99 (153 %1001 1001)<br />
$9D (157 %1001 1101)<br />
STA (zero page,X)<br />
STA zero page<br />
STA absolute •. .<br />
STA (zero page),Y<br />
STA zero page,X<br />
STA absolute,Y<br />
k<br />
STA absolute,X<br />
2<br />
2<br />
3<br />
2<br />
2<br />
3<br />
3<br />
6<br />
3<br />
4<br />
6<br />
4<br />
5<br />
5<br />
Flags:<br />
|n V — B D I Z C<br />
Operation: <strong>The</strong> value in A is sent to <strong>the</strong> address referenced by <strong>the</strong> opcode. All reg<br />
isters and flags are unchanged.<br />
Uses:<br />
1. Intermediate storage. Transfer of blocks of data from one part of memory to an<br />
o<strong>the</strong>r needs a temporary intermediate store, usually in A, which is alternately<br />
loaded and stored. See LDA.<br />
2. Saving results of binary operations. Binary operations using <strong>the</strong> accumulator, nota<br />
bly ADC and SBC, are performed within <strong>the</strong> accumulator; a common bug in ma<br />
chine language programs is forgetting to save <strong>the</strong> result.<br />
LDA $90 ;STBYTE<br />
AND #$FD ; BIT 1 OFF<br />
STA $90 ; REMEMBER THIS!<br />
3. Setting <strong>the</strong> contents of certain locations to known values.<br />
LDA #$89<br />
STA $22 ; SETS VECTOR AT $22-$23<br />
LDA #$C3<br />
STA $23 ;TO$C389<br />
325
Vocabulary of <strong>the</strong> 6510 Chip<br />
STX<br />
Store <strong>the</strong> contents of <strong>the</strong> X register into memory. M:= X<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$86<br />
$8E<br />
$96<br />
(134<br />
(142<br />
(150<br />
%1000 0110)<br />
%1000 1110)<br />
%1001 0110)<br />
STX zero page<br />
STX absolute<br />
STX zero page,Y<br />
2<br />
3<br />
2<br />
3<br />
4<br />
4<br />
Flags:<br />
N<br />
V<br />
- B<br />
D<br />
I<br />
z<br />
c<br />
Operation: <strong>The</strong> byte in <strong>the</strong> X register is sent to <strong>the</strong> address referenced by <strong>the</strong><br />
opcode. All registers and flags are unchanged.<br />
Uses: <strong>The</strong> uses are identical to those of STA. <strong>The</strong>re is a tendency for X to be used as<br />
an index, so STX is less used than STA.<br />
STY 91 I<br />
Store <strong>the</strong> contents of <strong>the</strong> Y register into memory. M:= Y<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$84<br />
$8C<br />
$94<br />
(132 %1000 0100)<br />
(140 %1000 1100)<br />
(148 %1001 0100)<br />
STY zero page<br />
STY absolute<br />
STY zero page,X<br />
2<br />
3<br />
2<br />
3<br />
4<br />
4<br />
Flags:<br />
N<br />
V<br />
— B D I<br />
z<br />
c<br />
Operation: <strong>The</strong> byte in <strong>the</strong> Y register is sent to <strong>the</strong> address referenced by <strong>the</strong><br />
opcode. All registers and flags are unchanged.<br />
Uses: STY resembles STX; <strong>the</strong> comments under STX apply.<br />
TAX<br />
Transfer <strong>the</strong> contents of <strong>the</strong> accumulator into <strong>the</strong> X register. X: = A<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$AA (170 %1010 1010)<br />
TAX implied<br />
1<br />
2<br />
Flags:<br />
N V —<br />
B D I Z<br />
C<br />
X<br />
X<br />
326
Vocabulary of <strong>the</strong> 6510 Chip<br />
Operation: <strong>The</strong> byte in A is transferred to X. <strong>The</strong> N and Z flags are set as though<br />
LDX had taken place.<br />
Uses: This transfer is generally used to set X for use as an index or a parameter or to<br />
temporarily hold A. <strong>The</strong> example is from a high-resolution screen-plotting routine; it<br />
plots a black dot in a location with a coded value of 1, 2, 4, or 8 in $FB. On entry X<br />
holds <strong>the</strong> position of <strong>the</strong> current X in a table. On exit X holds <strong>the</strong> position of <strong>the</strong> new<br />
character. Intermediate calculations use <strong>the</strong> accumulator because <strong>the</strong>re is no "EOR<br />
with X" instruction.<br />
TXA<br />
EOR #$FF<br />
ORA $FB<br />
EOR #$FF<br />
TAX<br />
LDA TABLE,X<br />
Note that registers A, X, Y, and <strong>the</strong> stack pointer are interchangeable with one<br />
instruction in some cases, but not in o<strong>the</strong>rs. <strong>The</strong> connections are shown below:<br />
Y ^ A ^ X ^ S.<br />
TAY<br />
Transfer <strong>the</strong> contents of <strong>the</strong> accumulator into <strong>the</strong> Y register. Y:= A<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$A8 (168 %1010 1000)<br />
TAY implied<br />
1<br />
2<br />
Flags:<br />
N V —<br />
B D I Z<br />
C<br />
X<br />
X<br />
Operation: <strong>The</strong> byte in A is transferred to Y. <strong>The</strong> N and Z flags are set as though<br />
LDY had taken place.<br />
Uses: See TAX.<br />
TSX<br />
Transfer <strong>the</strong> stack pointer into <strong>the</strong> X register. X:= SP<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$BA (186 %1011 1010)<br />
TSX implied<br />
1<br />
2<br />
Flags:<br />
N V -<br />
B D I Z<br />
C<br />
X<br />
X<br />
327
Vocabulary of <strong>the</strong> 6510 Chip<br />
Operation: <strong>The</strong> stack pointer is transferred to X. Note that <strong>the</strong> stack pointer is al<br />
ways offset onto $0100, so when <strong>the</strong> stack is accessed, <strong>the</strong> high byte of its memory<br />
location is $01. <strong>The</strong> pointer itself is a single byte.<br />
Uses:<br />
1. To look at current values on <strong>the</strong> stack. TSX/ LDA $0100,X loads A with <strong>the</strong> con<br />
tents presently at <strong>the</strong> top of <strong>the</strong> stack; LDA $0101,X loads <strong>the</strong> last item pushed on<br />
<strong>the</strong> stack (one byte higher) into A, and so on. BASIC tests for BRK or interrupt<br />
with PHA/ TXA/ PHA/ TYA/ PHA/ TSX/ LDA $0104,X/ AND #$10 because<br />
<strong>the</strong> return-from-interrupt address and <strong>the</strong> SR are pushed by <strong>the</strong> interrupt before<br />
<strong>the</strong> system saves its own three bytes. LDA $0104,X loads <strong>the</strong> flags saved when<br />
<strong>the</strong> interrupt or BRK happened.<br />
2. To determine space left on <strong>the</strong> stack. BASIC does this and signals ?OUT OF MEM<br />
ORY ERROR if <strong>the</strong>re are too many GOSUBs, FOR-NEXT loops, or complex<br />
calculations with intermediate results.<br />
3. Processing. Sometimes <strong>the</strong> stack pointer is stored and a lower part of <strong>the</strong> stack<br />
temporarily used for processing.<br />
TXA<br />
Transfer <strong>the</strong> contents of <strong>the</strong> X register into <strong>the</strong> accumulator. A: = X<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$8A (138 %1000 1010)<br />
TXA implied<br />
1<br />
2<br />
Flags:<br />
N V —<br />
B D I Z<br />
C<br />
X<br />
X<br />
Operation: <strong>The</strong> byte in X is transferred to A. <strong>The</strong> N flag and Z flag are set as though<br />
LDA had taken place.<br />
Uses: See TAX.<br />
TXS<br />
Transfer <strong>the</strong> X register into <strong>the</strong> stack pointer. SP:= X<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$9A (154 %1001 1010)<br />
TXS implied<br />
1<br />
2<br />
Flags:<br />
|N V — B D I Z c]<br />
328
Vocabulary of <strong>the</strong> 6510 Chip<br />
Operation: X is stored in <strong>the</strong> stack pointer. PHA or PHP will place a byte onto <strong>the</strong><br />
stack at $0100 plus <strong>the</strong> new stack pointer, and PLA or PLP will pull from <strong>the</strong> next<br />
byte up from this. In addition, RTI and RTS will return to addresses determined by<br />
<strong>the</strong> stack contents at <strong>the</strong> new position of <strong>the</strong> stack.<br />
Uses:<br />
1. As part of <strong>the</strong> RESET sequence. TXS is always part of <strong>the</strong> RESET sequence; o<strong>the</strong>r<br />
wise, <strong>the</strong> stack pointer could take any value. CBM computers use <strong>the</strong> top bytes of<br />
<strong>the</strong> stack for BASIC addresses. When <strong>the</strong> <strong>64</strong> is turned on, LDX #$FF/ TXS sets<br />
<strong>the</strong> pointer to <strong>the</strong> top of <strong>the</strong> stack, but if BASIC is to run (that is, if no autorun<br />
cartridge is in place), SP is moved to leave locations $01FA-$01FF ready for use<br />
by <strong>the</strong> RUN command.<br />
SP has high values to start with because it is decremented as data is pushed<br />
onto <strong>the</strong> stack. If too much data is pushed, perhaps by an improperly controlled<br />
loop, SP decrements right through $00 to $FF again, crashing its program.<br />
2. Switching to a new stack location. This is a rarely seen use of TXS. As a simple<br />
example, <strong>the</strong> following routine is an equivalent to PLA/ PLA which you have<br />
seen (under RTS) to be a "pop" command which deletes a subroutine's return ad<br />
dress. Incrementing <strong>the</strong> stack pointer by 2 has <strong>the</strong> identical effect.<br />
CLC<br />
TSX<br />
TXA<br />
ADC #$02<br />
TAX<br />
TXS<br />
TYA<br />
Transfer <strong>the</strong> contents of <strong>the</strong> Y register into <strong>the</strong> accumulator. A:= Y<br />
Instruction<br />
Addressing<br />
Bytes<br />
Cycles<br />
$98 (152 %1001 1000)<br />
T\A implied<br />
1<br />
2<br />
Flags:<br />
N V —<br />
B D I Z<br />
C<br />
X<br />
X<br />
Operation: <strong>The</strong> byte in Y is transferred to A. <strong>The</strong> N flag and Z flag are set as though<br />
LDA had taken place.<br />
Uses: See TAX. <strong>The</strong> transfers TAX, TAY, TXA, and TYA all perform similar functions.<br />
329
Chapter 11<br />
<strong>64</strong> ROM Guide<br />
<strong>64</strong> Memory Map
Chapter 11<br />
<strong>64</strong> ROM Guide<br />
<strong>64</strong> Memory Map<br />
This chapter maps in detail <strong>the</strong> first few hundred RAM locations, <strong>the</strong> BASIC ROM,<br />
and <strong>the</strong> Kernal ROM. It will be especially valuable to programmers who want to<br />
make full use of <strong>Commodore</strong> <strong>64</strong> BASIC.<br />
Locations are listed for both <strong>the</strong> <strong>Commodore</strong> <strong>64</strong> and <strong>the</strong> VIC, since many loca<br />
tions are <strong>the</strong> same on <strong>the</strong> two computers.<br />
<strong>Commodore</strong> <strong>64</strong> BASIC is stored in ROM from $A000 to $BFFF. <strong>The</strong> computer's<br />
operating system, <strong>the</strong> ML that controls input/output and related operations, is stored<br />
in ROM from $E000 to $FFFF, called <strong>the</strong> Kernal ROM. It contains a large number of<br />
routines, but generally Kernal routines are taken to be only those which are called<br />
through <strong>the</strong> Kernal jump table.<br />
<strong>Commodore</strong> recommends that ML programmers use only Kernal routines. That,<br />
however, rules out most of BASIC. Moreover, transportability between machines is<br />
likely to be very difficult even with <strong>the</strong> Kernal. Generally, you should use any of<br />
<strong>the</strong>se routines where <strong>the</strong>y are likely to make better programs.<br />
<strong>The</strong>re is a potential problem between machines of <strong>the</strong> same type. For example,<br />
several <strong>64</strong> ROM versions exist, with Kernal ROM variations. In practice this is rarely<br />
a problem. But if you want to be certain, relocate your routines into RAM as much as<br />
possible.<br />
A number of ROM routines are vectored through RAM; Chapter 8 explains how<br />
to take advantage of this.<br />
Notation<br />
Labels have been included as reference points, and where possible <strong>the</strong>y refer back to<br />
well-known labels.<br />
BASIC number handling is a bit complex. FAC1 and FAC2 refer to Floating Point<br />
Accumulators 1 and 2. <strong>The</strong>y hold two numbers during addition, multiplication, etc.,<br />
which is done in a six-byte format (EMMMMS, consisting of exponent/mantissa or<br />
data/sign), called FLPT for short. MFLPT refers to <strong>the</strong> way numbers are stored in<br />
memory after BASIC, in a five-byte format with one bit of data less than FLPT.<br />
MFLPT format is explained in Chapter 6. BASIC of course has routines to convert<br />
<strong>the</strong>se. INT or FIX format is <strong>the</strong> simpler format with bytes in sequence.<br />
A, X, and Y are <strong>the</strong> 6510/6502's registers. A/Y means <strong>the</strong> two-byte value with<br />
A holding <strong>the</strong> low byte and Y <strong>the</strong> high byte. String descriptors are three bytes of<br />
data, <strong>the</strong> first holding <strong>the</strong> string's length, <strong>the</strong> second and third <strong>the</strong> low and high<br />
bytes of <strong>the</strong> pointer to <strong>the</strong> start of <strong>the</strong> string.<br />
<strong>The</strong> following listings consist of three columns. <strong>The</strong> first column gives <strong>the</strong> label.<br />
<strong>The</strong> second column lists <strong>the</strong> <strong>64</strong> and VIC addresses; where one address is given, it<br />
applies to both computers unless o<strong>the</strong>rwise noted, but where two are given, <strong>the</strong> <strong>64</strong><br />
address comes first. Finally, a description of <strong>the</strong> use of <strong>the</strong> location or of <strong>the</strong> routine<br />
that begins at <strong>the</strong> specified address is given.<br />
333
<strong>64</strong> ROM Guide<br />
Page 0:<br />
Label<br />
D6510<br />
R6510<br />
FACINT<br />
INTFAC<br />
CHARAC<br />
INTEGR<br />
ENDCHR<br />
TRMPOS<br />
VERCHK<br />
COUNT<br />
DIMFLG<br />
VALTYP<br />
INTFLG<br />
GARBLF<br />
SUBFLG<br />
INPFLG<br />
TANSGN<br />
CHANNL<br />
LINNUM<br />
TEMPPT<br />
LASTPT<br />
TEMPST<br />
INDEX1<br />
INDEX2<br />
RESHO<br />
TXTTAB<br />
VARTAB<br />
ARYTAB<br />
STREND<br />
FRETOP<br />
FRESPC<br />
MEMSIZ<br />
CURLIN<br />
RAM$l<br />
<strong>64</strong>/VIC<br />
$00<br />
$01<br />
$02<br />
$03-$04<br />
$05-$06<br />
$07<br />
$07-$08<br />
$08<br />
$09<br />
$0A<br />
$0B<br />
$0C<br />
$0D<br />
$0E<br />
$0F<br />
$10<br />
$11<br />
$12<br />
$13<br />
$14-$15<br />
$16<br />
$17-$18<br />
$19-$21<br />
$22-$23<br />
$24-$25<br />
$26-$2A<br />
$2B-$2C<br />
$2D-$2E<br />
$2F-$30<br />
$31-$32<br />
$33-$34<br />
$35-$36<br />
$37-$38<br />
$39-$3A<br />
Descriptions<br />
6510 on-chip data direction register (<strong>Commodore</strong> <strong>64</strong> only).<br />
6510 on-chip input/output register (<strong>Commodore</strong> <strong>64</strong> only).<br />
Unused byte (<strong>Commodore</strong> <strong>64</strong> only).<br />
Vector to routine to convert FAC to integer in A/Y (usually<br />
$B1AA).<br />
Vector to routine to convert integer in A/Y to floating point in<br />
FAC (usually $B391).<br />
Delimiting character used when scanning. Also temporary integer<br />
(0-255) used during INT.<br />
Intermediate integer used during OR/AND.<br />
Delimiter used when scanning strings.<br />
Temporary location used for calculating TAB and SPC column.<br />
Flag to indicate LOAD (0) or VERIFY (1).<br />
Temporary pointer used with BASIC input buffer.<br />
Flag: default array dimension.<br />
Flag: current variable data type; 0 means numeric, $FF means<br />
string.<br />
Flag: current variable data type; 0 means floating point; $80 means<br />
integer.<br />
Flag used in garbage collection, LIST, DATA, error messages.<br />
Flag to indicate integers or array elements, which are forbidden as<br />
indexes of FOR/NEXT loops and in function definitions.<br />
Flag used by READ routine; $00 means INPUT, $40 means GET,<br />
$98 means READ.<br />
Sign byte used by TAN, SIN. Also set according to any comparison<br />
being performed: > sets this location to $01, = sets $02, and <<br />
sets $04.<br />
Current I/O device number; prompts suppressed if not 0.<br />
Line number integer (0-63999) or standard two-byte address used<br />
by GOTO, GOSUB, POKE, PEEK, WAIT, and SYS.<br />
Index to next entry on string descriptor stack (may be $19, $1C,<br />
$1F, or $22).<br />
Pointer to current entry on string descriptor stack.<br />
Stack for three temporary string descriptors.<br />
General-purpose pointer, for example, for memory moves.<br />
General-purpose pointer, for example, for number movements.<br />
Floating point workspace used by multiply and divide.<br />
Pointer to first byte of BASIC program (2049 for <strong>the</strong> <strong>64</strong>).<br />
Pointer to start of program variables; first byte beyond end of<br />
program.<br />
Pointer to start of arrays; first byte beyond end of variables.<br />
Pointer to start of free RAM available for strings; first byte beyond<br />
end of arrays.<br />
Pointer to current lower boundary of string area. (Set to <strong>the</strong> con<br />
tents of MEMSIZ on CLR or RUN.)<br />
Utility pointer used when new string is being added to string area.<br />
Pointer to one byte beyond <strong>the</strong> top of RAM available to BASIC.<br />
BASIC line number being interpreted ($FF in $003A indicates<br />
immediate mode).<br />
334
<strong>64</strong> ROM Guide<br />
OLDLIN<br />
OLDTXT<br />
DATLIN<br />
DATPTR<br />
INPTR<br />
$3B-$3C<br />
$3D-$3E<br />
$3F-$40<br />
$41-$42<br />
$43-$44<br />
VARNAM $45-$46<br />
VARPNT<br />
FORPNT<br />
OPPTR<br />
OPMASK<br />
DEFPNT<br />
TEMPF3<br />
DSCPNT<br />
SIZE<br />
FOUR6<br />
JMPER<br />
TEM^tl<br />
HIGHDS<br />
ARYPNT<br />
HIGHTR<br />
TEMPF2<br />
DECCNT<br />
TENEXP<br />
DPTFLG<br />
LINPTR<br />
EXPSGN<br />
FACl<br />
$47-$48<br />
$49-$4A<br />
$4B<br />
$4D<br />
$4E-$4F<br />
$4E-$52<br />
$50-$51<br />
$52<br />
$53<br />
$54-$56<br />
$57-$5B<br />
$58-$59<br />
$58-$59<br />
$5A-$5B<br />
$5C-$60<br />
$5D<br />
$5E<br />
$5F<br />
$5F-$60<br />
$60<br />
$61-$66<br />
If STOP, END, or BREAK occurs, this holds <strong>the</strong> last BASIC line<br />
number executed for CONT.<br />
Pointer to beginning of current BASIC line for CONT.<br />
Line number of current DATA statement. Initialized to $0000 on<br />
RUN.<br />
Pointer to one byte beyond <strong>the</strong> DATA item read by <strong>the</strong> last READ<br />
statement. Initialized to contents of TXTTAB on RUN.<br />
Temporary storage of DATPTR during READ statement; also<br />
pointer within input buffer during INPUT (points to last character<br />
entered).<br />
Current BASIC variable; two-character name with most significant<br />
bit (bit 7) of each byte used to indicate variable type: bit 7 clear in<br />
both bytes means floating point, bit 7 set in both means integer,<br />
bit 7 set in $46 means string, bit 7 set in $45 means function.<br />
Pointer to current variable's address in RAM. Points one byte<br />
beyond variable name.<br />
Temporary pointer to variables in memory for INPUT, assign<br />
ments, etc., and for loop variable in FOR/NEXT loops. Also holds<br />
<strong>the</strong> two parameters for WAIT statements.<br />
Pointer within operator table during expression evaluation in rou<br />
tine FRMEVL.<br />
Comparison mask used in FRMEVL: > sets this location to $01, =<br />
sets $02, and < sets $04.<br />
Pointer to variable in function definition, within variable table in<br />
RAM. Also used by garbage collection routine GARBAG.<br />
Temporary storage for a MFLPT item.<br />
Pointer to descriptor in variable list or to string in dynamic string<br />
area; used during string operations.<br />
Length of <strong>the</strong> current BASIC string.<br />
Length of string variable during garbage collection.<br />
Jump vector for function evaluations, JMP ($4C) followed by func<br />
tion address from function vector table.<br />
Temporary pointers (for example, in memory move); also tem<br />
porary floating point accumulator.<br />
Pointer used by block transfer routine BLTU.<br />
Pointer used when initializing arrays (when DIM is encountered).<br />
Pointer used by block transfer routine BLTU.<br />
Temporary floating point accumulator.<br />
Number of digits after/before decimal point in ASCII-to-FLPT and<br />
FLPT-to-ASCII conversion for <strong>the</strong> FIN and FOUT routines.<br />
Exponent used in ASCII-to-FLPT and FLPT-to-ASCII conversion in<br />
<strong>the</strong> FIN and FOUTroutines.<br />
Flag use4 by <strong>the</strong> FIN routine ($BCF3) when inputting numbers; set<br />
to $80 [i s'lring contains decimal point.<br />
Pointer used when searching for line numbers, searching for vari<br />
ables in variable list, doing block transfers.<br />
Sign of exponent of number being input by FIN routine; a value of<br />
$80 signifies negative.<br />
Floating Point Accumulator 1. Consists of exponent byte, four<br />
mantissa bytes, and a sign byte. (<strong>The</strong> results of most arithmetic op<br />
erations are placed here.) Integer results are stored in two bytes<br />
FACl+3andFACl+4.<br />
335
<strong>64</strong> ROM Guide<br />
SGNFLG $67<br />
BITS<br />
FAC2<br />
ARISGN $6F<br />
FACOV<br />
TEMPTX<br />
CHRGET<br />
CHRGOT $79<br />
TXTPTR<br />
RNDX<br />
STATUS<br />
STOPFL<br />
TSERVO<br />
VERCK<br />
ICHRFL<br />
IDATO<br />
TEOB<br />
TEMPXY<br />
NFILES<br />
DFLTI<br />
DFLTO<br />
TPARIT<br />
TBYTFL<br />
MSGFLG<br />
HDRTYP<br />
PTR1<br />
PTR2<br />
TIME<br />
TSFCNT<br />
TBTCNT<br />
CNTDN<br />
BUFPNT<br />
INBIT<br />
PASNUM<br />
BITCI<br />
RINONE<br />
TBITER<br />
RIDATA<br />
RIDATA<br />
$68<br />
$69-$6E<br />
$70<br />
$71-$72<br />
$7A-$7B<br />
$8B-$8F<br />
$90<br />
$91<br />
$92<br />
$93<br />
$94<br />
$95<br />
$96<br />
$97<br />
$98<br />
$99<br />
$9A<br />
$9B<br />
$9C<br />
$9D<br />
$9E<br />
$9E<br />
$9F<br />
$A0-$A2<br />
$A3<br />
$A4<br />
$A5<br />
$A6<br />
$A7<br />
$A7<br />
$A8<br />
$A9<br />
$A9<br />
$AA<br />
$AA<br />
Flag used by FIN when inputting numbers; set to $FF if <strong>the</strong> num<br />
ber is negative. Also stores count of terms in polynomial series<br />
when evaluating trig functions.<br />
Bit overflow area on normalizing FAC1.<br />
Floating Point Accumulator 2; used with FAC1 in evaluation of<br />
products, sums, differences, etc.<br />
Sign comparison between FAC1 and FAC2; $00 means same sign,<br />
$FF means opposite.<br />
Rounding/overflow byte for FAC1.<br />
General pointer used in CRUNCH, VAL, series evaluation, with<br />
tape buffer, etc.<br />
Subroutine to fetch next BASIC character into A (spaces are<br />
skipped) and set flags; C cleared if ASCII numeral 0-9; Z set if<br />
end-of-line or colon (:).<br />
Entry point within CHRGET to re-get current BASIC character and<br />
set flags as CHRGET does. Does not increment TXTPTR first.<br />
Pointer into BASIC text used by CHRGET and CHRGOT routines.<br />
Floating point random number seed and subsequent pseudo<br />
random values.<br />
Status ST for serial devices and cassette.<br />
Flag: contains $7F (127) if RUN/STOP key pressed.<br />
Tape timing constant.<br />
Flag to indicate LOAD (0) or VERIFY (1).<br />
Serial flag: a value of $FF indicates a character is awaiting output.<br />
Serial character to be output; a value of $FF indicates no character.<br />
Hag: end of data block from tape.<br />
Temporary X,Y storage during cassette read/RS-232 input.<br />
Number of files open (maximum of ten); index to file table.<br />
Current input device number; default value is 0 (keyboard).<br />
Current output device number; default value is 3 (screen).<br />
Parity of byte written to tape.<br />
Flag: byte read from tape is complete.<br />
Flag: $00 means program mode; $80 means direct mode.<br />
Tape buffer header ID.<br />
Cassette pass 1 read errors.<br />
Cassette pass 2 read errors.<br />
Three-byte jiffy clock for TI, updated 60 times per second. Bytes<br />
arranged in order of decreasing significance.<br />
Tape read/write bit counter.<br />
Tape read/write pulse counter.<br />
Tape synchronization write countdown.<br />
Count of bytes in tape I/O buffer.<br />
RS-232 temporary storage for received bits.<br />
General temporary store for cassette read/write.<br />
RS-232 received bit count. Also temporary store for cassette read/<br />
write.<br />
RS-232 receive: check for start bit.<br />
Write start bit/read bit sequence error.<br />
Tape read mode; 0 means scan, 1-15 means count, $40 means<br />
LOAD, $80 means end-of-tape marker.<br />
RS-232 received byte buffer.<br />
336
<strong>64</strong> ROM Guide<br />
TCKS<br />
RPRTY<br />
SAL<br />
EAL<br />
CMPO<br />
TAPE1<br />
BITTS<br />
TTIX<br />
NXTBIT<br />
TEOT<br />
RODATA<br />
TERRR<br />
FNLEN<br />
LA<br />
SA<br />
FA<br />
FNADR<br />
ROPRTY<br />
TCHR<br />
FSBLK<br />
MYCH<br />
CAS1<br />
STAL<br />
MEMUSS<br />
LSTX<br />
NDX<br />
RVS<br />
INDX<br />
LXSP<br />
LYSP<br />
KEYVAL<br />
BLNSW<br />
BLNCT<br />
GDBLN<br />
BLNON<br />
CRSW<br />
PNT<br />
PNTR<br />
QTSW<br />
LNMX<br />
TBLX<br />
TMPD7<br />
$AB<br />
SAB<br />
$AC-$AD<br />
$AE-$AF<br />
$BO-$B1<br />
$B2-$B3<br />
$B4<br />
$B4<br />
$B5<br />
$B5<br />
$B6<br />
$B6<br />
$B7<br />
$B8<br />
$B9<br />
$BA<br />
$BB-$BC<br />
$BD<br />
$BD<br />
$BE<br />
$BF<br />
SCO<br />
SC1-SC2<br />
SC3-SC4<br />
$C5<br />
$C6<br />
$C7<br />
$C8<br />
$C9<br />
$CA<br />
$CB<br />
$CC<br />
$CD<br />
$CE<br />
$CF<br />
$D0<br />
$D1-$D2<br />
$D3<br />
$D4<br />
$D5<br />
$D6<br />
$D7<br />
INSRT $D8<br />
Counter of seconds before tape write. Also checksum.<br />
RS-232 received byte parity.<br />
Start address for LOAD/SAVE. Pointer also used by screen<br />
scrolling and INSert routines.<br />
End address for LOAD/SAVE. Also used as pointer to color RAM<br />
used by <strong>the</strong> INSert routine.<br />
Timing constants for tape.<br />
Pointer to start of cassette buffer, usually $033C.<br />
RS-232 transmit bit count.<br />
Tape read timer flag.<br />
RS-232 transmit: next bit to send.<br />
End of tape read.<br />
RS-232 transmit: byte to be sent.<br />
Tape read error flag.<br />
Number of characters in filename; a value of 0 means no name.<br />
Current logical file number.<br />
Current secondary address.<br />
Current device number; for example, 3 means screen, 4 means<br />
printer, etc.<br />
Pointer to start of current filename.<br />
RS-232 output parity.<br />
Byte to be written to/read from tape.<br />
Number of blocks remaining to read/write.<br />
Serial word buffer where byte is assembled.<br />
Cassette motor control flag.<br />
Start address for LOAD and cassette write.<br />
Pointer for general use, for example, calculating LOAD address.<br />
Matrix value of key pressed during last keyboard scan; a value of<br />
$40 means no key pressed.<br />
Number of characters in keyboard buffer.<br />
Flag: print reverse characters; 0 means normal, $12 means reverse.<br />
Count of characters in line input from screen.<br />
Cursor Y value (row) at start of input.<br />
Cursor X value (column) at start of input.<br />
Copy of keypress LSTX checked by interrupt so that a held key<br />
registers only once.<br />
Flag: cursor blink mode; a value of 0 means enabled, 1 means<br />
disabled.<br />
Countdown to next cursor toggle (from $14).<br />
Character (screen code) at cursor position.<br />
Flag: 1 means cursor in blink phase, 0 means not in blink phase.<br />
Flag: 3 means input from screen, 0 means input from keyboard.<br />
Address of start of current line on <strong>the</strong> screen.<br />
Cursor position (X value) along current logical line (0-$4F).<br />
Quote mode flag: flips each time quotes are encountered; 0 means<br />
move cursor, etc.; 1 means print reverse characters.<br />
Length of current logical screen line.<br />
Row of cursor.<br />
CHR$ value of last character input/output to screen; tape tem<br />
porary I/O storage and checksum.<br />
Number of keyboard inserts outstanding.<br />
337
<strong>64</strong> ROM Guide<br />
LDTBl<br />
USER<br />
KEYTAB<br />
RIBUF<br />
ROBUF<br />
BASZPT<br />
$D9-$F2<br />
$F3-$F4<br />
$F5-$F6<br />
$F7-$F8<br />
$F9-$FA<br />
$FB-$FE<br />
$FF<br />
Table of 25 high bytes of pointers to <strong>the</strong> start of screen lines in<br />
RAM. (<strong>The</strong> low bytes are held in ROM from $ECF0.) lines with<br />
wraparound have bit 7 set to 0; o<strong>the</strong>rwise, bit 7 is 1.<br />
Pointer to byte in color RAM corresponding to beginning of cur<br />
rent line on <strong>the</strong> screen.<br />
Address of current keyboard decoding table.<br />
RS-232: pointer to start of receive buffer.<br />
RS-232: pointer to start of transmit buffer.<br />
Unused; available for user programs.<br />
Temporary storage area for FLPT-to-ASCII conversion.<br />
Page 1 (Stack Area): RAM $O1OO-$O1FF<br />
Label <strong>64</strong>/VIC Descriptions<br />
ASCWRK $0FF-$10A Area for conversion of numerals into ASCII string format for<br />
printing.<br />
BAD $100-$13E Table of tape read errors.<br />
STACK $14O-$1FF BASIC stack area.<br />
Page 2:<br />
Labe}<br />
BUF<br />
LAT<br />
FAT<br />
SAT<br />
KEYD<br />
LORAM<br />
HIRAM<br />
TIMOUT<br />
COLOR<br />
GDCOL<br />
HIBASE<br />
XMAX<br />
RPTFLG<br />
KOUNT<br />
DELAY<br />
SHFLAG<br />
RAM $0200-$02FF<br />
<strong>64</strong>/VIC<br />
$200-$258<br />
$259-$262<br />
$263-$26C<br />
$26D-$276<br />
$277-$280<br />
$281-$282<br />
$283-$284<br />
$285<br />
$286<br />
$287<br />
$288<br />
$289<br />
$2§A<br />
$28B<br />
$28C<br />
$28D<br />
LSTSHF $28E<br />
Descriptions<br />
System input buffer; all keyboard input is read into here.<br />
Table of up to ten active logical file numbers.<br />
Table of up to ten corresponcjing device numbers.<br />
Table of ten corresponding secondary addresses as used by system.<br />
Keyboard buffer: maximum of ten characters are read from key<br />
board and placed here by <strong>the</strong> interrupt routine.<br />
Pointer to lowest available byte of RAM for BASIC program stor<br />
age (initialized on power-up; normally 2048).<br />
Pointer to highest available BASIC RAM byte (initialized on<br />
power-up).<br />
Serial bus time-out flag.<br />
Current color code: POKEd into color RAM when printing charac<br />
ters to screen.<br />
Color of character under cursor.<br />
High byte of screen memory address.<br />
Maximum number of characters storable in keyboard buffer<br />
(initialized to 10).<br />
Flag controlling key repeats; a value of $00 means repeat cursor<br />
move and space keys; $80 means repeat all keys; $40 means no<br />
keys repeat. Default is $00.<br />
Delay before repeat operates (system resets this).<br />
Delay between repeats.<br />
Detect SHIFT, <strong>Commodore</strong> key, CTRL keypress: a value of $01<br />
means SHIFT is pressed, $02 means <strong>Commodore</strong> key, $04 means<br />
CTRL. <strong>The</strong>se are additive: $05 means SHIFT and CTRL keys are<br />
both pressed, etc.<br />
Last SHFLAG pattern; used for debouncing.<br />
338
<strong>64</strong> ROM Guide<br />
KEYLOG<br />
MODE<br />
AUTODN<br />
M51CTR<br />
M51CDR<br />
M51AJB<br />
RSSTAT<br />
BITNUM<br />
BAUDOF<br />
RIDBE<br />
RIDBS<br />
RODBS<br />
RODBE<br />
IRQTMP<br />
$28F-$290<br />
$291<br />
$292<br />
$293<br />
$294<br />
$295-$296<br />
$297<br />
$298<br />
$299-$29A<br />
$29B<br />
$29C<br />
$29D<br />
$29E<br />
$29F-$2A0<br />
$2A1-$2A5<br />
$2A6<br />
$2A7-$2FF<br />
Vector to routine to check SHIFT pattern; used by SCNKEY Kernal<br />
routine.<br />
Flag: $00 means enable upper/lowercase toggle using SHIFT and<br />
<strong>Commodore</strong> key; $80 means disable <strong>the</strong> toggle.<br />
Flag: autoscroll down during input; $00 means disable.<br />
RS-232: control register.<br />
RS-232: command register.<br />
RS-232: nonstandard transmission rate value (not used).<br />
RS-232: status register ST.<br />
RS-232: number of bits to send/receive.<br />
RS-232: baud rate timing constant.<br />
RS-232: input buffer pointer; points to latest character input (end<br />
of buffer).<br />
RS-232: input buffer pointer; points to first available character<br />
(start of buffer).<br />
RS-232: output buffer pointer: start of buffer.<br />
RS-232: output buffer pointer: end of buffer.<br />
Temporary storage for IRQ vector during tape operations.<br />
Temporary storage during tape operations.<br />
PAL/NTSC Flag (0 means NTSC, 1 means PAL)<br />
Free RAM available to user<br />
Page 3:<br />
RAM $0300-$03FF<br />
Label <strong>64</strong>/VIC Descriptions<br />
IERROR<br />
IMAIN<br />
$300-$301<br />
$302-$303<br />
Vector to BASIC print error message (normally $E38B); X register<br />
holds error message number.<br />
Vector to routine to input or execute line of BASIC (normally<br />
$A483).<br />
ICRNCH $304-$305 Vector to BASIC tokenizing routine (normally $A57C).<br />
IQPLOP $306-$307 Vector to BASIC LIST routine (normally $A71A).<br />
IGONE $308-$309 Vector to BASIC RUN routine (normally $A7E4).<br />
IEVAL $30A-$30B Vector to BASIC single-expression evaluation routine (normally<br />
$AE86).<br />
SAREG $30C 6510/6502 Accumulator storage for SYS; A is loaded from this<br />
location on SYS call and stored back into it when <strong>the</strong> SYS call<br />
ends.<br />
SXREG $30D 6510/6502 X register storage for SYS; handling as above.<br />
SYREG $30E 6510/6502 Y register storage for SYS; handling as above.<br />
SPREG $30F 6510/6502 Status register storage for SYS; handling as above.<br />
USRPOK $310/$00 USR function JMP instruction ($4C).<br />
USRADD $311-$312/ USR function address, low/high byte form, initialized to point to<br />
$01-$02 BASIC error message routine ($B284).<br />
$313 Unused byte.<br />
(Note that <strong>the</strong> vectors from $3U-$333 are initialized each time RUN/STOP-RESTORE is pressed,<br />
assuming NMINV is normal)<br />
CINV $314-$315 Vector for IRQ interrupt (normally $EA31). Called from $FF58.<br />
CBINV $316-$317 Vector for BRK (normally $FE66). Called from $FF55.<br />
NMINV $318-$319 Vector for NMI (normally $FE47).<br />
339
<strong>64</strong> ROM Guide<br />
IOPEN<br />
ICLOSE<br />
ICHKIN<br />
ICKOUT<br />
ICLRCH<br />
IBASIN<br />
IBSOUT<br />
ISTOP<br />
IGETIN<br />
ICLALL<br />
USRCMD<br />
ILOAD<br />
ISAVE<br />
TBUFFR<br />
$31A-$31B<br />
$31C-$31D<br />
$31E-$31F<br />
$320-$321<br />
$322-$323<br />
$324-$325<br />
$326-$327<br />
$328-$329<br />
$32A-$32B<br />
$32C-$32D<br />
$32E-$32F<br />
$330-$331<br />
$332-$333<br />
$334-$33B<br />
$33C-$3FB<br />
$3FC-$3FF<br />
Vector to Kernal OPEN routine (normally $F34A). Called from<br />
$FFC0.<br />
Vector to Kernal CLOSE routine (normally $F291). Called from<br />
$FFC3.<br />
Vector to Kernal CHKIN routine (normally $F20E). Called from<br />
$FFC6.<br />
Vector to Kernal CHKOUT routine (normally $F250). Called from<br />
$FFC9.<br />
y<br />
Vector to Kernal CLRCHN routine (normally $F333). Called from<br />
$FFCC.<br />
Vector to Kernal CHRIN routine (normally $P157). Called from<br />
$FFCF.<br />
Vector to Kernal CHROUT routine (normally $F1CA). Called from<br />
$FFD2.<br />
Vector to Kernal STOP routine (normally $F6ED). Called from<br />
$FFE1.<br />
Vector to Kernal GETIN routine (normally $F13E). Called from<br />
$FFE4.<br />
Vector to Kernal CLALL routine (normally $F32F). Called from<br />
$FFE7.<br />
Unused vector: May be defined by user; initialized to BRK vector<br />
($FE66).<br />
Vector to Kernal LOAD routine (normally $F4A5).<br />
Vector to Kernal SAVE routine (normally $F5ED).<br />
Eight unused bytes.<br />
Tape I/O buffer (192 bytes long). Can be used for ML programs<br />
but tape use will overwrite.<br />
Four unused bytes.<br />
Note: <strong>The</strong> following summary of <strong>the</strong> memory map applies to <strong>the</strong> <strong>64</strong> only, since <strong>the</strong>re are consid<br />
erable hardware differences between <strong>the</strong> <strong>64</strong> and o<strong>the</strong>r CBM machines.<br />
In <strong>the</strong> <strong>Commodore</strong> 6£, locations $8000 and above are subject to memory management by both<br />
hardware (EXROM and GAME lines) and software (locations 0 and 1), and <strong>the</strong>refore can contain<br />
different things at different times. All this is explained in Chapter 5. Note that a plug-in cartridge<br />
is assumed to be ROM in what follows.<br />
Hex Decimal Description<br />
$0400-$07E7 1024-2023 Usual screen memory area: 25 lines with 40 columns each.<br />
$07F8-$07FF<br />
$0800-$9FFF<br />
2040-2047<br />
2048-40959<br />
Pointers to sprite data blocks (assuming screen starts at<br />
$0400).<br />
Space normally occupied by BASIC programs and associated<br />
variables, arrays, and strings.<br />
$8000-$9FFF 32768-40959 RAM or cartridge ROM (usually with autorun feature).<br />
$A000-$BFFF 40960-49151 BASIC ROM or RAM or cartridge ROM (may have<br />
autostart).<br />
$C000-$CFFF 49152-53247 RAM.<br />
$D000-$DFFF 53248-57343 I/O chips and color RAM or cartridge ROM (without<br />
autostart).<br />
<strong>The</strong> region $D000-$DFFF is configured as follows (note that I/O chips have repeat images):<br />
$D000-$D02E 53248-53294 VIC chip (see Chapter 12).<br />
$D400-$D41C 54272-54300 SID chip (see Chapter 13).<br />
340
<strong>64</strong> ROM Guide<br />
$D800-$DBE7 55296-56295 Color RAM (low nybbles store character colors 0-15).<br />
$DC00-$DC0F 56320-56335 CIA 1 (see Chapter 5).<br />
$DD00-$DD0F 56576-56591 CIA 2 (see Chapter 5).<br />
This region also includes <strong>the</strong> character generator ROM:<br />
$D000-$D7FF 53248-55295 Uppercase/graphics character set.<br />
$D800-$DFFF 55296-57343 Lower/uppercase character set.<br />
BASIC and Kernal ROM<br />
<strong>Commodore</strong> <strong>64</strong> and VIC-20 BASIC and Kernal ROMs are similar. VIC's BASIC ROM<br />
starts at $C000 and is exactly $2000 bytes up from <strong>the</strong> <strong>64</strong> BASIC ROM, which starts<br />
at $A000. Both Kernal ROMs start at $E000, but <strong>the</strong> <strong>64</strong> has an extra JMP instruction<br />
to bridge <strong>the</strong> gap between BASIC and <strong>the</strong> Kernal, so <strong>the</strong> addresses of routines in <strong>the</strong><br />
Kernal initially differ by three bytes between <strong>the</strong>se machines.<br />
Label<br />
BCOLD<br />
BWARM<br />
FNDFOR<br />
BLTU<br />
<strong>64</strong>/VIC<br />
$A000/$C00(K<br />
$A002/$C002<br />
$A004/$C004<br />
$A00C/$C00C<br />
$A052/$C052<br />
$A080/$C080<br />
$A09E/$C09E<br />
$A129/$C129<br />
$A140/$C140<br />
$A14D/$C14D<br />
$A19E/$C19E<br />
$A328/$C328<br />
$A3<strong>64</strong>/$C3<strong>64</strong><br />
$A38A/$C38A<br />
$A3B8/$C3B8<br />
/Descriptions<br />
BASIC cold start vector ($E394). NEWs BASIC, prints BYTES<br />
FREE and READY. Part of <strong>the</strong> reset sequence; see routines at<br />
$E394 and $FCE2.<br />
BASIC warm start vector ($E37B). CLRs BASIC, prints READY.<br />
Part of <strong>the</strong> NMI sequence; see routines at $E37B and $FE43.<br />
CBM BASIC message.<br />
Table of addresses — 1 of routines for handling BASIC state<br />
ments (FOR, RUN, PRINT, REM, CONT, etc.). (Address - 1<br />
because of <strong>the</strong> way <strong>the</strong>y are utilized.)<br />
Table of true addresses of routines for handling numeric and<br />
string functions (FRE, POS, SQR, etc.).<br />
Table of addresses — 1 of routines for handling BASIC op<br />
erators (add, subtract, divide, etc.); each address is followed by<br />
a byte indicating <strong>the</strong> operator priority.<br />
BASIC keywords as CBM ASCII strings with bit 7 of final<br />
character of each keyword set high.<br />
Table of miscellaneous keywords (TAB, STEP, etc., with no ac<br />
tion address) with bit 7 of final character of each keyword set<br />
high.<br />
Table of operator tokens; also AND, OR as strings with bit 7 of<br />
final character of each operator set high.<br />
Table of function keywords (SGN, INT, ABS, etc.) with bit 7 of<br />
final character of each keyword set high.<br />
Table of 28 error messages (TOO MANY FILES, FILE OPEN,<br />
etc.) with bit 7 of final character of each message set high.<br />
Table of pointers to error messages.<br />
Table of o<strong>the</strong>r messages: OK, ERROR IN, READY, BREAK.<br />
Check stack for FOR entry. Called by NEXT; if FOR not found,<br />
?NEXT WITHOUT FOR results. Also clears stack of a FOR data<br />
block if called by RETURN.<br />
Open up a gap in BASIC text to allow insertion of new BASIC<br />
line. Check whe<strong>the</strong>r <strong>the</strong>re is enough room.<br />
341
<strong>64</strong> ROM Guide<br />
BLTUC<br />
GETSTK<br />
REASON<br />
ERROR<br />
READY<br />
MAIN<br />
MAIN1<br />
INSLIN<br />
FINI<br />
LNKPRG<br />
INLIN<br />
CRUNCH<br />
FNDLIN<br />
FNDLNC<br />
NEW<br />
SCRTCH<br />
RUNC<br />
CLEAR<br />
STXPT<br />
LIST<br />
LIST1<br />
$A3BF/$C3BF<br />
$A3FB/$C3FB<br />
$A408/$C408<br />
$A437/$C437<br />
$A474/$C474<br />
$A480/$C480<br />
$A49C/$C49C<br />
$A4A4/$C4A4<br />
$A52A/$C52A<br />
$A533/$C533<br />
$A560/$C560<br />
$A579/$C579<br />
$A613/$C613<br />
$A617/$C617<br />
$A<strong>64</strong>2/$C<strong>64</strong>2<br />
$A<strong>64</strong>4/$C<strong>64</strong>4<br />
$A659/$C659<br />
$A65E/$C65E<br />
$A68E/$C68E<br />
$A69C/$C69C<br />
$A6C9/$C6C9<br />
Move block starting at address pointed to by $5F-$60 and end<br />
ing at address pointed to by $5A-$5B — 1 up to a new block<br />
ending at <strong>the</strong> address pointed to by $58-$59 — 1.<br />
Test to see whe<strong>the</strong>r stack will accommodate A*2 bytes: ?OUT<br />
OF MEMORY if not.<br />
Check whe<strong>the</strong>r address pointed to by A/Y is below FRETOP<br />
(current bottom of string area). If yes, exit; o<strong>the</strong>rwise, do gar<br />
bage collection and check again. If still not, <strong>the</strong>n print ?OUT<br />
OF MEMORY.<br />
Print error message; X holds error number (half of offset within<br />
error message address table). Vectored via ($0300) to $E38B.<br />
<strong>The</strong>n set keyboard input and screen output, reset stack, and<br />
print IN with line number if in program mode.<br />
Restart BASIC; print READY, set direct mode. .<br />
Receive a line into input buffer and add a terminating zero<br />
byte. Check for program line or immediate mode command; if<br />
immediate mode command, execute it. MAIN is vectored via<br />
($0302) to $A483.<br />
If program line, tokenize it.<br />
If <strong>the</strong> line number already exists, replace it. If it's new, insert it.<br />
Line number is in $14-$15 on entry, length 4- 4 is in Y. If <strong>the</strong><br />
first byte in buffer is 0, <strong>the</strong> line is null; delete it.<br />
Having inserted a new line, do RUNC (thus, variables are lost<br />
on editing, and you cannot CONT after editing) and LNKPRG;<br />
<strong>the</strong>n jump to MAIN.<br />
Chain link pointers in BASIC program using end-of-line zero<br />
markers.<br />
Input a screen line into <strong>the</strong> BASIC text buffer at $200, and add<br />
a zero terminating byte.<br />
Tokenize keywords in input buffer. Vectored via ($0304) to<br />
$A57C.<br />
Search BASIC text from beginning for line number in $14-$ 15.<br />
Carry bit set if line found. Locations $5F-$60 point to link<br />
address.<br />
Search BASIC text from address in A (low byte) and X (high<br />
byte) for line number in $14-$15.<br />
NEW routine enters here; check syntax, and continue with<br />
SCRTCH.<br />
Reset first two bytes of text (first link pointer) to 0; load startof-variables<br />
pointer $2D-$2E with start-of-BASIC + 2, and<br />
continue with RUNC.<br />
Set pointer within CHRGET to start of BASIC text, using<br />
STXPT, <strong>the</strong>n continue with CLEAR.<br />
BASIC CLR routine; erase variables by resetting end-ofvariables<br />
pointers to coincide with end-of-program pointer;<br />
appropriate string variable pointers are also reset. Abort I/O<br />
activity and reset stack.<br />
Reset pointer within CHRGET routine to beginning of BASIC<br />
text ($2B-$2C - 1 is loaded into $7A-$7B).<br />
Entry point of routine to process LIST command.<br />
List one line of BASIC; line number, <strong>the</strong>n text.<br />
342
<strong>64</strong> ROM Guide<br />
QPLOP<br />
FOR<br />
NEWSTT<br />
CKEDL<br />
GONE<br />
EXCC<br />
RESTOR<br />
STOP<br />
CONT<br />
RUN<br />
GOSUB<br />
GOTO<br />
RETURN<br />
DATA<br />
DATAN<br />
$A717/$C717<br />
$A742/$C742<br />
$A7AE/$C7AE<br />
$A7C4/$C7C4<br />
$A7E1/$C7E1<br />
$A7ED/$C7ED<br />
$A81D/$C81D<br />
$A82C/$C82C<br />
$A857/$C857<br />
$A871/$C871<br />
$A883/$C883<br />
$A8A0/$C8A0<br />
$A8D2/$C8D2<br />
$A8F8/$C8F8<br />
$A906/$C906<br />
Handle character to be listed; if ordinary character or control<br />
character in quotes, print it; expand and print tokens. Vectored<br />
via ($0306) to $A71A.<br />
Entry point for routine to handle FOR statement. Push 18 bytes<br />
onto stack: pointer to following statement, current line number,<br />
upper-loop value, step value (defaults to 1), loop variable<br />
name, and FOR token.<br />
Execute BASIC; test for RUN/STOP key and check for end-ofline<br />
zero byte or colon.<br />
If at end of text, stop; o<strong>the</strong>rwise, set pointer within CHRGET to<br />
beginning of next line.<br />
Handle <strong>the</strong> BASIC statement in <strong>the</strong> current line. Vectored via<br />
($0308) to $A7E4, loop back to NEWSTT.<br />
Execute a BASIC keyword. Uses address for start of routine<br />
from table at $A00C. Assumes LET if a token is not <strong>the</strong> first<br />
byte in <strong>the</strong> statement. Address pushed on stack so RTS of<br />
GETCHR jumps to it.<br />
Entry point for routine to handle RESTORE; set <strong>the</strong> data<br />
pointer at $41-$42 to start of BASIC text.<br />
Entry point for routine to handle STOP; also END and break in<br />
program. Information for CONT (pointer in BASIC text, line<br />
number) is stored. STOP prints BREAK IN nnn while END<br />
skips this to READY. <strong>The</strong> RUN/STOP key invokes STOP.<br />
Reaching <strong>the</strong> end-of-BASIC program text c^Jls END.<br />
Entry point for routine to handle CONT; performs this by set<br />
ting current linj number (stored in $39-$3A) and <strong>the</strong> pointer<br />
within CHRGET to values stored by STOP. 7CANNOT CON<br />
TINUE ERROR occurs if <strong>the</strong> high byte of <strong>the</strong> pointer has been<br />
set to 0 on syntax error.<br />
Entry point for routine to handle RUN; if RUN is encountered<br />
alone, <strong>the</strong>n CLR variables and reset stack, set CHRGET to start<br />
of BASIC, and begin execution. If RUN nnn, CLR variables and<br />
reset stack, <strong>the</strong>n do GOTO nnn.<br />
Entry point for routine to handle GOSUB; push five bytes onto<br />
stack: pointer within CHRGET (two bytes), current line number<br />
(two bytes), and <strong>the</strong> GOSUB token. <strong>The</strong> GOTO routine is <strong>the</strong>n<br />
called.<br />
Entry point for routine to handle GOTO; fetch <strong>the</strong> line number<br />
following <strong>the</strong> GOTO command and search BASIC text for this<br />
line. If high byte of destination is higher than high byte of cur<br />
rent line number, search from position of current line onward<br />
to shorten search time; o<strong>the</strong>rwise, search from beginning. Put<br />
pointer to found line into CHRGET pointer.<br />
Entry point for routine to handle RETURN; stack is cleared up<br />
to GOSUB token (7RETURN WITHOUT GOSUB if not found);<br />
<strong>the</strong>n <strong>the</strong> calling line number and pointer are reinstated, and<br />
execution continues.<br />
Entry point for routine to handle DATA statements; routine to<br />
let CHRGET skip DATA statement up to terminating byte or<br />
colon.<br />
Search for statement terminator; exits with Y containing<br />
displacement to end of line from CHRGET's pointer.<br />
343
<strong>64</strong> ROM Guide<br />
REMN<br />
IF<br />
REM<br />
DOCOND<br />
ONGOTO<br />
LINGET<br />
LET<br />
PUTINT<br />
PTFLPT<br />
PUTTIM<br />
ASCADD<br />
GETSPT<br />
PRINTN<br />
CMD<br />
STRDON<br />
PRINT<br />
VAROP<br />
CRDO<br />
STROUT<br />
STRPRT<br />
OUTSTR<br />
OUTSPC<br />
PRTSPC<br />
OUTSKP<br />
OUTQST<br />
OUTDO<br />
TRMNOK<br />
$A909/$C909<br />
$A928/$C928<br />
$A93B/$C93B<br />
$A940/$C940<br />
$A94B/$C94B<br />
$A96B/$C96B<br />
$A9A5/$C9A5<br />
$A9C4/$C9C4<br />
$A9D6/$C9D6<br />
$A9E3/$C9E3<br />
$AA27/$CA27<br />
$AA2C/$CA2C<br />
$AA80/$CA80<br />
$AA86/$CA86<br />
$AA9A/$CA9A<br />
$AAA0/$CAA0<br />
$AAB8/$CAB8<br />
$AAD7/$CAD7<br />
$AB1E/$CB1E<br />
$AB21/$CB21<br />
$AB24/$CB24<br />
$AB3B/$CB3B<br />
$AB3F/$CB3F<br />
$AB42/$CB42<br />
$AB45/$CB45<br />
$AB47/$CB47<br />
$AB4D/$CB4D<br />
Search for end-of-BASIC line.<br />
Entry point for routine to handle IF statement. Evaluate <strong>the</strong> ex<br />
pression; if result is false (0), skip <strong>the</strong> THEN or GOTO clause<br />
by doing REM.<br />
Entry point for routine to handle REM; scan for end of line and<br />
update pointer in CHRGET, to ignore contents of REM<br />
statement.<br />
Continue IF; if expression true, <strong>the</strong>n execute next command, or<br />
do GOTO if digit follows.<br />
Entry point for routine to handle ON-GOTO and ON-GOSUB<br />
statements; evaluate expression, test for GOTO or GOSUB<br />
token, scan line number list, skipping commas for specified line<br />
number, and GOTO or GOSUB it.<br />
Read an integer (usually a line number) from <strong>the</strong> BASIC text<br />
into locations $14 and $15; must be in range 0-63999.<br />
Entry point for routine to handle LET statement; find target<br />
variable in variable list (or create it if it doesn't exist), test for<br />
= token, evaluate expression, and move result or string<br />
descriptor into <strong>the</strong> variable list.<br />
Round FAC1 and put, as integer, into variable list at current<br />
variable position, pointed to by $49-$4A.<br />
Put FAC1 into variable list at location pointed to by $49-$4A.<br />
Assign <strong>the</strong> system variable TI$.<br />
Add ASCII digit to FAC1.<br />
LET for strings; put string descriptor pointed to by<br />
FAC1+3-FAC1+4 into variable list at location pointed to by<br />
$49-$4A.<br />
Entry point for routine to handle PRINT# statement; call CMD,<br />
<strong>the</strong>n clear I/O channels and restore default I/O device<br />
numbers.<br />
Entry point for routine to handle CMD; set output device from<br />
file table using Kernal CHKOUT routine, <strong>the</strong>n call PRINT.<br />
Part of PRINT routine; print string and continue with punctua<br />
tion of PRINT.<br />
Entry point for routine to handle PRINT statement; identify<br />
PRINT parameters (TAB, SPC, comma, semicolon, etc.), and<br />
evaluate expression.<br />
Print variable; if numeral, convert to string before printing.<br />
Print carriage return (ASCII 13) followed (if channel > 128) by<br />
linefeed (ASCII 10).<br />
Print string beginning at address specified in A/Y, and ter<br />
minated by a zero byte or quotes.<br />
Print string; FAC1+3-FAC1+4 points to string descriptor.<br />
Output string; locations $22-$23 point to string, length in A.<br />
Output cursor-right (or space if <strong>the</strong> screen is not <strong>the</strong> current<br />
output device).<br />
Output space.<br />
Output cursor-right.<br />
Output question mark for error messages.<br />
Output <strong>the</strong> character in A.<br />
Output appropriate error messages for GET, READ, and<br />
INPUT.<br />
344
<strong>64</strong> ROM Guide<br />
GET<br />
INPUTN<br />
INPUT<br />
QINLIN<br />
READ<br />
INPCON<br />
INPCO1<br />
DATLOP<br />
VAREND<br />
EXINT<br />
NEXT<br />
FRMNUM<br />
CHKNUM<br />
CHKSTR<br />
FRMEVL<br />
EVAL<br />
PIVAL<br />
PARCHK<br />
CHKCLS<br />
$AB7B/$CB7B<br />
$ABA5/$CBA5<br />
$ABBF/$CBBF<br />
$ABF9/$CBF9<br />
$AC06/$CC06<br />
$AC0D/$CC0D<br />
$AC0F/$CC0F<br />
$ACB8/$CCB8<br />
$ACDF/$CCDF<br />
$ACFC/$CCFC<br />
$AD1E/$CD1E<br />
$AD8A/$CD8A<br />
$AD8D/$CD8D<br />
$AD8F/$CD8F<br />
$AD9E/$CD9E<br />
$AE83/$CE83<br />
$AEA8/$CEA8<br />
$AEF1/$CEF1<br />
$AEF7/$CEF7<br />
Entry point for routine to handle GET and GET# statements;<br />
test for direct mode (illegal) and fetch one character from key<br />
board or file.<br />
Entry point for routine to handle INPUT# statement; fetch file<br />
number, turn <strong>the</strong> device on, call INPUT, and <strong>the</strong>n turn <strong>the</strong> de<br />
vice off.<br />
Entry point for routine to handle INPUT statement; output<br />
user's prompt string if present, <strong>the</strong>n continue with QINLIN<br />
routine.<br />
Print 7 prompt and receive line of text (terminated by<br />
RETURN) into input buffer.<br />
Entry point for routine to handle <strong>the</strong> READ statement. GET<br />
and INPUT also share this routine, but are distinguished by a<br />
flag in location $11.<br />
Entry point into READ routine for INPUT; set flag and call<br />
READ, with buffer at <strong>the</strong> address specified in X (low byte) and<br />
Y (high byte).<br />
Entry point into READ routine for GET; set flag and call<br />
READ, with buffer at <strong>the</strong> address specified in X (low byte) and<br />
Y (high byte).<br />
Scan text and read DATA statements.<br />
Tests for 0 at end of input buffer; if not found, print 7EXTRA<br />
IGNORED.<br />
Messages 7EXTRA IGNORED and 7REDO FROM START.<br />
Entry point for routine to handle NEXT; check for FOR token<br />
and matching variable on stack, and print 7NEXT WITHOUT<br />
FOR if not found; calculate next value. If <strong>the</strong> loop increment is<br />
still valid, reset current line number and <strong>the</strong> pointer in<br />
CHRGET and continue.<br />
Evaluate a numeric expression for BASIC by calling FRMEVL,<br />
<strong>the</strong>n CHKNUM.<br />
Check that FRMEVL has returned a number by testing flag at<br />
location $0D. If a number was not returned, issue a 7TYPE<br />
MISMATCH ERROR message.<br />
Check that FRMEVL has returned a string by testing flag at<br />
location $0D. If a string was not returned, issue a 7TYPE MIS<br />
MATCH ERROR message.<br />
Evaluate any BASIC expression in text and report any syntax<br />
errors; set $0D (VALTYP) to $00 if <strong>the</strong> expression is numeric<br />
and $FF if it is a string. For numeric expressions, location $0E<br />
(INTFLG) is set to $00 if <strong>the</strong> expression is floating point, and<br />
<strong>the</strong> value is placed in FAC1. If <strong>the</strong> variable type is integer, set<br />
INTFLG to $80, but leave <strong>the</strong> result in floating point format in<br />
FAC1. Complicated expressions may need simplifying to retain<br />
stack space and prevent 7OUT OF MEMORY.<br />
Evaluate a single term in an expression; look for ASCII<br />
numeral strings, variables, pi, NOT, arithmetic functions, etc.<br />
Value of pi in five-byte floating point format.<br />
Evaluate expression within paren<strong>the</strong>ses.<br />
Check whe<strong>the</strong>r CHRGET points to a ) character; issue a 7SYN-<br />
TAX ERROR message if not.<br />
345
<strong>64</strong> ROM Guide<br />
CHKOPN<br />
CHKCOM<br />
SYNCHR<br />
SYNERR<br />
DOMIN<br />
TSTROM<br />
ISVAR<br />
TISASC<br />
ISFUN<br />
OROP<br />
ANDOP<br />
DOREL<br />
NUMREL<br />
STRREL<br />
DIM<br />
PTRGET<br />
ORDVAR<br />
ISLETC<br />
NOTFNS<br />
346<br />
$AEFA/$CEFA<br />
$AEFD/$CEFD<br />
$AEFF/$CEFF<br />
$AF08/$CF08<br />
$AF0D/$CF0D<br />
$AF14/$CF14<br />
$AF28/$CF28<br />
$AF48/$CF48<br />
$AFA7/$CFA7<br />
$AFE6/$CFE6<br />
$AFE9/$CFE9<br />
$B016/$D016<br />
$B01B/$D01B<br />
$B02E/$D02E<br />
$B081/$D081<br />
$B08B/$D08B<br />
$B0E7/$D0E7<br />
$B113/$D113<br />
$B11D/$D11D<br />
Check whe<strong>the</strong>r CHRGET points to a ( character; issue a ?SYN-<br />
TAX ERROR message if not.<br />
Check whe<strong>the</strong>r CHRGET points to a comma; issue a 7SYNTAX<br />
ERROR message if not.<br />
Check whe<strong>the</strong>r CHRGET points to a byte identical to that in A;<br />
if it does, routine exits with next byte in A; o<strong>the</strong>rwise, a ?SYN-<br />
TAX ERROR message is issued.<br />
Output a 7SYNTAX ERROR message and return to READY.<br />
Evaluate NOT.<br />
Set carry flag to 1 if FAC1+3-FAC1+4 point to <strong>the</strong> ROM area<br />
indicating reserved variables TI$, TI, ST.<br />
Search variable list for variable named in locations $45-$46; on<br />
exit FAC1 will hold numeric value in FLPT format (whe<strong>the</strong>r<br />
integer or floating point variable); FAC1+3-FAC1+4 will<br />
point to <strong>the</strong> descriptor if it's a string variable.<br />
Read clock and set up string containing TI$.<br />
Identify function type and evaluate it.<br />
Entry point for routine to handle <strong>the</strong> OR function; set flag and<br />
do OR between two two-byte integers in FAC1 and FAC2.<br />
Entry point for routine to handle <strong>the</strong> AND function. Both AND<br />
and OR are performed by one routine; a flag (in Y) holds $FF<br />
for OR, $00 for AND. Convert FLPT to integer (and give an er<br />
ror message if <strong>the</strong> result is out of range). <strong>The</strong> result in FLPT<br />
format is left in FAC1.<br />
Entry point for routine to handle string and numeric compari<br />
sons (< = >). Check variable types, <strong>the</strong>n continue with<br />
NUMREL or STRREL, as appropriate.<br />
Perform numeric comparison, using FCOMP at $BC5B.<br />
Perform string comparison; exit with X holding $00 if strings<br />
equal, $01 if <strong>the</strong> first string is greater than <strong>the</strong> second, and $FF<br />
if <strong>the</strong> second is greater than <strong>the</strong> first.<br />
Entry point for routine to handle <strong>the</strong> DIM statement; set up<br />
each array element using <strong>the</strong> PTRGET routine.<br />
Validate a variable name in BASIC text; <strong>the</strong> first character must<br />
be alphabetic, <strong>the</strong> second may be ei<strong>the</strong>r alphabetic or numeric;<br />
subsequent alphanumerics are discarded. Set VALTYP (location<br />
$0D) to $FF to indicate a string variable if $ is found; o<strong>the</strong>r<br />
wise, set VALTYP to $00 to indicate a numeric variable. Set<br />
INTFLG (location $0E) to $80 to indicate an integer variable if<br />
% is found. <strong>The</strong> name is Stored in VARNAM (locations<br />
$45-$46) with high bits set to indicate <strong>the</strong> variable type, as de<br />
scribed in Chapter 5.<br />
Search variable list for variable whose name is in VARNAM<br />
(locations $45-$46) and set VARPNT (locations $47-$48) to<br />
point to it. Create new variable if <strong>the</strong> name is not currently in<br />
<strong>the</strong> list.<br />
Set <strong>the</strong> carry flag if <strong>the</strong> accumulator holds A-Z.<br />
Create a new simple (not array) variable in variable list im<br />
mediately before arrays; name is in VARNAM ($45-$46). Any<br />
arrays have to be moved up by seven bytes to accommodate<br />
<strong>the</strong> new variable. Exit with locations $5F-$60 pointing to<br />
newly created variable.
<strong>64</strong> ROM Guide<br />
FMAPTR<br />
N32768<br />
FACINX<br />
INTIDX<br />
AYINT<br />
ISARY<br />
FNDARY<br />
BSERR<br />
NOTFDD<br />
INPLN2<br />
UMULT<br />
FRE<br />
GIVAYF<br />
POS<br />
SNGET<br />
ERRDIR<br />
DEF<br />
GETFNM<br />
FNDOER<br />
STRD<br />
$B194/$D194<br />
$B1A5/$D1A5<br />
$B1AA/$D1AA<br />
$B1B2/$D1B2<br />
$B1BF/$D1BF<br />
$B1D1/$D1D1<br />
$B218/$D218<br />
$B245/$D245<br />
$B261/$D261<br />
$B30E/$D30E<br />
$B34C/$D34C<br />
$B37D/$D37D<br />
$B391/$D391<br />
$B39E/$D39E<br />
$B3A2/$D3A2<br />
$B3A6/$D3A6<br />
$B3B3/$D3B3<br />
$B3E1/$D3E1<br />
$B3F4/$D3F4<br />
$B465/$D465<br />
Calculate pointer value in $5F-$60, to be used when setting up<br />
space for arrays.<br />
Holds —32768 as a five-byte floating point number.<br />
Convert contents of FAC1 to two-byte integer (-32768 to<br />
+32767) in A/Y.<br />
Fetch and evaluate a positive integer expression from <strong>the</strong> next<br />
part of BASIC text; if result is 0-32767, store in FAC1+3 and<br />
FAC1+4.<br />
Convert <strong>the</strong> contents of FAC1 to integer in range 0-32767;<br />
leave <strong>the</strong> result in FAC1+3-FAC1+4.<br />
Get array parameters from BASIC text (number of dimensions<br />
and number of elements) and push <strong>the</strong> values onto <strong>the</strong> stack.<br />
Find array named in VARNAM ($45-$46), with o<strong>the</strong>r details of<br />
<strong>the</strong> array stored on <strong>the</strong> stack.<br />
?BAD SUBSCRIPT error. BSERR+3 will print 7ILLEGAL<br />
QUANTITY error message.<br />
If <strong>the</strong> specified array is not found, create it using details on<br />
stack with DIMension 10.<br />
Locate specified element within array and point VARPNT<br />
($47-$48) to it.<br />
Compute offset of specified array element relative to array<br />
pointed at by VARPNT ($47-$48); put in X/Y.<br />
Entry point for routine to handle FRE function; perform gar<br />
bage collection and set Y/A to point to lowest string minus<br />
pointer to end of arrays; <strong>the</strong>n place in FAC1 and continue with<br />
GIVAYF.<br />
Convert two-byte integer in Y/A (range -32768 to +32767)<br />
to FLPT in FAC1.<br />
Entry point for routine to handle POS function; calls Kernal<br />
routine PLOT to fetch cursor position, <strong>the</strong>n loads it into FAC1<br />
using SNGET.<br />
Convert byte in Y to FLPT in FAC1 (0-255).<br />
Test that command was not entered in direct mode;<br />
CURLIN+1 ($3A) containing $FF indicates direct mode.<br />
7ILLEGAL DIRECT ERROR if it was. Called by routines that<br />
may not be used in direct mode (for example, GET).<br />
Entry point for routine to handle DEF statement; create func<br />
tion definition and find or set up dependent variable. When an<br />
FN is invoked, <strong>the</strong> pointer within CHRGET is set to <strong>the</strong> begin<br />
ning of <strong>the</strong> FN definition in <strong>the</strong> BASIC text and <strong>the</strong> expression<br />
found <strong>the</strong>re is evaluated; it is <strong>the</strong>n switched back. Information<br />
to enable it to do this is stored within <strong>the</strong> function variable set<br />
up in GETFNM.<br />
Check syntax of FN; find or set up variable with function name<br />
and set DEFPNT ($4E-$4F) to point to it (must be numeric, not<br />
string, variable).<br />
Evaluate function; evaluate expression within paren<strong>the</strong>ses in<br />
statement invoking function, leaving it in FAC1, <strong>the</strong>n evaluate<br />
<strong>the</strong> FN expression (see DEF).<br />
Entry point for routine to handle STR$ function; evaluate ex<br />
pression and convert to ASCII string.<br />
347
<strong>64</strong> ROM Guide<br />
STRINI<br />
STRLIT<br />
GETSPA<br />
GARBA2<br />
DVARS<br />
CAT<br />
MOVINS<br />
FRESTR<br />
FRETMS<br />
CHRD<br />
LEFTD<br />
RIGHTD<br />
MIDD<br />
PREAM<br />
LEN<br />
LEN1<br />
ASC<br />
GTBYTC<br />
VAL<br />
GETNUM<br />
GETADR<br />
PEEK<br />
POKE<br />
WAIT<br />
$B475/$D475<br />
$B487/$D487<br />
$B4F4/$D4F4<br />
$B526/$D526<br />
$B606/$D606<br />
$B63D/$D63D<br />
$B67A/$D67A<br />
$B6A3/$D6A3<br />
$B6DB/$D6DB<br />
$B6EC/$D6EC<br />
$B700/$D700<br />
$B72C/$D72C<br />
$B737/$D737<br />
$B761/$D761<br />
$B77C/$D77C<br />
$B782/$D782<br />
$B78B/$D78B<br />
$B79B/$D79B<br />
$B7AD/$D7AD<br />
$B7EB/$D7EB<br />
$B7F7/$D7F7<br />
$B80D/$D80D<br />
$B824/$D824<br />
$B82D/$D82D<br />
Make room in string space for a string to be inserted: A con<br />
tains length and FAC1+3-FAC1+4 points to <strong>the</strong> string. On<br />
exit, $61-$63 contains descriptor for new string. CHR$, LEFT$,<br />
and so on all use this routine.<br />
Copy a string into string space at top of memory; A/Y points<br />
to <strong>the</strong> start of <strong>the</strong> string. Scans for quotation mark ("), colon (:),<br />
or zero byte as terminator to determine length. Exit with<br />
descriptor in $61-$63.<br />
Allocate space for string, length in A, in dynamic string space<br />
at top of memory; do garbage collection if space exhausted.<br />
Called by STRINI.<br />
Do garbage collection; eliminate unwanted strings in string<br />
area and collect toge<strong>the</strong>r valid strings. <strong>The</strong> garbage collection<br />
routine is slow for large numbers of strings.<br />
Search variables and arrays for next string to be saved by gar<br />
bage collection.<br />
Concatenate two strings.<br />
Move string to string area high in RAM; entered with $6F-$70<br />
pointing at <strong>the</strong> descriptor of <strong>the</strong> string to be stored.<br />
Discard string; entered with pointer to string descriptor in<br />
FAC1+3-FAC1+4, exits with new string length and pointer in<br />
INDEX1.<br />
Clean <strong>the</strong> descriptor stack.<br />
Entry point for routine to handle CHR$ function; sets up a<br />
one-byte string.<br />
Entry point for routine to handle LEFT$.<br />
Entry point for routine to handle RIGHTS.<br />
Entry point for routine to handle MID$.<br />
Pull string descriptor pointer to $50-$51, length to A (also<br />
inX).<br />
Entry point for routine to handle LEN function; floating point<br />
value of string length parameter placed in FAC1.<br />
Extract length of string, put in Y, leave string mode, and enter<br />
numeric mode. Called by LEN, VAL.<br />
ASC function; get first character of string and convert to float<br />
ing point in FAC1. String of length 0 gives 7SYNTAX ERROR.<br />
Read and evaluate an expression from BASIC text; must eval<br />
uate to a one-byte value; value left in X and FAC1+4.<br />
Entry point for routine to handle VAL function; convert value<br />
to floating point value in FAC1.<br />
Read parameters for WAIT and POKE from BASIC text; put<br />
first (two-byte integer) in $14-$15, second in X.<br />
Convert FAC1 to two-byte integer (range 0-65535) in $14-$15<br />
and Y/A.<br />
Entry point for routine to handle PEEK function; on entry<br />
FAC1 contains address to be PEEKed in FLPT form; exit with<br />
PEEKed value in Y.<br />
Entry point for routine to handle POKE statement; fetch two<br />
parameters from BASIC text; do POKE.<br />
Entry point for routine to handle WAIT statement; fetch two<br />
parameters from text, plus optional third, which is 0 if none<br />
found; do WAIT loop.<br />
348
<strong>64</strong> ROM Guide<br />
FADDH<br />
FSUB<br />
FSUBT<br />
FADD<br />
FADDT<br />
COMPLT<br />
OVERR<br />
MULSHF<br />
FONE<br />
LOG<br />
FMULT<br />
FMULTT<br />
MLTPLY<br />
CONUPK<br />
MULDIV<br />
MUL10<br />
TENC<br />
DIV10<br />
FDIVF<br />
FDIV<br />
FDIVT<br />
MOVFM<br />
MOV2F<br />
MOV1F<br />
MOVVF<br />
MOVMF<br />
$B849/$D849<br />
$B850/$D850<br />
$B853/$D853<br />
$B867/$D867<br />
$B86F/$D86F<br />
$B947/$D947<br />
$B97E/$D97E<br />
$B983/$D983<br />
$B9BC/$D9BC<br />
$B9EA/$D9EA<br />
$BA28/$DA28<br />
$BA30/$DA30<br />
$BA59/$DA59<br />
$BA8C/$DA8C<br />
$BAB7/$DAB7<br />
$BAE2/$DAE2<br />
$BAF9/$DAF9<br />
$BAFE/$DAFE<br />
$BB07/$DB07<br />
$BB0F/$DB0F<br />
$BB14/$DB14<br />
$BBA2/$DAB2<br />
$BBC7/$DBC7<br />
$BBCA/$DBCA<br />
$BBD0/$DBD0<br />
$BBD4/$DBD4<br />
Add 0.5 to contents of FAC1; used when rounding.<br />
Floating point subtraction; FAC1 is replaced by MFLPT value<br />
pointed to by A/Y, minus FAC1.<br />
Entry point for routine to handle floating point subtraction;<br />
FAC1 is replaced by FAC2 minus FAC1.<br />
Floating point addition; FAC1 is replaced by MFLPT value<br />
pointed to by A/Y, plus FAC1.<br />
Entry point for routine to handle floating point addition; FAC1<br />
is replaced by FAC2, plus FAC1. On entry, A holds FACl's<br />
exponent (contents of $61) to speed <strong>the</strong> addition in <strong>the</strong> event<br />
that FAC1 contains 0.<br />
Replace FAC1 with twos complement of <strong>the</strong> value currently<br />
<strong>the</strong>re.<br />
Output 7OVERFLOW ERROR message, <strong>the</strong>n READY.<br />
Multiply by a byte.<br />
Table of constants in MFLPT format: first 1, <strong>the</strong>n constants for<br />
LOG evaluation; SQR(0.5), SQR(2), -0.5, and LOG(2).<br />
Entry point for routine to handle <strong>the</strong> LOG function; compute<br />
logarithm to <strong>the</strong> base e of FAC1.<br />
Floating point multiply; FAC1 is replaced by MFLPT value<br />
pointed to by A/Y times FAC1.<br />
Entry point for routine to handle floating point multiplication;<br />
FAC1 is replaced by FAC1 times FAC2.<br />
Multiply FAC1 by a byte and store in $26-$2A.<br />
Load FAC2 from MFPLT value pointed to by A/Y, unpacking<br />
sign bit and storing it separately, forming FLPT format. On<br />
exit, A holds FACl's first byte.<br />
Test floating point accumulators for multiply and divide; if<br />
FAC2 is 0, set FAC1 to 0; if exponents toge<strong>the</strong>r are too large<br />
<strong>the</strong>n 7OVERFLOW ERROR. If <strong>the</strong>y are too small, force <strong>the</strong> re<br />
sult to 0 without an underflow message.<br />
Multiply FAC1 by 10 and put result in FAC1.<br />
<strong>The</strong> value 10 in MFLPT format.<br />
Divide FAC1 by 10 and put result in FAC1.<br />
Floating point division; FAC1 is replaced by FAC2 divided by<br />
MFLPT value pointed at by A/Y; on entry, X contains sign of<br />
result.<br />
Floating point division; FAC1 is replaced by MFLPT divided by<br />
FAC1.<br />
Entry point for routine to handle floating point division; FAC1<br />
is replaced by FAC2 divided by FAC1. On entry, A holds<br />
FACl's first byte.<br />
Load FAC1 from MFLPT value pointed to by A/Y, unpacking<br />
sign bit and storing it separately, forming FLPT format.<br />
Convert FAC1 to MFLPT format and store at $5C-$60,<br />
TEMPFP2.<br />
Convert FAC1 to MFLPT format and store at $57-$5B,<br />
TEMPFP1.<br />
Convert FAC1 to MFLPT format and store at address pointed<br />
to by $49-$4A.<br />
Convert FAC1 to MFLPT format and store at address pointed<br />
to by A/Y .<br />
349
<strong>64</strong> ROM Guide<br />
MOVFA<br />
MOVAF<br />
ROUND<br />
SIGN<br />
SGN<br />
ABS<br />
FCOMP<br />
QINT<br />
INT<br />
FIN<br />
AADD<br />
STCONS<br />
INPRT<br />
LINPRT<br />
FOUT<br />
FOUTIM<br />
TICONS<br />
SQR<br />
FPWRT<br />
NEGOP<br />
EXCONS<br />
EXP<br />
POLYX<br />
RMULC<br />
RADDC<br />
$BBFC/$DBFC<br />
$BC0C/$DC0C<br />
$BC1B/$DC1B<br />
$BC2B/$DC2B<br />
$BC39/$DC39<br />
$BC58/$DC58<br />
$BC5B/$DC5B<br />
$BC9B/$DC9B<br />
$BCCC/$DCCC<br />
$BCF3/$DCF3<br />
conversion.<br />
$BD7E/$DD7E Add contents of A to FAC1.<br />
$BDB3/$DDB3 Three constants used in string conversions, in MFLPT form:<br />
99999999.9, 999999999, and 1000000000.<br />
$BDC2/$DDC2 Print IN followed by current line number in CURLIN<br />
($39-$3A).<br />
$BDCD/$DDCD Output integer in A/Y, range 0-65535.<br />
$BDDD/$DDDD Convert contents of FAC1 to ASCII string starting at location<br />
$100 and ending with zero byte. On exit, A/Y holds start ad<br />
dress, so STROUT can print string.<br />
$BE68/$DE68<br />
$BF11/$DF11<br />
$BF71/$DF71<br />
$BF7B/$DF7B<br />
$BFB4/$DFB4<br />
$BFBF/$DFBF<br />
$BFED/$DFED<br />
$E059/$E056<br />
$E08D/$E08A<br />
$E092/$E08F<br />
Copy FAC2 into FAC1.<br />
Round FAC1 by calling ROUND, <strong>the</strong>n copy into FAC2.<br />
Round FAC1.<br />
Get sign of FAC1; on exit A holds 0 if value is 0, 1 if value is<br />
positive, or $FF if value is negative.<br />
Entry point for routine to handle SGN function; calls SIGN,<br />
<strong>the</strong>n converts A into floating point form in FAC1.<br />
Entry point for routine to handle ABS function; replace FAC1<br />
with <strong>the</strong> absolute value of <strong>the</strong> current contents of FAC1.<br />
Compare FAC1 with MFLPT value pointed to by A/Y; on exit,<br />
A holds 0 if values were equal, 1 if FAC1>MFLPT, or $FF if<br />
FAC1
<strong>64</strong> ROM Guide<br />
RND<br />
RNDO<br />
QSETNR<br />
RND1<br />
RNDRNG<br />
BIOERR<br />
BGHOUT<br />
BCHIN<br />
BCKOUT<br />
BCKIN<br />
BGETIN<br />
SYS<br />
SAVET<br />
VERFYT<br />
LOADT<br />
LOADR<br />
LDFIN<br />
OPENT<br />
CLOSET<br />
SLPARA<br />
$E097/$E094<br />
$E09E/$E09B<br />
$EOBE/$EO66<br />
$E0D3/$E0D0<br />
$E0E3/$E0E0<br />
$E0F9/$E0F6<br />
$E10C/$E109<br />
$E112/$E10F<br />
$E118/$E115<br />
$E11E/$E11B<br />
$E124/$E121<br />
$E12A/$E127<br />
$E156/$E153<br />
$E165/$E162<br />
$E168/$E165<br />
$E16F/$E177<br />
$E195/$E195<br />
$E1BE/$E1BB<br />
$E1C7/$E1C4<br />
$E1D4/$E1D1<br />
Entry point for routine to handle RND function; set FAC1 to a<br />
number according to sign of FAC1 by branching to ei<strong>the</strong>r<br />
RNDO, QSETNR, or RND1.<br />
If FAC1=O, Joad FAC1 from VIA timer registers; a simple way<br />
of reseeding it with a random number.<br />
If FACl>0, load FACl with <strong>the</strong> result of multiplying <strong>the</strong> stored<br />
random number (in $88-$8C) generated by previous calls, by<br />
RMULC, and adding RADDC.<br />
If FACl
<strong>64</strong> ROM Guide<br />
COMBYT<br />
CMMERR<br />
OCPARA<br />
COS<br />
SIN<br />
TAN<br />
ATN<br />
BASSFT<br />
INIT<br />
CHRCPY<br />
INITCZ<br />
INITMS<br />
INITV<br />
CPATCH<br />
IOBASK<br />
SCRENK<br />
PLOTK<br />
CINT<br />
HOME<br />
INITVC<br />
$E2OO/$E1FD<br />
$E20E/$E20B<br />
$E219/$E216<br />
$E2<strong>64</strong>/$E261<br />
$E26B/$E268<br />
$E2B4/$E2B1<br />
$E2E0/$E2DD<br />
$E30E/$E30B<br />
$E33E/$E33B<br />
$E37B/$E467<br />
$E394/$E378<br />
$E3A2/$E387<br />
$E3BF/$E3A4<br />
$E422/$E404<br />
$E453/$E45B<br />
$E4DA<br />
$E500/$E500<br />
$E505/$E505<br />
$E50A/$E50A<br />
$E518/$E518<br />
$E566/$E581<br />
$E5A0/$E5C3<br />
Check for comma and evaluate <strong>the</strong> following one-byte param<br />
eter, which is put in X.<br />
Check for comma followed by anything o<strong>the</strong>r than end of<br />
statement; o<strong>the</strong>rwise, issue a 7SYNTAX ERROR message.<br />
Get parameters from BASIC text for OPEN or CLOSE calls; set<br />
defaults if not supplied.<br />
Entry point for routine to handle <strong>the</strong> COS function; <strong>the</strong> value<br />
in FAC1 is replaced by <strong>the</strong> cosine of that value.<br />
Entry point for routine to handle <strong>the</strong> SIN function; <strong>the</strong> value in<br />
FAC1 is replaced by <strong>the</strong> sine of that value.<br />
Entry point for routine to handle <strong>the</strong> TAN function; <strong>the</strong> value<br />
in FAC1 is replaced by <strong>the</strong> tangent of that value.<br />
Table of constants in MFLPT format: tt/2, tt*2, and 0.25. <strong>The</strong>n<br />
comes a counter value (5) and six MFLPT constants used in<br />
evaluating SIN, COS, and TAN.<br />
Entry point for routine to handle ATN; <strong>the</strong> value in FAC1 is<br />
replaced by <strong>the</strong> arctangent of that value.<br />
A counter value (11) and table of 12 constants in MFLPT for<br />
mat for ATN evaluation.<br />
BASIC warm start routine, entered on JMP ($A002); part of <strong>the</strong><br />
break sequence performed if BRK instruction encountered or<br />
RUN/STOP-RESTORE keys are pressed. Close all I/O I/O<br />
channels, initialize stack, output 7BREAK ERROR, and jump to<br />
READY.<br />
BASIC cold start routine, entered on JMP ($A000); part of <strong>the</strong><br />
reset sequence. Performs INITV, INITCZ, INITMS; sets stack<br />
and jumps to READY.<br />
CHRGET routine and RND seed in ROM for relocation into<br />
RAM.<br />
Initialize USR jump instruction and default vector, vectors from<br />
$003 to $006; transfer CHRGET and RND seed to RAM; call<br />
Kernal routines MEIvfBOT and MEMTOP to set start-of-BASIC<br />
and top-of-memory pointers ($2B-$2C and $37-$38) from <strong>the</strong><br />
pointers at $282-$285 initialized on power-up. Set end-ofprogram<br />
zero byte at 2048.<br />
Output start-up message: **** CBM BASIC V2 ****, <strong>the</strong>n num<br />
ber of free bytes, <strong>the</strong>n BYTES FREE.<br />
Initialize vectors for ERROR, MAIN, etc., at $0300-$030B.<br />
Patch to diminish screen sparkle; called from $EA0B (used by<br />
CLR). <strong>Commodore</strong> <strong>64</strong> only.<br />
Returns base address of CIA in X/Y (used by SCNKEY).<br />
Returns screen columns (40) in X, lines (25) in Y.<br />
Set/Read cursor row (X), column (Y).<br />
General screen and VIC chip initialization; set up screen<br />
editing tables at $D9-$F2, initialize VIC chip, set character<br />
color to light blue, do CLR and HOME, reset default I/O de<br />
vice numbers at $99 and $9A.<br />
<strong>Home</strong> <strong>the</strong> cursor.<br />
Initialize <strong>the</strong> VIC chip from table of values at $ECB9-$ECE6<br />
(international variations).<br />
352
<strong>64</strong> ROM Guide<br />
GETKBC<br />
INPPRO<br />
QTSWC<br />
PRT<br />
CHKCOL<br />
COLTAB<br />
SCROL<br />
DSPP<br />
KEY<br />
KBDTBL<br />
VICINT<br />
LDRUN<br />
RSTRAB<br />
SPMSG<br />
$E5B4/$E5CF<br />
$E5CA/$E5E5<br />
$E684/$E6B8<br />
$E716/$E742<br />
$E8CB/$E912<br />
$E8DA/$E921<br />
$E8EA/$E975<br />
$EA13/$EAA1<br />
$EA31/$EABF<br />
$EB81/$EC46<br />
$ECB9/$EDE4<br />
$ECE7/$EDF4<br />
$EEBB/$EFA3<br />
$EF2E/$F09F<br />
$F014/$F0ED<br />
$F086/$F14F<br />
$F0BD/$F17F<br />
$F12B/$F1E2<br />
$F179/$F230<br />
$F1DD/$F29O<br />
$F38B/$F44B<br />
$F3D5/$F495<br />
Get character from keyboard queue and move remaining<br />
characters along; queue must contain at least one character on<br />
entry (number of characters in queue is stored in $C6). On exit,<br />
<strong>the</strong> character is in A.<br />
Input and process SHIFT-RUN/STOP, RETURN, etc.<br />
Flip quotes flag ($D4) if A contains quotes on entry.<br />
Print character in A to screen, like PRINT CHR$; handles such<br />
characters as home cursor, clear screen, delete, etc.<br />
Test A for character color code; change color in $286 if one is<br />
found.<br />
Table of color-change codes, arranged Black, White, Red, Cyan,<br />
etc.<br />
Scroll screen up. If <strong>the</strong> top line is more than 40 characters long,<br />
<strong>the</strong> routine scrolls up appropriate number of lines to com<br />
pletely remove it. <strong>The</strong> CTRL key is tested for by directly<br />
interrogating <strong>the</strong> CIA chip, and a slight delay is performed if it<br />
is held down.<br />
Put <strong>the</strong> character in A onto <strong>the</strong> screen at <strong>the</strong> current cursor po<br />
sition; no checking for control characters, etc., is performed.<br />
<strong>The</strong> color for <strong>the</strong> character is held in X.<br />
Interrupt servicing routine: All IRQ interrupts are processed by<br />
this routine unless <strong>the</strong> vector in $0314-$0315 has been altered.<br />
<strong>The</strong> functions of KEY are to update <strong>the</strong> clock and location $91<br />
using Kernal routine UDTIM, maintain flashing cursor if cursor<br />
is enabled (see $CC-$CF), set <strong>the</strong> cassette motor on or off<br />
according to <strong>the</strong> flags at $C0, and test <strong>the</strong> keyboard for new<br />
character using Kernal routine SCNKEY. Finally, <strong>the</strong> interrupt<br />
register at $DC0D in <strong>the</strong> CIA is cleared; <strong>the</strong> A, X, and Y reg<br />
isters are pulled from <strong>the</strong> stack and restored; and a return from<br />
interrupt instruction (RTI) continues processing <strong>the</strong> main<br />
program.<br />
Tables to convert keyboard matrix values to CBM ASCII<br />
values.<br />
Table of values from which VIC chip is initialized (<strong>the</strong> exact<br />
values vary internationally, depending on <strong>the</strong> local television<br />
standards).<br />
<strong>The</strong> characters LOAD RUN , trans<br />
ferred to <strong>the</strong> keyboard queue when SHIFT-RUN/STOP is<br />
pressed.<br />
Part of <strong>the</strong> routine used by NMI when servicing RS-232<br />
output.<br />
Flag RS-232 errors into ST byte.<br />
Output RS-232 character.<br />
Get RS-232 character.<br />
Tape messages.<br />
Output Kernal message from table starting at $F0BD if flag at<br />
$9D is set.<br />
Get character from tape.<br />
Output character to tape.<br />
Open tape file.<br />
Open serial device (printer, disk) file.<br />
353
<strong>64</strong> ROM Guide<br />
FAH<br />
READ<br />
WRITE<br />
START<br />
RAMTAS<br />
IOINIT<br />
NMI<br />
PULS<br />
$F409/$F4C7<br />
$F4BF/$F563<br />
$F539/$F5D1<br />
$F5FA/$F692<br />
$F65F/$F6F8<br />
$F6FB/$F77E<br />
$F72C/$F7AF<br />
$F76A/$F7EF<br />
$F7EA/$F867<br />
$F84A/$F8C9<br />
$F867/$F8E6<br />
$F92C/$F98E<br />
$FBA6/$FBEA<br />
$FCE2/$FD22<br />
$FD50/$FD8D<br />
$FDA3/$FDF9<br />
$FE43/$FEA9<br />
$FEC2/$FF5C<br />
$FF48/$FF72<br />
Open RS-232 file.<br />
Load from disk.<br />
Load from tape.<br />
Save to disk.<br />
Save to tape.<br />
Table of I/O error numbers (1-9) and messages.<br />
Load next tape header.<br />
Write tape header.<br />
Load named tape header.<br />
Load tape.<br />
Write tape.<br />
Routines for tape reading.<br />
Routines for tape writing.<br />
Reset routine; entered from <strong>the</strong> 6510/6502 RESET vector at<br />
$FFFC. If a ROM cartridge is present, JMP ($8000) runs it.<br />
O<strong>the</strong>rwise, <strong>the</strong> routine calls RAMTAS, RESTOR, IOINIT,<br />
CINT, and NEW. Note that all o<strong>the</strong>r RAM is unaltered, so<br />
BASIC programs can be recovered after reset.<br />
Fill low RAM (except for <strong>the</strong> stack area) with zeros, find <strong>the</strong><br />
start and end of contiguous RAM for BASIC, and set <strong>the</strong><br />
appropriate screen position according to <strong>the</strong> amount of mem<br />
ory present.<br />
Initialize CIA chips on power-up.<br />
NMI routine; entered from <strong>the</strong> 6510/6502 NMI vector at<br />
$FFFA. <strong>The</strong> JMP ($318) at $FE44 routes control back to $FE47;<br />
altering this vector is one way to modify RUN/STOP-<br />
RESTORE. If <strong>the</strong> RUN/STOP key is down, Kernal routines<br />
RESTOR, IOINIT, and CINT are called, and a warm start of<br />
BASIC is performed by doing a JMP ($A002). This sequence is<br />
also performed on BRK. O<strong>the</strong>rwise, <strong>the</strong> interrupt is <strong>the</strong> result of<br />
RS-232 activity.<br />
RS-232 baud rate table (22 bytes; varies internationally).<br />
IRQ or BRK routine; entered from <strong>the</strong> 6510/6502 IRQ vector at<br />
$FFFE. Save <strong>the</strong> contents of A, X, and Y on <strong>the</strong> stack, and<br />
examine <strong>the</strong> status register already pushed onto <strong>the</strong> stack to<br />
determine whe<strong>the</strong>r a hardware IRQ interrupt or <strong>the</strong> execution<br />
of a BRK instruction occurred. If it was a standard IRQ inter<br />
rupt, perform JMP ($314), usually to KEY at $EA31; if it was a<br />
BRK operation, JMP ($316), usually to part of <strong>the</strong> NMI se<br />
quence at $FE66, which resets chips and restarts BASIC.<br />
Kernal Jump Table Routines<br />
In <strong>Commodore</strong> computers, <strong>the</strong> uppermost half-page of ROM contains a very im<br />
portant collection of vectors known as <strong>the</strong> Kernal jump table. Each three-byte table<br />
entry consists of a JMP instruction and a two-byte address. <strong>The</strong> JMP may be ei<strong>the</strong>r a<br />
direct JMP to an absolute address in ROM, or an indirect JMP through a RAM vec<br />
tor, such as those in locations $314-$333. <strong>The</strong> significance of <strong>the</strong> table is that <strong>the</strong><br />
location of table entries should remain fixed regardless of future revisions of <strong>the</strong><br />
ROM routines.<br />
354
<strong>64</strong> ROM Guide<br />
For example, if you use JMP $FFD2, <strong>the</strong> jump table entry for <strong>the</strong> CHROUT rou<br />
tine, you could have some assurance that your program would still work on future<br />
<strong>64</strong>s; moreover, that jump table entry would also work on <strong>the</strong> <strong>Commodore</strong> VIC-20<br />
and PET/CBM computers. On <strong>the</strong> <strong>64</strong>, JMP $FFD2 arrives at $F1CA via an indirect<br />
jump through <strong>the</strong> RAM vector in locations $326-$327.<br />
<strong>The</strong> <strong>64</strong>'s Kernal jump table begins at location $FF81. Note that <strong>the</strong> table entries<br />
have <strong>the</strong>ir own labels, which may be different from <strong>the</strong> labels of <strong>the</strong> routines <strong>the</strong>y<br />
point to.<br />
Label<br />
PCINT<br />
IOINIT<br />
RAMTAS<br />
RESTOR<br />
VECTOR<br />
SETMSG<br />
<strong>64</strong>/VIC<br />
$FF5B/$E518<br />
$FDA3/$FDF9<br />
$FD50/$FD8D<br />
$FD15/$FD52<br />
$FD1A/$FD57<br />
$FE18/$FE66<br />
Jump Table<br />
Entry<br />
$FF81 CINT<br />
$FF84 IOINIT<br />
$FF87 RAMTAS<br />
$FF8A RESTOR<br />
SFF8D VECTOR<br />
SECNDK $EDB9/$EEC0 $FF93 SECOND<br />
TKSAK $EDC7/$EECE $FF96 TKSA<br />
MEMTOP<br />
MEMBOT<br />
SCNKK<br />
SETTMO<br />
ACPTRK<br />
CIOUTK<br />
UNTLKK<br />
UNLSNK<br />
LISTNK<br />
TALKK<br />
JIEADSS<br />
SETLFS<br />
SETNAM<br />
NOPEN<br />
NCLOSE<br />
NCHKIN<br />
$FE25/$FE73<br />
$FE34/$FE82<br />
$EA87/$EB1E<br />
$FE21/$FE6F<br />
$EE13/$EF19<br />
$EDDD/$EEE4<br />
$EDEF/$EEF6<br />
$EDFE/$EF04<br />
$ED0C/$EE17<br />
$ED09/$EE14<br />
$FE07/$FE57<br />
$FE00/$FE50<br />
$FDF9/$FE49<br />
$F34A/$F40A<br />
$F291/$F34A<br />
$F20E/$F2C7<br />
$FF99 MEMTOP<br />
$FF9C MEMBOT<br />
$FF9F SCNKEY<br />
$FFA2 SETTMO<br />
$FFA5 ACPTR<br />
$FFA8 CIOUT<br />
$FFAB UNTALK<br />
$FFAE UNLSN<br />
$FFB1 LISTN<br />
$FFB4 TALK<br />
$FFB7 READST<br />
$FFBA SETLFS<br />
SFFBD SETNAM<br />
$FFC0 OPEN<br />
$FFC3 CLOSE<br />
$FFC6 CHKIN<br />
Descriptions<br />
Initialize screen editor and video chip, set<br />
interrupt frequency.<br />
Initialize input/output chips.<br />
Test and initialize RAM.<br />
Restore standard input/output vectors.<br />
Store/set input/output vectors.<br />
Enable/disable Kernal control message out<br />
put to screen.<br />
Send secondary address for LISTEN com<br />
mand on serial bus; LISTEN must be called<br />
before using this routine.<br />
Send secondary address for TALK com<br />
mand on serial bus; TALK must be called<br />
before using this routine.<br />
Read/set BASIC top-of-memory limit.<br />
Read/set BASIC bottom-of-memory limit.<br />
Scan keyboard.<br />
Set serial bus time-out.<br />
Get a byte from a serial device (usually<br />
disk).<br />
Output a byte to a serial device (usually a<br />
printer or disk).<br />
Send an UNTALK command to devices on<br />
<strong>the</strong> serial bus.<br />
Send an UNLISTEN command to devices<br />
on <strong>the</strong> serial bus.<br />
Cause a device on <strong>the</strong> serial bus (usually a<br />
printer or disk) to listen.<br />
Cause a device on <strong>the</strong> serial bus (usually a<br />
disk drive) to talk.<br />
Read status byte into A.<br />
Set file number, device number, and<br />
secondary address.<br />
Set filename.<br />
Open a file for reading or writing. Uses<br />
RAM vector at $031A.<br />
Close a file. Uses RAM vector at $031C.<br />
Prepare a file for input. Uses RAM vector at<br />
$031E.<br />
355
<strong>64</strong> ROM Guide<br />
NCKOUT<br />
NCLRCH<br />
NBASIN<br />
NBSOUT<br />
LOADSP<br />
SAVESP<br />
SETTMK<br />
RDTIMK<br />
NSTOP<br />
NGETIN<br />
NCLALL<br />
UDTIMK<br />
SCRENK<br />
PLOTK<br />
$F250/$F309<br />
$F333/$F3F3<br />
$F157/$F20E<br />
$F1CA/$F27A<br />
$F49E/$F542<br />
$F5DD/$F675<br />
$F6E4/$F767<br />
$F6DD/$F760<br />
$F6ED/$F770<br />
$F13E/$F1F5<br />
$F32F/$F3EF<br />
$F69B/$F734<br />
$E505/$E505<br />
$E50A/$E50A<br />
$FFC9 CHKOUT<br />
$FFCC CLRCHN<br />
$FFCF CHRIN<br />
$FFD2 CHROUT<br />
$FFD5 LOAD<br />
SFFD8 SAVE<br />
$FFDB SETTIM<br />
$FFDE RDTIM<br />
$FFE1 STOP<br />
$FFE4 GETIN<br />
$FFE7 CLALL<br />
$FFEA UDTIM<br />
$FFED SCREEN<br />
$FFF0 PLOT<br />
Prepare a file for output. Uses RAM vector<br />
at $0320.<br />
Restore default I/O devices. Uses RAM vec<br />
tor at $0322.<br />
Get a character from <strong>the</strong> designated input<br />
device. Uses RAM vector at $0324.<br />
Send a character to <strong>the</strong> designated output<br />
device. Uses RAM vector at $0326.<br />
Load data into memory from disk or tape.<br />
Save memory block to disk or tape.<br />
Set TI clock.<br />
Read TI clock.<br />
Test whe<strong>the</strong>r RUN/STOP key is pressed.<br />
Uses RAM vector at $0328.<br />
Get a character, usually from <strong>the</strong> keyboard.<br />
Uses RAM vector at $032A.<br />
Abort all I/O and close all files. Uses RAM<br />
vector at $032C.<br />
Add 1 to TI clock; reset to 0 if <strong>the</strong> count<br />
reaches 240000.<br />
Return <strong>the</strong> maximum number of screen col<br />
umns and rows in X and Y (40 and 25,<br />
respectively, for <strong>the</strong> <strong>Commodore</strong> <strong>64</strong>).<br />
Move <strong>the</strong> cursor to a specified row and col<br />
umn, or read <strong>the</strong> current row and column<br />
position of <strong>the</strong> cursor.<br />
IOBASK<br />
$E500/$E500<br />
$FFF3 IOBASE<br />
Find <strong>the</strong> starting address of <strong>the</strong> keyboard<br />
CIA chip registers.<br />
6510/6502 Hardware Vectors<br />
<strong>The</strong> 6510/6502 microprocessor chip reserves <strong>the</strong> highest six bytes of <strong>the</strong> address<br />
space (locations $FFFA-$FFFF) for use as vectors. <strong>The</strong>se three vectors point to<br />
routines that handle processing under three special sets of circumstances. <strong>The</strong> chip<br />
automatically causes a JMP through one of <strong>the</strong>se vectors when external hardware<br />
sends a signal on <strong>the</strong> 6510/6502's NMI, RESET, or IRQ lines.<br />
<strong>The</strong> list below shows <strong>the</strong> label of <strong>the</strong> vector, <strong>the</strong> address of <strong>the</strong> first byte of <strong>the</strong><br />
vector, and <strong>the</strong> address to which <strong>the</strong> vector points in <strong>the</strong> <strong>64</strong> and VIC.<br />
Label<br />
NMI<br />
RESET<br />
IRQ<br />
Vector<br />
$FFFA<br />
$FFFC<br />
$FFFE<br />
<strong>64</strong>/VIC<br />
$FE43/$FEA9<br />
$FCE2/$FD22<br />
$FF48/$FF72<br />
Descriptions<br />
When <strong>the</strong> 6510/6502 receives an NMI (Non-Maskable<br />
Interrupt) signal, it causes a jump to <strong>the</strong> address held<br />
here.<br />
When <strong>the</strong> 6510/6502 receives a RESET signal, it causes a<br />
jump to <strong>the</strong> address held here.<br />
When <strong>the</strong> 6510/6502 receives an IRQ (Interrupt Re-<br />
Quest) signal or processes a machine language BRK<br />
instruction, it causes a jump to <strong>the</strong> address held here.<br />
356
Chapter 12<br />
Graphics<br />
Graphics with BASIC<br />
Graphics with Machine Language<br />
<strong>The</strong> VIC-II Chip<br />
User-Defined Characters<br />
Bitmapped Graphics<br />
Sprites
Chapter 12<br />
Graphics<br />
This chapter starts with <strong>the</strong> simplest types of graphics using only ordinary BASIC<br />
and progresses to full-screen graphics and motion. All <strong>the</strong> special graphics effects of<br />
<strong>the</strong> <strong>64</strong> are covered.<br />
Graphics with<br />
Basic<br />
Effective graphics can be obtained with ordinary BASIC. Before going into program<br />
ming details, here's a look at <strong>the</strong> way <strong>the</strong> <strong>64</strong> stores its standard characters.<br />
Each character is made up of 8 dots by 8 dots on <strong>the</strong> screen. (<strong>Commodore</strong>'s<br />
printers use different dot layouts and cannot easily give an identical copy of <strong>the</strong><br />
screen—apart from <strong>the</strong> difficulty with color.) <strong>The</strong> actual pattern of dots making up<br />
each character is stored in ROM at $D000 (53248) to $DFFF (57343), a total of 4K<br />
bytes of memory. <strong>The</strong>re are four subdivisions of this ROM:<br />
$D000-$D3FF Uppercase plus extended graphics<br />
$D400-$D7FF Reversed uppercase plus extended graphics<br />
$D800-$DBFF Lowercase with uppercase and some graphics<br />
$DC00-$DFFF Reversed lowercase with uppercase and some graphics<br />
This is where <strong>the</strong> character ROM is placed from <strong>the</strong> point of view of <strong>the</strong> 6510;<br />
as you'll see in <strong>the</strong> section on user-defined graphics, <strong>the</strong> VIC-II chip is wired to<br />
"see" <strong>the</strong> character ROM elsewhere in memory.<br />
Each byte is made up of eight bits, which correspond neatly to a single row of<br />
dots in a character definition. So every character takes eight bytes to define. For ex<br />
ample, <strong>the</strong> first definition, for <strong>the</strong> @ sign, from 53248, is stored like this:<br />
Bit Equivalent Which<br />
Byte Value Defines <strong>the</strong> Character<br />
28 ($1C) 00011100<br />
34 ($22) 00100010<br />
74 ($4A) 01001010<br />
86 ($56) 01010110<br />
76 ($4C) 01001100<br />
32 ($20) 00100000<br />
30 ($1E) 00011110<br />
0 ($00) 00000000<br />
Each 1 in <strong>the</strong> character definition appears on <strong>the</strong> screen as <strong>the</strong> foreground color<br />
and each 0 as <strong>the</strong> background color. Elsewhere in <strong>the</strong> ROM, similar patterns appear.<br />
<strong>The</strong> eight bytes from 54272, for example, correspond to <strong>the</strong> reverse-video @ and<br />
have exactly <strong>the</strong> opposite bit pattern; dots which were foreground with @ are now<br />
background and vice versa.<br />
Since <strong>the</strong> total amount of memory dedicated to character definitions is 4K, it<br />
provides room for 4096/8 = 512 characters. Because <strong>the</strong> screen memory area holds<br />
only ordinary bytes, it is possible for each position in <strong>the</strong> screen RAM to select only<br />
1 of 256 possibilities; thus, 256 characters at most can be displayed simultaneously.<br />
In BASIC, much of <strong>the</strong> screen usually contains spaces, of course, so <strong>the</strong> display is<br />
made from some of <strong>the</strong> 254 nonspace characters. <strong>The</strong>re are two distinct sets of<br />
359
Graphics<br />
characters; SHIFT-<strong>Commodore</strong> key switches between <strong>the</strong>m by directing <strong>the</strong> VIC-II to<br />
fetch its character definitions ei<strong>the</strong>r from $1000, which is set when <strong>the</strong> <strong>64</strong> is<br />
switched on and is <strong>the</strong> uppercase mode, or from $1800, lowercase mode. Lowercase<br />
can be selected by PRINT CHR$(14), and uppercase by PRINT CHR$(142).<br />
<strong>The</strong> two sets are identical to those in <strong>Commodore</strong>'s o<strong>the</strong>r machines; <strong>the</strong> idea is<br />
that one can be used for word-processing applications, where <strong>the</strong> distinction between<br />
capital and lowercase letters is important, and <strong>the</strong> o<strong>the</strong>r can be used for pictorial<br />
applications, for example, using playing-card symbols. Because of this <strong>the</strong>y are often<br />
called text and uppercase/graphics modes.<br />
This simple program displays all 256 characters of ei<strong>the</strong>r mode in white at <strong>the</strong><br />
top of <strong>the</strong> screen. Press SHIFT-<strong>Commodore</strong> key to flip between modes; note how<br />
many characters are present in both modes:<br />
10 FOR J=0 TO 255: REM 256 CHARACTERS NEED 256 SCREEN LOCATIONS<br />
20 POKE 1024+J, J: REM SCREEN STARTS AT 1024: POKE 0,1,2 ETC<br />
30 POKE 55296+J,l: REM SET COLOR RAM TO WHITE<br />
40 NEXT<br />
Tables of <strong>the</strong>se characters are available for reference in <strong>the</strong> Appendices. Apart<br />
from space and SHIFT-space, which PEEK as 32 and 96, <strong>the</strong>re is no duplication of<br />
character definitions. <strong>The</strong>re is a ra<strong>the</strong>r confusing distinction between characters as<br />
<strong>the</strong>y are POKEd into <strong>the</strong> screen (Appendix I) and character codes that are printed<br />
(Appendix H). PRINT translates many characters in special ways—changing color,<br />
clearing <strong>the</strong> screen, moving <strong>the</strong> cursor up and down or to <strong>the</strong> top of <strong>the</strong> screen, and<br />
so on. Some, like RETURN, are fairly standard, while o<strong>the</strong>rs are peculiar to <strong>the</strong> <strong>64</strong>.<br />
Appendix G lists <strong>the</strong> control functions associated with certain ASCII codes. True<br />
ASCII reserves <strong>the</strong> first 32 character codes for control information, and <strong>Commodore</strong><br />
has borrowed this idea. <strong>The</strong> displayed characters corresponding to PRINTed codes<br />
are in fact closer to true ASCII than is <strong>the</strong> case in earlier CBM machines, so conver<br />
sion to true ASCII is easier. However, <strong>the</strong> upper- and lowercase alphabets are inter<br />
changed in relation to true ASCII.<br />
Only some of <strong>the</strong> 256 screen characters can be displayed by using statements of<br />
<strong>the</strong> form PRINT CHR$(N). Since some CHR$ codes are for control purposes, like<br />
cursor-move commands, <strong>the</strong>re are only 128 ordinary printing characters; all are<br />
obtainable by typing key combinations on <strong>the</strong> keyboard. <strong>The</strong> reverse feature allows<br />
any of <strong>the</strong> 256 screen characters to be displayed using PRINT; <strong>the</strong> ordinary character<br />
is preceded by a {RVS} character.<br />
Within both blocks of 256 characters, reverse characters are arranged in step<br />
with <strong>the</strong> unreverse characters, but displaced by 128. An easy way to reverse charac<br />
ters in <strong>the</strong> screen RAM is to set bit 7, or in BASIC terms, add 128 (or, OR 128). Try<br />
POKE 55296,1 (to set <strong>the</strong> color RAM location for <strong>the</strong> top left of <strong>the</strong> screen) <strong>the</strong>n<br />
POKE 1024,128. <strong>The</strong> fact that this flag or {RVS} is necessary to print a complete<br />
graphics set can be irritating if you have laboriously designed a large graphic display<br />
on <strong>the</strong> screen. It is impossible to save reverse characters in strings by inserting a line<br />
number and quotation marks before <strong>the</strong> characters, and <strong>the</strong>n pressing RETURN In<br />
stead, <strong>the</strong> strings need embedded {RVS} and {OFF} characters to flip between<br />
modes. Block saving of <strong>the</strong> relevant part of memory may be best. See Chapter 6.<br />
<strong>The</strong>re is no simple translation between unSHIFTed and SHIFTed keys but<br />
usually setting bit 6 of <strong>the</strong> screen code to 1 displays <strong>the</strong> SHIFTed version. In BASIC<br />
360
Graphics<br />
terms, add <strong>64</strong> (or, OR with <strong>64</strong>). Try POKE 55296,1: POKE 55297,1 to set <strong>the</strong> color<br />
RAM, <strong>the</strong>n POKE 1024,1, and POKE 1025,65 in lowercase mode.<br />
Note that <strong>the</strong> pairs of characters on <strong>the</strong> front right of most keys apply only in<br />
uppercase/graphics mode, <strong>the</strong> mode selected when <strong>the</strong> machine is switched on.<br />
After SHIFT-<strong>Commodore</strong> key puts <strong>the</strong> machine into lowercase, only <strong>the</strong> left-hand<br />
graphics symbol can be displayed on <strong>the</strong> screen, and a SHIFTed key gives <strong>the</strong> upper<br />
case version, except for a few keys with no SHIFTed version, like @ and *. So <strong>the</strong><br />
right-hand set of graphics is unobtainable in lowercase mode. Fortunately, some very<br />
useful graphics are retained; for example, boxes can be ruled on <strong>the</strong> screen, in ei<strong>the</strong>r<br />
mode, using <strong>Commodore</strong> key-A, <strong>Commodore</strong> key-S, <strong>Commodore</strong> key-Z, Com<br />
modore key-X, SHIFT-* and SHIFT-.<br />
Toggling between <strong>the</strong> two modes with SHIFT-<strong>Commodore</strong> key can be disabled<br />
by PRINT CHR$(8) and reenabled with PRINT CHR$(9), or with POKE 657,128 and<br />
POKE 657,0 which set <strong>the</strong> relevant flags. If programs with user-defined characters<br />
fail to disable this toggle, SHIFT-<strong>Commodore</strong> key can produce odd results as charac<br />
ter definitions are looked for in a region $800 bytes away from that intended by <strong>the</strong><br />
programmer.<br />
Some graphics symbols are missing from <strong>the</strong> keys. Thirty-one keys have a pair<br />
of symbols, making 62. Adding pi and SHIFT-space gives <strong>64</strong> graphics characters. But<br />
four characters, only accessible in lowercase mode, also exist and are listed in <strong>the</strong><br />
cross-reference table of graphics: <strong>the</strong>y are <strong>Commodore</strong> key-up arrow (checkerboard<br />
characters), <strong>Commodore</strong> key-* and SHIFT-E (sloping diagonal lines), and SHIFT-@<br />
(a square root or check mark).<br />
Printing BASIC Graphics<br />
This is certainly <strong>the</strong> easiest way to produce graphics effects. First, though, let's exam<br />
ine what PRINT actually does. PRINT has to interpret <strong>the</strong> information it's given and,<br />
in <strong>the</strong> case of printing characters, convert <strong>the</strong>m into POKEs into <strong>the</strong> correct part of<br />
screen and color RAM. In <strong>the</strong> case of special, nonprinting characters, PRINT per<br />
forms operations like selecting uppercase mode, changing foreground color, moving<br />
<strong>the</strong> cursor around on <strong>the</strong> screen, and so on. This is complicated and relatively slow.<br />
It uses memory locations to store current color (<strong>64</strong>6/$286), status of <strong>the</strong> reverse flag<br />
(199/$C7), and <strong>the</strong> position on <strong>the</strong> screen at which <strong>the</strong> next character is to be<br />
printed (row is 214/$D6, column is 211/$D3), among o<strong>the</strong>r things. Try POKE <strong>64</strong>6,7.<br />
<strong>The</strong> <strong>64</strong> now prints in yellow, as though you'd typed PRINT "{YEL}" or CTRL-YEL.<br />
POKE 199,1: PRINT "HELLO" prints HELLO in reverse. <strong>The</strong> reverse flag is, how<br />
ever, turned off when RETURN is printed.<br />
PRINT uses <strong>the</strong> Kernal output routine $FFD2 to put characters in <strong>the</strong> screen. Try<br />
POKE 780,65 : SYS 65490. This uses $FFD2 and has <strong>the</strong> same effect as PRINT<br />
CHR$(65). ML programmers can trace <strong>the</strong> routine to $E716; program control goes to<br />
$E7D4 for characters above 128, while characters from 0 to 127 are processed from<br />
$E72A. Comparison instructions look for RETURN, space, SHIFT-space, and so on.<br />
<strong>The</strong> actual routine which POKEs <strong>the</strong> character is at $EA1C. <strong>The</strong> accumulator holds<br />
<strong>the</strong> character and <strong>the</strong> X register holds its color code.<br />
Examples using PRINT. Programmers unused to <strong>the</strong> graphics set, or looking<br />
for new ideas on graphics, could experiment with a short program like Program 12-<br />
1, which fills <strong>the</strong> screen with repeats of whatever string is entered:<br />
361
Graphics<br />
Program 12-1. Simple Print Demo<br />
10 INPUT "GRAPHICS";G$<br />
20 PRINT "{CLR}";<br />
30 PRINT G$;:IF PEEK(214) = " " AND C$
Graphics<br />
Program 12-4. Froggie Graphics<br />
10 POKE 53281,0:POKE 53280,0:PRINT "{WHT}{CLR}"<br />
20 A$="{7 SPACES}11<br />
30 B$=A$+ " Q "+A$+"A"+A$+"Z"+A$+"S"+A$+"X "<br />
40 B$=B$+B$+B$+B$<br />
100 FOR J=l TO 40<br />
110 D1$=MID$(B$,J,40)<br />
120 D2$=MID$(B$,80-J,40)<br />
130 D3$=MID$(B$,J+20,40)<br />
140 D4$=MID$(B$,100-J,40)<br />
150 PRINT "{HOME}H+D1$+"{HOME}{3 DOWN}H+D2$+M<br />
{HOME}{6 DOWN}"+D3$+"{HOME}{9 D0WN}"+D4$<br />
160 NEXT:FOR D=l TO 50:NEXT:GOTO 100<br />
Lines 110-140 extract <strong>the</strong> substrings from a different position in B$ with each<br />
pass through <strong>the</strong> loop; each substring is like a window moving along <strong>the</strong> string. Line<br />
150 prints <strong>the</strong>m, first positioning <strong>the</strong> cursor at <strong>the</strong> top-left position of <strong>the</strong> screen and<br />
spacing <strong>the</strong>m out vertically using {DOWN} characters. B$ contains a repeating cycle<br />
of 40 characters (graphics characters separated by spaces) to produce continuity in<br />
<strong>the</strong> movement of <strong>the</strong> graphics characters.<br />
In Program 12-5, colors are randomly selected and mirrored, using string arrays,<br />
to give an attractive symmetry when used to color reverse spaces (see line 60). Each<br />
display takes about 15 seconds to generate. Because of <strong>the</strong> large number of strings<br />
and despite <strong>the</strong> fact that most are very short, <strong>the</strong>re is a potential problem with gar<br />
bage collection. <strong>The</strong> CLR in line 70 discards all <strong>the</strong> strings that have just been used,<br />
allowing <strong>the</strong> next display to be constructed with a completely uncluttered string<br />
memory, and avoids <strong>the</strong> problem.<br />
Program 12-5. Kaleidoscope<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
5 POKE 53281,0:POKE 53280,0:PRINT"{CLR}" :rem 40<br />
10 DIM M$(38,19),A$(19),RN$(38) :rem 246<br />
15 CO$="{BLK}{WHT}{RED}{CYN}{PUR}{GRN}{BLU}{YEL}<br />
Bl3g23g33E4lB5§B63B73E8i" :rem 13<br />
20 FOR J=0 TO 38:RN$(J)=MID$(CO$#RND(1)*16+1,1)+"<br />
{SPACE}11: NEXT :rem 173<br />
25 FOR J=8 TO 19:FOR K=0 TO J:C$=RN$(J-K) :rein 149<br />
30 M$(K,J)=C$:M$(J,K)=C$ :rem 235<br />
35 M$(38-J,K)=C$:M$(38-K,J)=C$ :rem 32<br />
40 NEXT:NEXT :rem 28<br />
45 FOR J=8 TO 19:A$( J) = fl" :FOR K=0 TO 38 : rem 190<br />
50 A$(J)=A$(J)+M$(K,J) :rem 80<br />
55 NEXT:NEXT :rem 34<br />
60 PRINT "{HOME}":FOR J=8 TO 19:PRINT "{RVS}" A$(J<br />
):NEXT :rem 205<br />
65 FOR J=18 TO 8 STEP-1:PRINT "{RVS}" A$(J):NEXT<br />
:rem 77<br />
70 CLR:GOTO 10 :rem 28<br />
363
Graphics<br />
POKEing BASIC Graphics<br />
PRINTing to <strong>the</strong> screen involves <strong>the</strong> <strong>64</strong> in time-consuming calculations, but<br />
POKEing to <strong>the</strong> screen can be even slower. POKE itself is not a fast command in<br />
BASIC. Machine language "pokes" that write characters directly to <strong>the</strong> screen RAM<br />
are much faster than BASIC ones, because <strong>the</strong> 6510 processor has fundamental com<br />
mands which perform this function of transferring data from one memory location to<br />
ano<strong>the</strong>r. But <strong>the</strong> BASIC POKE command spends a lot of time in calculations, so<br />
<strong>the</strong>re is no great speed advantage. Color RAM will also need to be POKEd, unless<br />
<strong>the</strong> screen background color has been selected to make this unnecessary or <strong>the</strong> color<br />
RAM is already set satisfactorily. FOR 1=0 TO 999: POKE 1024+I,1:POKE<br />
55296+1,1: NEXT fills <strong>the</strong> whole screen. Note that it's slower, in fact, than<br />
PRINTing.<br />
POKE has some advantages over PRINT. Any part of <strong>the</strong> screen can be changed<br />
without <strong>the</strong> need to keep track of <strong>the</strong> cursor position. PRINT is also liable to produce<br />
unwanted effects, like scrolling <strong>the</strong> screen when <strong>the</strong> bottom-right character is<br />
printed, and linking two lines toge<strong>the</strong>r which makes <strong>the</strong> effect of RETURN un<br />
predictable. POKEing has none of <strong>the</strong>se side effects. <strong>The</strong> complete character set is<br />
available using POKE, too, without <strong>the</strong> need to use {RVS}.<br />
To POKE to <strong>the</strong> screen, you need to know where <strong>the</strong> screen is. For now, <strong>the</strong> dis<br />
cussion will concern itself with <strong>the</strong> default position. Later you'll see how to move <strong>the</strong><br />
screen around in <strong>the</strong> <strong>64</strong>'s memory. <strong>The</strong> screen can display 25 lines, each containing<br />
40 characters, so its RAM consists of 1000 memory locations. It normally lies be<br />
tween 1024 ($0400) and 2023 ($07E7); <strong>the</strong> color RAM, each byte of which corre<br />
sponds to a byte in <strong>the</strong> screen RAM, is at 55296 ($D800) to 56295 ($DBE7). Note<br />
that <strong>the</strong> color RAM is always here, irrespective of which bank <strong>the</strong> VIC-II chip is cur<br />
rently looking at. If you are unused to color RAM, try POKE 1024+40*3,1: POKE<br />
55296+40*3,0 to plot a black A at <strong>the</strong> start of <strong>the</strong> third line. It is essential to know<br />
what value to POKE; consult Appendix I for a table of <strong>the</strong> 256 characters available<br />
by POKEing, in both lowercase and uppercase mode.<br />
As an example, Program 12-6 puts solid squares of random color onto random<br />
locations in <strong>the</strong> <strong>64</strong>'s screen:<br />
Program 12-6. Simple Poke<br />
10 L=RND(1)*1001<br />
20 POKE 1024+L,160<br />
30 POKE 55296+L,RND(l)*16<br />
40 GOTO 10<br />
Finding <strong>the</strong> offset from <strong>the</strong> start of <strong>the</strong> screen for any given line and column is<br />
simple if you take some care in numbering: it is easiest to start at 0, so <strong>the</strong> horizontal<br />
position is 0-39, and <strong>the</strong> vertical position is 0-24, with 0 being <strong>the</strong> top of <strong>the</strong> screen.<br />
<strong>The</strong> offset is <strong>the</strong>n 40*vertical position + horizontal position. <strong>The</strong> subroutine below,<br />
Program 12-7, POKEs <strong>the</strong> character X with color C into <strong>the</strong> screen at position H<br />
across and V down.<br />
3<strong>64</strong>
Graphics<br />
Program 12-7. Simple Subroutine Poke<br />
30000 POKE 1024+40*V+H,X<br />
30010 POKE 55296+40*V+H,C<br />
30020 RETURN<br />
<strong>The</strong> next example program draws a maze. This example (based on <strong>the</strong> work of<br />
C. Bond in COMPUTED First Book of <strong>Commodore</strong> <strong>64</strong> Games—which includes an ML<br />
translation) draws a simply connected maze (a maze that is basically a contorted tube<br />
with no isolated islands within it). <strong>The</strong> algorithm uses space characters to mark<br />
boundaries, so <strong>the</strong>re's an unused border of space characters. This version selects a<br />
random start point, and on finishing, POKEs A and B into <strong>the</strong> two points fur<strong>the</strong>st re<br />
moved from each o<strong>the</strong>r in <strong>the</strong> maze. Line 114 records <strong>the</strong> current longest path and<br />
can be deleted. Conversion to ML is needed to make it run faster; white-on-white<br />
plotting, for example, followed by color RAM POKEs is necessary if <strong>the</strong> plotting pro<br />
cess is to be invisible.<br />
Program 12-8. Maze Demo<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
10 POKE 53280,PEEK(53281):PRINT "{CLR}11 :rem 213<br />
20 A(0)=-2:A(1)=-80:A(2)=2:A(3)=80 :rem 160<br />
30 SC=1024:A=SC+81+80*INT(10*RND(1))+2*INT(10*RND(<br />
1)) :rem 125<br />
40 FOR J=l TO 23:PRINT"{RVS}{39 SPACES}11:NEXT<br />
:rem 161<br />
100 POKE A,4 :rem 97<br />
110 J=INT(RND(1)*4):X=J :rem 50<br />
112 IP S>SMAX THEN SMAX=S:FIN=B :rem 123<br />
114 PRINT "{HOME}" SMAX;FIN :rem 203<br />
120 B=A+A(J):IF PEEK(B)=160 THEN POKE B,J:POKE A+A<br />
(J)/2,32:A=B:S=S+1:GOTO 110 :rem 224<br />
130 J=J+1 AND 3:IF JX THEN 120 :rem 110<br />
140 J=PEEK(A):POKE Af32:S=S-1:IF J
Graphics<br />
memory. Type in <strong>the</strong> following short BASIC program, which loads <strong>the</strong> machine lan<br />
guage routine. <strong>The</strong> last line of Program 12-9 holds <strong>the</strong> ASCII values for <strong>the</strong> letters<br />
HELLO preceded by a space and followed by a zero byte which signals <strong>the</strong> end of<br />
<strong>the</strong> string.<br />
Program 12-9. Simple ML Output<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
10 FOR J=49152 TO 49171:READ X:POKE J,X:NEXT<br />
:rem 219<br />
20 DATA 162,0,189,14,192,240,6 :rem 188<br />
30 DATA 32,210,255,232,208,245,96 :rem 84<br />
40 DATA 72,69,76,76,79,0 :rem 172<br />
This is <strong>the</strong> ML<br />
RPT<br />
LOOP<br />
LDX<br />
LDA<br />
BEQ<br />
#$00<br />
TABLE,X<br />
EXIT<br />
; X TO 0. USED AS POINTER<br />
; GET NEXT CHARACTER<br />
; 0 SIGNALS END OF STRING<br />
JSR $FFD2<br />
; OUTPUT CHARACTER<br />
INX<br />
; INCREMENT POINTER<br />
BNE LOOP<br />
; CONTINUE<br />
EXIT RTS<br />
TABLE .BYT "HELLO",0 ; STRING TERMINATED BY 0: MAX LENGTH 255<br />
Run Program 12-9 to load <strong>the</strong> ML, <strong>the</strong>n type SYS 49152 to print HELLO. Al<br />
though this example uses only straightforward lettering, <strong>the</strong> technique can easily be<br />
extended to include color change or cursor-move characters so that a 3 X 3 colored<br />
block of characters, say, can be printed at <strong>the</strong> current cursor position with a SYS call.<br />
Reversing Part of <strong>the</strong> Screen<br />
This effect, useful in highlighting parts of <strong>the</strong> screen or making <strong>the</strong>m flash (by<br />
repeating <strong>the</strong> reversal several times) is easy to obtain. <strong>The</strong> high bit of each character<br />
code in screen RAM determines whe<strong>the</strong>r it's reverse or not; all we need to do is re<br />
place each character by its equivalent with <strong>the</strong> high bit reversed. LDA from an ad<br />
dress, EOR #$80 to flip <strong>the</strong> high bit, <strong>the</strong>n STA back into <strong>the</strong> address does this in<br />
ML. <strong>The</strong> program given here has been made user-friendly by incorporating param<br />
eters. It is relocatable, but <strong>the</strong> loader places it at 49152. When it's been loaded SYS<br />
49152,1024,40 reverses 40 characters starting at 1024, <strong>the</strong> topmost line of <strong>the</strong> <strong>64</strong>'s<br />
screen.<br />
Program 12-10. ML Reverse<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C<br />
10 FOR J=49152 TO 49178:READ X:POKE J,X:NEXT<br />
:rem 226<br />
20 DATA 32,178,177,165,100,133,253,165,101 :rem 12<br />
30 DATA 133,252,32,155,183,138,168,136,177 :rem 29<br />
40 DATA 252,73,128,145,252,136,16,247,96 :rem 188<br />
Here is <strong>the</strong> ML routine this loads:<br />
366
Graphics<br />
START<br />
LOOP<br />
JSR<br />
LDA<br />
STA<br />
LDA<br />
STA<br />
JSR<br />
TXA<br />
TAY<br />
DEY<br />
LDA<br />
EOR<br />
STA<br />
DEY<br />
BPL<br />
RTS<br />
$B1B2<br />
$<strong>64</strong><br />
$FD<br />
$65<br />
$FC<br />
$B79B<br />
Graphics<br />
Line 10 loads <strong>the</strong> ML routine into RAM. To make <strong>the</strong> routine easy to use, three<br />
parameters are input as part of <strong>the</strong> SYS call. <strong>The</strong> syntax is:<br />
SYS 49152,bottom of column, color, height<br />
Lines 100-120 draw columns with increasing heights across <strong>the</strong> screen; lines<br />
200-210 draw a histogram derived from a sine wave. Finding <strong>the</strong> offset from <strong>the</strong><br />
start of <strong>the</strong> screen is simple with some care in numbering: it is easiest to start at 0, so<br />
<strong>the</strong> horizontal position is 0-39 and <strong>the</strong> vertical position is 0-24. Now <strong>the</strong> offset is<br />
40*vertical position + horizontal position. <strong>The</strong> parameters to <strong>the</strong> SYS call in line 110<br />
illustrate this.<br />
Table 12-1. Quick Cross Reference to <strong>64</strong> Graphics<br />
KEY:<br />
CHR$:<br />
POKE:<br />
C-@<br />
228<br />
100<br />
SH-R<br />
210<br />
82<br />
SH-F<br />
198<br />
70<br />
SH-*<br />
192<br />
<strong>64</strong><br />
SH-C<br />
195<br />
67<br />
SH-D<br />
196<br />
68<br />
SH-E<br />
197<br />
69<br />
C-T<br />
227<br />
99<br />
KEY:<br />
CHR$:<br />
POKE:<br />
C-G<br />
229<br />
101<br />
SH-T<br />
212<br />
84<br />
SH-G<br />
199<br />
71<br />
SH-B<br />
194<br />
66<br />
SH--<br />
221<br />
93<br />
SH-H<br />
200<br />
72<br />
SH-Y<br />
217<br />
89<br />
C-M<br />
231<br />
103<br />
KEY:<br />
KEY:<br />
CHR$:<br />
POKE:<br />
KEY:<br />
KEY:<br />
CHR$:<br />
POKE:<br />
KEY:<br />
CHR$:<br />
POKE:<br />
KEY:<br />
CHR$:<br />
POKE:<br />
C-@<br />
228<br />
100<br />
C-G<br />
229<br />
101<br />
SH-O<br />
207<br />
79<br />
□<br />
C-X<br />
189<br />
125<br />
C-P<br />
239<br />
111<br />
C-H<br />
244<br />
116<br />
C-O<br />
249<br />
121<br />
C-J<br />
245<br />
117<br />
□ □ i<br />
SH-P<br />
208<br />
80<br />
C-Z<br />
173<br />
109<br />
SH-@<br />
186<br />
122<br />
□<br />
C-A<br />
176<br />
112<br />
REVERSE, CHR$(18), THEN-<br />
C-I C-U C-Y C-T SH-space<br />
226 184 183 163 160<br />
98 248 247 227 224<br />
u y m m *<br />
C-K<br />
225<br />
97<br />
SH-L<br />
204<br />
76<br />
D<br />
C-S<br />
174<br />
110<br />
REVERSE, CHR$(18), THEN-<br />
C-L C-N C-M SH-space<br />
182 170 167 160<br />
246 234 231 224<br />
SH-V<br />
214<br />
86<br />
C-E<br />
177<br />
113<br />
SH-+<br />
219<br />
91<br />
C-R<br />
178<br />
114<br />
SH-M<br />
205<br />
77<br />
C-W<br />
179<br />
115<br />
SH-N<br />
206<br />
78<br />
C-Q<br />
17f<br />
107<br />
368
Graphics<br />
KEY:<br />
CHR$:<br />
POKE:<br />
KEY:<br />
CHR$:<br />
POKE:<br />
C-V<br />
190<br />
126<br />
H<br />
SH-K<br />
203<br />
75<br />
J<br />
C-C<br />
188<br />
124<br />
H<br />
SH-J<br />
202<br />
74<br />
C-D<br />
172<br />
108<br />
SH-U<br />
213<br />
85<br />
C-F<br />
187<br />
123<br />
SH-I<br />
201<br />
73<br />
C-B<br />
191<br />
127<br />
SH-W<br />
215<br />
87<br />
C-I<br />
226<br />
98<br />
SH-Q<br />
209<br />
81<br />
r o •<br />
C-K<br />
225<br />
97<br />
KEY:<br />
CHR$:<br />
POKE:<br />
SH-£<br />
169<br />
105<br />
C-*<br />
223<br />
95<br />
C- +<br />
166<br />
102<br />
C-£<br />
168<br />
104<br />
220<br />
92<br />
KEY:<br />
CHR$:<br />
POKE:<br />
SH-A<br />
193<br />
65<br />
SH-S<br />
211<br />
83<br />
SH-Z<br />
218<br />
90<br />
SH-X<br />
216<br />
88<br />
* ♦ 4<br />
SH-t<br />
222<br />
94<br />
Notes:<br />
1. C- means press <strong>the</strong> <strong>Commodore</strong> key and <strong>the</strong> indicated character; SH- means press <strong>the</strong> SHIFT key and <strong>the</strong><br />
indicated character.<br />
2. <strong>The</strong>re are ambiguities in many of <strong>the</strong> CHR$ figures—CHR$(227) or CHR$(163) for example might equally<br />
well be chosen. I've preferred <strong>the</strong> values with a constant difference of <strong>64</strong> or 128 from <strong>the</strong> screen<br />
POKE/PEEK value.<br />
3. As <strong>the</strong> characters are made of 8 x 8 dots, a line cannot appear exactly in <strong>the</strong> center of any character; some<br />
characters, when positioned as neighbors, will not exactly line up toge<strong>the</strong>r.<br />
4. In lowercase mode, some characters aren't available; those with POKE values 65-90 appear as A-Z. <strong>The</strong> full<br />
128 graphics characters are obtained by reversing all those in <strong>the</strong> table, whe<strong>the</strong>r by PRINTing <strong>the</strong> reverse<br />
character first or by POKEing <strong>the</strong> values listed here + 128.<br />
5. Four extra graphics, obtainable only in lowercase mode, are:<br />
Double-Density Graphics<br />
This program exploits <strong>the</strong> fact that all 16 possible graphics with internal quadrants<br />
exist on <strong>the</strong> <strong>64</strong>.<br />
Program 12-12. Double Density<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader/' Appendix C.<br />
10 DATA 32,155,183,134,5,32,155,183,134,6,32,155<br />
:rem 50<br />
369
Graphics<br />
11 DATA 183,134,2,165,5,-48,89,201,80,176,85,165,6<br />
:rem 120<br />
12 DATA 48,81,201,50,176,77,169,0,133,4,169,50,229<br />
:rem 163<br />
13 DATA 6,70,5,38,4,106,38,4,133,6,166,4,169,0,133<br />
:rem 146<br />
14 DATA 4,133,210,56,38,4,202,16,251,165,6,10,10<br />
:rem 30<br />
15 DATA 101,6,10,38,210,10,38,210,10,38,210,133,20<br />
9 :rem 167<br />
16 DATA 169,4,101,210,133,210,1<strong>64</strong>,5,177,209,162,15<br />
:rem 145<br />
17 DATA 221,117,192,240,4,202,16,248,96,138,5,4,17<br />
0 :rem 203<br />
18 DATA 189,117,192,145,209,32,36,234,165,2,145,24<br />
3 srem 219<br />
19 DATA 96,32,126,123,97,124,226,255,236,108,127<br />
:rem 74<br />
20 DATA 98,252,225,251,254,160 :rem 197<br />
100 FOR J=49152 TO 49284:READ X:POKE J,X:NEXT<br />
:rem 16<br />
Figure 12-1. Sixteen Quadrant Pictures<br />
0 12 3 4 5 6 7 8 9 10 11 12 13 14 15<br />
<strong>The</strong> ML routine replaces one of <strong>the</strong>se graphics symbols with ano<strong>the</strong>r, depending<br />
on <strong>the</strong> position of <strong>the</strong> "dot" to be plotted, so <strong>the</strong> whole screen in effect has a resolu<br />
tion of 80 X 50 small squares. <strong>The</strong> color can be set in any pair of squares. Graphics<br />
of this type don't compare to full hi-res pictures, buf tfiey have <strong>the</strong> advantage of be<br />
ing completely compatible with BASIC, needing no special POKEs or bitmap calcula<br />
tions. Note that <strong>the</strong> algorithm is designed to ignore text and o<strong>the</strong>r characters which<br />
aren't among <strong>the</strong> 16 quadrants, so BASIC text can be intermingled with dot<br />
diagrams.<br />
<strong>The</strong> syntax is SYS 49152,X,Y,COLO2? where X=0-79, Y=0-49, COLOR=0-15.<br />
All parameters are evaluated, so using variables (SYS P,X,Y,Q is accepted. <strong>The</strong> point<br />
X=0, Y=0 starts at <strong>the</strong> bottom left of <strong>the</strong> screen.<br />
Changing Color RAM with ML<br />
Color RAM occupies 1000 bytes from 55296 ($D800) on. Unlike, for example, <strong>the</strong><br />
background color, which can be changed with one single POKE, each byte in <strong>the</strong><br />
color RAM has to be changed if an entire screen of characters' colors is to be<br />
changed at once. Program 12-13 alters color RAM only, leaving <strong>the</strong> characters un<br />
changed. POKE 842 with <strong>the</strong> color number (1-15), and SYS 830 to run <strong>the</strong> routine.<br />
370
Graphics<br />
Program 12-13. Change Color RAM<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
10 FOR J=830 TO 853:READ X:POKE J,X:NEXT :rem 11<br />
20 DATA 169,216,133,3,169,0,133,2,168,162,4,169<br />
:rem 8<br />
30 DATA 7,145,2,200,208,251,230,3,202,208,246,96<br />
:rem 41<br />
When <strong>the</strong> screen is cleared, screen RAM is filled with space characters. <strong>The</strong> fore<br />
ground color is now irrelevant; only <strong>the</strong> background color shows. <strong>The</strong> original ROM<br />
version of <strong>the</strong> <strong>64</strong> fills color RAM with white after clearing <strong>the</strong> screen, so that charac<br />
ters POKEd to screen RAM show up in white. Some newer <strong>64</strong>s fill color RAM with<br />
<strong>the</strong> background color, stored in 53281, so that POKEs to a cleared screen are in<br />
visible. <strong>The</strong> most recent version of <strong>the</strong> <strong>64</strong> fills color RAM with <strong>the</strong> current character<br />
color (stored in <strong>64</strong>6), so POKEs will be visible.<br />
Because of <strong>the</strong>se ROM changes, <strong>the</strong> effect of a simple screen POKE depends on<br />
when your <strong>64</strong> was built; <strong>the</strong> POKEd character may appear white, invisible, or <strong>the</strong><br />
same color as <strong>the</strong> cursor. To make screen POKEs work correctly on all <strong>64</strong>s, POKE<br />
color RAM with <strong>the</strong> desired value whenever you POKE a character into screen<br />
memory.<br />
ML modifications of color RAM can give many effects. <strong>The</strong> following program<br />
gives <strong>the</strong> effect of motion by drawing colored bars, <strong>the</strong>n cycling through RAM<br />
colors:<br />
Program 12-14. Color RAM Motion<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
10 FOR J=49152 TO 49177:READ X:POKE J,X:NEXT<br />
:rem 225<br />
20 B1=55296:B2=56296 :rem 20<br />
30 FOR J=1024 TO 2023:POKE J,160:NEXT :rem 241<br />
40 FOR Y=*0 TO 24:FOR X=0 TO Y : rem 252<br />
50 P1=X+40*Y:P2=Y+40*X :rem 240<br />
60 POKE B1+P1,Y:POKE B2-P1-1,Y :rem 167<br />
70 IF Y
Graphics<br />
Scrolling <strong>the</strong> Entire Screen<br />
This section shows how to scroll <strong>the</strong> entire screen left, right, up, or down, moving<br />
one character's width or height. Color RAM must be moved to match so that <strong>the</strong><br />
characters keep <strong>the</strong>ir colors, unless a deliberate effect of static color is desired. Be<br />
cause <strong>the</strong> movement is in whole characters, scrolling is somewhat jerky. Smooth<br />
scrolling is discussed later.<br />
<strong>The</strong>se routines have to move 960 or 975 characters to new positions in screen<br />
memory and repeat <strong>the</strong> process for color RAM. Not 1000, because in each direction<br />
25 or 40 characters are lost when <strong>the</strong> scrolling effect overwrites <strong>the</strong>m. A new row or<br />
column of characters has to be POKEd to complete <strong>the</strong> scroll, or <strong>the</strong> displayed<br />
characters can be stored and reused, giving an indefinitely repeating scroll effect.<br />
Two thousand BASIC PEEKs and POKEs, with calculations, are slow, but repet<br />
itive loads and moves are easily written in ML, so this is a good application. ML pro<br />
grammers can alter <strong>the</strong> routines, for example, to scroll strips of background across<br />
<strong>the</strong> screen at different speeds to simulate motion.<br />
<strong>The</strong> following four routines are each placed in RAM by a BASIC loader. When<br />
loaded, <strong>the</strong>y can be called by a simple SYS command from BASIC and are <strong>the</strong>refore<br />
easy to use. All are relocatable, so if <strong>the</strong> start address is altered from 49152 <strong>the</strong>y will<br />
run correctly, provided all <strong>the</strong> bytes are POKEd to RAM, and <strong>the</strong> SYS calls <strong>the</strong> cor<br />
rect starting address. <strong>The</strong>re are limitations to <strong>the</strong> programs, but <strong>the</strong>y do illustrate <strong>the</strong><br />
basic concept of scrolling.<br />
Program 12-15. Scroll Down<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix G<br />
10 FOR J=49152 TO 49206:READ X:POKE J,X:NEXT<br />
:rem 218<br />
30 DATA 169,7,133,253,169,191,133,252,169 :rem 240<br />
40 DATA 219,133,255,169,191,133,254,160,0 :rem 225<br />
50 DATA 177,252,160,40,145,252,160,0,177 :rem 171<br />
60 DATA 254,160,40,145,254,165,252,208,2 :rem 172<br />
70 DATA 198,253,198,252,165,254,208,2,198 :rem 253<br />
80 DATA 255,198,254,165,253,201,3,208,218,96<br />
:rem 135<br />
Program 12-16. Scroll Up<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
10 FOR J=49152 TO 49209:READ X:POKE J,X:NEXT<br />
:rem 221<br />
30 DATA 169,4,133,253,169,0,133,252,169,216:rem 71<br />
40 DATA 216,133,255,169,0,133,254,160,40,177<br />
:rem 114<br />
50 DATA 252,160,0,145,252,160,40,177,254,160<br />
:rem 106<br />
60 DATA 0,145,254,230,252,208,2,230,253,230:rem 47<br />
70 DATA 254,208,2,230,255,165,252,201,192 :rem 221<br />
80 DATA 208,222,165,253,201,7,208,216,96 :rem 180<br />
372
Graphics<br />
Program 12-17. Scroll Right<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
10 FOR J=49152 TO 49215:READ X:POKE J,X:NEXT<br />
:rem 218<br />
30 DATA 169,4,133,253,169,0,133,252,169,216:rem 71<br />
40 DATA 133,255,169,0,133,254,162,24,160,39:rem 65<br />
50 DATA 177,252,200,145,252,136,177,254,200:rem 66<br />
60 DATA 145,254,136,136,16,241,24,165,252 :rem 226<br />
70 DATA 105,40,133,252,165,253,105,0,133,253<br />
:rem 100<br />
80 DATA 165,254,105,40,133,254,165,255,105 :rem 19<br />
90 DATA 0,133,255,202,16,211,96 :rem 236<br />
Program 12-18. Scroll Left<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
10 FOR J=49152 TO 49214:READ X:POKE J,X:NEXT<br />
:rem 217<br />
20 DATA 162,22,189,235,3,157,234,3,189,235 :rem 25<br />
30 DATA 215,157,234,215,232,208,241,189,234:rem 70<br />
40 DATA 4,157,233,4,189,234,216,157,233,216:rem 73<br />
50 DATA 232,208,241,189,233,5,157,232,5,189:rem 77<br />
60 DATA 233,217,157,232,217,232,208,241,189:rem 74<br />
70 DATA 232,6,157,231,6,189,232,218,157 :rem 136<br />
80 DATA 231,218,232,208,241,96 :rem 198<br />
100 FOR J=1063 TO 2024 STEP 40:POKE J,160:NEXT<br />
:rem 195<br />
110 R=RND(1) :rem 135<br />
120 IF R.6 THEN X=X-1 :rem 145<br />
140 IF X>15 THEN X=15 :rem 74<br />
150 IF X
Graphics<br />
Figure 12-2. Scrolling Down<br />
Screen<br />
A<br />
B<br />
C<br />
D<br />
?<br />
?<br />
?<br />
?<br />
1<br />
2<br />
3<br />
4<br />
Scroll<br />
A<br />
B<br />
C<br />
D<br />
E<br />
F<br />
G<br />
H<br />
1<br />
2<br />
3<br />
4<br />
5<br />
6<br />
7<br />
8<br />
Down<br />
E<br />
F<br />
G<br />
H<br />
Screen<br />
B<br />
RAM<br />
1 C<br />
before and after scrolling down<br />
D 1<br />
2<br />
|3|4|E|<br />
F | G<br />
1"<br />
I5<br />
6 | 7<br />
8<br />
i'<br />
?<br />
h<br />
? A<br />
B<br />
|C|D|1|<br />
2|3<br />
1*<br />
1"<br />
f|g<br />
H<br />
Figure 12-3. Scrolling Left<br />
Screen<br />
A<br />
B<br />
C<br />
D<br />
B<br />
C<br />
D<br />
?<br />
1<br />
2<br />
3<br />
4<br />
Scroll<br />
2<br />
3<br />
4<br />
?<br />
E<br />
F<br />
G<br />
H<br />
F<br />
G<br />
H<br />
?<br />
5<br />
6<br />
7<br />
8<br />
Left<br />
6<br />
7<br />
8<br />
?<br />
Screen RAM before and after scrolling left<br />
|a|b|c<br />
D 1 2<br />
3 | 4 | E | F G | H<br />
5<br />
6 7 8<br />
|b|c|d<br />
2<br />
3<br />
4|t l|O|H|f|<br />
• r|. 7<br />
It is easy to deduce that for <strong>the</strong> <strong>64</strong>, scrolling up or down requires that <strong>the</strong> entire<br />
screenful of characters, excluding a set of 40 at <strong>the</strong> end, be moved 40 places along.<br />
Scrolling right or left, in <strong>the</strong> simplest case, only requires every screen character but<br />
one to be shifted one place along. This is what <strong>the</strong> left-scrolling program does.<br />
Simply enter <strong>the</strong> line 0 SYS 49152: GOTO 0 and run it to see <strong>the</strong> screen roll sideways.<br />
With scroll down, <strong>the</strong> characters at <strong>the</strong> bottom-right must be moved first; o<strong>the</strong>r<br />
wise, because <strong>the</strong>re is no temporary storage and characters are moved directly into<br />
<strong>the</strong> screen, characters will be overwritten before <strong>the</strong>y have been moved.<br />
374
Graphics<br />
Speedy ML routines help. If screen RAM and color RAM are both moved, <strong>the</strong><br />
fastest methods use LDA $0401,X:STA $0400,X:LDA $D801,X:STA $D800,X or simi<br />
lar commands to move each character; this takes about 1/50 second on <strong>the</strong> <strong>64</strong>.<br />
<strong>The</strong> VIC-II<br />
Chip<br />
Color TVs and monitors display color by electronically causing small patches of<br />
color, called phosphor, to glow on <strong>the</strong> screen. This is additive color. Each extra color<br />
adds to <strong>the</strong> brightness, unlike painting or printing, which uses subtractive color. <strong>The</strong><br />
primary colors for additive color are red, green, and blue. All three colors combined<br />
equally produce white.<br />
<strong>The</strong> phosphor dots can be seen with a low-power magnifier; typically, <strong>the</strong>re are<br />
columns of red, green, and blue repeating <strong>the</strong> full width of <strong>the</strong> screen. TVs are likely<br />
to show some defects—a red screen will have some green and blue, too. As <strong>the</strong> color<br />
is turned down, TVs have a device to average colors locally, giving shades of gray.<br />
Secondary colors are yellow, blue green, and magenta; <strong>the</strong>se are known by a<br />
variety of names, <strong>the</strong> <strong>64</strong>'s choice being yellow, cyan, and purple. Each is made of<br />
about equal amounts of two primary colors: yellow is red and green, cyan is green<br />
and blue, and purple is blue and red. On <strong>the</strong> <strong>64</strong>, adjacent pairs of color-control keys<br />
are complementary: <strong>The</strong>y add to white or gray. For example, red and cyan contain<br />
between <strong>the</strong>m red, green, and blue, in <strong>the</strong> right proportions to mix to give white.<br />
<strong>The</strong> eight foreground colors obtainable with CTRL and a color key are simply <strong>the</strong><br />
three primaries, each of which is ei<strong>the</strong>r on or off, giving eight combinations. Of <strong>the</strong><br />
fur<strong>the</strong>r eight colors, three are light versions of <strong>the</strong> primary colors, but <strong>the</strong> three<br />
secondary colors are turned to shades of gray. Brown and orange have also been<br />
added.<br />
<strong>The</strong>re are three perceptual effects worth mentioning. One is that <strong>the</strong> weaker, less<br />
saturated colors are very influenced by stronger, adjacent colors, so <strong>the</strong> <strong>64</strong>'s gray<br />
tones can be visually stabilizing.<br />
Ano<strong>the</strong>r is <strong>the</strong> advancing effect of red and <strong>the</strong> receding effect of blue, which can<br />
be startling with solid blocks of <strong>the</strong>se colors. Blue can look a long way behind red.<br />
<strong>The</strong>re are related problems in getting a bright color to look bright when compared<br />
with, say, white, which has three times as many phosphors lighted. <strong>The</strong> VIC-II chip<br />
doesn't allow control over <strong>the</strong> luminance of screen pixels, so many color combina<br />
tions haven't enough contrast to be distinct. Red lettering on blue is unreadable, for<br />
example.<br />
Color RAM<br />
<strong>The</strong> border and screen background colors are straightforward. Color RAM is a more<br />
difficult concept, but seems natural enough after a time. It is a block of RAM<br />
paralleling <strong>the</strong> screen RAM; each screen location has a corresponding color RAM<br />
location, whose lower nybble (four bits) determines <strong>the</strong> character's foreground color;<br />
thus, each screen character may have its own foreground color. (All characters have<br />
a common background color determined by <strong>the</strong> register at 53281.)<br />
<strong>The</strong> colors that correspond to numbers in <strong>the</strong> foreground and background reg<br />
isters, and in <strong>the</strong> color RAM locations, follow:<br />
375
Graphics<br />
Color Nybble Color (Descriptions Vary Somewhat)<br />
0 Black<br />
1 White<br />
2 Red<br />
3 Cyan<br />
4 Purple<br />
5 Green<br />
6 Blue<br />
7 Yellow<br />
8 Orange<br />
9 Brown<br />
10 Light Red<br />
11 Gray 1, Dark Gray<br />
12 Gray 2, Med. Gray<br />
13 Light Green<br />
14 Light Blue<br />
15 Gray 3, Light Gray<br />
Color RAM occupies 55296-56295 ($D800-$DBE7), a total of 1000 bytes. N<br />
that <strong>the</strong> VIC-II chip always fetches its foreground color information from here,.ii<br />
respective of which VIC-II bank is in use.<br />
Multicolor Mode<br />
Understanding user-definable characters is essential to getting <strong>the</strong> most from mu<br />
color mode. However, <strong>the</strong> general idea is fairly easy to grasp. It is ano<strong>the</strong>r Com<br />
modore compromise: In order to get more color into <strong>the</strong> screen, resolution is cut<br />
half. Below is a discussion of how this works with ordinary graphics; <strong>the</strong> principl<br />
<strong>the</strong> same in high-resolution mode.<br />
Normally, a one in a character definition shows up in <strong>the</strong> foreground color, a<br />
a zero shows up in <strong>the</strong> background color; so only two colors are obtainable withii<br />
each 8X8 dot character area. Multicolor mode allows four colors to be selected ]<br />
character, at <strong>the</strong> cost of halving <strong>the</strong> horizontal resolution. Instead of 8 X 8 dots, i<br />
offers 4X8 "wider" dots, each of which can take one of four colors.<br />
Multicolor mode is enabled by setting bit 4 of VIC-II register $16 to 1. This is<br />
done by using POKE 53270,PEEK(53270)OR16 (normally, POKE 53270,216). <strong>The</strong><br />
following command switches back to normal mode: POKE 53270,PEEK(53270)<br />
AND239 (normally, POKE 53270,200).<br />
<strong>The</strong> above POKEs enable and disable multicolor mode globally, over <strong>the</strong> who<br />
text area; but it must also be enabled on a character-by-character basis to have an)<br />
effect. This is done by <strong>the</strong> value in <strong>the</strong> corresponding color RAM location: If it is '<br />
from 0 to 7, <strong>the</strong>n <strong>the</strong> character appears in ordinary mode, and if it is from 8 to 15,<br />
<strong>the</strong>n <strong>the</strong> character will be in multicolor mode. In o<strong>the</strong>r words, bit 3 in a color RAM<br />
location determines whe<strong>the</strong>r <strong>the</strong> corresponding character is in ordinary or multicolor<br />
mode. Thus, <strong>the</strong> screen may simultaneously display multicolored and ordinary<br />
characters.<br />
To get <strong>the</strong> feel of this, type some lettering in several colors, including <strong>the</strong> less<br />
saturated <strong>Commodore</strong>-key colors. Enable multicolor mode with <strong>the</strong> POKE given<br />
above. You'll see that characters in black through yellow are unchanged, while those<br />
376
Graphics<br />
in orange through light gray alter dramatically, because bit 3 of <strong>the</strong>ir color RAM has<br />
this dual function.<br />
<strong>The</strong> source of <strong>the</strong> color in each two-dot unit is shown by <strong>the</strong> following table:<br />
Bit Pattern: Color Specified By: Address of Register:<br />
0 0 Background 0 color register 53281 $D021<br />
(screen background color)<br />
0 1 Background 1 color register 53282 $D022<br />
1 0 Background 2 color register 53283 $D023<br />
1 1 Lower three bits of color RAM<br />
(character color)<br />
<strong>The</strong> three registers can take values from 0 to 15; <strong>the</strong> three bits specified by color<br />
RAM select values 0-7. Notice that units containing 00 appear as <strong>the</strong> background<br />
color whe<strong>the</strong>r <strong>the</strong> display is in standard or multicolor mode. Note that <strong>the</strong> border<br />
color in 53280 is independent of <strong>the</strong> background colors, unlike VIC-20's multicolor<br />
mode.<br />
It follows from this table that an orange (<strong>Commodore</strong> key-BLK) character will<br />
be displayed as black when multicolor mode is enabled—try it with reverse-space<br />
block in orange. Similarly, a light green character switches to green.<br />
Consider how <strong>the</strong> character A is defined in ROM:<br />
Normal: Multicolor: Displays As:<br />
0 0 0 110 0 0 00 01 10 00 BG0 BGl BG2 BG0<br />
0 0 11110 0 00 11 11 00 BG0 CR CR BG0<br />
0 110 0 110 01 10 01 10 BGl BG2 BGl BG2<br />
0 1111110 01 11 11 10 BGl CR CR BG2<br />
0 110 0 110 01 10 01 10 BGl BG2 BGl BG2<br />
0 110 0 110 01 10 01 10 BGl BG2 BGl BG2<br />
0 110 0 110 01 10 01 10 BGl BG2 BGl BG2<br />
0 0 0 0 0 0 0 0 00 00 00 00 BG0 BG0 BG0 BG0<br />
<strong>The</strong> first illustration shows how <strong>the</strong> definition is interpreted in normal mode: ze<br />
ros display in <strong>the</strong> background color and ones display in <strong>the</strong> foreground color, speci<br />
fied by <strong>the</strong> character's color RAM.<br />
<strong>The</strong> second and third illustrations show how <strong>the</strong> bits are interpreted as grouped<br />
in pairs by <strong>the</strong> <strong>64</strong> in multicolor mode. <strong>The</strong> abbreviations BG0, BGl, and BG2 repre<br />
sent <strong>the</strong> three background color registers, which are set to 6 (dark blue), 1 (white),<br />
and 2 (red), respectively, on power-up. (<strong>The</strong> SX-<strong>64</strong> sets BG0 to white, however.) CR<br />
is color RAM, which is 14 (pale blue) on powering-up <strong>the</strong> <strong>64</strong>. Note that <strong>the</strong> three<br />
background colors apply over <strong>the</strong> whole screen area; only <strong>the</strong> character color can<br />
vary from character to character. <strong>The</strong>refore, when designing multicolor graphics, se<br />
lect <strong>the</strong> three colors you wish to spread most widely on <strong>the</strong> screen, and let <strong>the</strong><br />
character color vary locally.<br />
Assuming <strong>the</strong> <strong>64</strong> has its power-up values, enter POKE 53270,216 to enable<br />
multicolor mode. All characters will be displayed in multicolor mode, since <strong>the</strong>ir<br />
color RAM value is greater than 7. Assuming <strong>the</strong> relevant registers have <strong>the</strong>ir powerup<br />
values, BG0 will show up as dark blue, BGl as white, BG2 as red, CR as a dark<br />
blue (produced by <strong>the</strong> pale blue value with bit 3 stripped off). This is what <strong>the</strong> col<br />
ors should be, but <strong>the</strong>y may not show up particularly clearly on your TV or monitor.<br />
377
Graphics<br />
<strong>The</strong> cursor disappears because <strong>the</strong> reverse space character is a block of bit pairs in<br />
<strong>the</strong> pattern 11; <strong>the</strong> color is given by color RAM and thus shows up in multicolor<br />
mode as dark blue. Type <strong>Commodore</strong> key-GRN to make it reappear: printing will<br />
continue in multicolor mode; CTRL-GRN will also make it reappear, but causes<br />
printing to continue in standard mode because of <strong>the</strong> different color RAM settings.<br />
Enter POKE 53283,1 to make BG2, as well as BG1, white; <strong>the</strong> multicolored charac<br />
ters now contain large areas of white. Type <strong>Commodore</strong> key-WHT followed by a<br />
few more characters: even larger areas now show as white, as BG1 and BG2 and CR<br />
are all now white. Usually, of course, contrasting colors will be used. CTRL-WHT<br />
will select a foreground color value less than 8; type this and <strong>the</strong>n fur<strong>the</strong>r characters:<br />
<strong>the</strong>se display in standard mode, because of <strong>the</strong> color RAM value, and are unaffected<br />
by BG1 and BG2 settings.<br />
<strong>The</strong>se multicolor characters have a chunky appearance, since <strong>the</strong>y have half <strong>the</strong><br />
horizontal resolution of standard characters. <strong>The</strong>y can be used for decorative borders<br />
and designs, and for graphics. You may need to experiment to find <strong>the</strong> best combina<br />
tions of colors for this effect. <strong>The</strong>y are easier to use than user-defined characters and<br />
take up no extra space in RAM. Finding characters which look right may be difficult,<br />
though.<br />
With some work characters in multicolor mode can produce impressive results.<br />
For example, BGO may be set to 12, and BG1 and BG2 to 8 and 14, giving orange<br />
and light blue and <strong>the</strong> local colors on a medium gray background, allowing, say, a<br />
gray sky, orange ground, and light blue middle-distance, with small objects in any of<br />
<strong>the</strong> eight main colors.<br />
<strong>The</strong> following BASIC program lets you experiment with all combinations of<br />
BGO, BG1, BG2, and CR. It displays almost <strong>the</strong> entire character set twice, once at <strong>the</strong><br />
top of <strong>the</strong> screen in standard mode and again below it in multicolor mode. <strong>The</strong> func<br />
tion keys fl, f3, f5,17 advance <strong>the</strong> values in <strong>the</strong> three register values and <strong>the</strong> color<br />
RAM of <strong>the</strong> multicolor mode characters.<br />
You may prefer to experiment with character sets in two colors only; if so, mod<br />
ify <strong>the</strong> program to POKE <strong>the</strong> background registers with 0, and make <strong>the</strong> function<br />
keys toggle, with POKE 53281,1 -(PEEK(53281) AND 15) or a similar statement.<br />
<strong>The</strong> AND 15 is necessary to remove <strong>the</strong> high nybble, which varies. Also try replac<br />
ing line 250 with 250 NC = 1-NC.<br />
Program 12-19. Multicolor Mode<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
10 VIC=53248:COL=55296 :rem 230<br />
20 PRINT M{CLR}M :rem 198<br />
30 FOR J=0 TO 239 :rem 66<br />
40 POKE 1024+J*2,J:POKE COL+J*2,0 :rem 21<br />
50 POKE 1024+520+J*2,J:POKE COL+J*2+520,8 :rem 162<br />
60 NEXT :rem 165<br />
100 POKE VIC+22,PEEK(VIC+22) OR 16 :rem 76<br />
200 GET X$:IF X$=IMI THEN 200 :rem 117<br />
210 IF X$=M{F1}M THEN POKE 53281,(PEEK(53281)+l) A<br />
ND 15 :rem 144<br />
220 IF X$="{F3}" THEN POKE 53282,(PEEK(53282)+l) A<br />
ND 15 :rera 148<br />
378
Graphics<br />
230 IF X$="{F5}M THEN POKE 53283,(PEEK(53283)+l) A<br />
ND 15 :rem 152<br />
240 IF X$oll{F7}il THEN 200 : rem 168<br />
250 NC=((PEEK(COL+520)+l) AND 15) OR 8 :rem 139<br />
260 FOR J=0 TO 239:POKE COL+J*2+520,NC:NEXT<br />
:rem 135<br />
270 GOTO 200 :rem 100<br />
Multicolor mode is probably <strong>the</strong> <strong>64</strong>'s most popular graphics mode. Although in<br />
<strong>the</strong>ory resolution is halved, in practice TV limitations mean that 320 individual col<br />
ored dots (that is, 40 sets of 8) aren't really distinguishable across a TV screen. <strong>The</strong><br />
<strong>64</strong>'s <strong>Commodore</strong> key-+ character, for example, is not made of alternate 0's and l's.<br />
It's composed of alternate 00 and 11 pairs. This is why multicolor characters often<br />
look similar to <strong>the</strong>ir normal equivalents, and why normal characters—<strong>Commodore</strong><br />
key-Z, for instance—often appear thicker than you'd expect.<br />
Even with multicolor mode enabled, characters don't have to be displayed in<br />
multicolor mode, which adds to <strong>the</strong> mode's versatility. Programs can be developed<br />
using PRINT and/or POKE to move characters around; such programs will work just<br />
as well if <strong>the</strong> graphics are redefined in multicolor form. This requires more work,<br />
since character definitions must be loaded into RAM and <strong>the</strong> VIC chip made to ac<br />
cess <strong>the</strong>m. However, this is still easier than full bitmapping.<br />
Extended Background Color Mode<br />
This is a relatively new display mode, and <strong>the</strong> YIC-20 has no analogous mode; it<br />
cannot coexist with o<strong>the</strong>r modes. <strong>The</strong> screen blacks out as long as multicolor mode<br />
or bitmapping is also switched on. Like multicolor mode, <strong>the</strong> full graphics set is di<br />
vided by four to allow more color. Usually <strong>the</strong> <strong>64</strong>'s background color (BGO) extends<br />
over <strong>the</strong> whole background, and though each of 1000 character colors can vary, <strong>the</strong><br />
background has to be in common—though this is disguisable by including solid<br />
blocks of local color. Extended background color mode allows <strong>the</strong> background and<br />
color of each character to be chosen from one of four colors. Dots are interpreted<br />
singly, not in pairs like multicolor mode.<br />
<strong>The</strong> trade-off is only <strong>64</strong> characters can be displayed at one time. <strong>The</strong> two high<br />
est bits of each character determine <strong>the</strong> background color:<br />
High Bits<br />
of Character<br />
0 0<br />
0<br />
1<br />
1<br />
1<br />
0<br />
1<br />
BG Color Specified By: Address of Register:<br />
Background 0 color register 53281 $D021<br />
(usual screen background)<br />
Background 1 color register 53282 $D022<br />
Background 2 color register 53283 $D023<br />
Background 3 color register 53284 $D024<br />
<strong>The</strong> displayable characters are <strong>the</strong> first <strong>64</strong> in <strong>the</strong> character definition area. <strong>The</strong><br />
foreground color is set by <strong>the</strong> color RAM nybble. In summary, each of <strong>the</strong> 1000<br />
characters' foregrounds can be set to colors 0-15; each background can be set to one<br />
of four colors, each of which may be 0-15; and only <strong>64</strong> differently shaped characters<br />
can be displayed, each in two colors at most.<br />
379
Graphics<br />
Extended background color mode is selected by setting bit 6 of VIC-II register<br />
$11 to 1; this can be done with POKE 53265,PEEK(53265) OR <strong>64</strong>. <strong>The</strong> command<br />
POKE 53265,PEEK(53265) AND 191 switches back to normal mode. (POKE<br />
53265,91 for on and POKE 53265,27 for off normally work fine.)<br />
You can reset <strong>the</strong> VIC-II registers using RUN/STOP-RESTORE. Background col<br />
ors 0-3 are set to 6, 1, 2, and 3, corresponding to blue, white, red, and cyan. Now,<br />
enable extended background color mode with <strong>the</strong> above POKE. <strong>The</strong> cursor flashes<br />
red ra<strong>the</strong>r than pale blue, because reverse-space is POKEd as 160 to <strong>the</strong> screen: <strong>the</strong><br />
bit pattern is %10100000, which is 32 with %10 as <strong>the</strong> leading bits. So it shows as a<br />
space character with background color governed by BG2, which is red.<br />
Type a few unSHIFTed letters, and <strong>the</strong>y will appear <strong>the</strong> usual light blue on dark<br />
blue. Now try SHIFTed letters; <strong>the</strong>y are unSHIFTed on <strong>the</strong> screen, but <strong>the</strong>ir back<br />
ground is now white, governed by BG1. <strong>The</strong> POKE codes for A and SHIFT-A differ<br />
by <strong>64</strong>, so <strong>the</strong> same character is displayed in extended background color mode.<br />
Type CTRL-{RVS} followed by unSHIFTed letters; now <strong>the</strong> background is red,<br />
like <strong>the</strong> cursor, because bits %10 select register BG2. Finally, without pressing RE<br />
TURN, type in a few SHIFTed letters: this {RVS}-SHIFT combination adds bits %11,<br />
selecting BG3's cyan background. <strong>The</strong> result is a bit hard to read on some sets; try<br />
POKE 53281,0: POKE 53283,7, setting BG0-BG3 to black, white, yellow, and cyan,<br />
with red lettering (POKE <strong>64</strong>6,2).<br />
For a fur<strong>the</strong>r demonstration, add <strong>the</strong>se four lines to <strong>the</strong> "Multicolor Mode"<br />
demo program and run <strong>the</strong> result:<br />
Program 12-20. Extended Background Color Mode<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
235 IF X$="{F8}" THEN POKE 53284,(PEEK(53284)+1) A<br />
ND 15 :rem 1<strong>64</strong><br />
236 IF X$="M" THEN POKE(53265),PEEK(53265) AND 191<br />
i POKE 53270fPEEK(53270)OR16 :rem 82<br />
237 IF X$="EM THEN POKE(53270)#PEEK(53270) AND 239<br />
: POKE 53265,PEEK(53265)OR<strong>64</strong> :rem 81<br />
250 NC=PEEK(COL+520)+l :rem 215<br />
Extended background color mode can now be selected by typing E, and multi<br />
color mode by typing M. Pressing f8 advances <strong>the</strong> value in register BG3, while <strong>the</strong><br />
o<strong>the</strong>r keys function as before.<br />
You'll see <strong>the</strong> reduced character set and extra background colors clearly. <strong>The</strong><br />
small available range of character shapes makes this mode unsuitable for most pur<br />
poses. But if you're content with numerals, uppercase letters, and punctuation sym<br />
bols, extended background color mode allows colored highlighting which is<br />
o<strong>the</strong>rwise much harder to program. <strong>The</strong> unSHIFTed, SHIFTed, reverse, and reverse-<br />
SHIFTed characters (as ordinarily entered) will be displayed on background colors as<br />
stored in 53281-53284. Note that unSHIFTed space, conveniently, appears as <strong>the</strong> de<br />
fault background color.<br />
Smooth Screen Scrolling<br />
Sometimes it's nice to be able to display text scrolling smoothly up <strong>the</strong> screen or<br />
landscape-type graphics shifting left or right. <strong>The</strong> scrolling we've looked at so far<br />
380
Graphics<br />
shifts whole characters and is ra<strong>the</strong>r jerky. We can improve on this with <strong>the</strong> VIC-II<br />
chip's facility to move <strong>the</strong> screen. It positions <strong>the</strong> screen with single-dot resolution,<br />
allowing a maximum of eight dots of movement, so <strong>the</strong> whole picture can be shifted<br />
slightly. This allows screen scrolling, but since sprites have to be handled separately,<br />
<strong>the</strong> technique can be difficult. Bits 0-2 of $D011 (53265) set <strong>the</strong> vertical position,<br />
and bits 0-2 of $D016 (53270) set <strong>the</strong> horizontal position.<br />
Here's <strong>the</strong> method for upward scrolling: <strong>the</strong> screen is moved up by one row of<br />
pixels (dots) seven times; on every eighth movement <strong>the</strong> screen moves back to <strong>the</strong><br />
lowest position, and <strong>the</strong> screen characters are scrolled up by one whole character.<br />
Normally, this gives a jiggling motion at <strong>the</strong> top and bottom borders, which can be<br />
reduced by matching <strong>the</strong> border and background colors, or, better, by using <strong>the</strong> VIC<br />
chip to cut out <strong>the</strong> picture edges.<br />
<strong>The</strong> screen scroll must be fast; o<strong>the</strong>rwise, <strong>the</strong> TV scan will display part of <strong>the</strong><br />
old picture, giving an uneven effect. <strong>The</strong> first demonstration, Program 12-21, is in<br />
BASIC.<br />
Program 12-21. Change Vertical Position<br />
10 V=(PEEK(53265)-l) AND 7<br />
20 WAIT 53266,128,128:WAIT 53266,128<br />
30 POKE 53265,PEEK(53265) AND 248 OR V<br />
40 GOTO 10<br />
Program 12-21 repeatedly moves <strong>the</strong> screen up, <strong>the</strong>n flips <strong>the</strong> register back<br />
down when <strong>the</strong> register changes from 0 to 7. Line 20 helps insure that <strong>the</strong> VIC chip<br />
is altered only when <strong>the</strong> TV is scanning outside <strong>the</strong> picture; <strong>the</strong> timing can be finetuned<br />
by putting in extra colons.<br />
Black bands may appear at <strong>the</strong> top or bottom of <strong>the</strong> picture. When <strong>the</strong> Y value<br />
is 3, as it is on power-up, <strong>the</strong> screen fits perfectly, but not o<strong>the</strong>rwise. <strong>The</strong> VIC chip<br />
allows <strong>the</strong> edges of <strong>the</strong> picture to be suppressed. Bit 3 in $D016 (53270) selects 38<br />
columns when set to 0, and bit 3 in $D011 (53265) selects 24 rows when set to 0.<br />
This affects only <strong>the</strong> appearance onscreen. <strong>The</strong> <strong>64</strong> still internally assumes a 40 X 25<br />
format.<br />
Add <strong>the</strong> line 5 POKE 53265, PEEK(53265) AND (255-8) to Program 12-21 and<br />
run it. <strong>The</strong> black bars are no longer displayed and <strong>the</strong> screen has one row less. In or<br />
der to demonstrate scrolling, we need to scroll <strong>the</strong> screen up at regular intervals. A<br />
simple PRINT statement, or SYS 59626, will scroll up a whole line. BASIC has no<br />
sideways scroll, so a simple demonstration has to scroll up.<br />
Add 25 IF V=7 THEN READ X$: PRINT "{HOME}{25 DOWN}" X$; to <strong>the</strong><br />
program (don't forget <strong>the</strong> semicolon), plus some DATA statements with strings.<br />
You'll see <strong>the</strong> strings scroll up, after being written invisibly to <strong>the</strong> bottom line of <strong>the</strong><br />
picture. However, it isn't possible to get a completely smooth scroll by this method,<br />
unless <strong>the</strong> text or graphics doesn't change. This is because screen scrolling typically<br />
takes about 1/25 second, so a mixture of <strong>the</strong> new and <strong>the</strong> old screen is displayed.<br />
This doesn't matter if <strong>the</strong> text is identical at each line of <strong>the</strong> screen, as you can see<br />
by altering line 25 to print some fixed X$ string.<br />
Improving this requires <strong>the</strong> use of ML. <strong>The</strong> problem is <strong>the</strong> time spent scrolling<br />
<strong>the</strong> screen. (<strong>The</strong> situation is worse for bitmapped screens, which have more data to<br />
381
Graphics<br />
move.) One scan of <strong>the</strong> TV picture takes 1/60 (U.S.) or 1/50 (U.K.) second. A full<br />
screen scroll must take less than about 1/50 second to be invisible. A full screen,<br />
and color RAM, cannot be scrolled in this time using BASIC; <strong>the</strong> example program<br />
above moves characters, not color RAM, because of this.<br />
Program 12-22. Smooth Scroll<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
5 POKE 53270, PEEK(53270) OR 16 :rem 16<br />
10 FOR J=l TO 10: C$=C$+"KLMN":NEXT :rem 99<br />
20 C$=LEFT$(C$,39) :rem 165<br />
30 POKE 53265,PEEK(53265) AND 247 :rem 174<br />
40 PRINT M{CLR}M:FOR J=l TO 25:PRINT "{DOWN}";:NEX<br />
T :rem 123<br />
50 FOR J=55296 TO 56295:POKE J,PEEK(<strong>64</strong>6 ) .-NEXT<br />
60 FOR J=49152 TO 49213:READ X:POKE J,X:NEXT<br />
:rem 250<br />
:rem 221<br />
70 POKE 53265,PEEK(53265) OR 7 :rem 26<br />
100 SYS 49152 :rem 149<br />
110 PRINT C$ "{UP}11 :rem 91<br />
115 C$=C$+CHR$(65+RND(1)*26):C$=RIGHT$(C$,39)<br />
:rem 125<br />
120 FOR Y=l TO 7 :rem 27<br />
130 SYS 49152 :rem 152<br />
140 FOR D=l TO 15:NEXT :rem 176<br />
150 NEXT Y:GOTO 100 :rem 50<br />
200 DATA 173,17,208,48,0,173,18,208,201,255,208<br />
:rem 4<br />
210 DATA 244,169,7,44,17,208,208,8,13,17,208<br />
:rem 120<br />
220 DATA 141,17,208,208,4,206,17,208,96 :rem, 125<br />
230 DATA 169,3,141,45,192,141,48,192,162,4,160,<strong>64</strong><br />
:rem 109<br />
240 DATA 185,232,7,153,192,7,200,208,247,238,45<br />
:rem 15<br />
250 DATA 192,238,48,192,202,208,238,96 :rem 96<br />
Load and run Program 12-22, <strong>the</strong>n wait for <strong>the</strong> scrolling to begin. Each SYS call<br />
in <strong>the</strong> program scrolls <strong>the</strong> screen up one dot; it does this by checking <strong>the</strong> value in<br />
<strong>the</strong> Y register and decrementing it if it exceeds 0. When it becomes 0, Y is replaced<br />
by 7 and a scroll routine is called. <strong>The</strong> ML routine is located at 49152, so SYS 49152<br />
can be used as a versatile scroll (note that it's not relocatable). Line 70 insures that<br />
<strong>the</strong> first PRINT statement in line 110 is in <strong>the</strong> invisible, bottom screen line, so <strong>the</strong><br />
scroll is completely smooth. It's easy enough to modify <strong>the</strong> register checking and<br />
character shifting to scroll left.<br />
382
Graphics<br />
User-Defined Characters<br />
This section explains how new character sets can be created for <strong>the</strong> <strong>64</strong>, by exploiting<br />
<strong>the</strong> VIC-II chip and controlling BASIC'S memory map. Some of <strong>the</strong> earlier explana<br />
tory part also applies to <strong>the</strong> creation of bitmapped graphics, dealt with in <strong>the</strong> next<br />
section, and will not be repeated <strong>the</strong>re.<br />
Using <strong>the</strong> VIC-II Chip<br />
<strong>The</strong> Video Interface Chip (VIC-II) controls <strong>the</strong> <strong>64</strong>'s screen display. Two locations,<br />
$D018 and $DD00 (53272 and 56576), are crucial in this. In BASIC, location $0288<br />
(<strong>64</strong>8) is also important.<br />
Understanding <strong>the</strong> system is not particularly easy. Everything is designed<br />
around <strong>the</strong> hardware without any attempt to ease programming tasks, so don't feel<br />
too discouraged if it appears difficult. <strong>The</strong> key is to grasp a few details of <strong>the</strong> <strong>64</strong>'s<br />
software.<br />
VIC-II is a chip with 14 address lines. Since 214=16K, <strong>the</strong> chip can address a<br />
range of 16K bytes. This is actually <strong>the</strong> minimum amount necessary to <strong>the</strong> chip,<br />
since, as we'll see, it's required to look at at least 8K bytes of character definitions in<br />
bitmap mode. It could have been designed to address all <strong>64</strong>K, but wasn't—perhaps<br />
because all <strong>the</strong> pins were already used. Unlike <strong>the</strong> VIC-20, whose VIC chip cannot<br />
look outside a narrow range of memory, <strong>the</strong> <strong>64</strong>'s VIC-II can be made to address any<br />
one of <strong>the</strong> four 16K subdivisions of <strong>64</strong> memory. Two bits in a CIA control this. <strong>The</strong><br />
CIA extends <strong>the</strong> 14-bit range to 16 bits. Although this enhances <strong>the</strong> versatility of <strong>the</strong><br />
<strong>64</strong>, it means that only a 16K range is accessible to VIC-II at any given moment.<br />
<strong>The</strong> <strong>64</strong> has all its ROM at <strong>the</strong> top of memory, but screen RAM is positioned low<br />
in memory, starting at $0400. <strong>The</strong> designers of <strong>the</strong> <strong>64</strong> included a hardware patch so<br />
that <strong>the</strong> character generator ROM and <strong>the</strong> screen could be simultaneously used by<br />
VIC-II. Character ROM appears to <strong>the</strong> 6510 to start at $D000, but <strong>the</strong> VIC-II chip is<br />
wired to see it as RAM from $1000, in <strong>the</strong> same bank as <strong>the</strong> screen. When VIC-II's<br />
registers are pointed to a different area of memory from usual, character definitions<br />
are taken from RAM. This trick is carried out by <strong>the</strong> logic array chip and is transpar<br />
ent to <strong>the</strong> programmer.<br />
User-Defined and Bitmapped Displays<br />
<strong>The</strong> <strong>64</strong> |ias two graphics modes: user-defined characters, discussed here, and<br />
bitmapped graphics. Both modes divide up <strong>the</strong> screen into 1000 square regions.<br />
User-defined characters, which are similar to <strong>the</strong> normal <strong>64</strong> characters, allow VIC-II<br />
to display up to 256 different characters. It follows that many screen characters must<br />
be identical, and in fact <strong>the</strong> screen is often largely filled with space. For example,<br />
simple line-drawing illustrations, where much of <strong>the</strong> screen is blank, in user-defined<br />
graphics use much less space than <strong>the</strong> bitmapped equivalent, so many more can be<br />
fitted into <strong>the</strong> <strong>64</strong>'s memory. A complete set of 256 characters takes 8*256=2K of<br />
memory to store. (Character ROM at $D000-DFFF is 4K, enough for both <strong>the</strong> <strong>64</strong>'s<br />
complete character sets.) Bitmapping allows every part of <strong>the</strong> screen to be individ<br />
ually controlled; with practice, this mode is easily recognized, since <strong>the</strong>re's no<br />
duplication of portions of <strong>the</strong> screen. Images like pinball or flight simulations, and<br />
383
Graphics<br />
graphics-drawing programs which allow any part of <strong>the</strong> screen to be drawn on, must<br />
use bitmapping, where 8000 bytes are needed—nearly four times as many as userdefined<br />
character sets require. (Note that reduced-sized screens in ei<strong>the</strong>r mode per<br />
mit less memory to be used.)<br />
A character editor is a program that helps you design your own user-defined<br />
characters, allowing up to 256 individual 8X8 dot characters to be designed. A full<br />
screen graphics editor is appropriate to bitmapping. Examples of each are included in<br />
this book. Sprites, if <strong>the</strong>y're used, must also be defined within <strong>the</strong> VIC-II's 16K bank.<br />
How <strong>the</strong> VIC-II Controls <strong>the</strong> Display<br />
<strong>The</strong> registers at $D018 and $DD00 control two major parameters. <strong>The</strong>se are <strong>the</strong><br />
screen starting position (or video matrix) and <strong>the</strong> character definition start (or charac<br />
ter base). Try PRINT PEEK(53272) on your <strong>64</strong> just after power-up; <strong>the</strong> result is 21,<br />
and <strong>the</strong> <strong>64</strong>'s screen starts at $0400, while <strong>the</strong> character images are at $1000.<br />
SHIFT-<strong>Commodore</strong> key changes <strong>the</strong> PEEKed value to 23, implying that <strong>the</strong> charac<br />
ter now starts at $1800 in lowercase mode. <strong>The</strong>se registers are two special cases of<br />
<strong>the</strong> VIC-II chip's control over graphics; <strong>the</strong>ir relevant bits are usually labeled as<br />
shown here:<br />
$D018 (53272) VM13 VM12 VM11 VM10 CB13 CB12 CB11 1<br />
$DD00 (56576) x x x x x x Bl B0<br />
Bit 0 of $D018 is unused; it is always set to 1. Bits 2-7 of $DD00 are irrelevant to<br />
graphics.<br />
<strong>The</strong> Screen start position is controlled by bits VM13-VM10 (<strong>the</strong> four high bits of<br />
53272), and by Bl and B0 (<strong>the</strong> two low bits of 56576). We can find <strong>the</strong> actual full 16-<br />
bit address by listing bits 0-15 and inserting <strong>the</strong> register values, like this:<br />
Bit Number: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 10<br />
Screen Start: Bl B0 VM13 VM12 VM11 VM10 0000000000<br />
Note that bits Bl and B0 are inverted (that is, a 1 value is treated as 0), and that all<br />
bits not controlled by <strong>the</strong> registers are set to 0.<br />
Usually, $D018 and $DD00 contain 12 and 191 (=%0001 0101 and %xxxx<br />
xxll). Thus, bits VM13 to VM10 are 0001, and bits Bl and B0 are both 1. <strong>The</strong> screen<br />
<strong>the</strong>refore starts at %0000 0100 0000 00000 = $0400. Because bit VM10 is <strong>the</strong> least<br />
significant programmable bit controlling <strong>the</strong> screen position, and 210=1024, screen<br />
positions can be selected only to <strong>the</strong> nearest IK. Where multiple screens are used,<br />
<strong>the</strong>y can be stored next to each o<strong>the</strong>r, but not overlapping, in RAM.<br />
<strong>The</strong> start of <strong>the</strong> character definitions is controlled by bits CB13-CB11, and by Bl<br />
and B0, in exactly <strong>the</strong> same way:<br />
Bit Number:<br />
Screen Start:<br />
15<br />
Bl<br />
14<br />
B0<br />
13<br />
CB13<br />
12<br />
CB12<br />
11<br />
CB11<br />
10<br />
VS10<br />
9<br />
0<br />
8<br />
0<br />
7<br />
0<br />
6<br />
0<br />
5<br />
0<br />
4<br />
0<br />
3<br />
0<br />
2<br />
0<br />
1<br />
0<br />
0<br />
0<br />
384
Graphics<br />
Again, Bl and BO are inverted, and all lower bits are 0. <strong>The</strong> reset <strong>64</strong> has $15 in<br />
$D018, so, taking <strong>the</strong> right nybble 0101 and dropping bit 0, we see that CB13-CB11<br />
are 010. <strong>The</strong> characters, <strong>the</strong>refore, are taken from %0001 0000 0000 0000 = $1000,<br />
<strong>the</strong> RAM image of <strong>the</strong> character-generator ROM. SHIFT-<strong>Commodore</strong> key toggles<br />
$D018 between $15 and $17, and we can see now that <strong>the</strong> second value sets <strong>the</strong><br />
right nybble of $D018 to %0111, so CB13-CB11 are now 0111. <strong>The</strong> characters now<br />
come from %0001 1000 0000 0000 = $1800.<br />
Bit CB11 has <strong>the</strong> least effect on assigning <strong>the</strong> memory region from which VIC<br />
draws its character definitions, controlling it to <strong>the</strong> nearest $800, or 2K, bytes. In<br />
o<strong>the</strong>r words, character memory is treated as though it's divided into 2K chunks by<br />
<strong>the</strong> <strong>64</strong>'s VIC chip. This allows multiple character sets to be placed in memory ad<br />
jacent to each o<strong>the</strong>r. One example is <strong>the</strong> built-in graphics and lowercase sets, which<br />
are adjacent in memory.<br />
How Characters Are Displayed<br />
Figure 12-4 shows how screen RAM and character definitions are combined by <strong>the</strong><br />
VIC-II chip as it generates <strong>the</strong> TV display. In <strong>the</strong> example <strong>the</strong> top-left screen contains<br />
a space, <strong>the</strong>n ABC; <strong>the</strong>se appear as <strong>the</strong>ir ASCII values 32, 65, 66, and 67. When <strong>the</strong><br />
VIC-II chip generates <strong>the</strong> TV scan for <strong>the</strong> top lines, it looks at screen RAM for <strong>the</strong><br />
current character, multiplies this by 8, and offsets <strong>the</strong> result from <strong>the</strong> characterdefinition<br />
start. This takes it to <strong>the</strong> eight bytes which define <strong>the</strong> character. <strong>The</strong> byte<br />
selected depends on <strong>the</strong> line being scanned; <strong>the</strong> first line gets <strong>the</strong> first byte, <strong>the</strong> sec<br />
ond line <strong>the</strong> second byte, and so on. Whe<strong>the</strong>r a pixel is set depends on <strong>the</strong> bit value<br />
within <strong>the</strong> byte, and also on <strong>the</strong> color information, which VIC-II takes from color<br />
RAM. All this intricate work is performed by <strong>the</strong> VIC-II.<br />
To illustrate, consider a CHR$(32) at <strong>the</strong> top left of <strong>the</strong> screen. <strong>The</strong> VIC-II cal<br />
culates 8*32=256, and offsets this from <strong>the</strong> character base, which typically is $1000<br />
as <strong>the</strong> chip sees it. <strong>The</strong> bytes read are-256-263 (decimal) locations away from $1000.<br />
<strong>The</strong>y hold <strong>the</strong> <strong>64</strong> bits of information needed to define CHR$(32). Normally, <strong>the</strong>se<br />
bits are all 0, since CHR$(32) appears as <strong>the</strong> space character. Similarly, any number<br />
from 0 to 255 in screen RAM is automatically converted into its bit equivalent. It's<br />
clear that a full 256 characters aren't necessary; a game, for example, might have a<br />
number of frogs, aliens, spaceships, or whatever, some made up from several graph<br />
ics to increase <strong>the</strong> size, without using <strong>the</strong> full set.<br />
385
00<br />
ON<br />
Figure 12-4. VIC-II Screen and Character Management<br />
Character Base Address<br />
Character Definitions: I chr$(0) CHR$(1)<br />
CHR$(32) CHR$(65) CHR$(255)<br />
Bytes: 0-7 8-15<br />
256-263 520-527 2040-2047<br />
/ / / / / / / / /<br />
/<br />
/'<br />
Display<br />
VIC-II *<br />
A B C<br />
40 cells<br />
w<br />
w<br />
\\<br />
\ X<br />
\ \<br />
\ \<br />
\ \.<br />
\ \.<br />
\ ^-^<br />
i Start Address<br />
Screen RAM:<br />
Bytes: 999<br />
Color RAM:<br />
J_L<br />
+ Background Color Register(s)<br />
o<br />
3<br />
5
Graphics<br />
Memory Maps of <strong>the</strong> <strong>64</strong> with User-Defined Graphics<br />
<strong>The</strong> memory map in Figure 12-5 shows all <strong>the</strong> <strong>64</strong>'s memory features needed to de<br />
sign your own characters and incorporate <strong>the</strong>m in your programs. Advanced meth<br />
ods are possible—<strong>the</strong> use of several screens, allowing animation or instantaneous<br />
switching between displays, <strong>the</strong> use of several character sets, and <strong>the</strong> use of inter<br />
rupts for split-screen effects—and <strong>the</strong>y rely on <strong>the</strong> same principles.<br />
Figure 12-5. User-Defined Graphics Memory Maps<br />
VIC-II<br />
BankO<br />
Bank 1 Bank 2 Bank 3<br />
Chr.<br />
Image<br />
Chr.<br />
Imagei<br />
0 400 800<br />
4000 8000 9000 A000 C000 D000 E000 FFFF<br />
RAM<br />
j<br />
N<br />
I<br />
BASIC<br />
RAM Area<br />
1<br />
1<br />
RAM<br />
under ROM<br />
Free<br />
RAM<br />
RAM<br />
under<br />
Chr. ROM,<br />
I/O<br />
RAM<br />
under<br />
Kemal<br />
43/44 = Prog. Start<br />
45/46 = Prog. End<br />
5 i/56 = End of BASIC Area.<br />
ROM, I/O BASIC ROM RAM Chr. ROM<br />
I/O<br />
Kerrial<br />
ROM<br />
VIC-II's current bank at any one time must contain screen, character, and sprite<br />
data. But BASIC normally starts at $0800 and ends where <strong>the</strong>re's hardware, ei<strong>the</strong>r at<br />
$9FFF or, if an autorun cartridge is present which returns to BASIC, at $7FFF. To<br />
ge<strong>the</strong>r, <strong>the</strong>se facts mean that a long BASIC program with user-defined characters<br />
must ei<strong>the</strong>r store characters at <strong>the</strong> low end of BASIC, followed by BASIC itself, start<br />
ing higher in RAM than usual; store <strong>the</strong>m after <strong>the</strong> BASIC program, but before BA-<br />
SIC's area for variables; or store <strong>the</strong>m higher in memory, in banks 1, 2, or 3. In any<br />
of <strong>the</strong>se cases, it's necessary to alter some of <strong>the</strong> pointers in (43-44), (45-46), and<br />
(55-56) to alter <strong>the</strong> boundaries allocated to BASIC, unless <strong>the</strong> characters are stored<br />
under ROM, with <strong>the</strong> screen RAM at or near $C000. (This is quite simple, though<br />
it's best to avoid <strong>the</strong> I/O area. Note that <strong>the</strong>re's a possible problem if you use <strong>the</strong><br />
area to edit characters, because, being under ROM, <strong>the</strong>y're difficult to PEEK in <strong>the</strong><br />
usual way, and are <strong>the</strong>refore difficult to save; VIC-II, of course, is designed to PEEK<br />
<strong>the</strong>m without any problem.)<br />
387
Graphics<br />
User-Defined Graphics Examples<br />
Bank switching. Bits 0 and 1 in $DDOO (56576) control bank switching. Strictly, bits<br />
0 and 1 in $DD01 (56577) could also be set to 1, but as <strong>the</strong>y are 1 already unless<br />
specifically changed, <strong>the</strong>se bits can usually be left to take care of <strong>the</strong>mselves. Enter<br />
and run <strong>the</strong> following one-line program:<br />
0 INPUT X: X=3-X: POKE 56576,(PEEK(56576) AND 252) OR X: GOTO 0<br />
Entering 0 selects <strong>the</strong> normal default, bank 0, so <strong>the</strong>re's no change. Note <strong>the</strong> use of<br />
3—X which converts 0 to 3; this allows for <strong>the</strong> fact that <strong>the</strong> two controlling bits are<br />
inverted.<br />
Entering 2 gives garbage, because bank 2 now treats RAM at $8400 as <strong>the</strong> start<br />
of screen. <strong>The</strong> characters are displayed as normal <strong>64</strong> characters, however, because<br />
<strong>the</strong> ROM character set is active in this bank. Entering 1 or 3 displays a screenful of<br />
characters which aren't properly defined—<strong>the</strong>y're from RAM starting at $5000 or<br />
$D000, respectively. Try exiting <strong>the</strong> program and executing FOR J=33792 TO 34047:<br />
POKE J,X: X=X+1: NEXT which puts 0-255 into $8400 on. You'll find that bank 2<br />
now displays all 256 normal characters at <strong>the</strong> top of <strong>the</strong> screen, color RAM<br />
permitting.<br />
Moving <strong>the</strong> screen. <strong>The</strong> following line will move <strong>the</strong> screen in sixteen IK steps<br />
within its current bank (insert a value in <strong>the</strong> range 0-15 for X):<br />
POKE 53272/(PEEK(53272) AND 15) OR 16*X<br />
<strong>The</strong> two lines below will allow you to instruct VIC-II to treat $0, $0400, $0800,<br />
and so on, as <strong>the</strong> screen start. Enter 2, for example, to select $0800. You'll see <strong>the</strong><br />
BASIC program at <strong>the</strong> top of <strong>the</strong> screen. (SHIFT-<strong>Commodore</strong> key may make it<br />
clearer.)<br />
0 INPUT S: POKE 53281,247: REM MAKE CHRS VISIBLE<br />
1 POKE 53272,(PEEK(53272) AND 15) OR 16*S: GOTO 0<br />
Enter 0 to make <strong>the</strong> screen from <strong>the</strong> zero page on, so <strong>the</strong> region including <strong>the</strong> stack<br />
and <strong>the</strong> keyboard buffer is displayed. Entering 1 returns <strong>the</strong> screen to <strong>the</strong> normal<br />
position.<br />
Moving <strong>the</strong> character definitions. Enter and run <strong>the</strong> following line:<br />
0 INPUT C: POKE 53272,(PEEK(53272) AND 240) OR 2*C: GOTO 0<br />
As C varies from 0 through 7, <strong>the</strong> character definitions are taken by VIC-II from $0,<br />
$0800, $1000, and so on, in 2K steps. Two interesting examples are C=0 and C=3:<br />
<strong>the</strong> former draws character definitions from <strong>the</strong> zero page, which means that some<br />
of <strong>the</strong>m continually fluctuate with background BASIC activity. When C is 3, charac<br />
ters start from $1800, so <strong>the</strong> screen goes into lowercase mode. Toggling with<br />
SHIFT-<strong>Commodore</strong> key alternates with uppercase.<br />
Bank 0 characters can't occupy $1000-$lFFF, since VIC-II fetches data in this<br />
area from ROM. <strong>The</strong>y could be positioned anywhere else, subject to some restric<br />
tions. RAM from $0800 on can be used, but <strong>the</strong> start of BASIC would have to be<br />
moved up; $00 is usable, too, but an entire set of 256 characters couldn't be defined<br />
Bank 1 characters can be placed in any of <strong>the</strong> eight available areas. Bank 2 is accept<br />
able except for $9000-$9FFF, where VIC-II uses <strong>the</strong> ROM sets. Bank 3 is all avail<br />
able, though $D000-$DFFF RAM can be POKEd only after switching out I/O.<br />
388
Graphics<br />
Moving <strong>the</strong> character ROM into RAM. This requires care to avoid I/O and<br />
ROM selection clashes by VIC-II. Bit 2 of location 1 controls whe<strong>the</strong>r ROM or I/O<br />
appears from $D000 on. As an example, delete GOTO 0 from line 0 directly above,<br />
and add <strong>the</strong> following:<br />
1 POKE 56333,127: POKE 1,51<br />
2 FOR J=0 TO 2047: POKE 12288+X,PEEK(53248+X): X=X+1: NEXT<br />
3 POKE 1,55: POKE 56333,129<br />
4 POKE 56,48: CLR<br />
Line 2 moves 2K of character ROM from $D000-$D7FF down to $3000-$37FF.<br />
Lines 1 and 3 control <strong>the</strong> hardware line which turns on <strong>the</strong> ROM. Note that one of<br />
<strong>the</strong> POKEs turns off IRQ interrupts, which is necessary since <strong>the</strong>y make use of <strong>the</strong><br />
I/O chips. NMI interrupts aren't turned off; when using this, don't press<br />
RUN/STOP-RESTORE, or you'll crash <strong>the</strong> program. Line 4 lowers <strong>the</strong> top of BASIC<br />
to $3000, protecting <strong>the</strong> character set in RAM, and incidentally dramatically reducing<br />
<strong>the</strong> free bytes available to BASIC.<br />
Run <strong>the</strong> program, entering 6 to point <strong>the</strong> VIC-II to fetch its characters from<br />
$3000. When READY comes back, toggling with SHIFT-<strong>Commodore</strong> key produces<br />
garbage characters—since <strong>the</strong> lowercase set is not copied.<br />
Toggle back to <strong>the</strong> readable characters and enter <strong>the</strong> following direct mode line:<br />
FOR J=0 TO 255: FOR K=0 TO 7: POKE 14336+8*J+K,PEEK(12288+8*J+7-K): NEXT:<br />
NEXT<br />
This copies our RAM characters into $3800 on, but inverts <strong>the</strong>m. Now<br />
SHIFT-<strong>Commodore</strong> key toggles between ordinary and upside-down characters.<br />
Backward and o<strong>the</strong>r modified characters are also feasible. It's possible to have 80-<br />
column alphanumerics on <strong>the</strong> <strong>64</strong>. Unfortunately, <strong>the</strong>y're truly readable only with a<br />
monitor, not a TV.<br />
Saving and reloading character definitions. Character definitions (and screens)<br />
can be saved as DATA statements or as sequential files (written by PRINT#, read<br />
back by INPUT#) or, most efficiently, as program files, that is, as a block of data to<br />
be loaded back, typically with a forced (nonrelocatable) LOAD like this at <strong>the</strong> start of<br />
a program:<br />
0 IF X=0 THEN X=l: LOAD "CHRS",8,1.<br />
Chapter 6 explains how to save blocks of data; one method is used in <strong>the</strong> character<br />
editor program that follows.<br />
If you're familiar with <strong>the</strong> internal workings of BASIC, you'll appreciate that<br />
characters can be saved along with BASIC, as a single program, which makes a<br />
convenient package. All that's necessary is to manipulate <strong>the</strong> pointers at 43-44<br />
and/or 45-46, which mark <strong>the</strong> limits of <strong>the</strong> program which will be saved to tape or<br />
disk, and/or <strong>the</strong> pointer at 55-56 to <strong>the</strong> end of RAM allocated to BASIC.<br />
One way of including characters in BASIC is to follow <strong>the</strong> program almost im<br />
mediately with character definitions—<strong>the</strong>re must be a 2K boundary, however. This is<br />
mapped in Figure 12-6 below:<br />
389
Graphics<br />
Figure 12-6. Character Definitions Below BASIC Variables<br />
BASIC Program Char. Defns. BASIC Variables Space<br />
43/44 45/46 55/56<br />
<strong>The</strong> principle is exactly <strong>the</strong> same as saving BASIC followed by ML. All that's<br />
needed is to alter <strong>the</strong> address in (45-46) to point after <strong>the</strong> characters. This will save<br />
satisfactorily, and will also run properly when reloaded, assuming it loads back into<br />
<strong>the</strong> same RAM area.<br />
Figure 12-7 shows characters at <strong>the</strong> end of BASIC. This is a typical situation<br />
when bank 0 is used, and character definitions are stored from $3000 (if <strong>the</strong>re are<br />
two sets) or $3800 (if <strong>the</strong>re's just one).<br />
Figure 12-7. Character Definitions Above BASIC Variables<br />
BASIC Program BASIC Variables Space Char. Defns.<br />
43/44 45/46 55/56<br />
It's easy to save such a program: just POKE 45,0: POKE 46,<strong>64</strong> to force <strong>the</strong> <strong>64</strong> to<br />
save from BASIC start, right up to $4000. On LOAD, this program will store its vari<br />
ables after BASIC and will work perfectly well, becoming converted into <strong>the</strong> first<br />
type of program/character definitions. In fact, it may well have more RAM than it<br />
would have if its variables were forced to exist below <strong>the</strong> characters. But it makes<br />
sense, particularly with tape, to keep <strong>the</strong> characters after BASIC without too great a<br />
gap, as in <strong>the</strong> first example; saving right up to $4000 means saving about 14K bytes.<br />
<strong>The</strong> only problem where redefined characters are saved above BASIC and vari<br />
ables is that editing BASIC will probably disrupt <strong>the</strong> display. Changing a line or two<br />
moves <strong>the</strong> whole character set in RAM, so <strong>the</strong> characters alter with <strong>the</strong> program<br />
length. Obviously, this doesn't matter with a finished program, but where it's im<br />
portant to be able to edit, include this line:<br />
0 POKE 45,000: POKE 46,000: POKE 55,0: POKE 56,48: CLR<br />
This will allow you to put different values into 45 and 46. (<strong>The</strong>y must be put in just<br />
before saving <strong>the</strong> program. <strong>The</strong> three zeros allow any figure to be input without<br />
changing <strong>the</strong> program's length, 013 for 13, for example.)<br />
Memory Configurations with<br />
User-Defined Characters<br />
<strong>The</strong> examples show how to go about arranging memory as you choose. <strong>The</strong> simplest<br />
method stores characters below $4000, so bank switching isn't needed.<br />
VIC-II in bank 0. Single character set starting at $3800. This example shows<br />
BASIC'S variables stored below <strong>the</strong> character set; <strong>the</strong>y could just as easily be stored<br />
above, by putting POKE 45,0: POKE 46, <strong>64</strong>: CLR in <strong>the</strong> program, to move <strong>the</strong> endof-program<br />
up to $4000.<br />
390
Graphics<br />
Figure 12-8. VIC-II Batik 0, Characters at $3800<br />
$0<br />
$3800 $4000<br />
BASIC Prg. BASIC Vars. Chrs.<br />
Program 12-23. Mosaic<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
10 FOR J=0 TO 39:READ X:POKE 14336+J,X:NEXT<br />
:rem 152<br />
20 POKE <strong>64</strong>6,0:POKE 53280,1:POKE 53281,1:PRINT "<br />
{CLR}"; *rem 247<br />
30 POKE 53272,31 «rem 37<br />
40 POKE 55,0:POKE 56,56:CLR :rem 223<br />
50 GET Q$:IF Q$="" GOTO 50 srem 23<br />
60 L=ASC(Q$)-<strong>64</strong> srem 35<br />
70 A$=IIM: FOR J=l TO L: A$=A$+CHR$( RND(0)*5+<strong>64</strong>) :NE<br />
XT :rem 165<br />
80 A$=A$+A$+A$+A$+A$+A$+A$+A$:M=LEN(A$) : rem 184<br />
90 PRINT "{HOME}";:FOR J=l TO 960/M:PRINT A$;:NEXT<br />
:rem 255<br />
100 PRINT LEFT$(A$,960-M*INT(960/M));<br />
110 GOTO 50<br />
500 DATA 36,36,255,0,0,255,36,36<br />
501 DATA 36,36,231,36,36,231,36,36<br />
502 DATA 36,18,9,132,66,33,144,72<br />
503 DATA 36,72,144,33,66,132,9,18<br />
504 DATA 36,66,153,36,66,36,153,66<br />
:rem 36<br />
*rem 48<br />
:rem 31<br />
:rem 134<br />
:rem 90<br />
:rem 91<br />
:rem 152<br />
Program 12-23, above, is memory-mapped as Figure 12-8 shows. It uses only<br />
five characters, which print as @, A, B, C, and D, and PEEK as 0-4. (Try RUN/STOP,<br />
<strong>the</strong>n type keys @ through D.) Line 10 POKEs in <strong>the</strong> characters' bit patterns; line 30<br />
points VIC-II's character base to $3800 and line 40 puts BASIC'S topmost byte just<br />
below $3800.<br />
VIC-II in bank 3. Character sets under Kernal ROM. <strong>The</strong> screen RAM is<br />
moved to $CC00, and <strong>the</strong> character sets to $E000-$EFFF. This requires POKE <strong>64</strong>8,204:<br />
POKE 53272,57: POKE 56576, PEEK(56576) AND 252 to change <strong>the</strong> VIC-II chip,<br />
and also <strong>the</strong> following:<br />
0 POKE 56333,127: POKE 1,51: FOR J=0 TO 4095<br />
1 POKE 57344+J/PEEK(53248+J): NEXT: POKE 1,55: POKE 56333,129<br />
After this, characters are taken from $E000 on. <strong>The</strong>y can be altered by POKEing,<br />
but cannot easily be read back. <strong>The</strong> usual screen area from $400 to $7FF is now un<br />
used, and RAM from $C000 to $CBFF can be used for ML. No BASIC pointers need<br />
be set since <strong>the</strong> characters aren't in a part of RAM which BASIC can overwrite.<br />
391
Graphics<br />
Switching between several screens in bank 1 for animation. Sixteen full<br />
screens can be stored between $4000 and $7FFF. Switching between <strong>the</strong>m requires<br />
one POKE, and <strong>the</strong> effect is instantaneous, allowing animation. <strong>The</strong> screens share<br />
color RAM, so <strong>the</strong> easiest arrangement is to keep <strong>the</strong> color identical in all <strong>the</strong><br />
screens. <strong>The</strong> difficult part is <strong>the</strong> design of all <strong>the</strong> screens.<br />
Character Editor<br />
<strong>The</strong> following character editor, Program 12-24, processes 2K bytes, <strong>the</strong> definitions of<br />
<strong>the</strong> first 256 characters in ROM, which are stored at $3000-$37FF (12888-14335).<br />
<strong>The</strong> finished definitions can be stored and reloaded from tape or disk, giving a<br />
permanent record of your new characters.<br />
Program 12-24. Character Editor<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix G<br />
6 POKE 56,48: CLR: SCREEN=10<strong>64</strong>: VIC=53248: CHARS=1<br />
2288 srem 209<br />
8 DEF FN RV(Q)=Q+128*(1+(Q>127)*2) :rem 230<br />
11 DIM CO$(15):FOR J=0 TO 15:READ CO$(J):NEXT<br />
srem 170<br />
12 FOR J=49152 TO 49213:READ X:POKE J,X:NEXT<br />
:rem 218<br />
18 INPUT "FETCH CHARACTERS FROM ROM";X$ :rem 96<br />
19 IF X$="Y" THEN GOSUB 500 :rem 147<br />
22 POKE <strong>64</strong>6,0:POKE 650,128:PRINT "{CLR}11 :rem 248<br />
27 FOR J=0 TO 127:POKE SC+2M+401,J:POKE SC+J*2+72<br />
1,J+128:NEXT :rem 148<br />
40 POKE VIC+24,(PEEK(VIC+24)AND240) OR 12 :rem 217<br />
100 NC=0:OF=NC*8:GOSUB 3000 :rem 11<br />
140 POKE 53280,5:POKE 53281,5:POKE 53282,2:rem 191<br />
146 POKE 53283,8:POKE 48197,0:SYS 49188:GOSUB 2000<br />
0 :rem 248<br />
180 XC=0:YC=0:CC=SC:POKECC,PEEK(CC)OR128 :rem 202<br />
200 GET X$:IFX$=MM THEN 200 :rem 117<br />
202 IF X$=M{UP}" AND YC> 0 THEN YC=YC-1: GOTO 250<br />
:rem 154<br />
204 IF X$="{RIGHT}" AND XC
Graphics<br />
236 FOR Z=l TO 7:IF X$MID$("=DCIMT",Z,1) THEN NE<br />
XT :rem 117<br />
237 ON Z GOSUB 6000,7000,9000,9500,10000,10500<br />
:rem 250<br />
239 IF Z>0 AND Z
Graphics<br />
9000 FOR J=CH+OF TO CH+OF+7:POKE J,0:NEXT:GOSUB 30<br />
00 :rem 215<br />
9025 IF YC
Graphics<br />
21030 CP=CP+1: IF(CB AND 1)=1 THEN POKE CP,81:GOTO<br />
21050 :rem 183<br />
21040 POKE CPf45 :rem 78<br />
21050 GOSUB4000;PT=CH+YC+OF:BY=PEEK(PT) :rem 149<br />
21060 MP=7-XC AND 6:MK=2tMP*3:BY=(BY AND NOT MK) +<br />
CB*2tMP :rem 213<br />
21070 POKE PT,BY:POKE 214,YC:PRINT :rem 35<br />
21080 PRINT TAB(9)"{3 SPACES}{4 LEFT}" PEEK(PT):RE<br />
TURN :rem 213<br />
When you run <strong>the</strong> character editor, you're asked FETCH CHARACTERS FROM<br />
ROM? Enter Y to call an ML routine to copy character ROM into RAM. O<strong>the</strong>rwise,<br />
characters already present at $3000 are retained. <strong>The</strong> screen displays all 256 of <strong>the</strong><br />
RAM characters in <strong>the</strong> bottom half of <strong>the</strong> screen. <strong>The</strong> top displays an enlarged ver<br />
sion of <strong>the</strong> current character plus its screen code—for example, @ is shown as 0—<br />
and a list of <strong>the</strong> four current color settings. (All character colors are <strong>the</strong> same.)<br />
<strong>The</strong>re's also a central scratch-pad area where several graphics can be placed so that<br />
<strong>the</strong>ir joint effect can be checked. This is useful where several graphics characters to<br />
ge<strong>the</strong>r build a larger composite character.<br />
Editing is done by moving <strong>the</strong> inverted cursor with <strong>the</strong> usual cursor control keys<br />
on <strong>the</strong> enlarged 8X8 diagram, and typing <strong>the</strong> space bar to invert <strong>the</strong> dot beneath<br />
<strong>the</strong> cursor. <strong>The</strong> result of <strong>the</strong> modifications is instantly visible in <strong>the</strong> display at <strong>the</strong><br />
screen bottom and on <strong>the</strong> scratch area.<br />
New characters are selected for editing by moving <strong>the</strong> cursor to <strong>the</strong> lower part of<br />
<strong>the</strong> screen and typing an equal sign (=) over <strong>the</strong> chosen character. <strong>The</strong>n home <strong>the</strong><br />
cursor and edit.<br />
O<strong>the</strong>r commands are:<br />
C Clears a character, setting all bits 0.<br />
D Draws <strong>the</strong> current character in <strong>the</strong> scratch area.<br />
I Inverts <strong>the</strong> current character's bits.<br />
T With <strong>the</strong> cursor over a character in <strong>the</strong> bottom half of <strong>the</strong> screen, transfers<br />
<strong>the</strong> current character's definition <strong>the</strong>re—now <strong>the</strong>re are two of <strong>the</strong>m.<br />
f2 Advances <strong>the</strong> screen background color.<br />
f8 Advances <strong>the</strong> text color.<br />
M Toggles between ordinary and multicolor modes. <strong>The</strong> text color must be<br />
from orange to gray 3 for multicolor mode to show. When it does, keys fl,<br />
f3, f5, and 17 set pairs of points to 00, 01, 10, and 11, corresponding to <strong>the</strong><br />
four colors. Keys f4 and f6 also advance background colors 1 and 2.<br />
L and S Load and save (respectively) <strong>the</strong> character set, allowing your choice of file<br />
name and device.<br />
Applications could include <strong>the</strong> creation of chess and o<strong>the</strong>r game pieces, special<br />
alphabet sets, musical and o<strong>the</strong>r notations, monsters, bombs and so on, for arcade<br />
games If you're going to PRINT your graphics, remember that <strong>the</strong> second batch of<br />
128 characters on <strong>the</strong> screen is generated with {RVS}, even though characters need<br />
<strong>The</strong> early part of <strong>the</strong> program handles initialization. Lines 200-250 handle <strong>the</strong><br />
keypresses and call appropriate subroutines.<br />
395
Graphics<br />
Bitmapped Graphics<br />
Bitmap mode gives <strong>the</strong> best graphics available on <strong>the</strong> <strong>64</strong>. However, <strong>64</strong> BASIC has no<br />
specific commands to handle this mode: in fact, it would be possible to program a <strong>64</strong><br />
for years and never discover that bitmap mode existed.<br />
Bitmapping allows each dot on <strong>the</strong> screen to be controlled. Since VIC-II maps<br />
<strong>the</strong> screen into individual characters of 8 X 8 pixels, and displays 40 X 25 of <strong>the</strong>se<br />
characters, <strong>the</strong>re are 320 X 200 = <strong>64</strong>000 addressable dots. (In practice, resolution<br />
isn't this good, because, for example, ordinary TVs cannot display alternate on/off<br />
dots without color interference, and some color combinations don't have sufficient<br />
contrast to be properly distinguishable, even on color monitors.) <strong>The</strong>se figures still<br />
apply if <strong>the</strong> screen is narrowed or shortened—see <strong>the</strong> earlier section on smooth<br />
scrolling—or mixed with ordinary text by <strong>the</strong> use of split-screen techniques ex<br />
plained below, but offscreen graphics are obviously less important.<br />
Bitmapping is a more accurate expression than high resolution, with which it's<br />
often confused. Bitmapping resolution is in fact identical to that of normal characters.<br />
<strong>The</strong> distinction should be between high resolution and multicolor mode.<br />
A bitmap is 8000 consecutive bytes (not quite 8K, which is 8192 bytes), enough<br />
to map <strong>the</strong> whole screen as <strong>64</strong>,000 bits. <strong>The</strong> display is treated by VIC-II just like<br />
1000 consecutive user-definable characters. Bitmap mode is selected by bit 5 in<br />
53265 ($D011).<br />
In bitmap mode, since each bit in <strong>the</strong> bitmap can only be on or off, just two<br />
alternatives exist for each point on <strong>the</strong> screen, which means a choice of two colors.<br />
VIC-II allows each of <strong>the</strong> 1000 characters 2 independent colors, selectable from <strong>the</strong><br />
full range of 16 colors. In each bitmapped character, <strong>the</strong> 2 colors are not set in color<br />
RAM, which has only one usable nybble. Instead, <strong>the</strong>y're controlled by screen RAM,<br />
<strong>the</strong> area, usually starting at $400, treated by BASIC as <strong>the</strong> screen, which of course<br />
has two nybbles available for each character. This new usage can be confusing.<br />
Unlike all o<strong>the</strong>r modes, <strong>the</strong> screen's normal background color setup is no longer<br />
operative.<br />
You don't control <strong>the</strong> color of every dot on <strong>the</strong> screen, though. One reason is <strong>the</strong><br />
memory requirement: a choice of 16 colors per bit would require 32K of RAM to<br />
store <strong>the</strong> full bitmap.<br />
Multicolor mode is selected by bit 4 in 53270 ($D016). Where multicolor mode<br />
is used with bitmap mode, <strong>the</strong>re's <strong>the</strong> usual trade-off—pairs of bits toge<strong>the</strong>r allow a<br />
choice of 4 colors. Each of <strong>the</strong> 1000 characters has a choice of 3 colors, plus a com<br />
mon background color. All <strong>the</strong> colors are selected from <strong>the</strong> full palette of 16 colors.<br />
Any <strong>64</strong> graphics design program, and generally flight-simulators and games<br />
where <strong>the</strong> entire screen is filled without repetition, must be bitmapped. (Sprites can<br />
sometimes give a similar impression, though.) <strong>The</strong> high-resolution bitmap mode has<br />
finer resolution than <strong>the</strong> multicolor version, but is less colorful—except in <strong>the</strong> sense<br />
of being more prone to unwanted color fringing. For example, you may find an<br />
adventure game including black line drawings on white, which are colored by a fillin<br />
color (unless <strong>the</strong>y're on a boundary), since three colors can't coexist in one 8 X 8<br />
area. Multicolor mode builds <strong>the</strong> picture from 160 short horizontal bit pairs by 200<br />
down. This is more versatile than regular <strong>64</strong> multicolor graphics; <strong>the</strong> background<br />
color is in common, but all <strong>the</strong> o<strong>the</strong>r three colors are independently variable within<br />
396
Graphics<br />
every 8X8 block. This allows <strong>the</strong> character boundaries, where colors can change, to<br />
be made imperceptible, at <strong>the</strong> cost of some loss in resolution.<br />
<strong>The</strong> VIC-II Chip and Bitmapping<br />
Everything about <strong>the</strong> VIC-II chip in <strong>the</strong> previous section applies to bitmap mode, ex<br />
cept that <strong>the</strong> character base can only be made to start at 8K intervals (at $0, $2000,<br />
$4000, $6000, . . . $E000). Since 8000 bytes are necessary for a bitmap, this is<br />
reasonable enough. <strong>The</strong>re are eight possible positions for <strong>the</strong> bitmap area, and <strong>the</strong>re<br />
fore two possible positions within each of <strong>the</strong> four VIC-II banks. Note that bits 1 and<br />
2 in $D018 have no effect; it's not possible to adjust <strong>the</strong> bitmap character base to <strong>the</strong><br />
nearest $800 bytes.<br />
Screen RAM is controlled by VIC-II as in character mode, but interpreted dif<br />
ferently, as color information. With BASIC, this gives odd effects, as we'll see. Mean<br />
while, BASIC bitmap programs under development should be run from time to time<br />
with <strong>the</strong> bitmap POKEs REMed out, since syntax errors won't be readable in bitmap<br />
mode. O<strong>the</strong>rwise, you may not even know that a syntax error has occurred. Remem<br />
ber also that if you've used RUN/STOP-RESTORE to get back to normal, POKE<br />
<strong>64</strong>8,4 will be needed if <strong>the</strong> screen was moved.<br />
How to Use Bitmapping<br />
Bitmap mode is interesting, but needs to be approached with caution. Here is a brief<br />
discussion of when its use is appropriate.<br />
Normal BASIC screen commands don't work in bitmap mode, since <strong>the</strong> charac<br />
ters and screen are organized differently. <strong>The</strong>refore, POKEs and often PEEKs are<br />
essential. <strong>The</strong> 8X8 organization means it's essential to go through a conversion<br />
process. If you want to locate a pixel 100 dots across and 50 dots down <strong>the</strong> screen, it<br />
must be translated into <strong>the</strong> bit definition corresponding to <strong>the</strong> twelfth character<br />
across and seventh down <strong>the</strong> screen; <strong>the</strong>n <strong>the</strong> byte and its bit have to be found. This<br />
illustrates that ML is necessary if you cannot tolerate delays. BASIC is too slow even<br />
to clear <strong>the</strong> bitmapping screen satisfactorily.<br />
Bitmapped pictures can be loaded from disk and tape. All that's needed is to<br />
first save <strong>the</strong> relevant 8K bytes and <strong>the</strong>n save <strong>the</strong>ir screen RAM for color infor<br />
mation. Later a forced LOAD of both, with VIC-II set, reconstructs <strong>the</strong> picture. This<br />
works well; a disk can hold about 18 such pictures. Where this isn't suitable, <strong>the</strong> bit<br />
map can be loaded with <strong>the</strong> program. If movement is wanted, <strong>the</strong> 8K bitmap itself<br />
must be processed, probably with ML.<br />
Fairly simple pictures can be effective. <strong>The</strong> most efficient method is to draw <strong>the</strong><br />
figures while <strong>the</strong> program runs. Fairly simple line-segment drawings can be com<br />
posed like this more quickly than loading 8K of data. Subroutines to draw a line seg<br />
ment between two points and to color an enclosed region are often used.<br />
Several bitmaps can coexist in RAM; switching between <strong>the</strong>m requires a simple<br />
POKE, which can also change <strong>the</strong> color RAM. So it's possible to store two or more<br />
completely independent pictures and instantly switch from one to <strong>the</strong> o<strong>the</strong>r. How<br />
ever, this uses a lot of memory.<br />
397
Graphics<br />
Memory Maps in<br />
Bitmap Mode<br />
Figure 12-9 shows all eight available bitmap positions. Two of <strong>the</strong>se can't be used<br />
for full-screen bitmaps, since <strong>the</strong> ROM characters appear, immovably, in <strong>the</strong> lower<br />
half of <strong>the</strong> screen. Because $C000-$DFFF occupies what could be useful free RAM<br />
and also risks conflict with I/O, it is easier to use ano<strong>the</strong>r location.<br />
Figure 12-9. Available Bitmap Addresses<br />
$0 $2000 $4000 $6000 $8000 $A000 $C000 $E000 $FFFF<br />
BASIC<br />
Workspace<br />
+ Chr. ROM<br />
Chr. ROM<br />
Image<br />
BASIC<br />
ROM<br />
I/O<br />
Kernal<br />
ROM<br />
VIC Bank<br />
0 1<br />
2<br />
3<br />
Of <strong>the</strong> five o<strong>the</strong>r regions, $2000-$3FFF involves no bank switching and is<br />
easiest, but <strong>the</strong> o<strong>the</strong>rs aren't much more difficult. Remember that each bitmap screen<br />
must have 1000 bytes in <strong>the</strong> same bank for its color. So if $A000 onward holds <strong>the</strong><br />
bitmap, <strong>the</strong> colors must start somewhere like $8C00, avoiding character-ROM<br />
images.<br />
Bitmaps can be tucked away below ROM. VIC-II uses <strong>the</strong>se regions happily. <strong>The</strong><br />
drawback is that <strong>the</strong>y can't be PEEKed without switching ROM out; this makes<br />
graphics which need to be altered (for example, to give <strong>the</strong> effect of motion) slightly<br />
trickier.<br />
Note that $4000-$7FFF can't be used to store two full bitmaps, because <strong>the</strong>re's<br />
no place to put <strong>the</strong> color screens in <strong>the</strong> same bank. If you want to switch between<br />
full screens, it's <strong>the</strong>refore necessary to have bitmaps and color RAM in two or more<br />
different banks. If <strong>the</strong> positions are similar in each, a single POKE to bank select will<br />
switch between <strong>the</strong>m. This applies to full screens. However, it's possible to overlap<br />
<strong>the</strong> color with <strong>the</strong> bitmap—for example, by starting <strong>the</strong> bitmap at $2000 and <strong>the</strong><br />
screen at $3C00. At <strong>the</strong> screen bottom, 104 characters (about 2-1/2 lines) will echo<br />
<strong>the</strong> colors in bitmap form. This is acceptable with split-screen techniques. It's <strong>the</strong><br />
only way two full bitmaps can occupy <strong>the</strong> same VIC bank.<br />
Calculations<br />
One way to visualize bitmapping is to imagine that all 8000 bytes are divided into<br />
25 sets of 320 bytes. Each 320-byte block corresponds to a horizontal line, eight dots<br />
high, on <strong>the</strong> screen. Ano<strong>the</strong>r way to visualize bitmapping is to consider <strong>the</strong> infor<br />
mation as 1000 eight-byte chunks of memory-defined characters 0-999 in <strong>the</strong> famil<br />
iar 40 X 25 layout.<br />
To control a screen dot with given X and Y coordinates, we have to determine<br />
which bit of which byte to process. Let's consider X and Y relative to <strong>the</strong> top left of<br />
<strong>the</strong> screen, with X=0-319 and Y=0-199. <strong>The</strong> object is to calculate where a particu<br />
lar point, say, X=100, Y=50, will be. Points with Y from 0 to 7 lie in <strong>the</strong> top row of<br />
398
Graphics<br />
characters; Y from 8 to 15 must be in <strong>the</strong> next row, and generally Y has INT(Y/8)<br />
complete pixel rows above it, each of 320 bytes. Now, points with X from 0 to 7 fall<br />
within <strong>the</strong> first character in any row; X from 8 to 15 corresponds to <strong>the</strong> next character,<br />
and so on. Generally, <strong>the</strong> number of characters along a row is INT(X/8). <strong>The</strong> point<br />
can be in one of eight bytes in <strong>the</strong> character, determined by <strong>the</strong> remainder after divid<br />
ing Y by 8; in BASIC, this is Y AND 7 for our range of Y values. If MAP is <strong>the</strong> vari<br />
able storing <strong>the</strong> start of <strong>the</strong> bitmap, this is <strong>the</strong> address of <strong>the</strong> byte containing X,Y:<br />
MAP + 320*INT(Y/8) + 8*INT(X/8) + (Y AND 7)<br />
This expression can be improved to <strong>the</strong> following line, which BASIC evaluates faster:<br />
MAP + 40*(Y AND 248) + (X AND 504) + (Y AND 7)<br />
Finally, <strong>the</strong> actual bit within <strong>the</strong> target byte is 7 - (X AND 7), because X AND<br />
7 gives 0-7, increasing with X, but <strong>the</strong> bits are arranged in <strong>the</strong> sequence 7-0. Bit 0-7<br />
has to be set or cleared to set or clear <strong>the</strong> screen pixel, with:<br />
POKE AD, PEEK (AD) OR 2t(7-(X AND 7)): REM SETS PIXEL<br />
<strong>The</strong> same expression with AND clears <strong>the</strong> bit.<br />
Examples Using Bitmap Mode<br />
A bitmapped window. Turn on <strong>the</strong> <strong>64</strong>, <strong>the</strong>n enter and run this line:<br />
10 POKE 53265, PEEK(53265) OR 32: REM SET BITMAP MODE<br />
<strong>The</strong> <strong>64</strong> displays <strong>the</strong> first 8000 bytes, from $0, in bitmap mode. Character definitions,<br />
seen by <strong>the</strong> VIC-II at 4096 and following, are displayed in <strong>the</strong> bottom half of <strong>the</strong><br />
screen. <strong>The</strong> zero page and stack are displayed at <strong>the</strong> top of <strong>the</strong> screen, so some of<br />
<strong>the</strong>se locations continually change. <strong>The</strong> screen RAM, at $400-$7E7, is displayed in<br />
<strong>the</strong> top eighth to quarter of <strong>the</strong> screen; you'll see changes in <strong>the</strong> display if you cursor<br />
around <strong>the</strong> screen and type keys. Note that <strong>the</strong> colors are mainly red and black be<br />
cause spaces PEEK as 32 (=%0010 0000), so <strong>the</strong> high nybble is red, <strong>the</strong> low nybble<br />
black. Nonspace characters appear in colors depending on <strong>the</strong> characters' PEEK<br />
values.<br />
Multicolor mode is more complex. If you select it, you'll see <strong>the</strong> common blue<br />
background, <strong>the</strong> mode extending over <strong>the</strong> whole screen, and <strong>the</strong> light blue of <strong>the</strong> or<br />
dinary color RAM.<br />
Bitmapping at $6000. Add <strong>the</strong>se lines to <strong>the</strong> above example to alter <strong>the</strong> bitmap<br />
and color locations:<br />
20 POKE 56576,150: POKE <strong>64</strong>8,92: POKE 53272,121: REM BITMAP PARAMETERS<br />
30 FOR J=6*4096 TO 6*4096+7999: POKE J,l: NEXT: REM POKE BITMAP<br />
40 FOR J=23553 TO 24551: POKE J,l: NEXT:REM POKE COLOR<br />
Line 20 starts <strong>the</strong> bitmap at $6000, in bank 1, and starts its color just below at<br />
$5C00. Line 30 fills <strong>the</strong> bitmap with 1, giving 40 fine vertical lines on <strong>the</strong> screen.<br />
Line 40 sets <strong>the</strong> colors to black and white. This part is faster, by eight times, than<br />
filling <strong>the</strong> screen. (Note that line 30 could clear <strong>the</strong> screen by POKEing in 0 or 255.<br />
Random numbers would fill <strong>the</strong> screen with random dots.)<br />
After adding <strong>the</strong> following line, BASIC strings will move down to overwrite <strong>the</strong><br />
bitmap, <strong>the</strong>n <strong>the</strong> color, giving textilelike patterns.<br />
399
Graphics<br />
50 X$="": FOR J=l TO 7: X$=X$+CHR$(256*RND(1)): X$=X$+X$: NEXT: GOTO 50<br />
Inserting a line like 45 POKE 55, 0: POKE 56, 92: CLR prevents this. This sets <strong>the</strong><br />
top of BASIC memory at $5C00, protecting <strong>the</strong> color and bitmap information.<br />
Bitmapping at $2000. Program 12-25 POKEs 5 into <strong>the</strong> color area, setting colors<br />
to green (since <strong>the</strong> low nybble is 5, which determines <strong>the</strong> color of bits cleared to 0)<br />
and black (since <strong>the</strong> high nybble is 0, for bits set to 1). Lines 50 to 70 scan across <strong>the</strong><br />
screen, plotting dots. Note how <strong>the</strong> Y value is forced into <strong>the</strong> range 0-200. This can<br />
be made automatic by picking out <strong>the</strong> maximum and minimum within a loop.<br />
Program 12-25. Bitmap Draw Routine<br />
10 POKE 53265,PEEK(53265) OR 32<br />
20 POKE 53272,25:MAP=8192<br />
30 FOR J=MAP TO MAP+7999:POKE J,0:NEXT<br />
40 FOR J=1024 TO 2023:POKE J,5:NEXT<br />
50 FOR X=0 TO 319:Y=SIN(X*t/80)*50+100<br />
60 AD=MAP+40*(Y AND 248) + (X AND 504) + (Y AND 7)<br />
70 POKE AD,PEEK(AD) OR 2t(7-(X AND 7)):NEXT<br />
80 GET R$:IF R$="" THEN80<br />
Program 12-25 puts <strong>the</strong> bitmap at $2000, but keeps <strong>the</strong> BASIC screen RAM so<br />
READY prints as colored blocks. Try LIST when <strong>the</strong> program has finished running<br />
(drawing a figure on <strong>the</strong> screen); <strong>the</strong> bitmapped dots remain, like a sprite, as <strong>the</strong><br />
screen scrolls. This happens whenever BASIC'S screen shares <strong>the</strong> color area. <strong>The</strong> key<br />
E which has a PEEK value of 5 gives <strong>the</strong> same black on green color effect.<br />
Memory map examples. Bank 0 isn't very suitable for bitmapping. <strong>The</strong> range<br />
$2000-$4000 has to be used for <strong>the</strong> bitmap, and $1000-$lFFF is filled with character<br />
ROM. <strong>The</strong>refore, if <strong>the</strong> color isn't to coincide with BASIC'S screen, it must start at<br />
$0C00, leaving only IK if BASIC starts in its usual place.<br />
To set this configuration, POKE 53272,57: POKE 55,0: POKE 56,12: CLR,<br />
assuming bank 0 is on.<br />
However, BASIC'S start can be moved up to $4000, with POKE 43,1: POKE<br />
44,<strong>64</strong>: POKE 16384,0:NEW. To move <strong>the</strong> end of BASIC to $A000, POKE 55,0: POKE<br />
56,160: CLR. This provides 24K of RAM available for BASIC, <strong>the</strong> maximum possible<br />
with <strong>the</strong> bitmap and color both in free RAM. If you're writing BASIC and don't want<br />
a loader to first reconfigure BASIC, <strong>the</strong> setup with 21K for BASIC is better.<br />
Drawing lines. Program 12-26 draws black lines on a white bitmapped screen;<br />
an ML routine (by B. Grainger) plots individual points, and this is called by a BASIC<br />
routine which calculates optimum points to generate straight lines.<br />
Program 12-26. Drawing Lines<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader/' Appendix C.<br />
10 FOR J=49152 TO 49310:READ X:POKE J,X:NEXT<br />
:rem 214<br />
20 POKE 56576,(PEEK(56576) AND 252) OR 2 :rem 221<br />
30 POKE 53272,9 :rem 250<br />
40 POKE 53265,PEEK(53265) OR 32 :rem 69<br />
400
Graphics<br />
50 POKE 56,<strong>64</strong>:CLR :rem 176<br />
60 CLEAR=49152:PLOT=49197 :rem 211<br />
100 SYS CLEAR ;rem 247<br />
110 X2%=X1%:X1%=RND(0)*320:Y2%=Y1%:Y1%=RND(0)*200<br />
:rem 146<br />
120 GOSUB 1000:GOTO 110 :rem 217<br />
1000 XD%=X1%-X2% :rem 73<br />
1010 IF XD%>0 THEN XSH%=-1:GOTO 1100 :rem 151<br />
1020 XD%=-XD%:XSH%=1 :rem 111<br />
1100 YD%=Y1%-Y2% srem 77<br />
1110 IP YD%>0 THEN YSH%=-l:GOTO 1200 :rem 155<br />
1120 YD%=-YD%:YSH%=1 :rem 115<br />
1200 XC%=X1%:YC%=Y1% :rem 85<br />
1210 YC%=Y1% .rem 113<br />
1220 IF XD%
Graphics<br />
30009 DATA 250,133,187,101,139,133,139,165,188,101<br />
:rem 162<br />
30010 DATA 140,133,140,165,187,162,2,10,38,188,202<br />
:rem 143<br />
30011 DATA 208,250,101,139,133,139,165,188,101,140<br />
:rem 147<br />
30012 DATA 133,140,165,176,41,7,170,69,176,101,139<br />
:rem 158<br />
30013 DATA 133,139,165,177,101,140,133,140,232,56<br />
:rem 97<br />
30014 DATA 169,0,106,202,208,25-2,1,139,129,139,96<br />
:rem 110<br />
Summary of Bitmap Mode<br />
Start of bitmap and color. Bits 0 and 1 of $DD00 select <strong>the</strong> VIC-II bank. Bit 3 of<br />
$D018 defines <strong>the</strong> start of <strong>the</strong> bitmap from ei<strong>the</strong>r <strong>the</strong> beginning or <strong>the</strong> midpoint of<br />
<strong>the</strong> selected bank, while bits 4-7 select <strong>the</strong> main color area.<br />
Mode. Bit 5 of $D011 controls <strong>the</strong> <strong>64</strong>'s graphics mode. <strong>The</strong>refore, POKE 53265,<br />
PEEK(53265) OR 32 selects bitmapping, while POKE 53265, PEEK(53265) AND 223<br />
selects characters.<br />
Bit 4 of $D016 controls <strong>the</strong> color mode. Thus, POKE 53270, PpEK(53270) OR 16<br />
selects multicolor bitmap mode, and POKE 53270,PEEK(53270) AND 239 selects<br />
high-resolution bitmap mode.<br />
Colors. In high-resolution mode, colors are determined by 1000 bytes starting<br />
from <strong>the</strong> area set by VIC-II. <strong>The</strong> high nybble determines <strong>the</strong> color of bits set to 1,<br />
and <strong>the</strong> low nybble of bits set to 0, in each 8 X 8 dot area.<br />
In multicolor bitmap mode, one of four colors is chosen according to bit pairs:<br />
11 Low nybble of color RAM from $D800 (55296)<br />
10 Low nybble of screen RAM, often from $0400 (1024)<br />
01 High nybble of screen RAM<br />
00 Low nybble of $D021 (53281), <strong>the</strong> background color<br />
Drawing Onto <strong>the</strong><br />
Bitmapped Screen<br />
<strong>The</strong> pair of programs which follow allow drawings to be made directly to <strong>the</strong><br />
bitmapped screen, which is set up from 8192 to 16191, with its color from 1024 to<br />
2023. (Finished pictures can be saved, using techniques discussed in Chapter 6.)<br />
<strong>The</strong> high-resolution version, Program 12-27, is joystick controlled. Dots are plot<br />
ted in any of eight directions or erased if <strong>the</strong> delete mode is on. <strong>The</strong> fire button<br />
toggles between plot and delete. If <strong>the</strong> stick and button are pressed at <strong>the</strong> same time,<br />
a flashing cursor moves without altering <strong>the</strong> screen. Keys f3 and f5 advance back<br />
ground and foregound colors, respectively.<br />
Program 12-27. Bitmap Drawing with a Joystick<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
10 POKE 56,8192/256:CLR :rem 226<br />
14 POKE 53265,PEEK(53265) OR 32 :rem 70<br />
402
Graphics<br />
15 POKE 53272,PEEK(53272) OR 8 :rem 22<br />
24 CE=16*0 +3 :rem 74<br />
25 FOR J=1024 TO 2023:POKE J,CE:NEXT srem 230<br />
29 FOR J=49152 TO 49171:READ X:POKE J,X:NEXT<br />
:rem 229<br />
30 SYS 49152 : rem 103<br />
80 FOR 1=0 TO 7:P2(7-I)=2tl:NEXT :rem 40<br />
90 X=160:Y=100 srem 246<br />
100 PE=PEEK(56320) :rem 217<br />
101 R=PE AND 8 :rem 193<br />
102 D=PE AND 2 :rem 174<br />
103 L=PE AND 4 .rem 185<br />
104 U=PE AND 1 Srem 192<br />
105 B=PE AND 16 .rem 228<br />
200 IF B=0 THEN DEL=1-DEL :rem 68<br />
210 IF B=0 AND U*D*L*R=0 THEN MOV=l :rem 85<br />
220 IF B0 THEN MOV=0 srem 157<br />
340 IF L=0 THEN X=X-1:IF X319 THEN X=319 :rem 187<br />
360 IF U=0 THEN Y=Y-1:IF Y199 THEN Y=199 :rem 191<br />
500 OF=40*(Y AND 248)+(X AND 504) +(Y AND 7)<br />
:rem 47<br />
510 SCREENCHR=INT(OF/8) :rem 168<br />
520 BIT=X AND 7 srem 21<br />
530 CHAR=8192+OF srem 135<br />
540 PE=PEEK(CH) srem 108<br />
550 POKE CH,PEEK(CH) OR P2(BIT) srem 212<br />
560 IF DEL THEN POKE CH,PEEK(CH) AND NOT P2(BIT)<br />
:rem 139<br />
570 IF MOVE THEN POKE CH,PE: REM RESTORE VALUE IF<br />
{SPACE}MOVE srem 145<br />
580 IF MOVE=0 THEN POKE 1024+SC,CE :rem 106<br />
600 GET X$ srem 242<br />
610 IF X$="{F3}" THEN CE=((CE+1) AND 15) OR (CE AN<br />
D 240) srem 63<br />
620 IF X$=li{F5}11 THEN CE=(CE+16') AND 255 : rem 121<br />
700 GOTO 100 :rem 97<br />
20000 DATA 162,32,138,133#252,169,0,133,251,145<br />
:rem 251<br />
20010 DATA 251,200,208,251,230,252,202,208,246,96<br />
:rem 92<br />
<strong>The</strong> multicolor mode editor, Program 12-28, is keyboard controlled, using cursor<br />
keys ra<strong>the</strong>r than a joystick. Four colors are set when <strong>the</strong> program is run, and <strong>the</strong><br />
fourth, with bit pattern 11, chosen when plotting starts. Keys 1-4 select current<br />
background, high nybble, low nybble, and color RAM colors. Keys fl, f3, f5, and il<br />
advance <strong>the</strong> background, high nybble, low nybble, and color RAM, so plotting in<br />
different colors on <strong>the</strong> screen is simple. Note that <strong>the</strong> background color affects <strong>the</strong><br />
entire screen—press fl to see this. Typing <strong>the</strong> space bar toggles between plot and<br />
move modes.<br />
403
Graphics<br />
Program 12-28. Multicolor Bitmap Draw Routine<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader/' Appendix C.<br />
10 POKE 56,8192/256:POKE 55,0:CLR :rem 17<br />
11 PRINT "BACKGROUND?";:GOSUB 50000:BA=G :rem 125<br />
12 PRINT "{6 SPACES}AUX1?";:GOSUB 50000:A1=G<br />
:rem 172<br />
13 PRINT "{6 SPACES}AUX2?"y:GOSUB 50000:A2=G<br />
:rem 175<br />
14 PRINT " CHARACTER?";:GOSUB 50000:CC=G :rem 48<br />
15 POKE 53265,PEEK(53265) OR 32 :rem 71<br />
16 POKE 53272,PEEK(53272) OR 8 :rem 23<br />
17 POKE 53270,PEEK(53270) OR 16 :rem 67<br />
20 POKE 53281,BA :rem 67<br />
26 FOR J=49152 TO 49205:READ X:POKE J,X:NEXT<br />
:rem 224<br />
28 CE=16*A1 + A2:POKE 49188,CC:POKE 49192,CE<br />
:rem 31<br />
30 SYS 49152 :rem 103<br />
80 FOR J=0 TO 7:P2(J)=2tJ:NEXT :rem 199<br />
90 B=3:X=160:Y=100 :rem 226<br />
100 GET G$:G=ASC(G$+CHR$(0)) :rem 218<br />
110 IF G>48 AND G318 THEN X=318<br />
:rem 255<br />
220 IF G$="{LEFT}" THEN X=X-2:IF X
Graphics<br />
20001 DATA 145,251,200,208,251,230,252,202,208<br />
:rem 191<br />
20005 DATA 246,134,252,134,254,169,216,133,253<br />
:rem 219<br />
20006 DATA 169,4,133,255,162,4,160,0,169,3 :rem 12<br />
20010 DATA 145,252,169,53,145,254,200,208 :rem 221<br />
20011 DATA 245,230,253,230,255,202,208,238,96<br />
:rem 163<br />
50000 GET G$:IF G$="H GOTO 50000 :rem 35<br />
50010 PRINT G$ "{RVS} {8 .SPACES}11 :rem 68<br />
50020 G=PEEK(<strong>64</strong>6):PRINT "{ WHT }": RETURN :rem 187<br />
Sprites<br />
Sprites (or Movable Object Blocks) are large user-definable graphics which can be<br />
put anywhere on <strong>the</strong> screen. <strong>The</strong> VIC-II chip handles <strong>the</strong>m automatically—a consid<br />
erable technical achievement to <strong>Commodore</strong>'s credit. Since many people feel intimi<br />
dated by sprites, this section begins with simple demonstrations and leaves <strong>the</strong><br />
technical details for later.<br />
First, turn on <strong>the</strong> <strong>64</strong>, type POKE 53269,1 and press RETURN. You now have a<br />
sprite. However, you can't actually see it since it is not in <strong>the</strong> screen display area.<br />
Type POKE 53248,100 and POKE 53249,100 and a sprite will appear. Vary <strong>the</strong> val<br />
ues in <strong>the</strong>se locations and watch <strong>the</strong> sprite move. <strong>The</strong> sprite is, or is supposed to be,<br />
white. This color was set on power-up.<br />
At this point, <strong>the</strong> sprite's shape is not very satisfactory; it is defined by <strong>the</strong> first<br />
63 bytes of RAM. We can alter it by POKEing different values into location 2040, <strong>the</strong><br />
location which tells <strong>the</strong> VIC-II where to find this sprite's shape data. Some values<br />
yield a sprite which continuously changes; this means that <strong>the</strong> RAM which defines<br />
<strong>the</strong> sprite's shape is being used for BASIC workspace and isn't a flaw in <strong>the</strong> <strong>64</strong>.<br />
POKE 2040,16 causes <strong>the</strong> top one-and-a-half screen lines (strictly, <strong>the</strong> first 63<br />
characters) to define <strong>the</strong> sprite; try homing <strong>the</strong> cursor and redefining <strong>the</strong> sprite with<br />
{RVS}-<strong>Commodore</strong> key-B, {RVS}-*, SHIFT-U, and @, setting bit patterns<br />
11111111, 10101010, 01010101, and 00000000.<br />
Now, POKE 53287 with different values. <strong>The</strong>se change <strong>the</strong> color of <strong>the</strong> sprite.<br />
Color changes in <strong>the</strong> sprite correspond to bits set to 1 in <strong>the</strong> sprite's definition. (You<br />
may get color effects because of <strong>the</strong> spacing of <strong>the</strong> defining bits on <strong>the</strong> screen.) Bits<br />
set to 0 don't represent a color, but are treated as transparent by <strong>the</strong> VIC chip, so <strong>the</strong><br />
background shows through.<br />
POKE 53276,1 sets multicolor mode for <strong>the</strong> sprite. (Poke 53276,0 to return to<br />
high-resolution.) Multicolor mode increases <strong>the</strong> available colors from 1 to 3; <strong>the</strong> extra<br />
two are stored in 53285 and 53286, so POKEs into <strong>the</strong>se locations will alter multi<br />
color sprites (if bit patterns 10 or 01 are present), but will leave high-resolution<br />
sprites unchanged. All multicolor sprites share <strong>the</strong>se extra two colors.<br />
POKE 53277,1 makes <strong>the</strong> sprite expand horizontally, and POKE 53271,1 ex<br />
pands it vertically to twice <strong>the</strong> unexpanded dimensions. Without special techniques,<br />
only eight sprites are available at one time, so this can be useful where you'd like<br />
reasonable coverage of <strong>the</strong> screen.<br />
405
Graphics<br />
POKE 53275,1 changes <strong>the</strong> priority of <strong>the</strong> sprite with regard to text; check to see<br />
that characters are now displayed in front of it.<br />
PRINT PEEK(53279) is set to 1 if <strong>the</strong> sprite overlaps text. This is called a col<br />
lision. As you'll find, this location is reset only when it's read from, so two PEEKs<br />
are actually necessary to give <strong>the</strong> current status.<br />
Finally, POKE 53265,187 sets bitmap mode. You'll see that a sprite is still dis<br />
played—<strong>the</strong> graphics mode doesn't affect it.<br />
Detailed Description of Sprites<br />
Enabling and disabling sprites. <strong>The</strong> seven bits of <strong>the</strong> VIC register at $D015 (53269)<br />
control sprite enabling. <strong>The</strong> VIC chip can handle up to eight sprites at one time,<br />
numbered 0-7, turned on when corresponding bits 0-7 are 1, and turned off when<br />
bits 0-7 are 0. Examples: POKE 53269,1 turns on sprite 0—<strong>the</strong> introduction used<br />
this sprite; POKE 53269,255 turns on all sprites. Sprites coexist with all graphics<br />
modes—ordinary text, user-defined characters, and bitmapping.<br />
When sprites are on, <strong>the</strong> VIC-II needs extra processing time, which it takes at<br />
<strong>the</strong> expense of <strong>the</strong> 6510 central processor. For example, if all sprites are enabled, <strong>the</strong><br />
6510 operates at 0.95627 times <strong>the</strong> speed it runs at with all sprites disabled, and this<br />
effect is present even if <strong>the</strong> VIC chip is disabled in border-color mode. This slow<br />
down affects disk, tape, and RS-232 operations, so it makes sense to press<br />
RUN/STOP-RESTORE before loading or saving.<br />
Sometimes it's helpful to quickly disable sprites, <strong>the</strong>n reenable <strong>the</strong>m (for ex<br />
ample, when moving a sprite). Two POKEs are needed to alter <strong>the</strong> position, and if<br />
one acts noticeably before <strong>the</strong> o<strong>the</strong>r, motion will be in two parts, horizontal and<br />
vertical (ano<strong>the</strong>r way to handle sprites is to move <strong>the</strong>m when <strong>the</strong> raster scan is off<br />
<strong>the</strong> screen).<br />
Defining sprites. Unexpanded sprites are about 3X3 characters wide. Nine<br />
characters use 9*8*8 dots, and require 72 bytes for definition, but to fit sprite data in<br />
RAM compactly, VIC allows <strong>64</strong> bytes per definition, allowing 24 X 21 dot sprites.<br />
Byte <strong>64</strong> is ignored, while <strong>the</strong> o<strong>the</strong>r 63 are arranged in 21 three-byte groups as in Fig<br />
ure 12-10.<br />
Sprite definitions must coexist in <strong>the</strong> VIC-II bank with <strong>the</strong> screen and any userdefined<br />
characters or bitmap and color screen that may be in use. Since only <strong>64</strong><br />
bytes are needed per definition, <strong>the</strong>re's usually plenty of room.<br />
When sprites are enabled, VIC-II immediately displays <strong>the</strong>m in accordance with<br />
<strong>the</strong> values held in its various sprite control registers. <strong>The</strong> only parameters not in <strong>the</strong><br />
VIC-II chip are eight pointers to <strong>the</strong> sprite shape definitions, which are stored after<br />
<strong>the</strong> screen. If <strong>the</strong> screen is moved, <strong>the</strong>se pointers move with it, but normally loca<br />
tions 2040-2047 apply. <strong>The</strong>y can hold anything from 0 to 255, since 256*<strong>64</strong> is ex<br />
actly <strong>the</strong> size of a VIC-II bank. So, POKE 1020,13 points sprite 0's start to<br />
13*<strong>64</strong> = 832, near <strong>the</strong> start of <strong>the</strong> tape buffer.<br />
Positioning sprites. Since <strong>the</strong>re are 40 X 25 characters, and sprites can be con<br />
trolled to <strong>the</strong> nearest pixel, at least 320 horizontal (X) and 200 vertical (Y) positions<br />
have to be programmable. Each sprite's Y value has its own one-byte VIC register,<br />
but X values require nine bits. <strong>The</strong> X values are stored in one-byte registers, with all<br />
<strong>the</strong> extra high bits collected elsewhere in ano<strong>the</strong>r register. <strong>The</strong> first 16 VIC registers,<br />
406
Graphics<br />
$DOOO-$DOOF (53248-53263), hold X, <strong>the</strong>n Y positions for sprites 0-7, followed by<br />
<strong>the</strong> high-bit register at $D010 (532<strong>64</strong>). <strong>The</strong> X,Y pairs define <strong>the</strong> top-left corner of <strong>the</strong><br />
complete sprite because VIC scans <strong>the</strong> screen from <strong>the</strong> left and down.<br />
You might at first expect X values to range from 0 to 319, but this wouldn't take<br />
into account <strong>the</strong> way <strong>the</strong> VIC-II chip scans <strong>the</strong> screen. In fact, X values from 24 to<br />
342 define <strong>the</strong> screen width, and Y values from 50 to 249 define <strong>the</strong> height. <strong>The</strong>se<br />
figures refer to <strong>the</strong> sprite's top-left corner, and you must allow for <strong>the</strong> sprite's size.<br />
An unexpanded sprite is 24 dots across by 21 down, so to just fit a sprite in <strong>the</strong><br />
screen, X should range from 24 to 296, and Y from 50 to 208. By using X and Y val<br />
ues outside of <strong>the</strong>se ranges, you can move a sprite gradually off <strong>the</strong> screen in any<br />
direction. For instance, if Y=29, <strong>the</strong> sprite is just above <strong>the</strong> top of <strong>the</strong> screen; if<br />
Y=250, <strong>the</strong> sprite is below <strong>the</strong> screen, and so on.<br />
Note that X=0 makes an unexpanded sprite just vanish off <strong>the</strong> left border. But<br />
an expanded sprite with X=0 is still partly visible. It can be made to move fur<strong>the</strong>r<br />
left only by making X high, up to a maximum of 511. (PAL TVs, in <strong>the</strong> U.K., have a<br />
different maximum of 503. A larger X won't display at all.) This means it's tricky to<br />
move X-expanded sprites smoothly off <strong>the</strong> left side of <strong>the</strong> screen.<br />
Sprite colors. High-resolution sprite colors are stored in eight registers from<br />
$D027 (53287) to $D02E (53294). On power-up, <strong>the</strong>se are set to 1, 2, 3, 4, 5, 6, 7,<br />
and 12.<br />
Figure 12-10. Sprite Data Arrangement<br />
Byte Number<br />
21 bits<br />
24 Bits<br />
Sprite high-resolution and multicolor modes. Bits 0-7 of $D01C (53276) con<br />
trol <strong>the</strong> modes of each sprite independently. High-resolution sprites interpret each<br />
bit set to 1 in <strong>the</strong>ir definitions as <strong>the</strong> sprite color, stored in one of <strong>the</strong> color registers<br />
407
Graphics<br />
at $D027-$D02E. Bits cleared to 0 are transparent, using <strong>the</strong> background color.<br />
Multicolor sprites interpret pairs of bits in <strong>the</strong>ir shape definitions like this:<br />
00 Transparent<br />
01 Sprite MCM register 0 ($D025 = 53285)<br />
10 Sprite color in one of $D027-$D02E<br />
11 Sprite MCM register 1 ($D026 = 53286)<br />
<strong>The</strong> two new colors are shared by all multicolor sprites. Note that character or back<br />
ground colors aren't used, which makes sense, since you do not generally want <strong>the</strong><br />
sprite's colors to vary as it moves. Note that if <strong>the</strong> second bit is set to 1, an MCM<br />
register is selected.<br />
Expanded sprites in effect use each dot twice, so <strong>the</strong> resolution isn't improved.<br />
Note that expanded multicolor sprites are still displayed in multicolor—<strong>the</strong> sprite<br />
handling does not treat 01 as 0011, for example.<br />
Expansion of sprites. Bits 0-7 of $D01D (53277) and $D017 (53271) control <strong>the</strong><br />
X and Y expansion of each of <strong>the</strong> eight sprites. Thus, POKE 53277,1: POKE 53271,9<br />
causes sprite 0 to be doubled in both directions, and sprite 3 to be doubled in <strong>the</strong> Y<br />
direction. Expansion in <strong>the</strong> Y direction elongates a sprite downwards from its present<br />
position, and X expansion stretches it to <strong>the</strong> right.<br />
Priority of sprites. When sprites are superimposed, <strong>the</strong> VIC-II chip can't display<br />
both at once, but has to select which gets priority. <strong>The</strong> same happens on a back<br />
ground of graphics, so we have to distinguish two types of priority.<br />
Sprite-sprite priority determines which of two or more sprites is displayed where<br />
<strong>the</strong>y overlap. Lower numbered sprites always appear in front of higher numbered<br />
ones; this is built into VIC-II and must be taken into account when designing pro<br />
grams with sprites which may overlap. <strong>The</strong> nearest sprite could be 0, say, and <strong>the</strong><br />
fur<strong>the</strong>st sprite, 7. This priority applies to <strong>the</strong> nontransparent parts of both highresolution<br />
and multicolor sprites. Transparent parts of a sprite allow lower priority<br />
sprites or background graphics to show through.<br />
Sprite-data priority is more complicated. Seven bits of $D01B (53275) control<br />
sprite-data priority for each sprite. This determines whe<strong>the</strong>r a sprite appears behind<br />
or in front of character data when <strong>the</strong> two overlap. When a bit is 0, its sprite gets<br />
priority, and when 1, data gets priority. On power-up all bits are set to 0, so sprites<br />
initially have priority. This concept is sometimes termed sprite-background priority,<br />
since sprites are often displayed on a background of character data. Do not confuse<br />
this with <strong>the</strong> background color of <strong>the</strong> screen, over which sprites always have<br />
priority.<br />
With high-resolution sprites <strong>the</strong> transparent parts always allow what's under<br />
neath to show, but <strong>the</strong> parts mapped with 1 allow character data to show only if <strong>the</strong><br />
bit in $D01B is 1. Thus, an airplanelike sprite can be made to fly in front of or be<br />
hind user-defined character "mountains."<br />
Multicolor-sprite priority with data is controlled in <strong>the</strong> same way. When <strong>the</strong><br />
sprite's bit in $D01B is 0, <strong>the</strong> sprite, except <strong>the</strong> transparent areas defined by bit pat<br />
tern 0, has priority over data. When <strong>the</strong> bit is 1, data has priority over <strong>the</strong> entire<br />
sprite.<br />
Priority with several sprites and data is more complex. <strong>The</strong> setting of <strong>the</strong> lower<br />
numbered sprite gets priority. Now, suppose sprite 0 overlaps sprite 1, and <strong>the</strong>re's<br />
408
Graphics<br />
some data under both. If sprite 0 is set to appear below data, <strong>the</strong>n data will show<br />
through, even where sprite 1 is set with priority over data.<br />
Collision detection. This is an essential aspect of sprite programming. <strong>The</strong> idea<br />
is to signal whenever a nontransparent part of a sprite contacts screen data or an<br />
o<strong>the</strong>r sprite. VIC-II has two registers for this purpose, plus two interrupt registers.<br />
Without all <strong>the</strong>se, detecting collisions would be enormously difficult. <strong>The</strong>y can be<br />
PEEKed or used with ML to generate interrupts. Here's a simple example:<br />
Program 12-29. Sprite Collision<br />
10 FOR J=0 TO 62:POKE 832+J,7:NEXT<br />
20 POKE 2040,13:POKE 2041,13<br />
30 V=53248<br />
40 POKE V+21,3<br />
50 POKE V+39,1:POKE V+40,0<br />
60 POKE V,135:POKE V+l,70<br />
100 POKE V+3,60<br />
110 PRINT "{CLRHWHT} SPRITE-SPRITE{3 SPACES}SPRIT<br />
E-DATA{3 SPACES}INT REG"<br />
120 FOR J=0 TO 255: POKE V+2,J<br />
130 PRINT SPC(8) PEEK(V+30) SPC(ll) PEEK(V+31);<br />
140 PRINT SPC(8) PEEK(V+25) AND 6:PRINTM{UP}";<br />
150 POKE V+25,6<br />
160 NEXT:GOTO 20<br />
<strong>The</strong> seven bits of $D01E (53278) register sprite-sprite contact, so at least two bits<br />
are set on any sprite overlap. <strong>The</strong> register must be read to determine which sprites<br />
were involved. Reading resets all <strong>the</strong> bits to 0. If <strong>the</strong> register is not read, <strong>the</strong> collision<br />
bits simply stay 1 indefinitely, which is why reading twice is essential to find <strong>the</strong><br />
current status. Note that offscreen collisions set <strong>the</strong>se flags, too.<br />
Bits 0-7 of $D01F (53279) register sprite-data collisions in <strong>the</strong> same way. Typi<br />
cally, just one bit will be set. In high-resolution this is straightforward. With multi<br />
color sprites <strong>the</strong>re's more flexibility. <strong>The</strong> sprite color and <strong>the</strong> transparent "color" are<br />
each treated as transparent for collision flagging; only bit-pairs 10 or 11, that is, <strong>the</strong><br />
colors in multicolor registers $D025 and $D026, cause collisions to be registered.<br />
Sprite Memory Maps<br />
Each sprite definition takes up <strong>64</strong> bytes which must coexist in a VIC-II bank with<br />
screen RAM, characters, and/or a bitmap with its color. Areas giving images of ROM<br />
characters can't be used.<br />
Figure 12-11 shows how bank 0 can hold a full set of 256 user-defined charac<br />
ters, 32 sprite definitions, and a 10K BASIC program starting at <strong>the</strong> usual $0800:<br />
409
Graphics<br />
Figure 12-11. Mapping Sprites with User-Defined Characters<br />
$0 $400 $800 $3000 $4000<br />
Screen<br />
10K BASIC<br />
256<br />
User Def'd<br />
Chrs<br />
32<br />
Sprite<br />
Defns<br />
8 Sprite Pointers<br />
Program 12-30 uses <strong>the</strong> memory map in Figure 12-11. <strong>The</strong> program is long, but<br />
is as short as a reasonable demonstration can be:<br />
Program 12-30. <strong>Programming</strong> Sprites with User-Defined Charac<br />
ters<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
10 POKE 55,0:POKE 56,48:CLR :rem 221<br />
20 POKE 53272,29 :rem 43<br />
30 FOR J=12288 TO 12288+31:READ X:POKE J,X:NEXT<br />
:rem 107<br />
40 DATA 0,0,0,0,0,0,0,0 :rem 50<br />
50 DATA 1,3,7,31,31,63,63,127 :rem 130<br />
60 DATA 255,255,255,255,255,255,255,255 :rem 148<br />
70 DATA 128,128,224,240,240,254,255,255 :rem 130<br />
80 FOR J=l TO 5:M1$=M1$+M@@@AC@@@":NEXT :rem 134<br />
85 FOR J=l TO 5:M2$=M2$+II@
Graphics<br />
330 X0=X0+2:IF X0>255 THEN POKE 532<strong>64</strong>,PEEK(532<strong>64</strong>)<br />
{SPACE}OR 1 :rem 69<br />
340 IF X0360 GOTO 300 : rem 82<br />
360 POKE 53250,X0/3 :rem 221<br />
370 POKE 53248,X0 AND 255:GOTO 330 :rem 251<br />
Four user-defined characters build a mountain range (@, A, B, and C become<br />
blank, left slope, solid block, and right slope, respectively). One sprite, which looks<br />
like an airplane, is defined. It's displayed twice: once as sprite 0, in enlarged format<br />
in black, with priority over data, and again as sprite 1, in gray at normal size, with<br />
priority lower than data. <strong>The</strong> two planes move left to right, <strong>the</strong> nearer plane moving<br />
faster, <strong>the</strong> fur<strong>the</strong>r disappearing behind <strong>the</strong> mountains and also, because of automatic<br />
sprite-sprite priority, behind sprite 0 if <strong>the</strong>y cross. Lines 300 onward move <strong>the</strong><br />
planes, and lines 320 and 330 allow for <strong>the</strong> possible high bit in <strong>the</strong> X direction as <strong>the</strong><br />
plane moves. (If you experiment with sprite shape definitions, you'll see single dot<br />
widths aren't handled well by <strong>the</strong> VIC chip. Sprites generally need to be fairly<br />
chunky to work well.)<br />
<strong>The</strong> tape buffer has room for three sprite shape definitions (at $0340, $0380,<br />
and $03C) and is useful for small experiments with BASIC. Values of 13, 14, and 15,<br />
POKEd into <strong>the</strong> sprite pointers after <strong>the</strong> screen, access <strong>the</strong> area.<br />
<strong>The</strong> memory map shown in Figure 12-12 illustrates how a bitmapped screen, its<br />
color, and 16 sprite shape definitions can be used with <strong>the</strong> normal screen RAM in<br />
bank 0. BASIC has to be shifted up to start at $4000, with POKE 43,1: POKE 44,<strong>64</strong>:<br />
POKE 16384,0: NEW.<br />
Figure 12-12. Mapping Sprites and a Bitmap<br />
$0 $<br />
&C00<br />
ttiooo<br />
(2000<br />
$4000<br />
s<br />
c<br />
R<br />
E<br />
16<br />
Sprite<br />
Defn's<br />
C<br />
O<br />
L<br />
O<br />
n<br />
[ROM Image]<br />
Bitmap<br />
BASIC<br />
N<br />
t<br />
8 Sprite Pointers<br />
Notes on <strong>Programming</strong> with Sprites<br />
Extending <strong>the</strong> number of sprites available. Without special techniques, only eight<br />
sprites can exist on <strong>the</strong> screen at one time. Unexpanded sprites occupy about a 7.9<br />
character area, so even eight fully expanded sprites cover only 25 percent of <strong>the</strong><br />
screen. If this isn't enough, you'll need user-defined characters or bitmapping to add<br />
graphic interest. <strong>The</strong> VIC-II chip can be caused, with interrupt techniques, to display<br />
more than eight sprites simultaneously, so it is possible to fill <strong>the</strong> screen with sprites,<br />
but ML is essential.<br />
How to use sprites. <strong>The</strong> number of sprites is usually limited, so it is necessary<br />
to mix <strong>the</strong>m with built-in graphics. If that will not suffice, <strong>the</strong> next easiest method is<br />
411
Graphics<br />
to mix <strong>the</strong>m with user-defined graphics. In ei<strong>the</strong>r case <strong>the</strong>y must be used carefully.<br />
For example, to liven up a word processor or calculation program, you could use<br />
sprites to define arrows, pointing fingers, rectangular frames, or o<strong>the</strong>r prompts.<br />
Games might use several moving sprites to simulate cars or motorcycles on a con<br />
ventional graphics background.<br />
Ano<strong>the</strong>r example is a frog trying to cross a road and a stream without being hit.<br />
A screenful of moving trucks, cars, and logs isn't possible with simple sprite tech<br />
niques: user-defined graphics are better, allowing duplication of <strong>the</strong> moving objects.<br />
If <strong>the</strong> frog is defined as a sprite, with priority over character data, it will always be<br />
visible upon its road, log, or wherever. Several sprite definitions cover <strong>the</strong> cases<br />
where it moves left or right and extends its legs. You can choose ei<strong>the</strong>r a highresolution<br />
frog in one color or a chunkier multicolor frog.<br />
Sprites can be superimposed if extra color detail is needed, but this isn't often<br />
done, partly because it reduces <strong>the</strong> number of available sprites, partly because mo<br />
tion now requires two sprites to be moved, and partly because TVs may not display<br />
<strong>the</strong> result satisfactorily anyway.<br />
Movement with sprites. To animate sprites, you need to replace a sprite with a<br />
similar sprite, possibly repeating <strong>the</strong> process many times. We could change <strong>the</strong> sprite<br />
definitions <strong>the</strong>mselves, change <strong>the</strong> sprite pointers to point to different definitions, or<br />
cycle through <strong>the</strong> sprites, say, from 0 through 3.<br />
Generally, changing <strong>the</strong> definition pointers is best, since one POKE is all that's<br />
needed to update a sprite, and it's easy to store plenty of sprites in RAM. Some<br />
times, though, changing <strong>the</strong> actual definition is better (for example, where an object<br />
is fired at, and you want to make parts of it disappear). In this case, it may be easier<br />
to set bits in <strong>the</strong> sprite definition to 0. <strong>The</strong> third option isn't usually good; it uses up<br />
valuable sprites.<br />
Receding and approaching motion can be simulated to a certain extent by using<br />
<strong>the</strong> expansion feature along with changing definitions; a 2 X 2 and a 3 X 3 unexpanded<br />
sprite will give a sequence of four sprites in about <strong>the</strong> right ratio to sug<br />
gest constant speed. Note, however, that expansion stretches <strong>the</strong> sprite downward<br />
and to <strong>the</strong> right (ra<strong>the</strong>r than equally in all dimensions), which makes this feature less<br />
than ideal for three-dimensional effects. Lighter and bluer colors suggest distance, as<br />
opposed to deeper and redder colors.<br />
Sprite Editor<br />
Ra<strong>the</strong>r than drawing sprites on 24 X 21 areas of squared paper and converting <strong>the</strong><br />
result into bytes, try <strong>the</strong> following sprite editor, Program 12-31, which automates <strong>the</strong><br />
process. <strong>The</strong> program processes one sprite at a time, which is displayed in four<br />
ways, standard and enlarged, and both high-resolution and multicolor modes.<br />
<strong>The</strong>re's also an enlarged diagram of <strong>the</strong> sprite, on which individual points can be set<br />
and cleared with <strong>the</strong> space bar, while <strong>the</strong> cursor keys allow movement. Function<br />
keys control colors and allow plotting in pairs for multicolor mode; instructions on<br />
<strong>the</strong> screen explain which function keys to use. Press C to erase a sprite. (You may<br />
prefer to omit <strong>the</strong> C option to remove <strong>the</strong> risk of accidental deletion.)<br />
<strong>The</strong> program asks which block is to be used; if a sprite already exists in mem<br />
ory, it's not cleared and can be examined and altered.<br />
412
Graphics<br />
Program 12-31. Sprite Editor<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
5 INPUT "{CLR}{3 DOWN} WHICH BLOCK OF <strong>64</strong> BYTES (EG<br />
13)M;B:SP=<strong>64</strong>*B :rem 225<br />
10 FOR J=0 TO 3:POKE 2040+J,B:NEXT :rem 96<br />
15 V=53248:SS=1024 :rem 227<br />
20 POKE V+21,15 :rem 7<br />
25 POKE V+0,240:POKE V+2,240:POKE V+4,250:POKE V+6<br />
,250 :rem 171<br />
30 POKE V+1,90:POKE V+3f140:POKE V+5,190:POKE V+7,<br />
220 :rem 125<br />
35 FOR J=39 TO 42:POKE V+J,6:NEXT :rem 90<br />
40 POKE V+37,0:POKE V+38,13 :rem 191<br />
45 POKE V+32,1:POKE V+33,1 :rem 136<br />
50 POKE V+29,3:POKE V+23,3 :rem 141<br />
55 POKE V+28,5 :rem 229<br />
60 FOR J=0 TO 7:P(J)=2t(7-J):NEXT :rem 72<br />
65 GOSUB 500 :rem 128<br />
100 PRINT "{CLR}{2 DOWN}{BLK}":GOSUB 3000 :rem 36<br />
130 XC=0:YC=0:C=160:POKE SS+C,PEEK(SS+C) OR 128<br />
:rem 159<br />
140 GOSUB 20000 :rem 7<br />
200 GET X$:IF X$="" THEN 200 :rem 117<br />
210 IF X$=" " THEN GOSUB 2000:GOTO 400 :rem 151<br />
220 IF X$="C" THEN GOSUB 1000:GOTO 100 :rem 215<br />
230 IF X$="{Fl}M THEN CB=0:GOSUB 21000:GOTO 400<br />
:rem 123<br />
240 IF X$="{F3}lf THEN CB=1:GOSUB 21000:GOTO 400<br />
:rem 126<br />
250 IF X$=M{F5}" THEN CB=2:GOSUB 21000:GOTO 400<br />
:rem 129<br />
260 IF X$="{F7}" THEN CB=3:GOSUB 21000:GOTO 400<br />
:rem 132<br />
270 IF X$="{UP}" AND YC>0 THEN YC=YC-l:GOTO 400<br />
:rem 156<br />
280 IF X$="{DOWN}M AND YC
Graphics<br />
500 DATA "BLACK ","WHITE ","RED{3 SPACES }", "CYAN<br />
{2 SPACES}","PURPLE" :rem 111<br />
505 DATA "GREEN ","BLUE{2 SPACES}","YELLOW","ORANG<br />
E","BROWN " :rem 113<br />
510 DATA "LT RED"#"D GRAY","M GRAY","LT GRN","L BL<br />
UE","L GRAY" :rem 16<br />
520 DIM CO$(15):FOR J=0 TO 15:READ CO$(J):NEXT:RET<br />
URN :rem 249<br />
1000 FOR J=SP TO SP+62:POKE J,0:NEXT:RETURN:rem 77<br />
2000 PT=SP+XC/8+YC*3:BY=PEEK(PT) :rem 195<br />
2010 BP=XC-INT(XC/8)*8:MS=P(BP) :rem 68<br />
2020 IF (BY AND P(BP))=0 THEN POKE PT,BY+MS:rem 70<br />
2030 IF (BY AND P(BP))>0 THEN POKE PT,BY-MS:rem 74<br />
2040 POKE SS+C,254-PEEK(SS+C):RETURN :rem 161<br />
3000 FOR YC=0 TO 20:FOR XB=0 TO 2 :rem 181<br />
3010 PT=SP+XB+YC*3:BY=PEEK(PT) :rem 93<br />
3020 FOR J=0 TO 7 :rem 61<br />
3030 IF (BY AND P(J))>0 THEN PRINT "Q";: GOTO 3050<br />
:rem 180<br />
3040 PRINT "-"; :rem 0<br />
3050 NEXT:NEXT:IF YC20 THEN PRINT:NEXT :rem 246<br />
3060 RETURN :rem 169<br />
4000 T=(PEEK(V+32)+l) AND 15 :rem 151<br />
4010 POKE V+32,T:POKE V+33,T:GOTO 20000 irem 143<br />
5000 POKE V+37,(PEEK(V+3 7)+l) AND 15:GOTO 20000<br />
:rem 183<br />
6000 T=(PEEK(V+39)+l) AND 15 :rem 160<br />
6010 FOR J=V+39 TO V+42:POKE J#T:NEXT:GOTO 20000<br />
:rem 189<br />
7000 POKE V+38,(PEEK(V+38)+l) AND 15:GOTO 20000<br />
:rem 187<br />
20000 PRINT "{HOMEHbLK}"; : rem 161<br />
20010 PRINT "BACKGROUND = "CO$(PEEK(V+32) AND 15)"<br />
{2 SPACES}F2{3 SPACES}F1=00" :rem 77<br />
20020 PRINT "SPRITE MC0 = "CO$(PEEK(V+37) AND 15)"<br />
{2 SPACES}F4{3 SPACES}F3=01" :rem 15<br />
20030 PRINT "SPR COLOR{2 SPACES}= "CO$(PEEK(V+39)<br />
{SPACEjAND 15)"{2 SPACES}F6{3 SPACES}F5=10"<br />
:rem 243<br />
20040 PRINT "SPRITE MC1 = "CO$(PEEK(V+38) AND 15)"<br />
{2 SPACES}F8{3 SPACES}F7=11" :rem 28<br />
20050 RETURN .rem 215<br />
21000 CP=(C+SS) AND 2046:IF CB>1 THEN POKE CP,81:G<br />
OTO 21030 :rem H5<br />
21010 POKE CP,45 :rem 75<br />
21030 CP=CP+1:IF (CB AND 1)=1 THEN POKE CP,81:GOTO<br />
21050 :rem 183<br />
21040 POKE CP,45 srem 78<br />
21050 PT=SP+XC/8+YC*3:BY=PEEK(PT):MP=7-XC AND 6:MK<br />
=2tMP*3 .rem 174<br />
21060 BY=(BY AND NOT MK)+CB*2tMP:POKE PTfBY:RETURN<br />
414<br />
:rem 14
Graphics<br />
When sprites have been defined, press RUN/STOP to exit <strong>the</strong> program. <strong>The</strong><br />
sprite definition values can <strong>the</strong>n be saved as DATA or written to a file or block<br />
saved, using <strong>the</strong> techniques discussed earlier in this book.<br />
Using Interrupts with Graphics<br />
Chapter 8 explains NMI and IRQ interrupts on <strong>the</strong> <strong>64</strong>. Graphics applications include<br />
such things as clocks, countdown indicators, and radar displays to show approaching<br />
aliens and screen responses to keypresses. Any display which needs to be periodi<br />
cally updated is a candidate for processing during <strong>the</strong> interrupt. Interrupts require<br />
ML, but offer a solution to many problems. Often, a similar effect can be achieved in<br />
BASIC, but this is far clumsier and slower.<br />
Program 12-32 processes graphics during interrupts, PEEKing <strong>the</strong> first 256<br />
screen positions for values specified as DATA, replacing <strong>the</strong>m by <strong>the</strong> next in <strong>the</strong> se<br />
quence. Note how <strong>the</strong> interrupt routine is transparent to BASIC. <strong>The</strong> ML is set (by<br />
<strong>the</strong> 5 in line 200) to search and replace at about 1/10 second intervals.<br />
Program 12-32. IRQ Polling<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
10 FOR J=49152 TO 49207:READ X:POKE J,X:NEXT<br />
:rem 219<br />
20 POKE 56333,127:POKE 788,0:POKE 789,192:POKE 563<br />
33,129 :rem 213<br />
30 PRINT "{CLRj'^FOR J=l TO 100:PRINT CHR$(228) fl<br />
{ SPACE }";:NEXT :rem 129<br />
200 DATA 206,47,192,208,23,169,5,141,47,192,160,0<br />
201 DATA 162,7,185,0,4,221,48,192,240,9,202,16<br />
:rem 103<br />
:rem 202<br />
202 DATA 248,200,208,240,76,49,234,232,224,8,208<br />
:rem 58<br />
203 DATA 2,162,0,189,48,192,153,0,4,76,25,192<br />
:rem 165<br />
204 DATA 5 :rem 229<br />
210 DATA 100,111,121,98,248,247,227,224 :rem 120<br />
This process can be extended, with user-defined characters, to simulate flying birds,<br />
crawling insects, and so on.<br />
This style of interrupt is a poll At regular intervals, <strong>the</strong> 6510 goes off to perform<br />
what may be a long series of operations, deciding to do some and not do o<strong>the</strong>rs. CIA<br />
chip 1 allows <strong>the</strong> frequency of interrupts to be changed, but <strong>the</strong> process is always<br />
similar. However, <strong>the</strong> VIC-II chip provides a more active control over interrupts.<br />
VIC-ll's Interrupt Registers<br />
Locations $D019 (53273) and $D01A (53274) are VIC's interrupt flag register and<br />
interrupt mask register, respectively. <strong>The</strong>y allow <strong>the</strong> source of any interrupt to be<br />
identified, and also allow interrupts to be enabled or disabled (masked). Six o<strong>the</strong>r<br />
registers are related to <strong>the</strong> VIC-II chip's interrupts, as discussed below.<br />
415
Graphics<br />
Bit 7 of <strong>the</strong> interrupt flag register is set to 1 when <strong>the</strong> VIC chip generates an<br />
interrupt; o<strong>the</strong>rwise it's 0. This allows you to determine <strong>the</strong> source of an interrupt,<br />
where <strong>the</strong>re are several possibilities, since whenever bit 7 is on, one of <strong>the</strong> register's<br />
o<strong>the</strong>r four used bits will reveal this. Note, though, that bits 4-6 are unused and that<br />
bits 0-3 are set even when interrupts aren't enabled.<br />
PRINT PEEK(53273) AND 143 prints <strong>the</strong> value of this register; ANDing <strong>the</strong><br />
value with 143 masks <strong>the</strong> bits which are fixed at 1. Bits 0-3 act as <strong>the</strong> flags. When a<br />
bit is low (contains 0) <strong>the</strong> flag is clear; if a certain event occurs, <strong>the</strong> bit becomes high<br />
(contains 1) and <strong>the</strong> flag is set. Bit 0 is set by a raster compare; sprite-data collisions<br />
show in bit 1; sprite-sprite collisions show in bit 2; and connecting and using a light<br />
pen sets bit 3.<br />
Once a flag bit is set, it can be cleared only by POKEing it high (unlike many<br />
o<strong>the</strong>r flags, which are cleared by POKEing a bit low). This is called latching, and <strong>the</strong><br />
idea of this dual function in <strong>the</strong> register is to keep a record of interrupts within <strong>the</strong><br />
flags <strong>the</strong>mselves, saving programming effort. As an example, a sprite-data collision<br />
sets bit 1; if no o<strong>the</strong>r triggering events have occurred, <strong>the</strong> PEEKed value will be 2.<br />
POKE 53273,2 is required to turn off <strong>the</strong> collision flag (assuming <strong>the</strong> sprite no longer<br />
overlaps data) and return <strong>the</strong> bit to 0. <strong>The</strong> interrupt flag register, <strong>the</strong>refore, is de<br />
signed to store <strong>the</strong> past results of four possible events until <strong>the</strong>y're cleared, and also<br />
shows whe<strong>the</strong>r <strong>the</strong> VIC-II chip caused an interrupt currently in force.<br />
<strong>The</strong> use of this register requires considerable care. Confusion will result if you<br />
forget to clear a flag, try to clear it by POKEing its bit low, or try to clear it while <strong>the</strong><br />
condition that triggered it still exists. O<strong>the</strong>r anomalies might arise because <strong>the</strong> key<br />
board and light pen share common wiring.<br />
Using <strong>the</strong> interrupt enable register to enable interrupts is simple, but <strong>the</strong> IRQ<br />
routine which <strong>the</strong>y're wired to needs modifications to handle <strong>the</strong>m properly. Try<br />
PRINT PEEK(53274) AND 15. <strong>The</strong> result is 0, showing that <strong>the</strong> VIC chip has no<br />
interrupts enabled. Enter POKE 53266,0: POKE 53274,1. This enables raster-scan<br />
interrupts: each time <strong>the</strong> TV picture is scanned, an interrupt occurs. But, in addition,<br />
<strong>the</strong> flag register isn't cleared, so immediately when an interrupt finishes, a new one<br />
begins. This stops processing, although (try SHIFT-<strong>Commodore</strong> key) <strong>the</strong> keyboard is<br />
processed normally. Similarly, POKE 53274,2 has no effect until <strong>the</strong>re's a sprite-data<br />
collision, whereupon interrupts continuously come into effect. Because of this, userwritten<br />
interrupt routines based on <strong>the</strong> VIC-II chip always clear <strong>the</strong> interrupt flag<br />
register before exiting.<br />
Sprite Collision and Light Pen Interrupts and Registers<br />
<strong>The</strong> demonstration routine below, Program 12-33, adds a sprite-data interrupt to <strong>the</strong><br />
normal IRQ.<br />
Program 12-33. Sprite-Data Collision<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
10 FOR J=49152 TO 49172:READ X:POKE J,X:NEXT<br />
:rem 220<br />
20 POKE 2040,13:POKE 53269,1 :rem 182<br />
30 FOR J=832 TO 894:POKE J,255:NEXT :rem 170<br />
40 POKE 53248,100:POKE 53249,53 :rem 90<br />
416
Graphics<br />
100 POKE 56333,127:POKE 788,0:POKE 789,192:rem 207<br />
110 POKE 56333,129 :rem 141<br />
120 POKE 53274,2 :rem 37<br />
130 X=100:D=1 :rem 166<br />
140 PRINT M{CLR}{DOWN}{2 SPACES}gJ§{16 SPACES}gJ§"<br />
• :rem 174<br />
200 IF PEEK(2)=0 GOTO 230 :rem 4<br />
210 IF X>100 THEN D=-l : rem 87<br />
220 IF X
Graphics<br />
and even numbered lines; in o<strong>the</strong>r words, a single screen scan displays half <strong>the</strong> pic<br />
ture. Sixty top-to-bottom scans (50 in <strong>the</strong> U.K.), are displayed every second, so <strong>the</strong><br />
entire picture is refreshed every 1/30 second (1/25 in <strong>the</strong> U.K.). <strong>The</strong> VIC-II chip<br />
generates each raster line, keeping track of <strong>the</strong> current line as it does so.<br />
NTSC (U.S.) TVs have 262 lines per frame, PAL (U.K.) TVs have 312. <strong>The</strong> <strong>64</strong><br />
displays lines 51-251, making 200 lines—enough for 25 sets of eight dots. Raster<br />
lines 0-50 and 252 up aren't visible. TVs in <strong>the</strong> U.K., with more lines; give a more<br />
compressed picture.<br />
Bit 7 of $D011 (53265) with register $D012 (53266) toge<strong>the</strong>r make a nine-bit<br />
register for use with raster scanning. Nine bits are necessary to include all <strong>the</strong> raster<br />
lines, though <strong>the</strong> highest bit is often not used. <strong>The</strong> register has two different func<br />
tions. It allows you to PEEK <strong>the</strong> current raster line. Writing to one or both registers<br />
latches <strong>the</strong> new value, setting <strong>the</strong> VIC-II chip so an interrupt flag, bit 0 in $D019,<br />
goes high whenever <strong>the</strong> current raster line matches all nine bits. If <strong>the</strong> interrupt is<br />
enabled, an interrupt will be caused. POKEs which set bit 7 of $D011 and also put a<br />
high value in $D012, prevent raster interrupts from occurring. This fact allows <strong>the</strong> <strong>64</strong><br />
to deduce which type of VIC chip is fitted and set its PAL/NTSC flag in $02A6. On<br />
power-up, VIC's raster register is POKEd with 311, a value too high for U.S. (NTSC)<br />
signals, but within <strong>the</strong> range of U.K. (PAL) TVs.<br />
So, when we use raster lines in programs, we have a choice of methods: reading<br />
<strong>the</strong> raster line from its registers or, more ambitiously, generating precisely timed<br />
interrupts synchronizing with <strong>the</strong> screen.<br />
Using <strong>the</strong><br />
Raster Interrupt<br />
WAIT 53265,128,128: WAIT 53265,128 shows how <strong>the</strong> raster line can be used, in<br />
this case to detect <strong>the</strong> scan somewhere above midscreen. A carefully timed delay,<br />
followed perhaps by a change of background color, allows a smooth change when<br />
<strong>the</strong> scan is offscreen. But BASIC isn't generally fast enough; <strong>the</strong> raster may scan sev<br />
eral lines in <strong>the</strong> time it takes to perform one PEEK.<br />
A SYS call to <strong>the</strong> ML routine below changes screen colors twice every screen<br />
scan, until a keypress returns to BASIC.<br />
START<br />
WAIT<br />
LDA<br />
BNE<br />
LDA<br />
STA<br />
LDA<br />
CMP<br />
BNE<br />
LDA<br />
STA<br />
LDA<br />
CMP<br />
BEQ<br />
RTS<br />
$D012 ;READ EIGHT BITS OF SCREEN LINE<br />
START ;WAIT TILL WE GET POSITION 0<br />
#2<br />
$D021 ;RED BACKGROUND<br />
#$80<br />
$D012 ;WAIT TILL RASTER LINE IS 128<br />
WAIT<br />
#3<br />
$D021 ;CYAN BACKGROUND<br />
$C5 ;EXIT ON KEYPRESS<br />
#$40<br />
START<br />
Routines like this use <strong>the</strong> processor full time to handle <strong>the</strong> screen. Alter #$80 to<br />
watch <strong>the</strong> dividing-line move; <strong>the</strong> loops WAIT and START occupy almost all <strong>the</strong><br />
6510's time. Interrupts are trickier but allow o<strong>the</strong>r processing. Effects include <strong>the</strong> use<br />
418
Graphics<br />
of alternate fields: semitransparent sprites, fades, new colors obtained by super<br />
imposing one or more standard colors. Eighty-column lettering is sometimes dis<br />
played with half of each character in every o<strong>the</strong>r field. All that's needed is an<br />
interrupt routine synchronized with <strong>the</strong> screen with a counter to select alternative<br />
processing paths. Experiments like this, however, tend to be flickery and unclear.<br />
More important are split-screen methods, where <strong>the</strong> screen is separated across<br />
<strong>the</strong> middle into zones, allowing mixed bitmapping with text, multiple sprites, or dif<br />
ferent character sets in coexistence.<br />
Timing is important. For example, <strong>the</strong> keyscan routine takes roughly a milli<br />
second. Since <strong>the</strong> TV draws 15 raster lines in this time, interrupt-driven routines are<br />
vulnerable to slight timing errors which make <strong>the</strong> split-screen boundary unstable.<br />
Split screens. It is possible to divide <strong>the</strong> screen in half using a raster interrupt.<br />
<strong>The</strong> bottom half can be text, and <strong>the</strong> top half bitmapped graphics, stored from $2000<br />
in VIC bank 1, for example. (This means characters printed in <strong>the</strong> top half would<br />
show as colored squares.) However, you can allow scrolling by filling <strong>the</strong> top text<br />
line with spaces and filling <strong>the</strong> line above with color codes. It's possible to avoid this<br />
problem by moving <strong>the</strong> bitmap color elsewhere. Most of <strong>the</strong> interrupt routine would<br />
be taken up by alternately changing $D011 and $D018 between text and graphics,<br />
<strong>the</strong>n resetting $D012 to cause an interrupt half a screen later.<br />
If <strong>the</strong> program allows <strong>the</strong> normal CIA interrupt as well, you'll see <strong>the</strong> dividing<br />
line become irregular, since <strong>the</strong> exact raster synchronization is lost. <strong>The</strong> cursor<br />
flashes at twice <strong>the</strong> normal rate, if <strong>the</strong> ML exits to $EA31, <strong>the</strong> key-scan routine. For<br />
more on using split screens, see "Split Screens" and "Son of Split Screens" in Com<br />
pute's First Book of <strong>Commodore</strong> <strong>64</strong>.<br />
Thirty-two sprites demonstration. <strong>The</strong> next example, Program 12-34, avoids<br />
<strong>the</strong> fast cursor problem by exiting at $EA81 for all interrupts except one per frame. It<br />
puts 32 sprites on <strong>the</strong> screen, by generating multiple interrupts in each frame. Using<br />
sprites like this requires more work than usual. For example, to move a sprite down<br />
<strong>the</strong> screen means controlling X and Y for several sprites, some of which won't be<br />
displayed because <strong>the</strong> scan is in <strong>the</strong> wrong place for <strong>the</strong>m.<br />
Program 12-34. Thirty-Two Sprites<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
1 REM 32 SPRITES DEMONSTRATION PROGRAM :rem 163<br />
2 REM 828 COUNTS 0 TO 3 :rem 154<br />
3 REM 829-832 STORES 4 SETS OF SPRITE ENABLES<br />
:rem 61<br />
4 REM 833,834 &C=4 SETS LO/HI RASTERS. (HI=128)<br />
:rem 1<br />
5 REM 841-844 STORES 4 SETS OF X-POSN HIGH BITS<br />
:rem 130<br />
6 REM 845-860,861-876,877-892,AND 893-908 :rem 54<br />
7 REM STORE ALL 4 SETS OF SPRITES1 X,Y PAIRS<br />
:rem 54<br />
8 REM OTHER LINE 1001 REMOVES COLORED BANDS:rem 25<br />
9 REM srem 29<br />
10 FOR J=49152 TO 49242:READ X:POKE J,X:NEXT<br />
:rem 218<br />
419
Graphics<br />
19 REM ENABLE ALL 32 SPRITES, & SET POSNS :rem 46<br />
20 POKE 828,3 :rem 146<br />
30 POKE 829,255:POKE 830,255:POKE 831,255:POKE 832<br />
,255 :rem 100<br />
40 POKE 833,90:POKE 835,130:POKE 837,170:POKE 839,<br />
210 :rem 42<br />
50 POKE 834,0:POKE 836,0:POKE 838,0:POKE 840,0<br />
:rem 190<br />
60 POKE 841,0:POKE 842,0:POKE 843,0:POKE 844,0<br />
:rem 186<br />
70 FOR K=845 TO 860 STEP 2:READ X:POKE K,X:POKE K+<br />
16,X :rem 214<br />
75 POKE K+32,X:POKE K+48,X:NEXT :rem 66<br />
80 FOR J=0 TO 3:READ Y:FOR K=846 TO 861 STEP 2<br />
:rem 134<br />
85 POKE K+16*J,Y:NEXT:NEXT :rem 100<br />
99 REM ENABLE RASTER INTERRUPTS :rem 238<br />
100 POKE 56333,127 :rem 138<br />
110 POKE 788,0:POKE 789,192 :rem 157<br />
115 POKE53265,PEEK(53265)AND127 :rem 223<br />
120 POKE 53274,129 -rern 143<br />
199 REM PUT SPECIMEN MOB IN TAPE BUFFER :rem 45<br />
200 FOR J=2040 TO 2047:POKE J,15:NEXT :rem 244<br />
210 FOR J=960 TO 1023:POKE J,255:NEXT :rem 253<br />
999 REM ML FOR RASTER INTERRUPT HANDLING :rem 242<br />
1000 DATA 174,60,3,232,224,4,208,2,162,0,142,60,3<br />
:rem 72<br />
1001 DATA 142,33,208,189,61:REM 234,234,234,189,61<br />
:rem 206<br />
1002 DATA 3,141,21,208,189,73,3,141,16,208,138,10<br />
:rem 90<br />
1003 DATA 170,189,66,3,13,17,208,141,17,208,189,65<br />
:rem 166<br />
1004 DATA 3,141,18,208,138,10,10,10,170,160,0,189<br />
1005 DATA 77,3,153,0,208,189,78,3,153,1,208,#232,23<br />
2 :rem 202<br />
1006 DATA 200,200,192,16,208,236,169,1,141,25,208,<br />
173 :rem 36<br />
1007 DATA 13,220,41,1,240,3,76,49,234,76,129,234<br />
1999 REM SAMPLE X & Y POSNS OF 32 SPRITES Irem 16<br />
2000 DATA 40,70,100,130,160,190,220,250(17 SPACES}<br />
-*«« :rem 92<br />
3000 DATA 60,100,140,180 :rem 134<br />
At each interrupt, all <strong>the</strong> X and Y positions are reset, so <strong>the</strong> sprite positions are<br />
all independent. <strong>The</strong> sprite-enable registers are also reset, so any number of sprites<br />
from 0 to 32 can be chosen. <strong>The</strong> colors, priority, expansion, and so on, are shared<br />
between <strong>the</strong>m here to save space. Note <strong>the</strong> color bands, to show where interrupts<br />
occur: <strong>The</strong>ir positions can be changed in lines 40 and 50. <strong>The</strong> bands are removable.<br />
420
Graphics<br />
<strong>The</strong> ML disassembly is too long for inclusion here. It consists of loops which<br />
load new X,Y values, <strong>the</strong> sprite-enable register, and <strong>the</strong> timing for <strong>the</strong> next interrupt.<br />
Motion Without Sprites<br />
Although sprites are powerful, and <strong>the</strong> screen can be filled with <strong>the</strong>m, you may pre<br />
fer to PRINT strings of characters (or use JSR $FFD2 in ML) if, for example, you've<br />
defined your own graphics. It's fairly easy to move characters one full step in any<br />
direction, but <strong>the</strong> result is inevitably somewhat jerky, though this is less noticeable<br />
when bunches of characters move toge<strong>the</strong>r. Where it's a problem, an improvement is<br />
to make up intermediate characters from halves of <strong>the</strong> original. Following is a simple<br />
example routine to give you an idea of how to use <strong>the</strong> standard graphics that are<br />
available on <strong>the</strong> <strong>64</strong>.<br />
Program 12-35. Animation<br />
10 M$=g3B3<br />
20 PRINT M{2 SPACES}<br />
30 FOR J=l TO 40<br />
40 FOR 10=1 TO 30: NEXT<br />
50 PRINT "{2 LEFT} EB|";<br />
60 FOR K=l TO 30:NEXT<br />
70 PRINT "{LEFT}11 M$;:NEXT<br />
Program 12-36 makes use of <strong>the</strong> fact that <strong>the</strong> checked block can be imitated by<br />
ano<strong>the</strong>r pair of graphics. Resolution is to four dots. This method is unwieldy for a lot<br />
of movement in many directions, because any single character needs eight more<br />
characters to allow half-character motion in <strong>the</strong> main directions.<br />
If <strong>the</strong> movement is one-dimensional, as in games, 80 or so mobile characters can<br />
be used. Smooth motion like this requires 15 characters just to move one single<br />
character, so only 17 different objects would use <strong>the</strong> entire set. This is usually less<br />
practical than using sprites.<br />
Dynamic redefinition of characters. An advanced method to simulate motion<br />
and o<strong>the</strong>r effects is to alter <strong>the</strong> character definitions or bitmaps <strong>the</strong>mselves, so partial<br />
characters are generated when needed, ra<strong>the</strong>r than being stored. ML is necessary<br />
since 16 POKEs to alter <strong>the</strong> bytes defining two characters would be slow.<br />
Planning is important. First, <strong>the</strong> original definitions should usually be kept in<br />
memory, away from <strong>the</strong> area that will be redefined, because characters might be irre<br />
coverably changed by dynamic redefinition. Second, characters should be numbered<br />
conveniently—generally consecutively down or across <strong>the</strong> screen—to simplify ML.<br />
Vertical smooth motion. <strong>The</strong> following BASIC routine, Program 12-36, dem<br />
onstrates up-and-down motion in this way. ROM graphics are copied into $3000 up,<br />
and screen codes 128-137 (normally reverse-@ through reverse-I) are redefined as<br />
numerals 0-9. <strong>The</strong>se numerals, starting at $3400, are processed by ML routines, giv<br />
ing a realistic odometer effect, while retaining <strong>the</strong> normal numeral definitions. Upand-down<br />
motion is simple to program; as Figure 12-13 shows, all that's necessary is<br />
to move over <strong>the</strong> bytes making up <strong>the</strong> characters.<br />
421
Graphics<br />
Program 12-36. Vertical Motion<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader/' Appendix C<br />
10 FOR J=49152 TO 49177:READ XrPOKE J,X:NEXT<br />
20 POKE 56333,127:POKE 1,51<br />
30 FOR J=0 TO 511:POKE 12288+J,PEEK(53248+J):NEXT<br />
40 POKE 1,55:POKE 56333,129<br />
50 POKE 56,48:CLR:POKE 53272,29<br />
100 FOR J=0 TO 10:PRINT "{RVS}" RIGHT$("IHGFEDCBA@<br />
M,J);"{UP}M:NEXT<br />
110 FOR J=0 TO 79:POKE 13312+J,PEEK(12672+J):NEXT<br />
120 FOR J=0 TO 79:SYS 49152:NEXT<br />
130 FOR J=0 TO 79.-POKE 13312+J,PEEK(12672+J):NEXT<br />
140 FOR J=0 TO 79:SYS 491<strong>64</strong>:NEXT<br />
150 GOTO 100<br />
200 DATA 162,79,189,254,51,157,255,51,202<br />
210 DATA 208,247,96,162,1,189,0,52,157<br />
220 DATA 255,51,232,224,80,208,245,96<br />
Figure 12-13. Vertical Character Motion<br />
After Move<br />
Original<br />
After move up<br />
1 row<br />
Screen Character<br />
Character Definition<br />
SYS 49152 uses <strong>the</strong> loop below, which moves only <strong>the</strong> required portion of <strong>the</strong><br />
character definitions. (You may need to add a zero byte at <strong>the</strong> end points.)<br />
LDX #$4F<br />
LOOP LDA $33FE,X<br />
STA $33FF,X<br />
DEX<br />
BNE LOOP<br />
RTS<br />
Horizontal smooth motion. Because of <strong>the</strong> way screen and character memory<br />
are allocated, this is trickier. It is necessary to number <strong>the</strong> relevant screen locations<br />
consecutively. As Figure 12-14 shows, you must store a bit from each byte and ro<br />
tate it into ano<strong>the</strong>r byte eight bytes away.<br />
422
Graphics<br />
Figure 12-14. Horizontal Character Motion<br />
Original After Shift Right<br />
Screen Characters<br />
1 Column<br />
.illlll-l J_J I I L Before<br />
I 1 I I I 1 | I I 1 I I I. !■ After<br />
Character Definitions<br />
Replace <strong>the</strong> following lines in Program 12-36 and run it. Program 12-37 will<br />
produce a similar effect, as <strong>the</strong> special numerals 0-9 scroll smoothly sideways.<br />
Program 12-37. Horizontal Motion<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
10 FOR J=49178 TO 49205:READ X:POKE J,X:NEXT<br />
20 POKE 56333,127:POKE 1,51<br />
30 FOR J=0 TO 511:POKE 12288+J,PEEK(53248+J):NEXT<br />
40 POKE 1,55:POKE 56333,129<br />
50 POKE 56,48:CLR:POKE 53272,29<br />
100 FOR J=0 TO 10:PRINT "{RVS}" RIGHT$("@ABCDEFGHI<br />
M,J);"{UP}":NEXT<br />
110 FOR J=0 TO 79:POKE 13312+J,PEEK(12672+j):NEXT<br />
120 FOR J=0 TO 79:SYS 49178:FORK=1TO99:NEXT:NEXT<br />
150 GOTO 100<br />
200 DATA 160,7,152,170,24,126,0,52,8,138,24<br />
210 DATA 105,8,170,224,79,176,5,40,208,240<br />
220 DATA 240,238,40,136,16,231,96<br />
423
Chapter 13<br />
Sound<br />
Sound Waves: Analysis and Syn<strong>the</strong>sis<br />
<strong>The</strong> SID Chip<br />
Music <strong>The</strong>ory<br />
Sound Demonstrations
Chapter 13<br />
Sound<br />
This chapter explains sound and music on <strong>the</strong> <strong>64</strong> and shows how <strong>the</strong> SID chip<br />
handles both in practice. Although <strong>the</strong> <strong>the</strong>oretical side is important for full under<br />
standing, it is also quite difficult, so you may prefer to try <strong>the</strong> programs first to get<br />
<strong>the</strong> feel of SID.<br />
Sound Waves: Analysis and Syn<strong>the</strong>sis<br />
Sound is a psychological phenomenon produced by vibrations. <strong>The</strong> pressure waves<br />
that produce sound are three-dimensional and are mainly concentric waves in air<br />
centered on sound sources. Additional complications arise due to conductance,<br />
reflection, and so on, which cause reverberation, echo, and o<strong>the</strong>r acoustic effects.<br />
Any elastic medium (such as metal, bone, or water) propagates sound at a speed<br />
depending on <strong>the</strong> medium's elasticity—faster in warm air than in cold air, and much<br />
faster in metal. <strong>The</strong> medium's molecules oscillate with very little net movement, and<br />
this motion gives rise to compressions and rarefactions which transmit <strong>the</strong> wave. <strong>The</strong><br />
wave moves a lot, but <strong>the</strong> medium moves very little.<br />
Waves are usually depicted by graphs which show how <strong>the</strong> back-and-forth<br />
movements vary with time: A cycle of a regular wave is <strong>the</strong> interval from one point<br />
on <strong>the</strong> wave to <strong>the</strong> next similar point in <strong>the</strong> repeating wave pattern. <strong>The</strong> frequency of<br />
a wave is its number of cycles per second. One hertz (abbreviated Hz) means <strong>the</strong><br />
same as cycles per second. <strong>The</strong> distance in magnitude between <strong>the</strong> peak and <strong>the</strong><br />
trough of a wave is known as its amplitude. <strong>The</strong> larger <strong>the</strong> amplitude of an audible<br />
wave, <strong>the</strong> louder <strong>the</strong> sound will be. <strong>The</strong> process of perceiving sound from pressure<br />
waves is performed by <strong>the</strong> ear and brain. Frequency, a physical property of <strong>the</strong><br />
vibration, is related to <strong>the</strong> psychological property of pitch: high frequency is per<br />
ceived as a high note and low frequency as a low note. <strong>The</strong> maximum range of fre<br />
quencies audible to human beings is about 20-20,000 Hz, though <strong>the</strong> actual range<br />
varies between individuals and generally decreases with increased age and exposure<br />
to noise. <strong>The</strong>se frequencies are determined by such factors as <strong>the</strong> size of <strong>the</strong> ear<br />
drum, which resonates when vibrated.<br />
Sounds with a distinct, steady pitch, produced, for example, by tuning forks and<br />
some musical instruments, result from <strong>the</strong> repetition of similar vibrations. A note<br />
may sound very different on different instruments; this quality, <strong>the</strong> timbre, depends<br />
on <strong>the</strong> relation between its fundamental (lowest) frequency and its harmonics.<br />
Sine Waves<br />
Sine waves are important for two reasons. First, any note in any timbre can be an<br />
alyzed by breaking it into its component sine waves, and this analysis gives a com<br />
mon basis of comparison between all notes and timbres. This makes <strong>the</strong> results of<br />
electronic sound processing predictable. <strong>The</strong> second reason is psychological, in that a<br />
similar process of analysis happens in <strong>the</strong> brain. As a result, we can think of <strong>the</strong><br />
timbre of <strong>the</strong> sine wave as <strong>the</strong> simplest and purest sounding of any waveform. Fig<br />
ure 13-1 outlines a sine wave.<br />
427
Sound<br />
Figure 13-1. Sine Wave<br />
Circular motion generates sine curves; <strong>the</strong> SIN function describes this motion.<br />
As it happens, <strong>the</strong> prongs of a sounding tuning fork give a good approximation of si<br />
nusoidal motion. In fact, simple harmonic motion derives its name from <strong>the</strong> sound<br />
analogy.<br />
A tuning fork is a simple musical instrument. It works <strong>the</strong> way it does because a<br />
struck prong is pushed toward its still position by a force that varies with (among<br />
o<strong>the</strong>r things) <strong>the</strong> distance from its still position, and it can also be shown that this<br />
produces a sine curve. This is a typical application of physics to sound.<br />
Consider a tuning fork with a smaller tuning fork attached to one prong, so <strong>the</strong><br />
movement of both forks contributes to <strong>the</strong> movement of <strong>the</strong> small fork. <strong>The</strong> com<br />
posite motion will be two sine waves added toge<strong>the</strong>r.<br />
If <strong>the</strong> smaller fork's frequency is exactly 2, 3, 4, or any integer times <strong>the</strong> fre<br />
quency of <strong>the</strong> larger, <strong>the</strong>n <strong>the</strong> ear-brain mechanism fuses <strong>the</strong>m into a harmonious<br />
tone, of richer timbre than a pure sine wave. As stated above, sine waves which are<br />
multiples of a sine wave are known as harmonics of that sine wave. <strong>The</strong> fun<br />
damental frequency is known as <strong>the</strong> first harmonic, <strong>the</strong> wave at twice this frequency<br />
is known as <strong>the</strong> second harmonic, that at three times its frequency is <strong>the</strong> third har<br />
monic, and so on. Most musical instruments are designed to generate harmonics, and<br />
<strong>the</strong> relative importance of harmonics determines <strong>the</strong> instrument's timbre. For ex<br />
ample, a closed air column can vibrate stably along its full length, or half its length,<br />
or one-third, and so on, and <strong>the</strong>refore has a full range of harmonics; a string,<br />
plucked in <strong>the</strong> center, cannot easily vibrate for half its length and has only odd<br />
harmonics.<br />
Harmonic analysis is <strong>the</strong> process of determining <strong>the</strong> sine wave components that<br />
make up a waveform. Fourier analysis, a popular technique, is based on <strong>the</strong> principle<br />
that any repeating function f(x) is of form bl sin (x) + b2 sin (2x) + . . ., where <strong>the</strong><br />
integral from minus pi to pi of f(x) sin (kx) is bk. <strong>The</strong> idea isn't new, and ancient<br />
astronomers built up ellipses from many circular movements. <strong>The</strong> harmonics and<br />
<strong>the</strong>ir relative amplitudes make up <strong>the</strong> harmonic spectrum of <strong>the</strong> waveform, which<br />
can be drawn on a graph as amplitude against frequency.<br />
428
Sound<br />
Conversely, any repeating waveform, like that from <strong>the</strong> two tuning forks, can be<br />
built up by adding harmonics with <strong>the</strong> right amplitudes: this is additive syn<strong>the</strong>sis.<br />
<strong>The</strong> process of starting with timbres which are rich in harmonics and deriving a<br />
sound with a desired spectrum by filtering out <strong>the</strong> unwanted ones is known as<br />
subtractive syn<strong>the</strong>sis.<br />
<strong>The</strong> SID chip isn't designed to generate sine waves, but as we'll see, it does gen<br />
erate quite complex waves with rich spectra and also allows limited subtractive syn<br />
<strong>the</strong>sis, since it has a filter.<br />
Waveforms and <strong>The</strong>ir Harmonic Contents<br />
<strong>The</strong> SID has four main waveforms: noise, pulse (square), sawtooth, and triangle. You<br />
can view <strong>the</strong> SID's outputs by modifying <strong>the</strong> "ADSR Plotter," later in this chapter.<br />
Use PEEK (SID+27) to read voice 3's wave output, decrease SID+15 to reduce <strong>the</strong><br />
frequency, and POKE SID+18 with 129, 65, 33, or 17 for noise, pulse, sawtooth,<br />
and triangle waves, respectively (setting SID+16 and SID+17 when trying <strong>the</strong> pulse<br />
wave).<br />
Following is a discussion of <strong>the</strong> three repeating (and one nonrepeating) wave<br />
forms that <strong>the</strong> SID chip generates.<br />
Triangle<br />
<strong>The</strong> triangle wave (see Figure 13-2) is SIN (X) - SIN (3*X)/9 + SIN (5*X)/25 -<br />
SIN (7*X)/49 + . . . where X is <strong>the</strong> harmonic number. A triangular wave with 100<br />
Hz as its fundamental contains frequencies of 300, 500, 700, 900 Hz, and so on,<br />
which rapidly decrease in importance.<br />
Figure 13-2. Triangle Wave<br />
This is SID's closest approximation to a sine wave, and it sounds like a flute or<br />
xylophone. Four sinusoidal harmonics—<strong>the</strong> first, third, fifth, and seventh—add to<br />
form an approximately triangular waveshape.<br />
429
Sound<br />
Sawtooth<br />
<strong>The</strong> sawtooth wave is SIN (X) + SIN (2*X)/2 + SIN (3*X)/3 + SIN (4*X)/4 +<br />
It contains <strong>the</strong> complete harmonic series, with amplitude in inverse proportion to <strong>the</strong><br />
harmonic number, so <strong>the</strong> second harmonic has half <strong>the</strong> amplitude of <strong>the</strong> fun<br />
damental, and so on. <strong>The</strong> harmonics have more importance than <strong>the</strong> triangular<br />
wave's: Figure 13-3 adds six harmonics to provide an approximation.<br />
Figure 13-3. Sawtooth Wave<br />
<strong>The</strong> large numbers of significant harmonics enable this wave to simulate such<br />
instruments as <strong>the</strong> trumpet, oboe, clarinet, and accordion, especially when filtered to<br />
change <strong>the</strong> harmonic balance.<br />
This wave's asymmetry causes a few oddities. For example, every o<strong>the</strong>r term of<br />
its analysis makes up a square wave: if this is subtracted, what's left is SIN (2*X)/2<br />
+ SIN (4*X)/4 + . . ., ano<strong>the</strong>r sawtooth wave of twice <strong>the</strong> frequency and half <strong>the</strong><br />
amplitude. Add <strong>the</strong> square wave to <strong>the</strong> sawtooth on <strong>the</strong> diagram to see this. <strong>The</strong><br />
sawtooth also tends to sound higher than its fundamental would suggest.<br />
Pulse (Square) Wave<br />
This is <strong>the</strong> most useful waveshape provided by SID. <strong>The</strong> signal is alternately held<br />
high for a measured time period, <strong>the</strong>n low for ano<strong>the</strong>r, generally different period. In<br />
o<strong>the</strong>r words, <strong>the</strong>re is a regular pulse. <strong>The</strong> ratio of <strong>the</strong> time <strong>the</strong> signal is high to <strong>the</strong><br />
complete cycle is <strong>the</strong> duty cycle of <strong>the</strong> pulse wave.<br />
A square wave is <strong>the</strong> special case in which <strong>the</strong> duty cycle ratio is 1:2. It analyzes<br />
into SIN (X) + SIN (3*X)/3 + SIN (5*X)/5+ .... <strong>The</strong>refore, like <strong>the</strong> triangular<br />
wave, it contains only odd harmonics, but with larger amplitudes, inversely related<br />
to <strong>the</strong> harmonic number. <strong>The</strong> third harmonic has one-third <strong>the</strong> amplitude of <strong>the</strong> fun<br />
damental, for example. Figure 13-4 shows a wave constructed from <strong>the</strong> first, third,<br />
fifth, and seventh harmonics.<br />
<strong>The</strong> pulse wave's interesting feature is <strong>the</strong> way its harmonic content changes<br />
when <strong>the</strong> duty cycle is altered. Full analysis is tricky. <strong>The</strong> Kth harmonic varies with<br />
(COS(NK)-COS (K*PI))/K, but generally if <strong>the</strong> duty cycle is 1:N, <strong>the</strong>n every Nth<br />
harmonic will be absent, and harmonics between <strong>the</strong>se suppressed harmonics will be<br />
430
Sound<br />
boosted. Very asymmetrical pulse waves have very irregular harmonic spectra, with<br />
some large amplitudes in <strong>the</strong> harmonics. <strong>The</strong> square wave, with N=2, lacks all even<br />
harmonics, as we've seen.<br />
A wave of duty cycle 1:5 lacks fifth and tenth harmonics, and so on, but does<br />
have second, third, fourth, sixth, seventh, eighth, and ninth harmonics, with <strong>the</strong><br />
seventh and eighth harmonics of higher amplitude than <strong>the</strong> sixth and ninth. A duty<br />
cycle of 1:5.1 will cause <strong>the</strong> fifth, tenth, etc., harmonics to be much reduced in am<br />
plitude, but not totally negated.<br />
Altering <strong>the</strong> duty cycle while a note is playing causes most harmonics to change<br />
<strong>the</strong>ir levels, and some to vanish altoge<strong>the</strong>r or reappear. Thus, we have a way of<br />
producing a dynamically changing spectrum with richer, more interesting sounds<br />
than <strong>the</strong> triangle or sawtooth waves.<br />
If <strong>the</strong> pulse width is altered from square toward a narrow pulse, loudness<br />
diminishes, and <strong>the</strong> timbre becomes more nasal or buzzy compared with <strong>the</strong> wellrounded<br />
sound of <strong>the</strong> square wave, as some high harmonics are boosted. As <strong>the</strong><br />
width of <strong>the</strong> pulse becomes very narrow, <strong>the</strong> decreasing amount of energy present is<br />
spread out among very many harmonics, and <strong>the</strong> sound fades to inaudibility.<br />
<strong>The</strong> square wave is louder than any o<strong>the</strong>r pulse wave of equal amplitude,<br />
mainly because <strong>the</strong> total energy in a square wave is greater: A very short pulse is<br />
simply quieter. A secondary factor is <strong>the</strong> energy distribution amongst <strong>the</strong> harmonics:<br />
a square wave's harmonics taper off smoothly, but a pulse wave with duty cycle of<br />
1:2 has a very irregular distribution, and some boosted harmonics may be inaudible.<br />
Typical sound simulations are a piano (square wave), organ (1:4 wave), and<br />
banjo (1:10 wave). Very narrow pulses at low frequency give a car engine sound.<br />
Figure 13-4. Pulse Wave<br />
Noise<br />
Noise is sound with no fundamental frequencies and is typically generated by an un<br />
musical source which has no dominant modes of vibration. Rumbles and hisses,<br />
crashes and explosions, scrapes and rattles, jet engines and gas burners, wind and<br />
water flow, illustrate this sort of sound.<br />
431
Sound<br />
SID generates noise by loading new random values into <strong>the</strong> oscillator output, at<br />
regular intervals controlled by <strong>the</strong> frequency setting. While <strong>the</strong> result cannot be an<br />
alyzed into repeating sine waves, any single interval can be analyzed, so <strong>the</strong> ear gets<br />
a definite overall feeling of low, medium, or high pitch. White noise contains all fre<br />
quencies in equal proportion. <strong>The</strong> SID's noise generation method sets a minimum<br />
frequency and plays all available notes above this minimum with equal probability,<br />
so most SID noise is biased to high frequencies; this is called blue noise.<br />
Noise (diagrammed in Figure 13-5) is useful in simulating <strong>the</strong> sorts of sounds<br />
mentioned above, and also certain percussion sounds like snare drums, brushed<br />
drums, and cymbals. Dynamic changes in noise frequency can simulate ripping, tear<br />
ing sounds, fireworks in motion, and moving vehicles.<br />
Figure 13-5. Noise<br />
Ring<br />
Modulation and Synchronization<br />
Bells, gongs, chimes, clamped metal bars, and so on, vibrate differently from strings<br />
and air columns, generating nonsinusoidal waves, which analyze into complex wave<br />
forms containing several series of fundamentals and harmonics.<br />
Ring modulation simulates this process. <strong>The</strong> principle isn't too difficult, and<br />
understanding it makes it easier to achieve <strong>the</strong> desired effect.<br />
Ring modulation obtains a complex signal by combining two input signals. <strong>The</strong><br />
process is simply a point-by-point multiplication of one input signal by <strong>the</strong> o<strong>the</strong>r.<br />
<strong>The</strong> trigonometric identity SIN(Fl) * SIN(F2) = -SIN (Fl+F2+90)/2 + SIN<br />
(Fl— F2+90)/2 gives a clue as to what happens; two sine waves are combined, giv<br />
ing two new sine waves with frequencies F1+F2, of reduced amplitude and different<br />
phase.<br />
As mentioned, <strong>the</strong> ring modulated output from two sine waves with frequencies<br />
Fl and F2 consists of sine waves with frequencies (F1+F2) and (Fl—F2). <strong>The</strong> input<br />
signals aren't present in <strong>the</strong> output. So if one of <strong>the</strong> input signals to <strong>the</strong> ring modu<br />
lator has a frequency of 1 Hz, <strong>the</strong> output is two signals of F+l Hz and F—1 Hz,<br />
which should be heard as a tremolo effect, because notes which are close toge<strong>the</strong>r<br />
pulsate in loudness. Piano tuners use this beat effect. Its frequency is half <strong>the</strong> dif<br />
ference between <strong>the</strong> component frequencies. In fact, <strong>the</strong> SID cannot generate very<br />
432
Sound<br />
slow frequencies satisfactorily; you'll get clicks and pops ra<strong>the</strong>r than pulsations, so<br />
tremolo is better achieved by controlling volume.<br />
When one or both inputs contain harmonics, each harmonic gives rise to a sum<br />
and difference signal with each harmonic of <strong>the</strong> o<strong>the</strong>r input. SID's ring modulator<br />
can be used only when <strong>the</strong> triangular wave is selected for <strong>the</strong> ring modulated voice<br />
(even though this waveform is less rich in harmonics than o<strong>the</strong>rs). Ring modulation<br />
is controlled by setting <strong>the</strong> frequency of <strong>the</strong> modulating voice; o<strong>the</strong>r parameters of<br />
<strong>the</strong> modulating voice are irrelevant to <strong>the</strong> effect.<br />
Consider two frequencies, a and b. <strong>The</strong> triangular waves have harmonics a, 3a,<br />
5a, ... (and b, 3b, 5b, . . .). Combining only <strong>the</strong> first three harmonics of each (to<br />
save space) gives:<br />
Adding a+b a+3b a+5b<br />
3a+b 3a+3b 3a+5b<br />
5a+b 5a + 3b 5a+5b<br />
Subtracting a—b a —3b a—5b<br />
3a—b 3a—3b 3a—5b<br />
5a-b 5a-3b 5a-5b<br />
This shows <strong>the</strong>re are two triangular waveforms based on (a+b) and (a—b); look at<br />
<strong>the</strong> main diagonals to see this. <strong>The</strong>re are also many extra waves, with such fre<br />
quencies as 3a+b and 5a+3b.<br />
Consider <strong>the</strong> case where b is very small. <strong>The</strong> result will be many sine waves,<br />
each of frequency approximately a, many of frequency about 3a, and so on. <strong>The</strong> re<br />
sult is a modified triangular wave, where all <strong>the</strong> harmonics beat.<br />
In <strong>the</strong> special case where a is a multiple of b, <strong>the</strong> output is a harmonic series.<br />
For example, if 100 Hz is input with 300 Hz, all <strong>the</strong> sum and difference signals must<br />
be multiples of 100 Hz. <strong>The</strong> result is a distinctly pitched note, typically something<br />
like a square wave. If <strong>the</strong> frequencies aren't quite exact multiples, <strong>the</strong> output is a pair<br />
of enhanced triangular waves which will beat, since <strong>the</strong>ir fundamentals aren't quite a<br />
ratio. This gives realistic banjo twangs or rubber-band "boings."<br />
Where one input changes continuously relative to <strong>the</strong> o<strong>the</strong>r, some frequencies<br />
(those produced by addition of harmonics) slide up, and <strong>the</strong> o<strong>the</strong>rs (produced by<br />
subtraction) slide down, and <strong>the</strong> output passes through points where it has a single<br />
pitch.<br />
Bell-like timbres are produced with such inputs as 110 and 152 Hz, which give<br />
two audible notes (42 Hz and 262 Hz) of unrelated frequency.<br />
Synchronization also forms a joint output from two inputs, but in a different<br />
way. <strong>The</strong> frequency and waveform remain virtually unchanged. However, at inter<br />
vals decided by <strong>the</strong> inputs, <strong>the</strong> waveform restarts. <strong>The</strong> effect is to add high harmon<br />
ics to <strong>the</strong> wave, while keeping frequency fairly constant.<br />
Envelopes<br />
So far we've discussed steady tones, produced by stable waveforms. A note's en<br />
velope describes <strong>the</strong> rise and decay of <strong>the</strong> note's loudness against time. <strong>The</strong> time<br />
scale of SID envelopes is much longer than for waves.<br />
Figure 13-6 shows <strong>the</strong> envelope of a triangular wave over a tenth of a second.<br />
<strong>The</strong> envelope has a short attack phase, and a much longer decay phase. <strong>The</strong> wave<br />
form is independent of <strong>the</strong> envelope, but it's easy to forget <strong>the</strong> distinction.<br />
433
Sound<br />
Figure 13-6. Envelope Diagram<br />
Envelope of Above Note<br />
<strong>The</strong> SID chip's programmable envelope has four parts:<br />
• Attack. <strong>The</strong> time in which <strong>the</strong> note reaches full volume from zero. Typically very<br />
short.<br />
• Decay. Allows <strong>the</strong> volume to be reduced from its initial maximum to a steady<br />
level.<br />
• Sustain. Period where <strong>the</strong> volume is steady, like <strong>the</strong> waves discussed earlier in this<br />
chapter.<br />
• Release. Period during which amplitude falls from <strong>the</strong> sustain level to zero.<br />
<strong>The</strong> above parameters help <strong>the</strong> programmer emulate sounds by controlling <strong>the</strong><br />
envelope. Figure 13-7 gives some illustrations of how ADSR envelopes might be<br />
used.<br />
Energy is used to generate sound. In a system radiating sound with a relatively<br />
constant loudness, like an organ or violin, energy must be input as long as a steady<br />
note is desired. With notes produced by a piano or a guitar, though, energy is ap<br />
plied once, to dissipate as <strong>the</strong> note dies away.<br />
<strong>The</strong> guitar envelope has zero attack time, decay starts immediately, and <strong>the</strong>re is<br />
no sustain level. A triangular wave approximates its odd-harmonic wave. This en<br />
velope is characteristic of cymbals, bells, drums, and many percussion instruments.<br />
A piano envelope is similar, except that a piano key may be released, and if it is,<br />
434
Sound<br />
decay is interrupted and <strong>the</strong> release phase terminates <strong>the</strong> note. A square wave<br />
approximates <strong>the</strong> piano's timbre.<br />
<strong>The</strong> organ has a noticeable attack and decay, as <strong>the</strong> note is established or fades.<br />
While a key is pressed <strong>the</strong> sustain level is held, after which <strong>the</strong>re's a short fade-out.<br />
A pulse wave gives about <strong>the</strong> right timbre.<br />
A flute has a longer attack and release, and has roughly a triangular waveform.<br />
Figure 13-7. Common Envelope Shapes<br />
Guitar<br />
Percussion<br />
Piano (Sustained)<br />
Piano (Note<br />
Released)<br />
Flute<br />
Organ<br />
Filtering<br />
and Resonance<br />
Filtering in electronic music means removing a range of unwanted frequencies. <strong>The</strong><br />
result can be predicted by analysis of <strong>the</strong> input waveform: for example, a low-pass<br />
filter passes low harmonics, but cuts out high ones, cutting treble and giving a more<br />
bass tone. This is <strong>the</strong> most common type of filter. <strong>The</strong> level where it begins to act is<br />
called its cut-off frequency. A high-pass filter passes frequencies only above <strong>the</strong> cut<br />
off frequency, and a band-pass filter passes a narrow band of frequencies, removing<br />
those not in <strong>the</strong> vicinity of what's called <strong>the</strong> center frequency. <strong>The</strong> cut-off isn't al<br />
ways sharp. <strong>The</strong> SID's low-pass filter, for example, reduces perceived volume by<br />
about half for each octave above cut-off.<br />
A notch filter takes effect when low- and high-pass filters are on toge<strong>the</strong>r. <strong>The</strong><br />
SID's filter type is controlled by three bits, each of which can be on or off, so <strong>the</strong><br />
SID can be set as a notch. Resonance amplifies or boosts frequencies close to <strong>the</strong> cut<br />
off point. SID's filter has 16 linear resonance settings. See Figure 13-8.<br />
435
Sound<br />
Figure 13-8. Filters<br />
Band-pass<br />
High-pass<br />
%of<br />
Sound<br />
Passed, %<br />
I<br />
50 100 200 400 800 1500 3000 6000 12000 Hz<br />
J_<br />
Low-pass filter with cut-off frequency 100 Hz<br />
Band-pass filter with center frequency 800 Hz<br />
High-pass filter with cut-off 3000 Hz<br />
Notch High-pass filter with resonance<br />
Syn<strong>the</strong>sis Notes<br />
Any waveform can be syn<strong>the</strong>sized by sampling (by measuring <strong>the</strong> sound's amplitude<br />
at a series of points) and simply duplicating <strong>the</strong> sequence. This is not how most syn<br />
<strong>the</strong>sizers work, though <strong>the</strong>y generate sawtooth, noise, and variable pulse waves, and<br />
have controls for such things as vibrato and chorus (where all voices are turned on<br />
in approximate unison). ADSR envelopes are often used, <strong>the</strong> release phase being en<br />
tered when a key is no longer pressed. <strong>The</strong> <strong>Commodore</strong> <strong>64</strong> SID chip, designed by a<br />
specialist in music syn<strong>the</strong>sis, has many of <strong>the</strong>se features.<br />
Sounds produced by acoustic instruments tend to have extremely complex<br />
changing harmonic structures, especially in <strong>the</strong> attack phase. And harmonics aren't<br />
absolutely perfect. <strong>The</strong>ir frequencies aren't quite exact multiples of <strong>the</strong> fundamental<br />
frequency. For <strong>the</strong>se reasons, it's difficult to make electronic instruments sound just<br />
like acoustic ones.<br />
436
Sound<br />
Speech Syn<strong>the</strong>sis<br />
<strong>The</strong> vocal cords generate a roughly triangular wave of fundamental 70 (men) or 100<br />
(women), which resonates in <strong>the</strong> throat, mouth, and nose and is shaped by <strong>the</strong><br />
tongue, palate, and lips. Speech is typically syn<strong>the</strong>sized from three main frequencies<br />
(formants), plus low-frequency noise (nasals) and high-frequency noise (fricatives). For<br />
example, "oo" is roughly a sine wave, and "ee" a sine wave plus a 3000 Hz tone.<br />
<strong>The</strong> frequencies of formants typically change somewhat in <strong>the</strong> course of vowel<br />
sound. Consonants are often bursts of noise which are abruptly cut off. Since SID<br />
has only three voices, no sine wave capability, and a single filter, speech is not easy<br />
to syn<strong>the</strong>size with <strong>the</strong> <strong>64</strong>, although it is possible.<br />
Plug-in speech cartridges usually include <strong>the</strong>ir own speech chip, bypassing SID<br />
and providing a known quality of output. Such chips typically store waveforms of <strong>64</strong><br />
component parts of speech sound (phonemes) and allow control of such parameters<br />
as duration. <strong>The</strong>ir software converts some form of phonetic spelling into phonemes;<br />
for example, A$="H/E/LL/OO/P2" may define a string which sounds like "hello"<br />
followed by a pause. Standard words and sounds and <strong>the</strong> alphabet may be supplied<br />
in ROM. Commands are ei<strong>the</strong>r wedged into BASIC or accessed by SYS calls.<br />
<strong>The</strong> SID Chip<br />
Overview of <strong>the</strong> SID Chip<br />
Before a more technical discussion of <strong>the</strong> SID chip, here is a plain English descrip<br />
tion. <strong>The</strong> SID will generate a range of waveforms, as discussed above. <strong>The</strong>se waves<br />
are always output in an ADSR envelope. In o<strong>the</strong>r words, <strong>the</strong> chip is designed to pro<br />
duce individual notes, of programmable timbres and frequencies. Steady notes are<br />
obtainable in <strong>the</strong> extreme case where sustain is simply left on. Accurate direct con<br />
trol of <strong>the</strong> output signal isn't part of <strong>the</strong> SID chip's function. <strong>The</strong> next best option is<br />
to set a pulse wave at its far range so that it never actually pulses and to alter <strong>the</strong><br />
sustain level.<br />
Notes are controlled like an organ: individual notes must be consciously turned<br />
on and turned off. To play a full ADSR note <strong>the</strong>refore requires two POKEs or ML<br />
stores—one to start it, <strong>the</strong> o<strong>the</strong>r to trigger <strong>the</strong> final release phase.<br />
To save money and memory, <strong>Commodore</strong> didn't incorporate commands like<br />
SOUND or ENV into BASIC for <strong>the</strong> <strong>64</strong>. Consequently, programming sound involves<br />
many POKEs. Even <strong>the</strong> simplest sounds require four POKEs.<br />
It's fairly easy to play multipart tunes with <strong>the</strong> SID chip. Once each voice has its<br />
envelope set up and waveform selected, all that's needed is to periodically change a<br />
note's pitch, turn <strong>the</strong> note on, wait for some suitable sustain interval, turn <strong>the</strong> note<br />
off, and wait for sufficient release time. <strong>The</strong> automatic timing of attack, decay, and<br />
release phases saves some work. If you're happy to do without a sustain phase, a<br />
note can be made up of attack and decay components only; this simplifies program<br />
ming. BASIC programs are slow enough for some of <strong>the</strong> delays to occur invol<br />
untarily. This is <strong>the</strong> usual way <strong>the</strong> SID chip is programmed; it assumes that <strong>the</strong><br />
envelopes are satisfactory. Dynamic effects such as raining or ripping sounds, <strong>the</strong><br />
Doppler effect, and envelopes controlling filters ra<strong>the</strong>r than volume require more<br />
work and probably ML subroutines as well.<br />
437
Sound<br />
<strong>The</strong> SID chip (officially, <strong>the</strong> 6581 Sound Interface Device) has 29 eight-bit reg<br />
isters, numbered 0-28 in its specification. <strong>The</strong> SID chip starts at $D400 (54272) in<br />
<strong>the</strong> <strong>64</strong>. Address decoding is incomplete, and 31 extra images of <strong>the</strong> SID chip reg<br />
isters appear at $D420, $D440, and so on. <strong>The</strong> SID images are a side effect of <strong>the</strong><br />
way <strong>the</strong> SID chip is wired into <strong>the</strong> <strong>64</strong>; <strong>the</strong>y are not designed to be used at all. While<br />
it is possible to use image registers in place of <strong>the</strong> actual SID registers, <strong>the</strong>re is no<br />
real advantage in doing so, and <strong>the</strong> practice is likely to confuse anyone else who<br />
looks at such a program.<br />
<strong>The</strong> SID chip has three electronic voices. <strong>The</strong>y may be used independently, or<br />
linked, giving ring modulation from two voices, for instance. Each voice has an os<br />
cillator, able to generate sawtooth, triangular, variable-width pulse, noise, and o<strong>the</strong>r<br />
combination waveforms, of frequency ranging from about 1/20 Hz to 4000 Hz, in<br />
extremely small steps. Much higher frequencies than this relatively low top limit are<br />
generated by harmonics. Each voice has its own envelope shaper, with pro<br />
grammable attack, decay, sustain, and release phases. Any of <strong>the</strong> three voices may<br />
be routed toge<strong>the</strong>r via <strong>the</strong> SID chip's single filter, which has programmable lowpass,<br />
high-pass, and band-pass modes, resonance, and cut-off or center frequency.<br />
<strong>The</strong> often published block diagram of <strong>the</strong> SID chip charts all <strong>the</strong>se features.<br />
Most SID registers are write-only; PEEKing returns 0. Consequently, monitorstyle<br />
programs need an array to keep track of values which have been POKEd in.<br />
However, <strong>the</strong> last four registers are read-only. Two of <strong>the</strong>m return potentiom<br />
eter readings, used with game paddles, drawing tablets and o<strong>the</strong>r devices. <strong>The</strong>se are<br />
dealt with in Chapter 16. Although sound could be controlled by <strong>the</strong>m, perhaps as<br />
an alternative to <strong>the</strong> keyboard, <strong>the</strong>y're not directly relevant and we'll say little more<br />
about <strong>the</strong>m here.<br />
<strong>The</strong> two o<strong>the</strong>r read-only registers return, respectively, <strong>the</strong> oscillator and <strong>the</strong> en<br />
velope, as output by voice 3. We can use <strong>the</strong>se to inspect <strong>the</strong> SID chip's waveforms<br />
and envelopes, and for such control purposes as modifying oscillator frequencies for<br />
vibrato or siren effects, or changing filter frequency to get a wah-wah effect.<br />
<strong>The</strong> SID chip produces a reasonably high quality electrical signal, obtainable on<br />
<strong>the</strong> audio-video socket at <strong>the</strong> rear of <strong>the</strong> <strong>64</strong> (pin 3 is <strong>the</strong> signal, and pin 2 is <strong>the</strong><br />
ground). Play this signal through an amplifier and speaker, preferably with some tre<br />
ble removed, for a significantly higher quality sound than is available via your TV.<br />
External input can also be processed by <strong>the</strong> SID chip: <strong>the</strong> signal, to pin 5, must be in<br />
<strong>the</strong> range 5.7 to 6.3 volts DC, matched to <strong>the</strong> SID chip's 100K ohm impedance. <strong>The</strong><br />
signal can be filtered and mixed with output from <strong>the</strong> SID chip's voices, but not pro<br />
cessed in any o<strong>the</strong>r way. If you're not sure how to make such external connections,<br />
get help from someone who understands electronics. <strong>The</strong> SID chip can be damaged<br />
by an external input that exceeds its design levels.<br />
Voice Generator Registers<br />
<strong>The</strong> three voice's registers are arranged in identical patterns of seven bytes, so you<br />
may find it mnemonically helpful to use expressions like SID=54272 and HF=SID<br />
+ VOICE*7 + 1 in BASIC. <strong>The</strong> second example sets <strong>the</strong> high byte of <strong>the</strong> frequency<br />
of one of <strong>the</strong> voices, depending on <strong>the</strong> value of VOICE (which must be in <strong>the</strong> range<br />
0-2).<br />
438
Sound<br />
Frequency control registers (offsets of 0 and 1, 7 and 8,14 and 15). <strong>The</strong> first<br />
two registers of each group hold a two-byte value which controls <strong>the</strong> frequency of<br />
<strong>the</strong> note produced by that voice. <strong>The</strong> frequency is directly proportional to <strong>the</strong> value,<br />
which means that doubling <strong>the</strong> value doubles <strong>the</strong> frequency. <strong>The</strong> frequency resolu<br />
tion is extremely good. <strong>The</strong>re are about 250 values between middle C and <strong>the</strong> next<br />
semitone, permitting glissandos (pitch changes between notes with no perceptible<br />
steps), though resolution is not as good at extremely low frequencies.<br />
<strong>The</strong> registers' values increment every 256 clock cycles, setting a new output<br />
value whenever 0 is reached; this explains how <strong>the</strong> timing works out.<br />
<strong>The</strong> 16-bit register value is related to <strong>the</strong> true frequency like this:<br />
U.S. True frequency = 0.0609597 * Register value<br />
U.K. True frequency = 0.058717 * Register value<br />
Or <strong>the</strong> o<strong>the</strong>r way around:<br />
U.S. Register value = 16.40426 * True frequency<br />
U.K. Register value = 17.0309 * True frequency<br />
To produce a tuning standard note A (frequency 440 Hz) in <strong>the</strong> U.S., <strong>the</strong> value is<br />
440*16.404261, or about 7218. Note that U.S. software plays at a lower pitch in <strong>the</strong><br />
U.K.<br />
<strong>The</strong> highest obtainable pitch is about 0.0609597*65535 = 3995 Hz, about four<br />
octaves above middle C, and <strong>the</strong> lowest about 0.06 Hz, far below audibility. Two or<br />
three octaves below middle C are perceptible as a pitched sound, so <strong>the</strong> SID chip's<br />
total range of pitched sounds is about six or seven octaves.<br />
If <strong>the</strong> variable F contains a value to be placed into <strong>the</strong>se registers, <strong>the</strong>n<br />
H%=F/256 <strong>the</strong>n L%=F-H%*256 will assign <strong>the</strong> high and low bytes to H% and<br />
L%<br />
Pulse width registers (offsets of 2 and 3, 9 and 10,16 and 17). <strong>The</strong> pulse width<br />
is set by a 12-bit value. <strong>The</strong> lower 8 bits are set by <strong>the</strong> first of <strong>the</strong>se register pairs,<br />
and <strong>the</strong> upper 4 bits by <strong>the</strong> least significant nybble of <strong>the</strong> second register, leaving<br />
<strong>the</strong> upper nybble unused. So <strong>the</strong> maximum value it may take is 4095. <strong>The</strong> duty cycle<br />
is given by <strong>the</strong> ratio ?6f'pulse width: 4095. A duty cycle of 1:2 (a square wave) results<br />
from 2048 (8 in <strong>the</strong> second register, 0 in <strong>the</strong> first). <strong>The</strong> value 0 or 4095 gives a con<br />
stant DC voltage. Consequently, if a pulse waveform is to be audible, a pulse width<br />
must be set in addition to a frequency. <strong>The</strong> pulse width registers have no effect on<br />
o<strong>the</strong>r waveforms.<br />
Note that a 1:4 duty cycle sounds identical to a 3:4 duty cycle. It's <strong>the</strong> same<br />
waveform upside down, so <strong>the</strong> value 1024 will produce <strong>the</strong> same harmonic content<br />
as 3072.<br />
However, if o<strong>the</strong>r tones are sounding simultaneously, <strong>the</strong>y'll interact with <strong>the</strong><br />
pulse wave, and <strong>the</strong>re may be a noticeable difference between 1:4 and 3:4 ratio<br />
pulse waves. In particular, if a pulse wave and ano<strong>the</strong>r waveform are enabled to<br />
ge<strong>the</strong>r in a control register (see next section), a low ratio gives an inaudible result,<br />
while a high ratio creates a much louder signal.<br />
To load <strong>the</strong> contents of <strong>the</strong> variable PW into <strong>the</strong> SID chip, POKE <strong>the</strong> first reg<br />
ister of <strong>the</strong> pulse-width register pair with PW/256, and <strong>the</strong> second with PW AND<br />
255.<br />
439
Sound<br />
Control registers (offsets of 4,11, and 18). <strong>The</strong> fourth register in each group<br />
has eight more-or-less independent bits; as usual, each is off when 0, on when 1. In<br />
ordinary use, only one waveform will be enabled in a voice at any given time. En<br />
abling several waves (but not noise) in <strong>the</strong> same voice logically ANDs <strong>the</strong> outputs<br />
from point to point. A square wave cuts out half <strong>the</strong> o<strong>the</strong>r wave, giving variations on<br />
<strong>the</strong> sawtooth, but ANDed triangular and sawtooth waves mostly cancel out. A very<br />
wide pulse adds high frequencies without cutting volume too much. <strong>The</strong> following<br />
list describes <strong>the</strong> function of each bit of <strong>the</strong> control registers:<br />
Bit 7; Noise.<br />
Bit 6: Pulse waveform. Pulse width must be set.<br />
Bit 5: Sawtooth waveform.<br />
Bit 4: Triangular waveform.<br />
Bit 3: Test bit. This suspends <strong>the</strong> voice; when cleared to 0, <strong>the</strong> output is con<br />
tinued. This can be useful where <strong>the</strong> exact realtime phase of a waveform is im<br />
portant. But <strong>the</strong> most common use is probably to restart a voice, locked up after<br />
noise has been enabled with ano<strong>the</strong>r waveform.<br />
If noise is selected with ano<strong>the</strong>r waveform, <strong>the</strong> noise waveform is silenced and<br />
will remain silent until <strong>the</strong> test bit is set to 1 and <strong>the</strong>n reset to 0.<br />
Bit 2: Ring modulation. When this bit is set to 1, its voice's output (which must<br />
be a triangular wave) is replaced by <strong>the</strong> ring modulated signal obtained from its own<br />
signal and <strong>the</strong> previous voice, which is automatically treated by <strong>the</strong> ring modulation<br />
process as a triangular wave. Only <strong>the</strong> frequency setting of <strong>the</strong> previous voice has<br />
any effect. It isn't necessary to set <strong>the</strong> waveform or even turn on <strong>the</strong> voice. Both voices<br />
can still be used: voice 3 might play noise, while voice 1 could play a ring modu<br />
lated signal based on <strong>the</strong> triangular wave of voice 3's frequency setting.<br />
A common combination is to set <strong>the</strong> ring modulation bit of voice 1 and use<br />
voice 3 for <strong>the</strong> o<strong>the</strong>r input signal. <strong>The</strong> voice before voice 1 is treated as voice 3. If<br />
two or even three ring modulation bits are on, and appropriate triangular waveforms<br />
have been selected, still more elaborate ring modulation occurs.<br />
Bell timbre notes can be tuned by multiplying both input frequencies by<br />
1.059463, or using tables, to find semitone values which retain <strong>the</strong> same pattern of<br />
harmonics. Glockenspiel and vibraphone effects can be obtained in this way.<br />
Bit 1: Synchronization bit. This links two voices just as <strong>the</strong> ring mod bit does.<br />
As with ring modulation, only <strong>the</strong> frequency of <strong>the</strong> previous voice need be set for<br />
sync to operate. <strong>The</strong>re are two small differences: <strong>the</strong> voice with sync bit set can have<br />
any waveform, and <strong>the</strong> synchronized output frequency is determined by <strong>the</strong> earlier<br />
register, <strong>the</strong> one with sync bit not set.<br />
Synchronization can be used in several ways. It provides an easy way to vary<br />
timbre. Suppose voice 1 plays a tune on its own. If voice 2's sync bit is set, voice 1<br />
still plays <strong>the</strong> tune, but you can get new timbres on replaying <strong>the</strong> tune by simply<br />
POKEing a new value into voice 2's frequency.<br />
With ring modulation, sync adds more high frequencies and may improve <strong>the</strong><br />
sound. It's probably always worth trying. However, voice 3 alone, not voices 1 and 3<br />
toge<strong>the</strong>r, now controls <strong>the</strong> pitch.<br />
Bit 0: Gate bit. This controls <strong>the</strong> playing of <strong>the</strong> voice's note. It has two functions.<br />
Transition from 0 to 1 immediately starts <strong>the</strong> ADSR envelope. Transition from 1 to 0<br />
immediately starts <strong>the</strong> release phase of <strong>the</strong> ADSR envelope.<br />
440
Sound<br />
Both events take place irrespective of <strong>the</strong> stage reached by <strong>the</strong> current ADSR se<br />
quence in progress. For example, if <strong>the</strong> gate bit is turned off during a slow attack, re<br />
lease starts without decay or sustain phases.<br />
Envelope shape registers (offsets of 5 and 6,12 and 13,19 and 20). <strong>The</strong>se reg<br />
isters control <strong>the</strong> attack, decay, sustain, and release phases of <strong>the</strong> sound envelope.<br />
Each of <strong>the</strong> envelope register pairs is arranged as a four-nybble group in A, D, S, R<br />
order. Thus, <strong>the</strong> high nybble of <strong>the</strong> first register controls <strong>the</strong> attack, while <strong>the</strong> low<br />
nybble controls <strong>the</strong> decay. In like fashion, <strong>the</strong> high nybble of <strong>the</strong> second register is<br />
for sustain, and <strong>the</strong> low nybble determines release.<br />
Program 13-1 allows you to watch <strong>the</strong> SID chip generating envelopes. It reads<br />
<strong>the</strong> envelope generated by voice 3 and draws <strong>the</strong> result on <strong>the</strong> screen in a distinctive<br />
color, so several envelopes can be overlapped and compared. <strong>The</strong> envelope is timed<br />
so a BASIC loop can read it. Try entering ADSR values such as 3, 3, 3, 3 and 7, 5, 5,<br />
6 to get <strong>the</strong> idea.<br />
Note that A, D, and R set rates of increase or decrease, but sustain is a level, so<br />
a high S value raises <strong>the</strong> level portion of <strong>the</strong> envelope. Release is timed by <strong>the</strong> pro<br />
gram to occur about 2/3 <strong>the</strong> length of <strong>the</strong> axis.<br />
Program 13-1. ADSR Plotter<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
10 SID=54272:DIM X(500) srem 212<br />
20 INPUT "ATTACK, DECAY, SUSTAIN,RELEASE"; A,D,S,R<br />
:rem 233<br />
25 FOR J=SID TO SID+24:POKE J,0:NEXT :rem 81<br />
27 POKE SID+24,15 srem 155<br />
30 POKE SID+19,16*A+D:POKE SID+20,16*S:REM A,S,D<br />
:rem 168<br />
40 POKE SID+14,11 srem 145<br />
42 POKE SID+15,48 :rem 158<br />
50 POKE SID+18f0:POKE SID+18,33 :rem 211<br />
60 FOR J=0 TO 25:X(J)=PEEK(SID+28):NEXT :rem 220<br />
70 POKE SID+18,0 srem 102<br />
80 FOR J=26 TO 40:X(J)=PEEK(SID+28):NEXT :rem 19<br />
90 Z=(Z+1) OR 8 AND 15:POKE <strong>64</strong>6,Z :rem 168<br />
100 GOSUB 200:FOR J=0 TO 40 :rem 130<br />
110 FOR K=l TO X(J)/20:PRINT "{RVS} {UP}{LEFT}";:N<br />
EXT :rem 88<br />
120 GOSUB 200:FOR K=l TO J:PRINT "{RIGHT}";:NEXT:N<br />
EXT :rem 193<br />
130 PRINT "{WHTHll SPACES}ADSR=" A;D;S;R:GOTO 20<br />
:rem 129<br />
140 PRINT "{HOME}";:FOR X=l TO 24:PRINT "{DOWN}";:<br />
NEXT:RETURN :rem 142<br />
200 PRINT "{HOME}";:FOR X=l TO 24:PRINT "{DOWN}";:<br />
NEXT:RETURN :rem 139<br />
<strong>The</strong> attack/decay register (5,12,19) consists of two nybbles which hold <strong>the</strong> at<br />
tack value (0-15) and decay value (0-15). POKE 16*A+D.<br />
441
Sound<br />
<strong>The</strong> sustain/release register (6,13,20) consists of two nybbles which hold <strong>the</strong><br />
sustain level (0-15) and release value (0-15). POKE 16*S+D.<br />
Table 13-1 is a list of approximate attack times and maximum rates for decay<br />
and release.<br />
Table<br />
Dec<br />
0<br />
1<br />
2<br />
3<br />
4<br />
5<br />
6<br />
7<br />
8<br />
9<br />
10<br />
11<br />
12<br />
13<br />
14<br />
15<br />
Value<br />
13-L Envelope Register Values<br />
Hex<br />
0<br />
1<br />
2<br />
3<br />
4<br />
5<br />
6<br />
7<br />
8<br />
9<br />
A<br />
B<br />
C<br />
D<br />
E<br />
F<br />
Attack Rate<br />
2 ms<br />
8 ms<br />
16 ms<br />
24 ms<br />
38 ms<br />
56 ms<br />
68 ms<br />
80 ms<br />
100 ms<br />
250 ms<br />
500 ms<br />
800 ms<br />
1 second<br />
3 seconds<br />
5 seconds<br />
8 seconds<br />
Decay/Release Rate<br />
(Maximum time)<br />
6 ms<br />
24 ms<br />
48 ms<br />
72 ms<br />
114 ms<br />
168 ms<br />
204 ms<br />
240 ms<br />
300 ms<br />
750 ms<br />
1.5 seconds<br />
2.4 seconds<br />
3 seconds<br />
9 seconds<br />
15 seconds<br />
24 seconds<br />
Attack times are 1/3 <strong>the</strong> o<strong>the</strong>r times, because <strong>the</strong>y are shorter in practice. <strong>The</strong><br />
ratios between steps are erratic: incrementing from 5 to 6 makes little difference,<br />
while raising <strong>the</strong> value from 8 to 9 is more significant. <strong>The</strong> idea is to give more<br />
choice in <strong>the</strong> useful 1/20 to 1/3 second range. Attack rises to maximum output,<br />
which declines in <strong>the</strong> decay phase to <strong>the</strong> sustain level. Sustain <strong>the</strong>refore helps deter<br />
mine volume of a note. To increase a note's relative importance, increase its sustain<br />
level. Release, if and when it happens, drops from sustain to zero. <strong>The</strong> phases aren't<br />
quite linear.<br />
To understand sustain levels, consider <strong>the</strong> three similar notes shown in Figure<br />
13-9 with different si^tain levels.<br />
<strong>The</strong> notes diagrammed above are identical except for <strong>the</strong> sustain level. Note<br />
how <strong>the</strong> decay increases in importance and release decreases in importance as <strong>the</strong><br />
sustain level drops.<br />
With a sustain level of 0, <strong>the</strong>se envelopes decay to 0 without needing release to<br />
be gated. POKE SID+4,16: POKE SID+4,17 plays notes of this type. If release is<br />
gated, <strong>the</strong> envelope shape depends on <strong>the</strong> release value; <strong>the</strong> note is leng<strong>the</strong>ned, un<br />
changed, or shortened, respectively, if <strong>the</strong> release value is greater than, equal to, or<br />
less than <strong>the</strong> decay value.<br />
442
Sound<br />
Figure 13-9. Varying Sustain Levels<br />
Filter and Volume Registers<br />
Cut-off and center frequency registers (offsets of 21 and 22). <strong>The</strong> filter cut-off and<br />
center registers (FC) use 11 bits, and <strong>the</strong> range is 0-2047. <strong>The</strong> register value is re<br />
lated to <strong>the</strong> true filter frequency as follows:<br />
Filter frequency in Hz = 30 + 5.8 * Value in register<br />
Register value FC = (Desired filter frequency - 30) * 0.17<br />
<strong>The</strong> range of frequencies is <strong>the</strong>refore about 30-12,000 Hz.<br />
Filter and resonance control register (offset of 23). Bits 7-4: <strong>The</strong> high nybble<br />
of this register sets <strong>the</strong> amount of resonance on a linear scale. Clear all bits to 0 for<br />
none, and set all to 1 for maximum.<br />
Bit 3: External device filter. If external sound is routed through <strong>the</strong> <strong>64</strong>, it will be<br />
filtered if this bit is 1.<br />
Bit 2: Voice 3 filter. O=off; l=on.<br />
Bit 1: Voice 2 filter. O=off; l=on.<br />
Bit 0: Voice 1 filter. O=off; l=on.<br />
Volume and filter selection register (offset of 24). Bit 7: Disconnect voice 3.<br />
When this bit is set, voice 3 is turned off, even if o<strong>the</strong>rwise enabled. This allows<br />
voice 3 to generate both waves and envelopes, which can be read from <strong>the</strong> read-only<br />
registers for control purposes, without itself being audible.<br />
Bit 6: High-pass filter. O=off; l=on.<br />
Bit 5: Band-pass filter. O=off; l=on.<br />
Bit 4: Low-pass filter. O=off; l=on.<br />
Bits 3-0: Master volume. <strong>The</strong> low nybble of this register sets overall volume of<br />
<strong>the</strong> three voices.<br />
443
Sound<br />
Notes on filters. <strong>The</strong> SID chip has only one filter. When it's on, not all voices<br />
need pass through it. Bass accompaniment could be low-pass filtered to remove high<br />
harmonics and have resonance added. For example, if you put 17 into offset register<br />
21, 241 (or 15*16+1) into register 23, and 31 into register 24 to low-pass filter voice<br />
1 with cut-off starting at 128 Hz (an octave below middle C). High-passing imitation<br />
cymbal noises might need 21 in register 22, 1 in 23, and 79 in 24 to high-pass from<br />
1000 Hz without resonance.<br />
Bandpass filtering, notches, and resonance act on quite small regions of <strong>the</strong> fre<br />
quency spectrum, so it's important to get <strong>the</strong> register values correct.<br />
Read-Only Registers<br />
Potentiometer readings (offsets of 25 and 26). See Chapter 16.<br />
Waveform reading of voice 3 (offset of 27). Voice 3's frequency, and a wave<br />
form, must be set, but <strong>the</strong> gate needn't be on. For example, set register 14 (fre<br />
quency, low byte) to 4 and register 18 (waveform) to 32. This sets up a very low<br />
frequency sawtooth wave, which takes about eight seconds to climb from 0 through<br />
255. A sequence of PEEKs will show <strong>the</strong>se changing values. With much higher fre<br />
quencies, BASIC is too slow to PEEK consecutive values, but instead returns values<br />
taken from different waves, so <strong>the</strong> pattern appears to bear no relation to <strong>the</strong> wave<br />
shape. This is a standard result of wave sampling. It makes <strong>the</strong> effects of ML pro<br />
grams like <strong>the</strong> one below difficult to guess at.<br />
For random numbers, particularly when using ML, POKE register 15 with 255,<br />
and register 18 with 129. This generates a fast changing noise output. Simply PEEK<br />
(or load in ML) <strong>the</strong> location to obtain a value.<br />
Envelope reading of voice 3 (offset of 28). Voice 3's ADSR must be set, but a<br />
waveform needn't be selected. This is similar to <strong>the</strong> previous register; <strong>the</strong> major dif<br />
ference is that <strong>the</strong> envelope starts only when gated, and release starts only when <strong>the</strong><br />
gate is set to 0.<br />
Examples and Troubleshooting with <strong>the</strong> SID Chip<br />
It is easier to understand <strong>the</strong> functions of <strong>the</strong> SID chip once you have heard <strong>the</strong><br />
sound it generates. Program 13-2 controls individual bits in <strong>the</strong> SID chip's registers,<br />
so you can experiment with any combination. It displays <strong>the</strong> first 25 registers (<strong>the</strong><br />
write-only ones) as eight bits, with register numbers and text to remind you what<br />
<strong>the</strong> registers do. <strong>The</strong> cursor keys move <strong>the</strong> colored cursor, and <strong>the</strong> space bar inverts<br />
<strong>the</strong> bit under <strong>the</strong> cursor and loads <strong>the</strong> new register value into <strong>the</strong> SID, so any<br />
change will be immediately audible. Since <strong>the</strong> space bar repeats, holding it down is<br />
an easy way to repeatedly gate and ungate a tone.<br />
Program 13-2. Simple SIDmon<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader/' Appendix C.<br />
2 DATA "FREQ CONTROL LO{2 SPACES}{RVS}VOICE 1",FRE<br />
Q CONTROL HI :rem 63<br />
3 DATA PULSE WIDTH BITS 7-0,PULSE WIDTH XXXX 11-8<br />
:rem 120<br />
4 DATA "N OL&GlNgGlNM TST RING SYN GATE" :rem 116<br />
444
Sound<br />
5 DATA ATTACK{2 SPACES}DECAY, SUSTAIN RELEASE<br />
:rem 193<br />
6 DATA "FREQ CONTROL LO{2 SPACES}{RVS}VOICE 2",FRE<br />
Q CONTROL HI :rem 68<br />
7 DATA PULSE WIDTH BITS 7-0,PULSE WIDTH XXXX 11-8<br />
:rem 124<br />
8 DATA "N OL&GlN&GiNM TST RING SYN GATE" :rem 120<br />
9 DATA ATTACK{2 SPACES}DECAY, SUSTAIN RELEASE<br />
:rem 197<br />
10 DATA "FREQ CONTROL LO{2 SPACES}{RVS}VOICE<br />
{SHIFT-SPACE}3",FREQ CONTROL HI :rem 16<br />
11 DATA PULSE WIDTH BITS 7-0,PULSE WIDTH XXXX 11-8<br />
:rem 167<br />
12 DATA "N OLBG3NBG3NM TST RING SYN GATE" : rein 163<br />
13 DATA ATTACK{2 SPACES}DECAY, SUSTAIN RELEASE<br />
:rem 240<br />
16 DATA "XXXXX FC2-FC0{2 SPACES}{RVS}FILTER",FILTE<br />
R BITS FC10-FC3 :rem 237<br />
18 DATA RESONANCE FX F3 F2 Fl, V3OFF HP BP LP VOL3<br />
-VOL0 :rem 239<br />
20 DIM RV(24) :rem 155<br />
25 FOR X=0 TO 23:POKE 54272+X,0:NEXT :rem 224<br />
30 RV(1)=32:RV(4)=33:RV(6)=136:RV(24)=15 :rem 181<br />
40 PRINT "{CLRHWHT}"; :FOR Y=0 TO 24:FOR X=0 TO 7<br />
:rem 184<br />
50 PRINT CHR$(49 + ((RV(Y) AND (2\(7-X)))=0));<br />
:rem 57<br />
60 NEXT:POKE 54272+Y,RV(Y) :rem 20<br />
70 READ M$:PRINT Y; M$;:IF Y
Sound<br />
Type in <strong>the</strong> program and run it. A few registers are preset by <strong>the</strong> program.<br />
Sawtooth: Cursor over <strong>the</strong> gate bit of register 4 and tap <strong>the</strong> space bar a few<br />
times. A sawtooth tone sounds, with frequency about 500 Hz. Its envelope has zero<br />
attack and decay, but a sustain level of 8 (midvolume setting) and decay of 8, giving<br />
about a 1/3 second decay.<br />
Triangle: Turn <strong>the</strong> triangle bit on and <strong>the</strong> sawtooth off. Gating again turns on<br />
<strong>the</strong> note, this time with <strong>the</strong> mellower triangle waveform. Altering bits in registers 1<br />
and 2 changes <strong>the</strong> pitch. Also experiment with different attack and decay settings.<br />
Pulse: Select <strong>the</strong> pulse-wave bit in register 4. You'll get no pulse-wave sound<br />
until registers 2 and/or 3 are POKEd.<br />
Noise: Set <strong>the</strong> noise bit and experiment. A long attack, for example, helps simu<br />
late a steam train.<br />
Ring modulation: Set <strong>the</strong> ring bit in register 4; set bit 2 in register 15, which sets<br />
voice 3's frequency. This has no effect; however, selecting triangle wave in register 4<br />
gives ring modulation. Bell-like sounds are best obtained with 00001111 in register 5<br />
and 00000000 in register 6, so <strong>the</strong> note has only a decay phase.<br />
<strong>The</strong> array RV stores <strong>the</strong> current register values. <strong>The</strong> program initializes volume,<br />
as well as voice l's frequency, sustain phase, waveform, and gate. This is <strong>the</strong> mini<br />
mum required for <strong>the</strong> steady tone produced on RUN.<br />
<strong>The</strong> cursor is controlled by POKEing color RAM, so <strong>the</strong> new X,Y position<br />
changes color, and <strong>the</strong> old reverts to white.<br />
Experiments and examples. <strong>The</strong>se straightforward examples are typical SID<br />
approximations of musical sounds.<br />
Noise Cymbals A4 Dll SO R0 High-pass filter<br />
Pulse Piano A0 D9 SO R0 Square wave (2048)<br />
Organ Al D2 S5 Rl 1:4 Pulse (1024)<br />
Banjo A0 D9 SO R0 1:10 Pulse (410)<br />
Sawtooth Trumpet A6 DO S10 Rl Band-pass<br />
Accordion A6 D7 S5 R3 High-pass filter<br />
Triangle Flute A4 D2 S10 R5<br />
Problems with <strong>the</strong> SID Chip<br />
Often, <strong>the</strong> registers are simply not properly set. Volume (register 24) must be on.<br />
Each voice's frequency must be set; this takes ei<strong>the</strong>r one or two POKEs. <strong>The</strong> wave<br />
form must be kept enabled while <strong>the</strong> gate, in <strong>the</strong> same register, is POKEd on or off.<br />
And <strong>the</strong> envelope shape must be set, with one or two POKEs. Pulse width must be<br />
set for pulse waves, which takes one or two POKEs.<br />
Filtering requires registers 21 and/or 22 to set <strong>the</strong> filter frequency, and register<br />
23 to select <strong>the</strong> voices to be filtered (and perhaps resonance). Volume register 24 also<br />
controls <strong>the</strong> type of filter, as well as on/off for voice 3.<br />
Ring modulation must use a triangle waveform in <strong>the</strong> voice with ring bit set.<br />
And <strong>the</strong> frequency of <strong>the</strong> modulating voice must be set.<br />
Special difficulties. At power-on, <strong>the</strong> SID chip is POKEd with zeros, but<br />
RUN/STOP-RESTORE only turns off <strong>the</strong> volume. So it can happen that a music<br />
program doesn't work even after RUN/STOP-RESTORE, typically because a gate bit<br />
is set to 1. To avoid this problem, sound programs should always start by POKEing<br />
zeros into all <strong>the</strong> SID chip's locations.<br />
446
Sound<br />
Low-frequency (bass) notes always sound weaker than high-frequency notes of<br />
<strong>the</strong> same amplitude. A bass-drum simulation on <strong>the</strong> SID chip, low-pass filtered at 70<br />
Hz, is almost inaudible. If this is a problem, try increasing <strong>the</strong> relative sustain level<br />
of low frequencies.<br />
After an envelope has decayed to zero, sound may still leak through. Turning<br />
off <strong>the</strong> gate bit is <strong>the</strong> solution. Also, long decays aren't reliable. <strong>The</strong> volume doesn't<br />
fall smoothly, but jumps noticeably in places.<br />
<strong>The</strong> filter may not give repeatable results on o<strong>the</strong>r <strong>64</strong>s. Early <strong>64</strong> programmers<br />
were warned by <strong>Commodore</strong> that filter characteristics varied considerably.<br />
As noted, <strong>the</strong> noise waveform can't be mixed freely with o<strong>the</strong>r waveforms.<br />
ML routines with read-only registers. <strong>The</strong> routine below is about <strong>the</strong> shortest<br />
practical ML routine using <strong>the</strong> read-only registers:<br />
LDA $D41B ;READ VOICE 3 OSCILLATOR<br />
STA $D41F ;STORE IN FREE LOCATION<br />
LDA $D41C ;READ VOICE 3 ENVELOPE<br />
STA $D41F ;STORE IN FREE LOCATION<br />
JMP $EA31 ;CONTINUE INTERRUPT<br />
<strong>The</strong> BASIC loader, Program 13-3, POKEs this ML into RAM, <strong>the</strong>n points <strong>the</strong><br />
IRQ interrupt to it, so about every 1/60 second, voice 3's parameters are read and<br />
POKEd somewhere else. As it stands, <strong>the</strong> ML has no effect, since $D41F isn't used<br />
by <strong>the</strong> SID chip; but replacing <strong>the</strong> fifth byte with a number in <strong>the</strong> range 0-24 directs<br />
<strong>the</strong> oscillator into <strong>the</strong> register having that offset, and replacing <strong>the</strong> eleventh byte has<br />
<strong>the</strong> same effect with <strong>the</strong> envelope reading. For example, changing STA $D41F to<br />
STA $D400 means that voice l's frequency control (low byte) now gets altered every<br />
1/60 second to voice 3's output. Locations 56324 and 56325 control <strong>the</strong> interrupt<br />
rate (changing this rate also affects internal operations, making <strong>the</strong> cursor blink<br />
faster or slower, and so on).<br />
Program 13-3. ML Read-Only Routine<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
10 FOR J=49152 TO 49166:READ X:POKE J,X:NEXT<br />
:rem 223<br />
20 DATA 173,27,212,141,31,212,173,28,212,141,31,21<br />
2,76,49,234 :rem 168<br />
30 POKE 56333,127:POKE 788,0:POKE 789,192:POKE 563<br />
33,129 :rem 214<br />
40 SID=54272:FOR J=SID TO SID+24:POKE J,0:NEXT<br />
:rem 169<br />
100 POKE SID+24,9+32 :rem 38<br />
110 POKE SID+15,16 :rem 197<br />
112 POKE SID+18,16 :rem 202<br />
118 POKE SID+1,32 :rem 150<br />
120 POKE SID+6,160 :REM VOICE 1 SUSTAIN :rem 178<br />
130 POKE SID+4,16+1: REM GATE VI TRIANGLE :rem 13<br />
200 POKE 49156,0: REM V3 OUTPUT TO R0 :rem 227<br />
447
Sound<br />
Voice 3's frequency determines oscillator register 27's output. Register 27 is loca<br />
tion 54299 ($D41B). A very low frequency is inaudible, but when <strong>the</strong> register is<br />
controlling an audible frequency, it causes a slow, distinct pulsation. Higher fre<br />
quencies are audible in <strong>the</strong>mselves and also generate much faster changing output,<br />
giving frequency-modulated output (where <strong>the</strong> waveforms are rapidly condensed,<br />
<strong>the</strong>n rarefied).<br />
Voice 3's ADSR settings and <strong>the</strong> gate bit control register 28's output. Register 28<br />
(location 54300, $D41C) <strong>the</strong>refore needs more work than 27, often more than is jus<br />
tified in view of <strong>the</strong> similarity between <strong>the</strong> outputs of 27 and 28.<br />
Program 13-3 shifts <strong>the</strong> triangular oscillator output of voice 3 into register 0, so<br />
<strong>the</strong> low byte of voice l's frequency pulsates, giving vibrato. POKE 49156,1 uses reg<br />
ister 1 and, of course, usually sounds more erratic. Register 2 or register 3, with<br />
pulse wave selected, causes fluctuations in readiness. POKE 49156,4 causes clicks<br />
and buzzes as <strong>the</strong> interrupt POKEs in assorted waveform and gate configurations.<br />
Register 22 (54294, $D416) alters <strong>the</strong> filter setting; provided voice 1 is filtered, you'll<br />
get a repetitive wah-wah effect. Register 24 (54296, $D418) alters master volume and<br />
a few o<strong>the</strong>r things, and gives vibrato or such effects as random changes in loudness<br />
when <strong>the</strong> noise voice is chosen. Whenever a register is partly used for different pur<br />
poses, obviously LDA $D41B:AND #$0F:STA $D418 or similar constructions can<br />
help.<br />
<strong>The</strong> envelope can modulate frequency or filtering, producing somewhat similar<br />
results: A note might start deep and faint, rise to a louder, more treble sound, <strong>the</strong>n<br />
lose frequency as it decays. Voice 3 is easiest to modify in this way, since it can<br />
modulate itself. Run Program 13-3 and do <strong>the</strong>se POKEs: POKE SID+4,0 (turns voice<br />
1 off). POKE SID + 19,176 (sets AD of voice 3). POKE 49162,15 (causes envelope<br />
reading to alter frequency of voice 3). Now type POKE SID+18,0: POKE SID+18,17<br />
to gate a triangular wave, and you'll get a whistling sound. <strong>The</strong>re are innumerable<br />
ways to combine voices, waveforms, ADSR shape, and duration.<br />
Music <strong>The</strong>ory<br />
Figure 13-10 shows a section of a piano keyboard, starting at C.<br />
Figure 13-10. Piano Keyboard<br />
u<br />
It has 12 keys, tuned with frequencies in constant ratio to each o<strong>the</strong>r, so melodies<br />
can be played in any key, starting on any note. Adjacent notes are all separated by<br />
semitones. Octaves differ in frequency by a ratio of exactly 2, and similarly fre<br />
quencies of semitones differ by <strong>the</strong> twelfth root of 2, which is 1.059463. Repeated di<br />
vision of a high frequency by this value generates regular subdivisions and provides<br />
an efficient way to generate tuned frequency settings for <strong>the</strong> SID chip. American<br />
standard pitch sets A at 440, while international standard pitch is 435. Whichever<br />
scale is used, any notes' frequencies can be calculated from any one note. (<strong>The</strong> sub-<br />
448
Sound<br />
routine starting at line 4000 in Program 13-5 evaluates 95 semitones from a single<br />
value.)<br />
A conventional scale consists of 7 notes taken from <strong>the</strong>se 12, plus <strong>the</strong> eighth<br />
(octave higher) note. So all scales have 12 semitones between <strong>the</strong> starting keynote<br />
and its octave. Of <strong>the</strong> 800 or so possible scales, in practice only a few are used. C<br />
major is <strong>the</strong> best known scale, starting with C and using only <strong>the</strong> white notes C, D,<br />
E, F, G, A, B, C.<br />
<strong>The</strong> notes of this scale are separated by semitones like this: C tone D tone E<br />
semitone F tone G tone A tone B semitone C. All major scales have this pattern, and<br />
<strong>the</strong> arrangement of musical intervals in this type of scale has <strong>the</strong> same quality what<br />
ever <strong>the</strong> keynote. # #<br />
Any major scale o<strong>the</strong>r than C uses some black keys. For example, G major is G,<br />
A, B, C, D, E, F-sharp, and G. Black keys are sharps moving up, and flats moving<br />
down, so C-sharp is <strong>the</strong> same key as D-flat. White keys are called naturals.<br />
Natural minor scales have this pattern: A tone B semitone C tone D tone E semi<br />
tone F tone G tone A. Note <strong>the</strong> total of 12 semitones.<br />
A chord is a simultaneous combination of notes. Here is a look at two types of<br />
three-note chords. A major chord has four semitones between its first and second<br />
notes, and three between its second and third notes. <strong>The</strong> first, third, and fifth notes<br />
of a major scale identify this, so C major is C, E, and G. <strong>The</strong> fourth, sixth, and<br />
eighth notes of any major scale also produce a major chord (F, A, and C make F ma<br />
jor), as do <strong>the</strong> fifth, seventh, and ninth notes (G, B, and D in <strong>the</strong> next octave make G<br />
major). Check <strong>the</strong> intervals between <strong>the</strong>ir notes to verify that <strong>the</strong>y are major chords.<br />
<strong>The</strong>se three chords include all <strong>the</strong> notes in <strong>the</strong> scale; consequently, any tune in a<br />
major scale may be harmonized by using one of <strong>the</strong>se three chords. This is easy to<br />
implement on <strong>the</strong> <strong>64</strong>.<br />
A chord using <strong>the</strong> second, fourth, and sixth notes of <strong>the</strong> major scale is a minor<br />
chord, with three and four semitones separation between <strong>the</strong> notes. A minor chord is<br />
also produced if we build a chord on <strong>the</strong> third or <strong>the</strong> sixth notes of <strong>the</strong> scale. In <strong>the</strong><br />
scale of C major, we get D minor, E minor, and A minor. Again, any note can be<br />
harmonized by one of <strong>the</strong>se chords.<br />
As we can see, it's possible to automate harmony to some extent, although <strong>the</strong><br />
results are unexciting if prolonged. In general, minor chords are considered dull or<br />
sad and major chords are thought of as bright or triumphant.<br />
It's also possible to generate tunes, by deciding on features like rhythms of mea<br />
sures or bars, <strong>the</strong> next note to be played, perhaps with probability depending on<br />
previous notes, and ascending or descending sequences, and <strong>the</strong>n varying <strong>the</strong>m ran<br />
domly. Again, <strong>the</strong> results are generally unexciting.<br />
Musical notation uses a staff of five lines, or two staves, with symbols to repre<br />
sent notes. Pitch is specified by <strong>the</strong>ir vertical position; <strong>the</strong> clef determines <strong>the</strong> actual<br />
pitch, and <strong>the</strong> treble clef is usually set with E on <strong>the</strong> lowest line, G on <strong>the</strong> next, and<br />
so on. Duration, and thus <strong>the</strong> rhythm of <strong>the</strong> music, is signaled by <strong>the</strong> type of sym<br />
bol. Durations are relative, not absolute. A dot after a note symbol multiplies its<br />
duration by 1.5. Sharp or flat symbols precede <strong>the</strong> symbols for notes which are to be<br />
sharpened or flatted. Consult your dictionary for a list and explanation of basic<br />
musical notation.<br />
449
Sound<br />
Sound Demonstrations<br />
Bach to <strong>the</strong> BASICS<br />
<strong>The</strong> BASIC listing below, Program 13-4, plays a sequence of notes stored as three<br />
bytes of data each, two bytes controlling frequency and one, duration. It gates each<br />
note twice, allowing <strong>the</strong> full ADSR sequence, and consequently needs two delay<br />
loops, during sustain, <strong>the</strong>n during release.<br />
Program 13-4. Jesu Joy<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
1 DIM SH(81),SL(81) :rem 229<br />
5 FOR Y=l TO 81:READ SH(Y),SL(Y):NEXT :rem 120<br />
10 S=54272 :rem 245<br />
20 FOR J=0 TO 24:POKE S+J,0:NEXT jrem 15<br />
21 POKE S+12,<strong>64</strong>:POKES+13,152:POKE S+19,<strong>64</strong>:POKE S+2<br />
0,32 :rem 217<br />
30 POKE S+5,16*1+5 :rem 147<br />
40 POKE S+6#16*12+10 :rem 243<br />
50 X=1:POKE S+24,8 :rem 220<br />
60 READ HV,LV,DU :rem 179<br />
70 IF HV
Sound<br />
230 DATA 16,195,32,18,209,32,21,31,32,22,96,32,25,<br />
30,32,28,49,32 irem 62<br />
240 DATA 25,30,32,22,96,32,21,31,32,18,209,32,21,3<br />
1,32,16,195,32 :rem 47<br />
250 DATA 15,210,32,16,195,32,18,209,32 :rem 67<br />
255 DATA 12,143,32,15,210,32,16,195,32,18,209,32,2<br />
5,30,32,23,181,32,25,30,32 :rem 122<br />
257 DATA 18,209,32,15,210,32,12,143,32,15,210,32,1<br />
8,209,32 :rem 14<br />
260 DATA 22,96,32,21,31,32,18,209,32,21,31,32<br />
:rem 143<br />
270 DATA 16,235,32,18,209,32 :rem 94<br />
280 DATA 21,31,32,25,30,32,22,96,32,22,96,32,28,49<br />
,32,25,30,32 :rem 214<br />
290 DATA 25,30,32,33,135,32,31,165,32,33,135,32,25<br />
,30,32,21,31,32 :rem 88<br />
300 DATA 16,195,32,18,209,32,21,31,32,14,36,32,25,<br />
30,32,22,96,32 :rem 51<br />
310 DATA 21,31,32,18,209,32,16,195,32,12,143,32,16<br />
,195,32,15,210,32 :rem 195<br />
320 DATA 16,195,32,21,31,32,25,30,32,33,135,32,25,<br />
30,32,21,31,32 :rem 32<br />
330 DATA 16,195,96,-1,-1,-1 :rem 27<br />
A table of note values allows faster programming of short tunes. Weaknesses in<br />
clude <strong>the</strong> large amount of data needed per note, and <strong>the</strong> fact that all o<strong>the</strong>r process<br />
ing stops. <strong>The</strong> program in <strong>the</strong> next section avoids <strong>the</strong>se weaknesses.<br />
Music Program<br />
<strong>The</strong> music routine below, Program 13-5, is much more complex; it plays three-voice<br />
music from data written in approximately conventional notation, so music is fairly<br />
easy to write, compared with programs like <strong>the</strong> last, with <strong>the</strong>ir unnaturally formatted<br />
music data. <strong>The</strong> drawback is <strong>the</strong> time taken by BASIC to convert it into ML-readable<br />
bytes; however, once calculated, <strong>the</strong>se can be stored for later use.<br />
Program 13-5. Music Program<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader/'Appendix C.<br />
10 POKE 56,88:CLR:GOSUB 4500 J25J£<br />
20 INPUT "IS MUSIC DATA IN RAM";A$:IF A$="Y" THEN<br />
{SPACE} 1019 :rem "^<br />
30 GOSUB 5000XGOSUB 4000 :re?,J<br />
1000 TOR VN»0 TO 2:RA(VN)=0.50:DU=16:OC=4:P=:22528+<br />
VN*6144 :rem 150<br />
1005 READ VA$:IF VA$="Z" THEN POKE P,255:NEXT:GOTO<br />
1019 :rem 41<br />
1007 IF LEFT$(VA$,1)="O" THEN READ RA(VN):GOTO 100<br />
c srem 235<br />
1008 IF LEFT$(VA$,1)="W" THEN POKE P,253:P=P+1tREA<br />
D WV»POKEP,WViP=P+l:GOTO 1005 »rem 16<br />
451
Sound<br />
1009<br />
1010<br />
1012<br />
1013<br />
1014<br />
1016<br />
1019<br />
1020<br />
1022<br />
1023<br />
1024<br />
1025<br />
1026<br />
1028<br />
1038<br />
1039<br />
1040<br />
1400<br />
1410<br />
1500<br />
1505<br />
1510<br />
1520<br />
IF LEFT$(VA$,l)=nPM THEN GOSUB 1400:GOTO 1005<br />
:rem 143<br />
IF LEFT$(VA$,1)="R" THEN VA$=MID$(VA$,2):GOSU<br />
B 2130:PI=0:GOTO 1014 :rem 100<br />
GOSUB 2000:PI=PI+OC*12 :rem 249<br />
IF P>40950 THEN PRINT "TOO MUCH DATA":END<br />
:rem 46<br />
POKE P,PI:P=P+1:POKE P,DU*RA(VN):P=P+1:rem 32<br />
POKE P,DU-INT(DU*RA(VN)):P=P+1:GOTO 1005<br />
:rem 108<br />
INPUT "TEMPO (1-255)";TM:POKE 56325,TM<br />
:rem 134<br />
POKE 56334,0:GOSUB 1500 : rem 211<br />
POKE 165,0:POKE 167,0:POKE 169,0 :rem 180<br />
POKE 166,88:POKE 168,112:POKE 170,136:rem 189<br />
POKE 788,208:POKE 789,192:POKE 254,0 :rem 156<br />
FOR J=49355 TO 49359 STEP 2:POKE J,32:NEXT<br />
:rem 28<br />
FOR J=49349 TO 49354:POKE J,0:NEXT :rem 120<br />
FOR J=0 TO 2:POKE 49344+J*2,J*7:NEXT :rem 208<br />
POKE 56334,1 :rem 93<br />
IF PEEK(254)=7 THEN 1020 :rem 211<br />
GOTO 1039 :rem 203<br />
POKE P,254:P=P+1:READ RG:POKE P,RG :rem 235<br />
P=P+1:READ PA:POKE P,PA:P=P+1:RETURN :rem 233<br />
SI=54272:PRINT "INITIALIZING SID" :rem 25<br />
FOR J=SI TO SI+28:POKE J,0: NEXT :rem 49<br />
POKE SI+5,17:POKE SI+12,17:POKE SI+19,17<br />
:rem 219<br />
POKE SI+6,177:POKE SI+13,177:POKE SI+20,177<br />
1530 POKE SI+24,4<br />
1550 RETURN<br />
2000 IF LEFT$(VA$,1)="+<br />
,2):GOTO 2040<br />
2005 IF LEFT$(VA$,1)="-<br />
,2):GOTO 2040<br />
2010 IF LEFT$(VA$,1)
Sound<br />
4000 PRINT "INITIALIZING FREQUENCY TABLE" :rem 58<br />
4010 DIM FQ(95):FQ(95)=<strong>64</strong>814 :rem 201<br />
4040 TOR J=94 TO 84 STEP -1:FQ(J)=FQ(J+l)/2t(1/12)<br />
:NEXT :rem 207<br />
4050 FOR J=6 TO 0 STEP -1:FOR K=l TO 12 :rem 186<br />
4060 P1=12*J+K-1:FQ(P1)=FQ(P1+12)/2:NEXT:NEXT<br />
:rem 93<br />
4070 FOR J=l TO 95:POKE 49151+J,FQ(J)-256*INT(FQ(J<br />
)/256) :rem 233<br />
4080 POKE 49247+J#FQ(J)/256:NEXT:RETURN :rem 55<br />
4500 PRINT "LOADING ML" :rem 49<br />
4502 FOR J=49360 TO 49537:READ X:POKE J,X:NEXT:RET<br />
URN :rem 102<br />
4504 DATA 162,0,32,226,192,162,2,32,226,192,162,4<br />
:rem 98<br />
4512 DATA 32,226,192,76,49,234,189,197,192,240,18<br />
4524 DATA 222,197,192,240,1,96,188,192,192,189<br />
:rem 138<br />
:rem 249<br />
4530 DATA 203,192,41,254,153,4,212,96,189,198<br />
:rem 184<br />
4542 DATA 192,240,6,222,198,192,240,1,96,161,165<br />
:rem 71<br />
4554 DATA 240,61,201,255,208,18,188,192,192,169<br />
:rem 32<br />
4566 DATA 0,153,4,212,138,208,2,232,138,5,254<br />
:rem 1<strong>64</strong><br />
4572 DATA 133,254,96,201,254,240,55,201,253,240<br />
irem 10<br />
4584 DATA 71,168,138,72,189,192,1*92,170,185,255<br />
:rem 51<br />
4596 DATA 191,157,0,212,185,95,192,157,1,212<br />
:rem 134<br />
4608 DATA 138,168,104,170,189,203,192,9,1,153,4<br />
:rem 22<br />
4614 DATA 212,32,123,193,161,165,157,197 :rem 188<br />
4626 DATA 192,32,123,193,161,165,157,198,192,32<br />
:rem 32<br />
4632 DATA 123,193,96,32,123,193,161,165,168,32<br />
:rem 231<br />
4<strong>64</strong>4 DATA 123,193,161,165,153,0,212,32,123,193<br />
:rem 213<br />
4656 DATA 76,4,193,32,123,193,161,165,157,203,192<br />
:rem 127<br />
4662 DATA 32,123,193,76,4,193,246,165,208 :rem 244<br />
4674 DATA 2,246,166,96 :rem 77<br />
5000 PRINT "INITIALIZING PITCH ARRAY PTRS" :rem 97<br />
5010 FOR J=0 TO 6:READ PC(J):NEXT:RETURN :rem 84<br />
5020 DATA -2,0,1,3,5,6,8 :rem 127<br />
10000 DATA W,<strong>64</strong>,P,3,3,O,0.9 :rem 112<br />
10001 DATA R<strong>64</strong>,R,4BF8,5D,F16,4BF8,5EF,G20 :rem 197<br />
453
Sound<br />
10005 DATA BF4,A#G,F8,EF,D,C4,D,EFfD,C,4BF :rem 14<br />
10010 DATA 5C8,C,F20,EF4,D,EF,F,EF,D,C :rem 25<br />
10015 DATA D8,D,G24,C8,D4,EF,F16 :rem 177<br />
10020 DATA 4BF8,5C4,D,EF12,4G4,AF,5C,4BF,AF,G#F<br />
:rem 21<br />
10025 DATA 5F8,D,EF20,G4,F,EF,D,C,4BF,5C :rem 137<br />
10030 DATA DF,4AF,G,F,EF,G,AF,BF,5C,4BF,AF,G,F,BF,<br />
5C,D :rem 225<br />
10035 DATA EF,C,-BF,AF,G,+D,EF,F,-BF,+EF,F,G,C,F,-<br />
BF,AF srem 245<br />
10040 DATA G16,R8,G,F4,AF,+D,C,D,-AF,F,AF :rem 190<br />
10045 DATA G,BF,+EF#-BF,G,BF,EF,G,D8,F,BF16:rem 88<br />
10050 DATA EF8,G,BF20,+C4,E,D,E,-BF,G,BF :rem 132<br />
10055 DATA A,+C,F,C,-A,+C,-F,A,E8,G,+C16 :rem 46<br />
10060 DATA -F8,A,+C20,D4,F#,E,F#,C,-A,+C :rem 7<br />
10065 DATA -BF,+D,G,D,-BF,+D,-G,BF,+C8,-F,+F,EF4,D<br />
srem 130<br />
10070 DATA C,-F,G,A,BF,+C,D,EF,F8,D,-BF16 :rem 171<br />
10075 DATA +F8,D,-AF16,+F8,D,-G,+F<br />
:rem 249<br />
10080 DATA EF,C,-G,+C4,D,EF8,C,-F,+EF<br />
:rem 202<br />
10085 DATA D8,-BF,F,BF4,+C,DF8,-BF,E,+DF :rem 138<br />
10090 DATA C4,-BF,A,G,F,EF,D,C,D8,BF,C,A :rem 140<br />
10100 DATA BF16,R,R,R<br />
:rem 117<br />
19999 DATA Z<br />
.rem 137<br />
20000 DATA W,<strong>64</strong>,P,10,5,O,0.8<br />
:rem 160<br />
20001 DATA 4EF8,G,BF16,EF8,AF,5C20<br />
:rem 58<br />
20005 DATA EF4,D,C,4BF8,AF,G,F4,G,AF,G,F,EF:rem 93<br />
20010 DATA F,EF,D,C,3BF,4D,EF,F,G,F,EF,D,C,F,G,A<br />
srem 106<br />
20015 DATA BF12,5C4,4A12,BF4,BF8,F,BF20 :rem 53<br />
20020 DATA AF4,G,AF,BF,AF,G,F,G8,G,5C20 :rem 95<br />
20025 DATA 4BF4,A,BF,5C,4BF,AF,G,AF,G,AF,5C,4BF,AF<br />
'G'F :rem 211<br />
20030 DATA G,F#G,BF,AF,G,F,EF,F,EF,F,AF,GfF,EF,D<br />
:rem 152<br />
20035 DATA EF,D,C,3BF,4BF8,AF,G16,F :rem 132<br />
20040 DATA EF8,G,BF16,EF8,AF,5C20 :rem 9<br />
20045 DATA EF4,D,C,-BF8,AF,G,+EF,-F,+D :rem 14<br />
20050 DATA -EF16,R8,EF8,D,F,BF16 :rem 210<br />
20055 DATA EF8,G,BF16,F4,AF#+D#C#D#-AF#F,AF:rem 63<br />
20060 DATA G,BF#+EFf-BF,G#BF#EF#D#E8#G#+C16:rem 59<br />
20065 DATA -F8,A,+C16,-G4,BF,+E,D,E#-BF,G,BF<br />
:rem 61<br />
20070 DATA A,+C,F#C,-A,+C,-F,E,F#8,A,+D16 :rem 79<br />
20075 DATA -G8,BF,+EF20#D4#C#D#EF#D#C#-BF :rem 182<br />
20080 DATA A16#R4#AfBF#+C,-BF#+D#F#EF#F#D#-BF#+D<br />
:rem 44<br />
20085 DATA -AF#+D#F#EF#F,D,-AF#+D,-G#+D,F#EF#F,D,-<br />
G#+D :rem 111<br />
20090 DATA -G,+C,EF,D,EF,C#-G,+C#-F,+C,EF,D,EF,C,-<br />
F#+C :rem 105<br />
454
Sound<br />
20095 DATA -F,BF,+D,C,D,-BF,F,BF,E,BF ,+DF,C,DF,-BF<br />
,E,BF<br />
:rem 243<br />
20100 DATA F,G,A,BF,+C20,-F4,D,F,G,EF ,C,EF:rem 232<br />
20105 DATA D16,R,R,R<br />
:rem 55<br />
29999 DATAZ<br />
:rem 138<br />
30000 DATA W,<strong>64</strong>,P,17,7fOf0.98<br />
:rem 227<br />
30001 DATA 3EF16,R4,G,F,G,C16,R4,EF,D ,EF :rem 163<br />
30005 DATA -AF16,BF,EF8,+EF,D,C<br />
:rem 145<br />
30010 DATA D,F,D,-BF,+EF,G,EF,C<br />
:rem 144<br />
30015 DATA D,EF,F,-F,BF16,R8,+D<br />
:rem 139<br />
30020 DATA F16,R8,AF#C16,R8,EF<br />
:rem 104<br />
30025 DATA G16,R8,BF,C,EF,AF16<br />
30030 DATA -BF8,+D,G16,-AF8,+C,F,AF16<br />
30035 DATA -AF8,G,+C,-BF,+BF16,AF8<br />
30040 DATA G,BF,G,EF,AF,+C#-AF,F<br />
30045 DATA G,AF,BF,D,EF,C,-AF,BF<br />
30050 DATA EF,F,G,EF,BF16,R8,+D<br />
30055 DATA EF16,R8,-G,BF16,R8,+D<br />
30060 DATA EF16,R8,G,C16,R8,E<br />
30065 DATA F16,R8,-Af+C16,R8,E<br />
30070 DATA F16,R8,A,D16,R8,F#<br />
30075 DATA G16,R8,+C,-BF,AfBF,EF<br />
30080 DATA F,-F,+F,EF,D16,R8,+D<br />
30085 DATA C16,R8,-C,-B16,R8,+B<br />
30090 DATA +C16,R8,2BF,A16,R8,+A<br />
30095 DATA BF16,R8,AF,G16,R8,-G<br />
30100 DATA A,+C,-A,F,BF,+D,EF,F<br />
30105 DATA -BF16,R,R,R<br />
39999 DATA Z<br />
:rem 108<br />
:rem 176<br />
:rem 32<br />
:rem 213<br />
:rem 238<br />
:rem 165<br />
:rem 203<br />
:rem 43<br />
:rem 61<br />
:rem 6<br />
:rem 208<br />
:rem 118<br />
:rem 103<br />
:rem 169<br />
:rem 163<br />
urem 109<br />
:rem 169<br />
:rem 139<br />
To use Program 13-5, after entering <strong>the</strong> program and saving, type RUN and<br />
press RETURN. Answer N to <strong>the</strong> question IS MUSIC DATA IN RAM? and wait for<br />
<strong>the</strong> frequency table to be set up (about a minute and a half). To begin with, enter a<br />
tempo of 160 to hear <strong>the</strong> music. Press RUN/STOP to leave <strong>the</strong> routine, and enter<br />
RUN to restart, answering Y to IS MUSIC DATA IN RAM? this time. Enter a new<br />
tempo to hear <strong>the</strong> music again.<br />
This program uses an interrupt. <strong>The</strong> music can play during BASIC program run<br />
ning without much slowing effect. <strong>The</strong> ML sets frequency values, gates, and so on.<br />
Different rhythms between voices are handled correctly, which BASIC cannot do ex<br />
cept at a very slow tempo.<br />
Music encoding system. Notes can be specified by data items of this form:<br />
[Octave 1-7 or + or -] Note A-G [# or F] [duration 1-255]<br />
No spaces are allowed between parameters. <strong>The</strong> items in square brackets are op<br />
tional: When omitted, current values are used, or defaults, where no value has yet<br />
been input. Octave defaults to 4, <strong>the</strong> octave starting on middle C; 1 to 7 sets <strong>the</strong> oc<br />
tave; + or — can indicate a change to <strong>the</strong> next octave up or down. Note letters are,<br />
of course, A-G, with optional # (sharp) or F (flat) indicators. Duration is in terms of<br />
interrupts. A smaller duration means faster tempo; <strong>the</strong> default is 16. Some examples<br />
of valid data are:<br />
455
Sound<br />
C C in current octave with current duration.<br />
3D Change octave to 3. Play D.<br />
+DF8 Change to next octave up. Play D-flat, duration 8.<br />
4F#24 Change to octave 4, duration 24. Play F-sharp.<br />
And, for example, DATA C,D,E,F,G,A,B,+C,-B,A,G,F,E,D,C defines an ascend<br />
ing major scale followed by a descending major scale.<br />
R defines a rest. If no duration value is included, <strong>the</strong> current value is used. R8,<br />
R are examples.<br />
O resets <strong>the</strong> on/off ratio, <strong>the</strong> fraction of <strong>the</strong> note for which <strong>the</strong> gate is held on.<br />
It must be followed by a number from 0 to 1.0. DATA 0,0.1 is an example. <strong>The</strong> de<br />
fault is 0.5. Don't confuse <strong>the</strong> letter O with <strong>the</strong> number 0.<br />
W allows POKEs to <strong>the</strong> control register; W,128 selects noise and W,20 triangular<br />
wave with ring mod bit on. <strong>The</strong> default waveform is sawtooth.<br />
P allows POKEs to any SID register, which gives freedom to control filters,<br />
pulse widths, and o<strong>the</strong>r low-level SID features. Two parameters are necessary: for<br />
example, DATA P,24,6 sets SID register 24 to 6, POKEing volume to 6.<br />
Z marks <strong>the</strong> end of a voice's data. So DATA C16,D,E,F,Z,B8,A,G,F,E,D,C,D,Z,Z<br />
defines two music parts, with <strong>the</strong> second voice moving at twice <strong>the</strong> rate of <strong>the</strong> first,<br />
and <strong>the</strong> third silent.<br />
<strong>The</strong> program up to, but not including, line 10000 is <strong>the</strong> processing part; lines<br />
10000 on hold <strong>the</strong> data, which in our example is part of <strong>the</strong> first Bach Organ Sonata,<br />
in E-flat major. Its data illustrates all <strong>the</strong>se notations. W,<strong>64</strong> selects pulse wave; P,3,3<br />
initializes <strong>the</strong> pulse-width high register; O,0.9 sets <strong>the</strong> on/off ratio to 0.9, appro<br />
priately for an organ sound. <strong>The</strong> three voices have intentional slight differences in<br />
quality: each has a different pulse width and on/off ratio. R<strong>64</strong>, a rest of duration <strong>64</strong>,<br />
corresponds to a complete bar or measure. <strong>The</strong> quarter note has been defined as 16<br />
clock units in this piece, and <strong>the</strong>re are four of <strong>the</strong>se notes per bar. Most of <strong>the</strong> DATA<br />
statements contain exactly one bar of information.<br />
<strong>The</strong> program can be used to experiment with rapid, highly controlled modifica<br />
tions to <strong>the</strong> registers as a sophisticated sound-effects generator. For example, edit <strong>the</strong><br />
first data line to appear as follows:<br />
10000 DATA O<br />
This sets <strong>the</strong> on/off ratio to 1 so that <strong>the</strong> gate is permanently held on, selects <strong>the</strong><br />
pulse wave, and plays four C notes, changing <strong>the</strong> pulse width between each note. A<br />
note with a continuously changing timbre will be heard.<br />
How <strong>the</strong> Music Program Works<br />
ML data for <strong>the</strong> voices is stored at $5800-$6FFF, $7000-$87FF, and $8800-$9FFF.<br />
It's put <strong>the</strong>re by BASIC, which traps most format errors. Locations $C000-$C0BF<br />
have frequency data for 96 notes. Line 4010 determines <strong>the</strong>ir value and allows tun<br />
ing. <strong>The</strong> ML interrupt routine starts at $C0D0. All notes occupy three bytes: a pitch<br />
number, 1-95, allowing eight octaves of 12 semitones, as well as an on value and an<br />
off value, representing <strong>the</strong> number of interrupts before gating. Each voice can hold<br />
about 2000 notes maximum. Rests are stored with pitch 0. A code of 253 is followed<br />
by a waveform byte; 254 is followed by a SID register and new value; and 255 is <strong>the</strong><br />
456
Sound<br />
end-of-data marker. <strong>The</strong> BASIC lines from 1000 on convert music data into this<br />
format.<br />
<strong>The</strong> ML subroutine at $C0E2 processes this data. It's called three times each<br />
interrupt, once for each voice, and uses indirect addresses ($A5), ($A7), and ($A9).<br />
On and off values for <strong>the</strong> three notes are stored at $C0C5-$C0CA, to time down at<br />
each interrupt. Location 254 is set to 7 when <strong>the</strong> end of data has been reached in all<br />
three voices.<br />
On RUN, if data hasn't yet been read, parsed, and POKEd into RAM, lines 1000<br />
on and 2000 on do this. <strong>The</strong> tempo is POKEd into <strong>the</strong> CIA timer controlling inter<br />
rupts: Use 160-180 for <strong>the</strong> Bach piece. (Any tempo can be obtained by choosing an<br />
appropriate combination of tempo value and note duration values in <strong>the</strong> music data.)<br />
Line 1025 sets <strong>the</strong> default waveform. <strong>The</strong> subroutine at 1500-1550 initializes <strong>the</strong> en<br />
velope of each voice. <strong>The</strong> Bach piece has A, D, and R set at 1, and sustain at 11.<br />
Note that ADSR values should not be zero with this program.<br />
Organ Keyboard<br />
Program 13-6 below turns <strong>the</strong> <strong>64</strong> into a simple two-voice organ. It also handles three<br />
notes as far as <strong>the</strong> keyboard permits—some groups of three keys cannot be decoded<br />
unambiguously by <strong>the</strong> <strong>64</strong>. <strong>The</strong> keyboard design makes it impossible. <strong>The</strong> QWERTY<br />
row sounds white notes from G to <strong>the</strong> E, almost two octaves above. Keys 1, 2, 3, 5,<br />
6, and so on, play F-sharp, G-sharp, A-sharp, and so on. Keys R, Y, and I toge<strong>the</strong>r<br />
play C major; T, U, and O give D minor; T, *, and O give D major.<br />
Program 13-6. Organ Keyboard<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
5 DATA 120,160,0,132,252,132,253,132,254,162,3,142<br />
,74,192,169,254,141 :rem 52<br />
6 DATA 0,220,162,8,72,173,1,220,205,1,220,208,248,<br />
74,176,21,72,142,75 :rem 50<br />
7 DATA 192,174,74,192,185,129,235,149,251,206,74,1<br />
92,240,20,104,174,75 :rem 142<br />
8 DATA 192,200,192,65,176,12,202,208,224,56,104,42<br />
,141,0,220,208,205 :rem 255<br />
9 DATA 104,104,88,96 :rem 224<br />
20 FOR J=49152 TO 49225:READ X:POKE J,X:NEXT<br />
:rem 220<br />
40 GOSUB 3000 *rem 167<br />
45 PW=400:GOSUB 2000 :rem 93<br />
50 GOSUB 4000 srem 169<br />
60 GOSUB 5000 :rem 171<br />
70 POKE 53281,8 :rem 253<br />
74 BL$="{3 SPACES}{BLK}{RVS}{RIGHT}1{RIGHT}2<br />
{RIGHT}3{3 RIGHT}5{RIGHT}6{3 RIGHT}8{RIGHT}9<br />
{RIGHT}0{3 RIGHT}-{RIGHT}£" : rem 59<br />
75 PRINT M{CLR}" BL$:PRINT BL$ :rem 251<br />
79 WH$=M{4 SPACES}{WHT}{RVS}{RIGHT}Q{RIGHT}W<br />
{RIGHT}E{RIGHT}R{RIGHT}T{RIGHT}Y{RIGHT}U{RIGHT}<br />
I{RIGHT}O{RIGHT}P{RIGHT}@{RIGHT}*{RIGHT}t"<br />
:rem 53<br />
457
Sound<br />
80 PRINT "{UP}11 WH$:PRINT WH$:PRINT WH$ : rem 161<br />
90 L1=252:L2=253:L3=254:SR=49152 :rem 253<br />
95 WN=33:WF=32 :rem 47<br />
100 SYS SR :rem 53<br />
110 N1=MA%(PEEK(L1)):N2=MA%(PEEK(L2)):N3=MA%(PEEK(<br />
L3)) :rem 37<br />
115 IF N10 THEN POKE SI+ 4,WN:POKE SI,LQ%(Nl):P0<br />
KE SI+1,HQ%(N1) :rem 87<br />
116 IF N20 THEN POKE SI+11,WN:POKE SI+7 ,LQ%(N2)<br />
:POKE SI+8,HQ%(N2) :rem 242<br />
117 IF N30 THEN POKE SI+18,WN:POKE SI+14,LQ%(N3)<br />
:POKE SI+15,HQ%(N3)<br />
:rem 89<br />
131 IF PEEK(197)=4 THEN WN=33:WF=32<br />
:rem 156<br />
132 IF PEEK(197)=5 THEN WN=17:WF=16<br />
:rem 162<br />
133 IF PEEK(653)=1 THEN WN=65:WF=<strong>64</strong>:GOSUB 2000<br />
:rem 30<br />
150 IF PEEK(L1)=0 THEN POKE SI+4#WF<br />
:rem 167<br />
152 IF PEEK(L2)=0 THEN POKE SI+11,WF<br />
:rem 216<br />
153 IF PEEK(L3)=0 THEN POKE SI+18,WF<br />
:rem 225<br />
160 GOTO100<br />
:rem 97<br />
2000 REM **** ALTER PULSE WIDTH ****<br />
:rem 119<br />
2005 PW=(PW+50) AND 4095:PL=PW AND 255:PH=PW/256<br />
:rem 131<br />
2010 FOR 1=2 TO 16 STEP 7:POKE SI+I,PL:POKE SI+I+1<br />
,PH:NEXT<br />
:rem 50<br />
2020 RETURN<br />
:rem 1<strong>64</strong><br />
3000 REM<br />
:rem 167<br />
3003 SI=54272<br />
:rem 163<br />
3005 FOR J=SI TO SI+24:POKE J,0:NEXT<br />
:rem 42<br />
3010 POKE SI+ 5,16*5+11<br />
:rem 110<br />
3011 POKE SI+12,16*5+11<br />
:reift 157<br />
3012 POKE SI+19,16*5+11<br />
:rem 165<br />
3020 POKE SI+6,16*15+12<br />
:rem 162<br />
3021 POKE SI+13,16*15+12<br />
:rem 209<br />
3022 POKE SI+20,16*15+12<br />
:rem 208<br />
3030 POKE SI+24,3<br />
:rem 129<br />
3040 POKE SI+ 4,0<br />
:rem 77<br />
3041 POKE SI+11,0<br />
:rem 124<br />
3042 POKE SI+18,0<br />
:rem 132<br />
3050 RETURN<br />
:rem 168<br />
4000 REM<br />
:rem 168<br />
4005 DIM FQ(95),LQ%(95),HQ%(95)<br />
:rem 79<br />
4010 FQ(95)=<strong>64</strong>814<br />
:rem 95<br />
4020 FOR J=94 TO 84 STEP -1:FQ(J)=FQ(J+l)/(2?71/12<br />
Aa*a )):NEXT :rem 30<br />
4030 FOR J=6 TO 0 STEP -1:FOR K=l TO 12 :rem 184<br />
4040 P1=J*12+K-1:FQ(P1)=FQ(P1+12)/2:NEXT:NEXT<br />
4045 FOR Pl=l TO 95<br />
4050 LQ%(P1)=FQ(P1)-256*INT(FQ(P1)/256):HQ%(P11)=|q<br />
(PD/256 :rem 1<strong>64</strong><br />
458
Sound<br />
4060 NEXT:RETURN :rem 35<br />
5000 REM :rem 169<br />
5005 DIM MA%(255) :rem 68<br />
5010 FOR J= 0 TO 22:READ V$,V:MA%(ASC(V$))=V:NEXT<br />
:rem 169<br />
5020 RETURN :rem 167<br />
5500 DATA 1,42,Q,43,2,44,W,45,3,46,E,47,R,48,5,49,<br />
T,50,6,51,Y,52,U,53,8,54,1,55 :rem 4<br />
5510 DATA 9,56,O,57,0,58,P,59,@,60,-,61,*,62,£,63<br />
,t,<strong>64</strong> :rem 228<br />
Running <strong>the</strong> program sets <strong>the</strong> waveform to sawtooth. Pressing f3 selects <strong>the</strong><br />
triangular wave, and SHIFT selects <strong>the</strong> pulse waveform and, if held, alters its duty<br />
cycle; it can operate when a note or chord is held, making audible <strong>the</strong> resulting<br />
change in timbre. Pressing fl reselects sawtooth.<br />
An ML routine is essential to determine which keys are pressed, as <strong>the</strong> keyscan<br />
routine returns just one value. This program's ML puts ASCII values in 254, or 254<br />
and 253, or 254, 253, and 252, depending on whe<strong>the</strong>r one, two, or three keypresses<br />
were detected. BASIC PEEKs <strong>the</strong>se locations and converts <strong>the</strong>m to pitches.<br />
Programmable Rhythm Box<br />
Program 13-7 has six voices available, with <strong>the</strong> waveform and envelope defined by<br />
DATA in lines 3040-3090. Each voice definition has eight bytes, one for each of <strong>the</strong><br />
seven SID registers plus an extra one. As it stands, this program uses no filtering,<br />
ring, or sync controls, partly because six voices would be harder to generate. But<br />
<strong>the</strong>se and o<strong>the</strong>r features can be added without much difficulty. Since <strong>the</strong> SID chip<br />
has only three voices, <strong>the</strong> six instruments cannot be played toge<strong>the</strong>r: 1 and 2 are<br />
played by SID voice 1; 3 and 4 by voice 2; and 5 and 6 by voice 3, so voices 1 and 2<br />
can't sound simultaneously. However, <strong>the</strong>y can alternate, so <strong>the</strong> rhythm might be 1,<br />
1, 2, 2, 1, 1, or whatever.<br />
Program 13-7. Rhythm Box<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader/' Appendix C.<br />
1 GOSUB 2000:NB=32:INPUT "NUMBER OF BEATS<br />
{3 SPACES}(1-32: DEFAULT=32)";NB :rem 122<br />
2 TM=5:INPUT "TEMPO{13 SPACES}(1-50: DEFAULT= 5)";<br />
TM :rem 121<br />
3 A$="Nil:INPUT "CLEAR RHYTHMS?{3 SPACES}(Y/N: DEFA<br />
ULT IS N)";A$ :rem 58<br />
4 IF A$="Y" THEN GOSUB 6000 s^em 119<br />
5 A$="N":INPUT "LOAD RHYTHMS{5 SPACES}(Y/N: DEFAUL<br />
T IS N)";A$ 5rem ^<br />
6 IF A$="Y" THEN GOSUB 7000 *rem 122<br />
8 GR$=" B+++B+++B+++B+++B+++B+++B+++B+++" :rem 142<br />
9 PS$=" 7.. .7.. .7. . .7.. .7 ":rem 111<br />
10 PRINT"{CLR}{2 DOWN}" LEFT$(GR$,NB+1) :rem 92<br />
20 FOR J=l TO 3:PRINT LEFT$(PS$,NB+1):NEXT:rem 150<br />
30 PRINT "{HOME}";:POKE 53280,6:POKE 650,128<br />
:rem 26<br />
459
Sound<br />
50 GOSUB 3000 :rem 168<br />
60 GOSUB 5000 srem 171<br />
70 N$="000000" :rem 122<br />
100 PRINT M ": PRINT "{UP}"; :rem 57<br />
110 TI$=N$:POKE 56334,17 :rem 4<br />
120 PRINT " V{LEFT}";:POKE 252,BT:SYS 49200:rem 14<br />
130 IF TKTM GOTO 130 :rem 106<br />
140 BT=BT+1:IF BT=NB THEN BT=0:GOTO 100 :rem 188<br />
150 GOTO 110 :rem 97<br />
1999 REM srem 192<br />
2000 SI=54272 :rem 159<br />
2010 POKE SI+4,8:POKE SI+11,8:POKE SI+18,8 :rem 68<br />
2020 FORI=SI TO SI+28:POKE 1,0 :rem 175<br />
2030 POKE SI+24,8 :rem 133<br />
2040 RETURN :rem 166<br />
2999 REM :rem 193<br />
3000 REM :rem 167<br />
3010 REM :rem 168<br />
3030 REM 1 FREQ LOW : rem 251<br />
3031 REM 2 FREQ HI :rem 156<br />
3032 REM 3 PW{3 SPACES}LO : rem 33<br />
3033 REM 4 PW{3 SPACES}HI :rem 25<br />
3034 REM 5 WAVEFORM + GATE :rem 150<br />
3035 REM 6 ATTACK +{3 SPACES}DECAY ;rem 46<br />
3036 REM 7 SUSTAIN +{2 SPACES} RELEASE : rem 58<br />
3037 REM{2 SPACES}PLUS ONE 'DUMMY1 BYTE TO GIVE AN<br />
8-BYTE BLOCK :rem 70<br />
3039 DATA "INSTRS" :rem 16<br />
3040 DATA 000,010,000,000,017,024,008,0 :rem 76<br />
3050 DATA 000,015,000,000,017,023,007,0 :rem 80<br />
3060 DATA 000,015,000,000,129,006,000,0 :rem 79<br />
3070 DATA 075,001,000,008,065,025,008,0 :rem 103<br />
3080 DATA 000,226,000,000,129,025,008,0 :rem 94<br />
3090 DATA 255,253,000,000,129,040,008,0 :rem 104<br />
3100 RESTORE:FOR J=l TO 1E9:READ X$:IF X$MINSTRS<br />
11 THEN NEXT :rem 57<br />
3110 FOR J=0 TO 6*8-1:READ X:POKE 49152+J,X:NEXT<br />
:rem 138<br />
3120 REM :rem 170<br />
3130 RETURN :rem 167<br />
4998 REM .rem 194<br />
4999 DATAMMLM :rem 214<br />
5000 DATA 165,252,141,245,192,9,32,141,246,192,73,<br />
96,141,247,192,32,228 srem 182<br />
5001 DATA 255,201,0,240,54,141,243,192,162,3,221,2<br />
39,192,208,17,189,244 :rem 162<br />
5002 DATA 192,170,169,63,157,92,193,169,46,32,214,<br />
192,76,124,192,202,208 :rem 243<br />
5003 DATA 231,173,243,192,56,233,49,201,6,176,14,4<br />
2,42,42,72,42,41,96,5 -rem 166<br />
460
Sound<br />
5004 DATA 252,170,104,157,92,193,174,245,192,189,9<br />
2,193,201,63,240,9,168 :rem 245<br />
5005 DATA 32,209,192,162,0,32,182,192,174,246,192,<br />
189,92,193,201,63,240 :rem 181<br />
5006 DATA 9,168,32,209,192,162,7,32,182,192,174,24<br />
7,192,189,92,193,201 :rem 151<br />
5007 DATA 63,240,9,168,32,209,192,162,14,32,182,19<br />
2,96,185,4,192,41,254 :rem 188<br />
5008 DATA 157,4,212,169,7,141,244,192,185,0,192,15<br />
7,0,212,200,232,206,244 :rem 3<br />
5009 DATA 192,208,243,96,106,106,106,105,49,72,138<br />
,201,32,144,9,201,<strong>64</strong> :rem 122<br />
5010 DATA 144,3,24,105,8,105,8,170,104,157,121,4,1<br />
69,1,157,121,216,96,133 :rem 249<br />
5011 DATA 134,135 :rem 62<br />
5015 RESTORE: FOR J=l TO 1E9: READ X$: IF X$"ML"<br />
THEN NEXT :rem 246<br />
5020 FOR J=49200 TO 49394: READ X: POKE J,X: NEXT:<br />
RETURN :rem 92<br />
5999 REM :rem 196<br />
6000 FOR J=49500 TO 49500+95:POKE J,63:NEXT:rem 53<br />
6010 REM :rem 171<br />
6020 POKE 49500+32,16 :rem 28<br />
6021 POKE 49500+40,16 :rem 28<br />
6022 POKE 49500+48,16 :rem 37<br />
6023 POKE 49500+56,16 :rem 37<br />
6040 RETURN :rem 170<br />
6998 REM :rem 176<br />
6999 DATA "RHYTHMS" :rem 110<br />
7000 DATA 63,63,63,63,63,63,63,63 :rem 93<br />
7002 DATA 63,63,63,63,63,63,63,63 :rem 95<br />
7004 DATA 08,63,00,63,63,63,00,63 :rem 78<br />
7006 DATA 08,63,08,63,08,63,08,63 :rem 95<br />
7007 REM :rem 178<br />
7010 DATA 16,63,63,63,63,63,63,63 :rem 92<br />
7012 DATA 16,63,63,63,63,63,63,63 :rem 94<br />
7014 DATA 16,63,63,63,63,63,63,63 :rem 96<br />
7016 DATA 16,63,16,63,63,63,63,63 :rem 96<br />
7017 REM :rem 179<br />
7020 DATA 40,63,63,63,40,63,40,63 :rem 80<br />
7022 DATA 40,63,63,63,40,63,63,63 :rem 87<br />
7024 DATA 32,63,63,63,32,63,32,40 :rem 82<br />
7026 DATA 40,63,40,32,32,40,32,63 :rem 74<br />
7030 RESTORE:FOR J=l TO 1E9:READ X$:IF X$"RHYTHM<br />
S" THEN NEXT :rem 139<br />
7040 FOR J=49500 TO 49500+95:READ X:POKE J,X:NEXT:<br />
RETURN :rem 241<br />
<strong>The</strong> program displays a grid of 3 rows and as many as 32 columns; <strong>the</strong> rows<br />
represent <strong>the</strong> SID voices and <strong>the</strong> columns stand for beats in <strong>the</strong> bar. An X character<br />
moves horizontally over <strong>the</strong> grid showing which beat is being played.<br />
461
Sound<br />
<strong>The</strong> program first asks <strong>the</strong> user to specify <strong>the</strong> number of beats to be used, <strong>the</strong><br />
tempo, whe<strong>the</strong>r <strong>the</strong> rhythms currently in RAM should be cleared, and whe<strong>the</strong>r <strong>the</strong><br />
rhythms defined in lines 7000-7026 should be loaded into memory. Press RETURN<br />
in response to <strong>the</strong> first three prompts to select <strong>the</strong> defaults, and Y to <strong>the</strong> last prompt<br />
to load <strong>the</strong> built-in rhythms.<br />
Typing 1-6 plays <strong>the</strong> designated instrument at that point in <strong>the</strong> bar and adds it<br />
to <strong>the</strong> display and to <strong>the</strong> rhythm stored in RAM so that it will be heard at that point<br />
in <strong>the</strong> bar from now on. Pressing fl, f3, or f5 as <strong>the</strong> program plays will delete voices<br />
1 and 2, 3 and 4, or 5 and 6, respectively, from <strong>the</strong> display and from memory at <strong>the</strong><br />
position in <strong>the</strong> bar where <strong>the</strong>y are typed. Thus, instruments can be added to and re<br />
moved from <strong>the</strong> rhythm. If you press RUN/STOP, <strong>the</strong>n run <strong>the</strong> program again, you<br />
can keep <strong>the</strong> new values by being careful not to clear <strong>the</strong>m or overwrite <strong>the</strong>m.<br />
DATA in lines 7000-7026 holds <strong>the</strong> start-up rhythms. <strong>The</strong>re are three groups of<br />
DATA statements, each of 32 values, one for each beat of <strong>the</strong> bar. A value of 63 in<br />
dicates that no note is present at that beat. Values of 0 and 8 in <strong>the</strong> first group mean<br />
that instrument 1 or 2 has a note on that beat; values of 16 and 24 in <strong>the</strong> second<br />
group mean that instrument 3 or 4 has a note on that beat; and values of 32 and 40<br />
in <strong>the</strong> third group mean that instrument 5 or 6 has a note on that beat. <strong>The</strong> data<br />
may be edited to provide accurately programmed rhythms, and rhythms can be<br />
edited as <strong>the</strong> system plays.<br />
Music <strong>Programming</strong> Aids<br />
A number of monitors and syn<strong>the</strong>sizer programs exist, some commercial, o<strong>the</strong>rs in<br />
<strong>the</strong> public domain (free). Typically, <strong>the</strong>se allow parameters to be altered relatively<br />
easily; for example, F may alter frequency, P pulse width. Ideally, <strong>the</strong>y should in<br />
clude gate timing, true frequencies in Hz, smooth gliding through values, indications<br />
of connections between ring, sync, and filter settings or registers, and facilities to use<br />
<strong>the</strong> read-only registers. <strong>The</strong>y should be able to report how sounds <strong>the</strong>y're producing<br />
are made. O<strong>the</strong>rwise, you can be put in <strong>the</strong> frustrating position of being unable to<br />
reconstruct some attractive sound. Some indication of waveform, or even a wave<br />
form analyzer/syn<strong>the</strong>sizer, would be useful.<br />
BASIC extensions are difficult to write for <strong>the</strong> <strong>64</strong>. Some versions of BASIC, like<br />
Simons' BASIC use <strong>the</strong> command WAVE 1,00010000 to set voice 1 to a triangular<br />
wave, and ENVELOPE to set up a voice's ADSR values. Both <strong>the</strong>refore require<br />
knowledge of <strong>the</strong> SID chip to use. <strong>The</strong> MUSIC command on some utilities has two<br />
parameters: one to set <strong>the</strong> tempo and a second to select notes. <strong>The</strong> string uses letters<br />
A-F (SHIFTed to indicate sharps; sometimes <strong>the</strong>re is no notation for a flatted note<br />
available, which is irritating to some musicians). This is followed by a number 0-8<br />
specifying <strong>the</strong> octave, with function keys to set durations, and a couple of o<strong>the</strong>r con<br />
trol characters. It calculates a set of frequencies corresponding to <strong>the</strong> notes. PLAY<br />
plays <strong>the</strong> tune previously created using MUSIC; but only one line can be played at a<br />
time. VOL, which sets <strong>the</strong> volume nybble, is <strong>the</strong> only o<strong>the</strong>r sound command. Threepart<br />
harmonies and so on aren't supported by Simons' BASIC.<br />
462
Chapter 14<br />
Tape Storage<br />
• Loading and Saving BASIC Programs<br />
with Tape<br />
• Handling Tape Data Files<br />
• Loading and Saving Machine<br />
Language<br />
• Tape Hardware Notes<br />
• Advanced Tape <strong>Programming</strong><br />
• Copy Protection for Tape
Chapter 14<br />
Tape Storage<br />
Tape is a popular storage medium for <strong>the</strong> <strong>Commodore</strong> <strong>64</strong>. This chapter discusses<br />
tape operations completely; it will give you <strong>the</strong> information you need to handle<br />
practically any tape operation.<br />
Loading and Saving BASIC Programs with<br />
Tape<br />
Loading and saving programs with tape is easy. <strong>The</strong> command LOAD prompts you<br />
with <strong>the</strong> message PRESS PLAY ON TAPE; when that is done, <strong>the</strong> next program is lo<br />
cated and loaded. Holding down <strong>the</strong> left SHIFT key and pressing <strong>the</strong> RUN/STOP<br />
key enters LOAD and RUN into <strong>the</strong> <strong>64</strong>; it is <strong>the</strong> method that uses <strong>the</strong> minimum of<br />
keystrokes.<br />
<strong>The</strong> command SAVE prompts with PRESS PLAY & RECORD ON TAPE. When<br />
this is done, <strong>the</strong> BASIC program currently in memory is saved on tape.<br />
Tape, as operated by <strong>the</strong> <strong>64</strong>, is not very fast. Table 14-1 shows approximate<br />
times needed to load or save BASIC programs. Obviously, longer programs take<br />
more time. It also indicates <strong>the</strong> number of programs which can be expected to fit<br />
onto one side of a cassette; as you might expect, longer tapes can store more<br />
programs.<br />
Table 14-1. Time Required to Load or Save Programs to Tape<br />
Length of<br />
Program<br />
Approx Time to<br />
Load or Save<br />
Approx Number of Programs, One Side of Cassette<br />
C5<br />
C10<br />
C20<br />
C30<br />
IK<br />
1/2 min<br />
4<br />
8<br />
16<br />
25<br />
4K<br />
1-1/2 min<br />
1<br />
3<br />
6<br />
9<br />
8K<br />
2-3/4 min<br />
—<br />
1<br />
3<br />
5<br />
Before considering <strong>the</strong> full syntax of LOAD and SAVE, it is helpful to look at a<br />
few aspects of BASIC storage. <strong>The</strong>se commands have <strong>the</strong> functions of loading RAM .<br />
from tape and of dumping RAM to tape, respectively. In fact, <strong>the</strong>y use <strong>the</strong> start- and<br />
end-of-BASIC pointers, in locations 43 and 44 (start) and 45 and 46 (end). This is<br />
why variables can't normally be stored along with BASIC. <strong>The</strong> zero byte at <strong>the</strong> very<br />
start of all BASIC programs is not used; nei<strong>the</strong>r is <strong>the</strong> byte at <strong>the</strong> end-of-BASIC<br />
position.<br />
<strong>The</strong> cassette recorder (sometimes called <strong>the</strong> Datassette) is not under full com<br />
puter control, which is why screen prompts are necessary. In particular, <strong>the</strong>re's only<br />
one line to test for a keypress on <strong>the</strong> cassette, so <strong>the</strong> <strong>64</strong> cannot distinguish PLAY<br />
from RECORD. Even <strong>the</strong> fast forward and rewind keys are detected as though PLAY<br />
or RECORD were being pressed. Thus, if you want to rewind a tape and record from<br />
<strong>the</strong> start, rewind before pressing RETURN after SAVE.<br />
In addition, be sure to press both RECORD and PLAY to save to tape. PLAY<br />
looks <strong>the</strong> same on <strong>the</strong> screen but of course doesn't work. If PLAY and RECORD are<br />
465
Tape Storage<br />
accidentally pressed for <strong>the</strong> LOAD command, <strong>the</strong> program on tape will be erased,<br />
unless <strong>the</strong> write-protect tabs at <strong>the</strong> back of <strong>the</strong> cassette are missing.<br />
Tape operations use <strong>the</strong> IRQ interrupt, locking out <strong>the</strong> keyboard. However, <strong>the</strong><br />
RUN/STOP key subroutine is called at intervals, so RUN/STOP and RUN/STOP-<br />
RESTORE still work. Without this, if tape reading failed in some way, <strong>the</strong> <strong>64</strong> would<br />
have to be switched off. Note that <strong>the</strong> TI clock is turned off during tape operations.<br />
Several programs can be stored consecutively on each side of a tape; however,<br />
<strong>the</strong> simple LOAD syntax can't distinguish between <strong>the</strong>m. So <strong>the</strong> system allows<br />
BASIC programs to be named. <strong>The</strong> complete syntax for SAVE is SAVE "filename"<br />
which saves <strong>the</strong> program along with a name. <strong>The</strong> corresponding LOAD "filename"<br />
searches for <strong>the</strong> named program and also (so you know where you are) lists any<br />
o<strong>the</strong>r programs it may find. For example, following <strong>the</strong> command LOAD "CHECK<br />
ERS" <strong>the</strong> screen may show something like this:<br />
LOAD "CHECKERS"<br />
PRESS PLAY ON TAPE<br />
OK<br />
SEARCHING FOR CHECKERS<br />
FOUND CHESS<br />
FOUND CHECKERS<br />
LOADING CHECKERS<br />
READY.<br />
<strong>The</strong> maximum length of a name, as it appears after FOUND, is 16 characters.<br />
Provided <strong>the</strong> found program name matches, <strong>the</strong> program is loaded. LOAD "CH"<br />
loads CHESS if it finds that program on <strong>the</strong> tape before CHECKERS. LOAD<br />
"CHEC" loads CHECKERS. This is why LOAD alone always loads <strong>the</strong> first program<br />
it finds.<br />
Full Syntax of LOAD and SAVE<br />
<strong>The</strong> full syntax introduces two new concepts: <strong>the</strong> forced-LOAD address and <strong>the</strong> endof-tape<br />
marker. A forced LOAD means that <strong>the</strong> starting address is <strong>the</strong> same as that<br />
specified on tape; no relocatability is allowed. This is primarily important with ML<br />
programs and hardly applies to BASIC.<br />
An end-of-tape marker signals that <strong>the</strong>re are no more programs on a tape. <strong>The</strong><br />
idea is to avoid <strong>the</strong> situation where time is wasted in reading blank tape. <strong>The</strong> marker<br />
needn't be near <strong>the</strong> physical end of <strong>the</strong> tape, and if you choose to do so you can<br />
record programs beyond it. When LOAD finds such a marker, it prints a message<br />
which should be ?END QF TAPE, but is instead 7DEV1CE NOT PRESENT ERROR.<br />
Full syntax for LOAD is LOAD string expression, device number, type-of-load<br />
number, where string expression is <strong>the</strong> program name (e.g., "CHESS", or X$). Device<br />
number is 1> or expression evaluating to 1 (tape is always device 1). Type-of-load is<br />
0 for a relocating LOAD and 1 for a forced LOAD, or an expression evaluating to 0<br />
or 1. Only bit 0 counts; a parameter of 16 is treated as 0.<br />
As you've seen, forced LOADs are seldom used with BASIC. Also, if <strong>the</strong> middle<br />
parameter is not specified, it is assumed to be 1, so <strong>the</strong> simpler syntax of LOAD<br />
"filename" is usually enough.<br />
Full syntax for SAVE is SAVE string expression, device number, type-of-save num-<br />
466
Tape Storage<br />
her. <strong>The</strong> type-of-save parameter uses two bits; 0 means SAVE allowing relocation,<br />
while 1 means SAVE with a forced-LOAD address. This, too, is seldom used in<br />
BASIC. If <strong>the</strong> parameter is 2, it means SAVE with an end-of-tape marker; a value of<br />
3 means SAVE with both forced-LOAD address and end-of-tape marker.<br />
Some examples will make this clearer. SAVE "TEST PROGRAM",1,2 stores<br />
TEST PROGRAM on tape, followed by an end-of-tape marker. SAVE CHR$(18) +<br />
CHR$(28) + "PROGRAM" adds an RVS/ON and a RED character to <strong>the</strong> program's<br />
name. When it's found, this will generate FOUND PROGRAM and <strong>the</strong> name will be<br />
reversed and in red characters.<br />
SAVE "EXCEPTIONALLY LONG NAME" stores <strong>the</strong> program in memory onto<br />
tape with <strong>the</strong> full name as it is given. Although LOAD checks only <strong>the</strong> first 16<br />
characters, <strong>the</strong> o<strong>the</strong>rs are in fact saved; as you'll see, <strong>the</strong>y can be put to use in pro<br />
gram protection.<br />
Direct and Program Modes<br />
So far, discussion has focused on direct mode. However, both LOAD and SAVE<br />
work from within programs, too. SAVE has <strong>the</strong> same effect as it does in direct mode.<br />
LOAD has a chaining effect; generally, <strong>the</strong> newly loaded program overwrites <strong>the</strong><br />
older program and GOTO is executed, pointing to <strong>the</strong> first line of <strong>the</strong> new program.<br />
Screen prompts don't appear when PLAY is pressed on <strong>the</strong> cassette. This helps to<br />
keep <strong>the</strong> screen layout clean.<br />
Validation and Errors with LOAD and SAVE<br />
SAVE, although very reliable, isn't 100 percent foolproof. <strong>The</strong> tape may be faulty,<br />
for example. <strong>The</strong> best protection is to save your program twice, perhaps with names<br />
like PROG and PROGCOPY to distinguish <strong>the</strong>m.<br />
An alternative is <strong>the</strong> VERIFY command. This has syntax identical to LOAD and<br />
SAVE, so VERIFY "filename" or simply VERIFY is acceptable. VERIFY works much<br />
like LOAD, except that <strong>the</strong> bytes aren't loaded into memory but are instead com<br />
pared with <strong>the</strong> present memory contents. If <strong>the</strong> two are not equal, 7VERIFY ERROR<br />
results. To use VERIFY, <strong>the</strong> tape must be rewound to <strong>the</strong> start of <strong>the</strong> program being<br />
verified; note, too, that VERIFY takes at least as much time as saving a second copy.<br />
If you use <strong>the</strong> VERIFY command, you will get <strong>the</strong> following screen display:<br />
SAVE "GRAPHICS DEMO"<br />
PRESS PLAY & RECORD ON TAPE<br />
OK<br />
SAVING GRAPHICS DEMO<br />
READY.<br />
(Rewind tape at this point.)<br />
VERIFY (or VERIFY "GRAPHICS DEMO" or VERIFY "GR")<br />
PRESS PLAY ON TAPE<br />
OK<br />
VERIFYING (or VERIFYING GRAPHICS DEMO or VERIFYING GR)<br />
OK<br />
READY.<br />
VERIFY also works within a program, but if you use it in that way, it is<br />
467
Tape Storage<br />
necessary to include a message telling <strong>the</strong> user to rewind.<br />
LOAD is generally reliable, but errors are possible for reasons explained in <strong>the</strong><br />
hardware section of this chapter. <strong>The</strong> message ?LDAD ERROR signals that <strong>the</strong> system<br />
found uncorrectable errors on tape. PRINT ST prints <strong>the</strong> value of <strong>the</strong> error status<br />
variable and gives a clue as to what happened; PRINT PEEK (159) indicates <strong>the</strong><br />
number of errors found.<br />
?WAD ERROR doesn't always signify a failure to load. If a program is loaded<br />
partly into ROM or into an area where <strong>the</strong>re's no RAM, <strong>the</strong> system will find an<br />
error. <strong>The</strong>se situations won't normally apply to BASIC.<br />
If you experiment with short test programs, deliberately recording over small<br />
sections to corrupt <strong>the</strong>m, you'll be able to generate LOAD errors. Note that <strong>the</strong><br />
resulting program is usually meaningless. Sometimes you'll generate an ?OUT OF<br />
MEMORY ERROR instead; this happens if <strong>the</strong> header (at <strong>the</strong> start of <strong>the</strong> program) is<br />
corrupted.<br />
Programs may very occasionally seem to have disappeared from <strong>the</strong> tape. Ei<strong>the</strong>r<br />
<strong>the</strong> program hasn't been recorded (this can be checked most easily with an ordinary<br />
audio tape recorder) or, more likely, <strong>the</strong> record/playback head needs to be cleaned<br />
or demagnetized.<br />
Handling Tape Data Files<br />
Files are more difficult to understand than programs. A file is a collection of stored<br />
data—in this case, data stored sequentially on tape. A typical use is with programs<br />
that give multiple-choice tests; once <strong>the</strong> program is in memory, a tape on a particular<br />
subject can be read and its information used. In principle, <strong>the</strong>re's no limit to <strong>the</strong><br />
number of subjects. Tapes can be changed indefinitely, so <strong>the</strong> tape files are a storage<br />
system which is independent of <strong>the</strong> program.<br />
<strong>The</strong> <strong>64</strong>'s tape system is slower in this mode than it is with program storage.<br />
Even in <strong>the</strong> best cases it's about half as fast. To put this in perspective, Table 14-2<br />
shows <strong>the</strong> approximate amount of data which can be stored as a file on one side of a<br />
cassette.<br />
Table 14-2. Tape Storage Capacity<br />
Cassette Type<br />
C5<br />
C10<br />
C20<br />
C30<br />
Maximum Length of File<br />
5K<br />
10K<br />
20K<br />
30K<br />
Minimum Time to Read or Write<br />
3 min<br />
6 min<br />
12 min<br />
18 min<br />
Because of this slow speed, data file handling may be absurdly slow. It may be<br />
worthwhile to save data along with programs, although this is a tricky technique,<br />
requiring <strong>the</strong> end-of-program pointer to be moved to include variables and <strong>the</strong> first<br />
line of <strong>the</strong> program to POKE in <strong>the</strong> correct value. In addition, strings are hard to<br />
save. <strong>The</strong> whole of BASIC memory needs to be saved, plus <strong>the</strong> pointers which<br />
handle strings.<br />
Files need a buffer. Unlike a program, which has a place allocated in memory at<br />
one time, files need to be written piecemeal, with data accumulating until <strong>the</strong> buffer<br />
468
Tape Storage<br />
is full. It's not possible to write directly to tape, because <strong>the</strong> motor needs to pick up<br />
speed, so <strong>the</strong>re's a stop/start option with files.<br />
<strong>Commodore</strong> <strong>64</strong> tape files are inevitably sequential. Since data has to be written<br />
(and read back) in order, <strong>the</strong> only way to access data that is part of <strong>the</strong> way into a<br />
file is to read <strong>the</strong> whole file from <strong>the</strong> start. Moreover, <strong>the</strong>re's no way to alter file<br />
information, without reading everything into memory, altering it, and writing it<br />
back. Thus, <strong>the</strong> system has severe limitations, although to be fair <strong>the</strong>se are largely<br />
constraints of <strong>the</strong> system and are unavoidable without advanced methods like those<br />
described later in this chapter.<br />
<strong>The</strong>re are three stages in file use. First, <strong>the</strong> file must be opened, meaning that<br />
preparations are made in memory to write data to tape. Second, write <strong>the</strong> file to<br />
tape. Finally, close <strong>the</strong> file, meaning that <strong>the</strong> file is correctly terminated.<br />
When <strong>the</strong> file is to be read back, perhaps by a different program, three o<strong>the</strong>r<br />
steps are necessary. First, open <strong>the</strong> file for reading, which prepares <strong>the</strong> <strong>64</strong> for input<br />
from tape. Second, read <strong>the</strong> file from tape; it may be read in parts. Finally, close <strong>the</strong><br />
file. <strong>The</strong> final step is often not really necessary; since nothing is being written to<br />
tape, <strong>the</strong> file will be left unchanged.<br />
File Handling Syntax<br />
<strong>The</strong> full syntax of OPEN is OPEN file number, device number, type of OPEN, filename.<br />
<strong>The</strong> file number is an expression evaluating a number from 1 to 255; <strong>the</strong> device<br />
number must evaluate to 1, corresponding to tape. <strong>The</strong> filename is a string ex<br />
pression, usually something like "TEST DATA".<br />
<strong>The</strong> type of OPEN is an arithmetic expression, usually 0, 1, or 2. Use 0 to open<br />
a file for reading, 1 to open a file for writing, and 2 to open a file for writing with an<br />
end-of-tape marker after <strong>the</strong> file.<br />
<strong>The</strong> rules for default values (values assumed by <strong>the</strong> system when not specifi<br />
cally set) are similar to those for LOAD and SAVE. For example, when no name is<br />
given to <strong>the</strong> file, it's saved without a name; when a file is opened for read, <strong>the</strong> first<br />
file conforming to <strong>the</strong> name in <strong>the</strong> OPEN statement is taken to be <strong>the</strong> correct file.<br />
Note that OPEN defaults to read; this prevents accidental overwriting of files.<br />
Also note that <strong>the</strong>re's no type 3. Reading a file, <strong>the</strong>n writing an end-of-tape marker,<br />
isn't allowed.<br />
Because <strong>the</strong> <strong>64</strong> supports only one tape drive, <strong>the</strong> normal <strong>64</strong> setup never has<br />
more than one file open. Thus, OPEN 1 is a typical command, assigning file number<br />
1 to tape. O<strong>the</strong>r file numbers are seldom used.<br />
To see how this works, consider <strong>the</strong> following example. Type in OPEN 1,1,1/<br />
"TESTING" in direct mode and press RETURN. This opens file number 1 (logical<br />
file 1 is ano<strong>the</strong>r name for it) to tape for writing. You'll be prompted PRESS RECORD<br />
& PLAY ON TAPE. When you do this, <strong>the</strong> screen blanks and before text returns to<br />
<strong>the</strong> screen, <strong>the</strong>re's a delay of 12 seconds or so. A preparatory block of information,<br />
giving <strong>the</strong> filename, has been written to tape. Now type PRINT #1,"HELLO" and<br />
press RETURN. Nothing happens, although <strong>the</strong> buffer has stored <strong>the</strong> word HELLO.<br />
But <strong>the</strong>re's room for more. Type CLOSE 1 and press RETURN. <strong>The</strong> buffer is<br />
written to tape. If you don't type CLOSE, no data is written; generally, if a file isn't<br />
closed, <strong>the</strong> last batch of data will be missing. In addition, <strong>the</strong> system won't recognize<br />
that <strong>the</strong> file has ended.<br />
469
Tape Storage<br />
To read this back, use <strong>the</strong> INPUT# command. This can only be used from within<br />
a program. <strong>The</strong>refore, most file reading is done in program mode. Type in Program<br />
14-1, rewind <strong>the</strong> tape, and run it. After PRESS PLAY ON TAPE <strong>the</strong>re'll be a delay<br />
while <strong>the</strong> header is found and read; <strong>the</strong>n <strong>the</strong> word HELLO should be printed on <strong>the</strong><br />
screen. <strong>The</strong> word was recovered from tape, showing in miniature how files work.<br />
Program 14-1. Using Files<br />
10 OPEN 1:REM OPENS FILE#1 TO TAPE,TO READ<br />
20 INPUT*1,X$:REM INPUT A STRING FROM FILE #1<br />
30 PRINT X$:REM PRINT THE SAME STRING TO THE SCREE<br />
N<br />
40 CLOSE 1:REM CLOSE THE TAPE FILE<br />
Using Files Effectively<br />
Note <strong>the</strong> 10 OPEN 1,1,O,"TESTING" is necessary if you wish to name <strong>the</strong> file to be<br />
read; <strong>the</strong> default parameters have to be put in. PRINT# is generally used to write to<br />
tape. <strong>The</strong> alternative is CMD 1, which causes PRINT to output to file 1. However, it<br />
has <strong>the</strong> drawback of sometimes working in unpredictable ways. In particular, GET<br />
prevents it from working.<br />
Ei<strong>the</strong>r INPUT# or GET# will let you read from a tape file. GET# takes in in<br />
dividual characters, exactly like GET, and <strong>the</strong>refore tends to be slower than INPUT#.<br />
But it is able to treat <strong>the</strong> various special characters of INPUT (comma, colon, quotes,<br />
RETURN) as ordinary characters.<br />
Generally, use INPUT# when you're sure of <strong>the</strong> format of each data item. Don't<br />
try to read a string with INPUT#1,X, for example, or try to input a string longer than<br />
88 characters. Null strings are also a problem and should be avoided.<br />
Note, too, that number storage is inefficient. For example, ten bytes are taken up<br />
storing 1234.56. When possible, write ASCII values to tape with PRINT#1,CHR$(X),<br />
and use GET# to read <strong>the</strong>m back.<br />
<strong>The</strong> full syntax of CLOSE is identical to that of OPEN, but only <strong>the</strong> file number<br />
is actually used. <strong>The</strong>refore, CLOSE 1 is typical.<br />
<strong>The</strong> Status Variable, ST<br />
You can use ST to detect <strong>the</strong> end of a file. ST changes from 0 to <strong>64</strong> when <strong>the</strong> last<br />
record of a file is read with INPUT#. However, it isn't necessary. Alternatives are to<br />
arrange <strong>the</strong> data as it's written into a definite pattern (for instance, 100 strings<br />
alternating with 100 numerals), <strong>the</strong>n read back using <strong>the</strong> same pattern, so no prob<br />
lems should arise. You could also write an end-of-file marker of your own (such<br />
as "****") which can be checked on read-back.<br />
ST will become 4 or 8 if a program is mistakenly read as a file. <strong>The</strong> errors mean<br />
that <strong>the</strong> program is ei<strong>the</strong>r too short or too long to fit <strong>the</strong> buffer.<br />
Loading and Saving Machine Language<br />
Programs in machine language are unlike BASIC in that <strong>the</strong>y need to be positioned<br />
in a fixed place in memory. O<strong>the</strong>rwise, <strong>the</strong>y generally won't work. <strong>The</strong> same applies<br />
to character definitions and screen memory; usually, it's easiest to keep <strong>the</strong>se at fixed<br />
470
Tape Storage<br />
locations. All <strong>the</strong>se examples occupy continuous chunks of RAM, so LOAD and<br />
SAVE can be used. Data files aren't necessary. BLOCK LOAD and BLOCK SAVE,<br />
discussed in Chapter 6, provide methods (with examples) for doing this reliably.<br />
Forced-LOAD addresses. If memory is saved with <strong>the</strong> forced-LOAD parameter,<br />
for example by SAVE "GRAPHICS",!,!, <strong>the</strong>n LOAD will always position GRAPH<br />
ICS back in <strong>the</strong> area it was saved from. SAVE "GRAPHICS" allows repositioning;<br />
LOAD will now load starting at <strong>the</strong> BASIC pointer area. But LOAD "GRAPH<br />
ICS", 1,1 forces a LOAD back into <strong>the</strong> original area. Thus, it is SAVE which deter<br />
mines whe<strong>the</strong>r LOAD always puts data back where it came from.<br />
<strong>The</strong>refore, when saving ML, it is usual to insure a forced LOAD by using <strong>the</strong><br />
syntax SAVE "ML",1,1. Loading into an ML area changes BASIC pointers; use <strong>the</strong><br />
BASIC command (not <strong>the</strong> disk command) NEW to set <strong>the</strong>m back to normal.<br />
A more easily understood method to save blocks of data is simply to PEEK in<br />
dividual bytes and write <strong>the</strong>m as files. Reconstructing <strong>the</strong> data requires <strong>the</strong> reverse<br />
process of reading back <strong>the</strong> file and POKEing individual bytes to RAM. This methoc}<br />
is slower than a BLOCK SAVE, though. Programs 14-2 and 14-3 illustrate <strong>the</strong><br />
method, applied to bytes from $8000 to $9FFF. O<strong>the</strong>r address ranges can of course<br />
be used instead:<br />
Program 14-2. Writing Bytes to Tape<br />
10 OPEN 1,1,1,"ROM AREA FILE"<br />
20 FOR J=8*4096 TO 10*4096-1<br />
30 P=PEEK(J)<br />
40 PRINT#1,CHR$(P);:REM ; IS ESSENTIAL<br />
50 NEXT<br />
60 CLOSE 1<br />
Program 14-3. Reading Bytes from Tape<br />
10 OPEN 1<br />
20 FOR J=8*4096 TO 10*4096-1<br />
30 GET#1,P$<br />
40 POKE J,ASC(P$+CHR$(0))<br />
50 NEXT<br />
60 CLOSE 1<br />
Tape Hardware Notes<br />
Many <strong>64</strong> owners also have a <strong>Commodore</strong> C2N Datassette recorder (also marketed<br />
with <strong>the</strong> model number VIC-1530). <strong>The</strong>se units have undergone several redesigns,<br />
both externally and internally. But all of <strong>the</strong>m, from <strong>the</strong> early black PET/CBM mod<br />
els to <strong>the</strong> newer compact rounded version, are compatible in most programming<br />
situations.<br />
<strong>The</strong> compact C2N has a tape counter and a SAVE light (lit when recording onto<br />
tape). It also has a braided ground strap on its connector, which is not used with <strong>the</strong><br />
<strong>64</strong>. Pressing RECORD also presses PLAY; earlier models have <strong>the</strong> standard security<br />
feature of requiring two separate RECORD and PLAY keys to be pressed<br />
independently.<br />
471
Tape Storage<br />
<strong>The</strong> C2N takes its power from <strong>the</strong> <strong>64</strong> through <strong>the</strong> same connector that handles<br />
data transfer, although it could be powered separately with a small modification, <strong>The</strong><br />
connector can be plugged in only one way; most recorders cannot be connected in<br />
correctly. Cable lengths vary between models but are generally adequate.<br />
C2Ns use an ordinary 1-7/8-inch-per-second tape transport mechanism, plus<br />
additional circuitry to control <strong>the</strong> <strong>64</strong> specific features like keypress detection. All tape<br />
recorders use similar principles: <strong>The</strong> C2N has an erase head, to remove signals (if<br />
any) from tape, followed by <strong>the</strong> record head, which records vertical magnetic stripes<br />
on <strong>the</strong> tape. On playback, <strong>the</strong> same head acts in reverse to play back <strong>the</strong> signals on<br />
<strong>the</strong> tape, generating induced voltage when <strong>the</strong> tape is drawn past <strong>the</strong> head.<br />
<strong>The</strong> <strong>64</strong> uses a square-wave system, alternately changing <strong>the</strong> direction of mag<br />
netization. Square waves are relatively difficult to copy with ordinary audio equip<br />
ment, which tends to round <strong>the</strong>m off, and this provides some protection against<br />
unauthorized tape copying. However, commercial tape duplication is done by<br />
recording on tape from an original with equipment designed to preserve <strong>the</strong> original<br />
signal shapes.<br />
<strong>The</strong> C2N's record/playback head is mounted so that its angle to <strong>the</strong> tape is vari<br />
able, although it's not usually advisable to alter it. However, if <strong>the</strong> angle isn't<br />
reasonably perpendicular, read errors are possible with tapes made on o<strong>the</strong>r record<br />
ers. <strong>The</strong> newest C2Ns have a small hole through which <strong>the</strong> relevant screw can be<br />
turned with a plastic screwdriver.<br />
A more common source of problems is a magnetized head. Demagnetizers are<br />
simple coils which use alternating house current to magnetize <strong>the</strong> heads alternately<br />
in opposite directions; as <strong>the</strong> demagnetizer is moved away, <strong>the</strong> inverse square law<br />
insures that <strong>the</strong> remaining magnetism is minimal. Tapes played with magnetized<br />
heads may be partly erased. If your recorder doesn't read tapes which it should be<br />
able to read, demagnetize it immediately.<br />
<strong>The</strong> capstan is <strong>the</strong> metal spike which drives <strong>the</strong> tape at a fairly constant speed.<br />
Tape is trapped between it and a hard rubber pinch wheel when reading or writing.<br />
It's best not to leave <strong>the</strong> PLAY key pressed with <strong>the</strong> recorder off, or <strong>the</strong> tape may<br />
become dented by <strong>the</strong> capstan and give irregular playback.<br />
When rewinding or running fast forward, <strong>the</strong> pinch wheel is disengaged and<br />
one of <strong>the</strong> spools is driven directly. When playing at normal speed, <strong>the</strong> right-hand<br />
spool is kept under tension so <strong>the</strong> tape is wound tightly.<br />
<strong>The</strong> tape counter is connected by a belt to <strong>the</strong> right-hand spool. One turn of <strong>the</strong><br />
counter <strong>the</strong>refore indicates more tape when <strong>the</strong> right-hand spool is full than it does<br />
when <strong>the</strong> spool is nearly empty. Actual tape length is a quadratic expression of <strong>the</strong><br />
counter reading, so <strong>the</strong> counter readings corresponding to programs of equal length<br />
on <strong>the</strong> same tape show progressively smaller differences.<br />
Routine recommended maintenance involves cleaning <strong>the</strong> heads, typically with<br />
a cleaning kit consisting of cotton swabs and cleaner. <strong>The</strong> cleaner is a liquid like isopropyl<br />
alcohol, never a plastic solvent like trichloroethane.<br />
<strong>The</strong> best type of tape is ordinary ferric oxide (not chromium) tape of reasonable<br />
quality. A screw-type cassette casing is preferable, since it can be taken apart if <strong>the</strong><br />
tape gets tangled. Very long tapes are good for storage, but shorter tapes, perhaps<br />
with only one program each, save search time. It's not really possible to test tapes;<br />
this is far too time-consuming.<br />
472
Tape Storage<br />
All cassettes have write-protect tabs at <strong>the</strong> back left of <strong>the</strong> cassette case, one tab<br />
for each side of <strong>the</strong> tape. If <strong>the</strong>se tabs are removed, <strong>the</strong> recorder won't save to that<br />
tape, so much commercial software is packaged in cassettes like this. Put a piece of<br />
masking tape over <strong>the</strong> gap if you wish to record over a protected tape.<br />
Since both sides of <strong>the</strong> tape are usable, only half <strong>the</strong> width (1/16 inch) is used<br />
at one time. Thin tape is prone to problems with print-through: in fact, a tightly<br />
wound spool left for some time may degrade as magnetism is transferred between<br />
adjacent turns of tape. However, even short tapes may be thin, and thus prone to<br />
this problem; <strong>the</strong>re's no easy way to be sure which tapes may have trouble and<br />
which will not.<br />
Most tapes start with a nonmagnetic leader to take <strong>the</strong> strain at <strong>the</strong> end of fast<br />
winding. <strong>The</strong> tape operating system allows for this, with seven or eight seconds of<br />
tone before actual recording proper starts, but you may prefer to manually wind for<br />
ward so all recording begins on <strong>the</strong> magnetic part of tape. Ano<strong>the</strong>r tip: Rewind<br />
brand-new tapes before recording on <strong>the</strong>m. High-speed manufacturing equipment<br />
stretches tape to some extent; by rewinding <strong>the</strong> tape first, you relieve <strong>the</strong> stretch and<br />
make <strong>the</strong> tape more stable.<br />
Non-<strong>Commodore</strong> Tape Hardware<br />
You can connect an ordinary tape recorder to your <strong>64</strong>, but it is not particularly easy<br />
to do. <strong>Commodore</strong> claims several advantages for its dedicated tape system: <strong>The</strong>re are<br />
no problems with recording levels, automatic or o<strong>the</strong>rwise, or with tone controls and<br />
o<strong>the</strong>r potential incompatibilities; control over motor stop/start makes file handling<br />
possible; and <strong>the</strong> system is monaural, using a full 1/16-inch of tape ra<strong>the</strong>r than <strong>the</strong><br />
1/32-inch tape used on stereo recorders.<br />
If building an interface to drive an ordinary tape recorder seems like too much<br />
trouble, you might experiment with connecting <strong>the</strong> tape write line (next to <strong>the</strong> righthand<br />
pin of <strong>the</strong> cassette port, looking from <strong>the</strong> <strong>64</strong>'s back) to <strong>the</strong> microphone socket,<br />
and <strong>the</strong> read line (third pin from <strong>the</strong> right—next to write—looking from <strong>the</strong> back) to<br />
<strong>the</strong> earphone socket of an ordinary recorder. Remember to connect <strong>the</strong> common, or<br />
ground, connection, too.<br />
It's actually possible to interface two (or more) recorders to <strong>the</strong> <strong>64</strong>, with <strong>the</strong><br />
possibility of file merges and updates.<br />
Tape Operating Systems<br />
Alternative ROM or RAM operating systems have been designed and are commer<br />
cially available. Rabbit and Arrow are two of <strong>the</strong>m; each is far faster (by about six<br />
times) than <strong>the</strong> <strong>64</strong>'s tape system. Each has commands to LOAD, SAVE, and VERIFY,<br />
and each has a BLOCK SAVE command. Syntax is typically something like *S<br />
"PROGRAM" or *S "SCREEN",lE00,2000.<br />
<strong>The</strong> speed improvement with <strong>the</strong>se systems is enormous; even 8K programs<br />
load in only about 25 seconds. This is not so far removed from disk speeds. But<br />
tapes recorded with <strong>the</strong>se systems aren't compatible with ordinary programs. In spite<br />
of <strong>the</strong> attractive speed performance, little software is written for <strong>the</strong>m.<br />
473
Tape Storage<br />
<strong>Programming</strong> <strong>the</strong> Recorder<br />
<strong>The</strong> tape port pinout is diagrammed in Figure 14-1. Pin D is connected to CIA 1.<br />
Pins E and F are connected to <strong>the</strong> 6510's built-in I/O port. Pin C (power for <strong>the</strong> mo<br />
tor) is also controlled by <strong>the</strong> 6510 I/O port.<br />
Figure<br />
14-1.<strong>64</strong><br />
Tape Port<br />
Pin<br />
A<br />
B<br />
C<br />
D<br />
E<br />
F<br />
Function<br />
Ground<br />
+5 Volts Motor On/Off<br />
Read<br />
Write<br />
Cassette Key On/Off<br />
<strong>The</strong> tape port is programmable. It is controlled by bit 5 of port 1; when this is 0,<br />
<strong>the</strong> motor is on (9 volts are delivered). <strong>The</strong>re's a complication in that <strong>the</strong> keyboard<br />
interrupt servicing checks for a cassette keypress; if it finds one, it turns <strong>the</strong> motor<br />
on and o<strong>the</strong>rwise it turns <strong>the</strong> motor off. However, this feature can be disabled. In<br />
fact, it has to be, to allow files to work properly.<br />
Provided a cassette key is pressed, POKE 192,1: POKE 1, PEEK(l) OR 32 turns<br />
<strong>the</strong> motor off, and POKE 1, PEEK(l) AND 31 turns <strong>the</strong> motor on. When a cassette<br />
key isn't pressed, <strong>the</strong> interrupt always sets 192 to 0.<br />
<strong>The</strong> cassette keypress can be detected by testing location 1. If you PEEK(l) AND<br />
16 and <strong>the</strong> value returned is 0, a key is pressed; if PEEK(l) AND 16=1, a key isn't<br />
pressed. It follows tHat WAIT 1,16 waits until no cassette key is detected, and WAIT<br />
1,16,16 waits for a cassette keypress. In ML programming use JSR $F82E followed<br />
by BNE if you want to detect if a key is pressed.<br />
As Chapter 3 mentions, an accidental POKE can alter location 0, perhaps<br />
reconfiguring <strong>the</strong> data direction. Bit 3 is <strong>the</strong> tape write line; if it is set for input,<br />
SAVE won't work. RUN/STOP-RESTORE (or POKE 0,57) returns to normal.<br />
Pin B of <strong>the</strong> cassette port is sometimes used to power equipment, such as lowpower<br />
amplifiers and printers. This is a 5-volt power source.<br />
Advanced Tape <strong>Programming</strong><br />
In this section you'll see how programs and files are stored on tape and how you<br />
can manipulate <strong>the</strong>m. You'll see how <strong>the</strong> headers and <strong>the</strong>ir programs or files are<br />
programmable independently, which means that you will be able to write tape pro<br />
grams which can load anywhere.<br />
Storage at Bit Level<br />
<strong>The</strong> <strong>64</strong>'s tape system uses three separate square wave frequencies; <strong>the</strong> actual values<br />
vary internationally. If you call <strong>the</strong>m long, medium, and short (L, M, and S), <strong>the</strong>n<br />
each byte is made of patterns of L, M, and S. Bit value 0 is represented as SSMM; bit<br />
1 as MMSS. An odd-parity bit is added as an internal check (<strong>the</strong> total of l's is made<br />
odd). LLMM marks <strong>the</strong> start of a byte. <strong>The</strong> system also has a standard tone which is<br />
used to allow for differences in tape motors.<br />
Storage off<br />
Programs on Tape<br />
Try recording a short program on tape and replaying it through an ordinary recorder.<br />
You will hear several seconds of a constant tone, <strong>the</strong>n about four seconds of header,<br />
474
Tape Storage<br />
<strong>the</strong>n two seconds of tone, <strong>the</strong>n <strong>the</strong> program. <strong>The</strong> header and <strong>the</strong> program are each<br />
written twice; you'll hear a short pause midway in each. Any program records or<br />
loads in about 15 seconds, plus 15 seconds per K (kilobyte).<br />
Storage of Files on Tape<br />
Data files are stored on tape as a sequence of fixed-length buffers. <strong>The</strong>re are two rea<br />
sons why files are slower than programs: One is <strong>the</strong> extra time spent writing or read<br />
ing <strong>the</strong> tones; <strong>the</strong> o<strong>the</strong>r is <strong>the</strong> extra time spent starting <strong>the</strong> cassette motor to read<br />
each buffer. If BASIC does <strong>the</strong> reading or writing, that slows things, too.<br />
Error Correction<br />
As data is read, errors in <strong>the</strong> first copy of <strong>the</strong> recording are noted (and corrected, if<br />
possible, by reading <strong>the</strong> second copy). Only 30 errors are allowed; <strong>the</strong>se are logged<br />
in RAM at $0100-$013D (at <strong>the</strong> bottom of <strong>the</strong> stack area). PEEK(159) gives a count<br />
of <strong>the</strong> errors after a full read; this should be zero. Small ML routines to be put in <strong>the</strong><br />
stack area are best started after $013D if tape is to be used.<br />
Headers in Detail<br />
Tape storage relies on headers; if you understand <strong>the</strong>m, you understand most of<br />
what you need to program tape.<br />
<strong>The</strong>re are five types of headers, as diagrammed in Figure 14-2. Only <strong>the</strong> first 21<br />
bytes are normally used, unless you wish to add ML or program protection.<br />
<strong>The</strong> tape buffer, which holds headers and file data, normally extends from<br />
$033C to $03FB (828-1019), a total of 192 bytes. You can change <strong>the</strong> location of <strong>the</strong><br />
buffer by POKEing into $B2 and $B3 (178 and 179). As it happens, OPEN 1 accepts<br />
any of <strong>the</strong>se header types, not just files, and provides a simple way to look at<br />
buffers.<br />
Put a <strong>64</strong> program tape in <strong>the</strong> recorder, type OPEN 1, and press RETURN. <strong>The</strong>n,<br />
when <strong>the</strong> header is found, type FOR J=828 TO 850: PRINT PEEK(J);: NEXT. Now,<br />
<strong>the</strong> first byte is 1-5, <strong>the</strong> second and third bytes are <strong>the</strong> start address, <strong>the</strong> fourth and<br />
fifth are <strong>the</strong> end address, and <strong>the</strong> following bytes are <strong>the</strong> name.<br />
Using SYS 63553 in place of OPEN 1 loads <strong>the</strong> first 192 bytes of any tape data<br />
into <strong>the</strong> buffer, so use this if you want a tape directory which allows you to examine<br />
<strong>the</strong> start of ML or BASIC programs, or read <strong>the</strong> whole of data files.<br />
Tape Directory<br />
<strong>The</strong> BASIC program below, Program 14-4, will identify and list programs and files<br />
on tape. It's easily modified if you wish. Note its use of SYS 63553. This is part of<br />
OPEN, but doesn't skip blocks starting with bytes o<strong>the</strong>r than 1, 3, 4, or 5. <strong>The</strong> pro<br />
gram uses PRINT statements to display data on <strong>the</strong> screen; if <strong>the</strong> data contains con<br />
trol characters like 147 (clear screen) or 13 (carriage return), <strong>the</strong> display will be<br />
altered accordingly.<br />
475
Tape Storage<br />
Figure 14-2. Types of Headers<br />
1<br />
Start<br />
Address<br />
End<br />
Address<br />
Name<br />
Program Header—Relocatable<br />
3<br />
Start<br />
Address<br />
End<br />
Address<br />
Name<br />
Program Header—Forced LOAD Address<br />
4<br />
Start<br />
Address<br />
End<br />
Address<br />
Name<br />
Data File Header<br />
DATA<br />
Data Buffer<br />
5<br />
Start<br />
Address<br />
End<br />
Address<br />
Name<br />
End of Tape<br />
Program 14-4. Tape Directory<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader/' Appendix C.<br />
10 SYS 63553:PRINT"{WHT}" :rem 118<br />
20 P=PEEK(828) :rem 7<br />
30 ON P GOTO 100,200,300,400,500 :rem 24<br />
40 PRINT "PROGRAM OR ML BLOCK":GOTO 3000 :rem 40<br />
99 REM TYPE 1 srem 201<br />
100 PRINT "RELOCATABLE PROGRAM "; :rem 211<br />
110 GOSUB 1000:GOSUB 2000:GOTO 4000 :rem 134<br />
199 REM TYPE 2 srem 251<br />
200 PRINT "BUFFER OF DATA ":GOTO 3020 :rem 4<br />
299 REM TYPE 3 :rem 253<br />
300 PRINT "FORCED-LOAD PROGRAM ";:GOTO 110:rem 188<br />
399 REM TYPE 4 :rem 255<br />
400 PRINT "DATA FILE ";:GOSUB 1000:GOTO 4000<br />
srem 140<br />
499 REM TYPE 5 :rem 1<br />
500 PRINT" END-OF-TAPE MARKER";:GOTO 4000 :rem 138<br />
999 REM PRINT NAME FROM HEADER :rem 26<br />
1000 PRINT CHR$(34);:FOR J=833 TO 848:PRINT CHR$(P<br />
EEK(J)); srem 227<br />
1010 NEXT:PRINT CHR$(34):RETURN :rem 155<br />
476
Tape Storage<br />
1999 REM PRINT START AND END ADDRESSES :rem 35<br />
2000 PRINT "{2 SPACES}START=M PEEK(829) + 256*PEEK<br />
(830) :rem 122<br />
2010 PRINT "{4 SPACES}END=" PEEK(831) + 256*PEEK(8<br />
32) :rem 191<br />
2020 RETURN :rem 1<strong>64</strong><br />
2999 REM PRINT 192 BYTES OF DATA IF REQUIRED<br />
:rem 16<br />
3000 INPUT "{CYNjVIEW THE PROGRAM";YN$ :rem 112<br />
3010 IF YN$="N" GOTO 4000 :rem 234<br />
3020 PRINT "{WHT}" CHR$(34);: FOR J=828 TO 1019: P<br />
RINT CHR$(PEEK(J));: NEXT :rem 212<br />
3999 REM AWAIT KEYPRESS :rem 174<br />
4000 PRINT: PRINT "{CYN}PRESS G TO CONTINUE11<br />
:rem 211<br />
4010 GET X$:IF X$=MC" THEN RUN :rem 78<br />
4020 GOTO 4010 :rem 196<br />
Ano<strong>the</strong>r way to inspect header storage is POKE 178,0: POKE 179,4 to move <strong>the</strong><br />
start-of-buffer to <strong>the</strong> start-of-screen. <strong>The</strong>n, OPEN 1 (for data and programs, SYS<br />
63553) will load 192 bytes directly into <strong>the</strong> screen. FORJ=55296 TO 55296+192:<br />
POKEJ,1: NEXT will make all <strong>the</strong> bytes visible in white. Remember to POKE 178,60:<br />
POKE 179,3 or reset with SYS <strong>64</strong>738 to return <strong>the</strong> buffer to normal.<br />
To summarize, executing ei<strong>the</strong>r a LOAD or an OPEN will cause 192 bytes to be<br />
read from whatever block is next on tape. If <strong>the</strong> first byte is 1 or 3, a program header<br />
is assumed found; if 2 or 4, a data file; and if 5, an end-of-tape marker. If BASIC or<br />
ML programs happen to start with a byte in <strong>the</strong> range 1-5, <strong>the</strong>y'll give a spurious<br />
appearance as a header, data file, or end-of-tape marker. This can happen if LOAD<br />
or OPEN misses <strong>the</strong> header. In fact, this isn't likely to occur, since it's impossible for<br />
normal BASIC'S first byte, which is part of <strong>the</strong> link address of <strong>the</strong> first line, to be less<br />
than 7, and <strong>the</strong> only ML commands in <strong>the</strong> range 1-5 are uncommon ORAs. Note<br />
that 0 isn't used as a marker because <strong>the</strong> earliest PETs saved <strong>the</strong> very first null byte<br />
as part of BASIC, so <strong>the</strong> headers, to avoid confusion, started with 1.<br />
Consequences of This Method of Storage<br />
Programs can be made to load into any area, even places normally impossible to<br />
load into, if <strong>the</strong> header is altered. <strong>The</strong> header itself can be used to store ML pro<br />
grams. However, note that saving ML starting at $033C with a monitor can't work,<br />
because <strong>the</strong> program will be overwritten by its header before it can be saved.<br />
Since a program's start and end are defined, <strong>the</strong>re's no need for an end-ofprogram<br />
identifier. However, files are saved as chunks, and <strong>the</strong> last chunk written<br />
(on CLOSEing <strong>the</strong> file) has a zero inserted after <strong>the</strong> data. This zero byte causes<br />
ST=<strong>64</strong> to be set if INPUT# reads <strong>the</strong> file back. <strong>The</strong> start and end addresses with file<br />
data are simply <strong>the</strong> start and end addresses of <strong>the</strong> buffer. Because "2" identifies a<br />
buffer of data, only 191 bytes are actually storable in <strong>the</strong> buffer.<br />
When files are written or read, <strong>the</strong>re's a pause between blocks; in this short<br />
interval, <strong>the</strong> VIC-II chip is reenabled and simultaneously <strong>the</strong> red LED on <strong>the</strong> recorder<br />
(if it has one) goes out. This periodic flashing at six-second intervals is typical<br />
477
Tape Storage<br />
of <strong>64</strong> files. (Programs have a single short screen enable just after <strong>the</strong> first copy is<br />
written or read.)<br />
Several tricks to make programs harder to copy are explained at <strong>the</strong> end of this<br />
chapter.<br />
ML Routines to Save, Load, and Run Tape Programs<br />
ML programmers may want to do <strong>the</strong> equivalent of LOAD and SAVE. Conversely,<br />
programmers might want to decipher LOAD and SAVE instructions in ML programs.<br />
<strong>The</strong>re are too many locations and subroutines for exhaustive listing here, but many<br />
of <strong>the</strong> most common can be outlined.<br />
LOAD'S ROM entry address is $E168. It uses <strong>the</strong> Kernal LOAD routine at<br />
$FFD5, which jumps to $F49E. All <strong>the</strong> parameters are set, and several branches test<br />
for <strong>the</strong> device number of 1; tape LOAD itself is at $F539. Normally, all programs<br />
have a header, and $F7EA finds a named header, while $F72C finds <strong>the</strong> next header<br />
of any kind. <strong>The</strong> routine at $F5D2 reads <strong>the</strong> program itself from tape into <strong>the</strong> correct<br />
part of RAM.<br />
A typical loader designed to run an ML program follows:<br />
LDA #$01<br />
TAX<br />
TAY<br />
JSR $FFBA ; File number, device, secondary address all 1<br />
LDA #$00<br />
JSR $FFBD ; Filename irrelevant—load next tape program<br />
JSR $FFD5 ; Forced LOAD to stored address<br />
JMP $1000 ; Or o<strong>the</strong>r start address of ML to be loaded<br />
This loads whatever program it finds next on tape with a forced LOAD, <strong>the</strong>n<br />
jumps to address $1000.<br />
SAVE's ROM entry point is $E156; its Kernal routine is $FFD8, which jumps to<br />
$F5DD. Tape saving is handled from $F65F; $F76A writes <strong>the</strong> header and $F867 <strong>the</strong><br />
program.<br />
ML saving might look like this:<br />
LDA #$01<br />
STA $BA ; Device 1 (tape)<br />
STA $B9 ; Secondary address = 1 (forced LOAD in header)<br />
LDX #$00<br />
LDY #$20 ; End address is $2000 here<br />
LDA $FB ; Start address presumed in ($FB)<br />
JSR $F5DD ;Save<br />
All conventional LOAD and SAVE routines use both a header and its sub<br />
sequent program. Before seeing how to operate <strong>the</strong>se separately, however, note <strong>the</strong><br />
useful RAM locations in Table 14-3.<br />
478
Tape Storage<br />
Table 14-3. Useful RAM Locations<br />
$90<br />
$93<br />
$9F<br />
$AB<br />
$AE/AF<br />
$B2/B3<br />
$B7<br />
$B8<br />
$B9<br />
$BA<br />
$BB/BC<br />
144<br />
ST status<br />
147<br />
Load/Verify flag (0=load, 1=verify)<br />
159<br />
Error log<br />
171<br />
Length of tone written to tape<br />
174/175 End address for saving<br />
178/179 Start of tape buffer<br />
183 Length of program name<br />
184 Current file number<br />
185 Secondary address parameter<br />
186 Device number (1=tape)<br />
187/188 Start address of program name<br />
Loading Tape Data Anywhere in<br />
RAM<br />
Program 14-5 first loads <strong>the</strong> header, <strong>the</strong>n loads <strong>the</strong> remaining program indepen<br />
dently to start at any new address you choose.<br />
Program 14-5. Load Anywhere<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
2 REM LINE 20 CONTROLS NEW START; EXAMPLE IS 1024<br />
:rem 54<br />
10 OPEN 1:REM LOADS HEADER (OR SYS 63276) :rem 247<br />
20 S=1024:REM START ADDRESS IS SCREEN :rem 199<br />
30 L=PEEK(831)-PEEK(829) + 256*(PEEK(832)-PEEK(830<br />
)) :rem 216<br />
40 E=S+L:REM COMPUTE NEW END ADDRESS FROM LENGTH<br />
:rem 168<br />
50 POKE 830,S/256:POKE 829,S-INT(S/256)*256:REM S<br />
{SPACE}IN HEADER :rem 5<br />
60 POKE 832#E/256: POKE 831,E-INT(E/256)*256:REM E<br />
IN HEADER :rem 201<br />
70 POKE 781,3:REM FOR FORCED LOAD :rem 109<br />
80 SYS 62820:REM LOAD REST OF PROGRAM :rem 146<br />
Programmers using ML monitors can use <strong>the</strong> following ML routine to load a<br />
block into RAM.<br />
LDA<br />
STA<br />
LDA<br />
STA<br />
LDA<br />
STA<br />
LDA<br />
STA<br />
LDA<br />
STA<br />
JSR<br />
6RK<br />
#$00<br />
$93<br />
#$20<br />
$C2<br />
#$00<br />
$C1<br />
#$30<br />
#|oo<br />
$AE<br />
$F5A2<br />
;LOAD, not SAVE<br />
;Example start address<br />
;is $2000, and<br />
;example end address<br />
;is $3000<br />
;or $F5A5 omitting <strong>the</strong> loading message<br />
479
Tape Storage<br />
Writing Tape Data Anywhere from RAM<br />
This is best done with ML, using a routine like that to load tape blocks. Program 14-<br />
6 uses a good method:<br />
Program 14-6. Save Anywhere<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
100 FOR J=320 TO 356:READ X:POKE J,X:NEXT :rem 51<br />
110 FOR J=828 TO 1018:POKE J,32:NEXT :rem 204<br />
200 PRINT "WRITE HEADER/FILE TO TAPE; OR EXIT"<br />
:rem 201<br />
210 INPUT "(H/F/X)11; YN$ :rem 2<br />
220 IF YN$=MX" THEN END :rem 205<br />
230 IF YN$="F" GOTO 500 :rem 132<br />
299 REM THIS WRITES A HEADER ONLY TO TAPE :rem 151<br />
300 PRINT "ENTER FIRST 5 PARAMETERS OF HEADER"<br />
:rem 209<br />
310 PRINT " EXAMPLE: 3,0,192,0,208 MEAN" :rem 69<br />
320 PRINT " FORCED PROGRAM LOAD FROM C000-D000"<br />
:rem 89<br />
330 INPUT A,B,C,D,E :rem 37<br />
340 POKE 828,A:POKE 829,B:POKE 830,C:POKE 831,D:PO<br />
KE 832,E :rem 174<br />
350 PRINT "PROGRAM NAME TO BE PUT IN HEADER":rem 5<br />
360 INPUT N$ :rem 155<br />
370 FOR J=l TO LEN(N$):POKE 832+J,ASC(MID$(N$,J)):<br />
NEXT :rem 253<br />
380 SYS 320:REM WRITE HEADER TO TAPE :rem 78<br />
390 RUN :rem 145<br />
499 REM THIS WRITES ANY BLOCK OF MEMORY TO TAPE<br />
:rem 46<br />
500 INPUT "START ADDRESS OF BLOCK TO BE SAVED";S<br />
:rem 40<br />
510 INPUT "{2 SPACES}END ADDRESS OF BLOCK TO BE SA<br />
VED";E srem 100<br />
520 POKE 321,20 :rem 234<br />
530 POKE 325,S/256:POKE 329,S-INT(S/256)*256<br />
:rem 129<br />
540 POKE 333,E/256:POKE 337,E-INT(E/256)*256<br />
:rem 86<br />
550 SYS 320:REM WRITE BLOCK TO TAPE :rem 15<br />
560 RUN :rem 144<br />
996 REM FOLLOWING ML IS SET UP FOR HEADER :rem 147<br />
997 REM LINE 1000•S 105 = LENGTH OF TONE; :rem 139<br />
998 REM LINE 1000'S 3 AND 60 SET START=$033C;<br />
:rem 76<br />
999 REM LINE 1010'S 3 AND 252 SET END =$03FC.<br />
:rem 208<br />
1000 DATA 169,105,133,171,169,3,133,194,169,60,133<br />
'193 :rem 103<br />
480
Tape Storage<br />
1010 DATA 169,3,133,175,169,252,133,174,169,1,133,<br />
184,133 :rem 251<br />
1020 DATA 186,169,0,133,183,169,255,133,185,76,107<br />
,248 :rem 119<br />
<strong>The</strong> ML routine contains five parameters which control <strong>the</strong> header tone. Any<br />
user-defined header can be written to tape, for example, to cause a forced LOAD<br />
into a normally difficult area of memory. And any consecutive block of bytes can be<br />
written to tape, allowing great flexibility in program construction.<br />
Copy Protection for Tape<br />
Security is an interesting aspect of tape programs. Before taking it too seriously, it's<br />
worth remembering that it may be possible to copy tapes by audio means; you<br />
should also keep in mind <strong>the</strong> fact that a number of commercial tape software houses<br />
believe that determined copiers will copy anyway and so don't put in protection.<br />
However, <strong>the</strong>re are opposing views.<br />
This section will survey some methods of complicating copying without arguing<br />
<strong>the</strong> pros and cons.<br />
Using <strong>the</strong> Header<br />
Because SAVE erases most of <strong>the</strong> header, a program which relies on information<br />
stored after 16 bytes of name is less easy to copy. For example, if your BASIC pro<br />
gram is "FRENCH LESSONS", save it as "FRENCH LESSONS [2 spaces]" +<br />
CHR$(96). This puts an extra ML instruction after <strong>the</strong> name. SYS 849 from within<br />
BASIC returns, but if 96 is missing, <strong>the</strong> program crashes. However, it is relatively<br />
easy to allow for this by simply POKEing 849 with 96.<br />
This is only a very simple example. <strong>The</strong> entire header can be filled with ML<br />
routines, which could modify BASIC, load new programs or data, or whatever.<br />
Also at <strong>the</strong> simple level, <strong>the</strong> SYS call can be concealed in a line erased by REM,<br />
followed by deletes. It can also be disguised—for example, as SYS 84923—by insert<br />
ing a zero byte after <strong>the</strong> 9 which won't list. <strong>The</strong> program's name can include control<br />
characters that clear <strong>the</strong> screen or change <strong>the</strong> cursor color, or it could even be some<br />
thing like ?LOAD ERROR. All that's needed is SAVE "NAME" + CHR$(147) +<br />
"ERROR" + CHR$(31) or o<strong>the</strong>r analogous strings.<br />
Using <strong>the</strong> Screen Positions<br />
ML programs are sometimes designed to load into <strong>the</strong> default screen position set by<br />
<strong>the</strong> <strong>64</strong>. An ML jump to $0400, for instance, makes <strong>the</strong> program impossible to stop in<br />
<strong>the</strong> usual way, as it will be corrupted or entirely erased by pressing RUN/STOP-<br />
RESTORE. This type of program can be developed ei<strong>the</strong>r by moving <strong>the</strong> screen to a<br />
nonstandard position or by writing <strong>the</strong> header separately from <strong>the</strong> program.<br />
Using Headerless Programs<br />
In BASIC or ML, you can load program data without a header. Such data can t be<br />
picked up by a normal LOAD and isn't copyable by a simple LOAD and SAVE. Of<br />
course, it must be written as a single chunk with <strong>the</strong> help of a tape write routine.<br />
481
Tape Storage<br />
Programs Which Automatically Run When Loaded<br />
Several techniques can be used to make programs run automatically, but all require<br />
some ML expertise on <strong>the</strong> part of <strong>the</strong> programmer. Figure 14-3 shows free RAM and<br />
key locations that are important for tape protection; three techniques can be used.<br />
Figure 14-3. Key Locations for Tape Protection<br />
$100 $200 |$300 $400<br />
Zero<br />
Page<br />
Stack<br />
Input<br />
Buffer<br />
Tape<br />
Buffer<br />
Number of Characters in Keyboard Buffer ($C6)<br />
Keyboard Buffer ($0277-0280)<br />
Tape IRQ SAVE ($029F)<br />
BASIC Start Vector ($0302)<br />
BRK Vector ($0316)<br />
NMI Vector ($0318)<br />
Input Vector ($0324)<br />
Free RAM: 4 bytes $FB-$FE<br />
89 bytes $02A7-$02FF<br />
8 bytes $0334-$033B<br />
4 bytes $03FC-$03FF<br />
RAM (with care): Tape Buffer, 192 bytes, $033C-$03FB<br />
Lower part of stack from $0100 or $013E if cautious about tape reading.<br />
BASIC warm start vector in ($0302). Normally $A483, this can be directed<br />
ei<strong>the</strong>r into <strong>the</strong> header or into <strong>the</strong> loaded program, perhaps spanning $02A7-$0303.<br />
<strong>The</strong>n ML LOAD and RUN will automatically run <strong>the</strong> BASIC program that comes<br />
afterward.<br />
Input vector in ($0324). Again, a loader might span $02A7-$0325, so <strong>the</strong> al<br />
tered input vector might jump to $02A7.<br />
Tape interrupt vector at ($029F). This vector is difficult to use on <strong>the</strong> <strong>64</strong>, since<br />
it is followed by operating system variables.<br />
A BASIC Autoloader<br />
<strong>The</strong> autoload routine in Program 14-7 will run <strong>the</strong> BASIC program immediately<br />
following it on tape. All that's required is to enter LOAD. It disables RUN/STOP and<br />
RUN/STOP-RESTORE, and scrambles LIST, to give some program protection.<br />
First, add <strong>the</strong>se few program lines to <strong>the</strong> program which writes any data to tape.<br />
Run <strong>the</strong> program, with a rewound tape in <strong>the</strong> recorder, and prepare to write a<br />
header: <strong>the</strong> parameters to put in are 3, 167, 2, 4, and 3. (Forced LOAD into<br />
$02A7-$0304. <strong>The</strong> very last address is unused, so <strong>the</strong> ML will straddle $02A7<br />
through $0303.)<br />
482
Tape Storage<br />
Program 14-7. BASIC Autoloader<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader/' Appendix C.<br />
100 FOR J=320 TO 356:READ X:POKE J,X:NEXT :rem 51<br />
110 FOR J=828 TO 1018:POKE J,32:NEXT :rem 204<br />
200 PRINT "WRITE HEADER/FILE TO TAPE; OR EXIT"<br />
:rem 201<br />
210 INPUT "(H/F/X)"; YN$ :rem 2<br />
220 IF YN$="X" THEN END :rem 205<br />
230 IF YN$="F" GOTO 500 :rem 132<br />
299 REM THIS WRITES A HEADER ONLY TO TAPE :rem 151<br />
300 PRINT "ENTER FIRST 5 PARAMETERS OF HEADER"<br />
:rem 209<br />
310 PRINT " EXAMPLE: 3,0,192,0,208 MEAN" :rem 69<br />
320 PRINT " FORCED PROGRAM LOAD FROM C000-D000"<br />
:rem 89<br />
330 INPUT A,B,C,D,E :rem 37<br />
340 POKE 828,A:POKE 829,B:POKE 830,C:POKE 831,D:PO<br />
KE 832,E :rem 174<br />
350 PRINT "PROGRAM NAME TO BE PUT IN HEADER":rem 5<br />
360 INPUT N$ :rem 155<br />
370 FOR J=l TO LEN(N$):POKE 832+J,ASC(MID$(N$,J)):<br />
NEXT :rem 253<br />
380 SYS 320:REM WRITE HEADER TO TAPE :rem 78<br />
390 RUN :rem 145<br />
499 REM THIS WRITES ANY BLOCK OF MEMORY TO TAPE<br />
:rem 46<br />
500 INPUT "START ADDRESS OF BLOCK TO BE SAVED";S<br />
:rem 40<br />
510 INPUT "{2 SPACES}END ADDRESS OF BLOCK TO BE SA<br />
VED";E :rem 100<br />
520 POKE 321,20 :rem 234<br />
530 POKE 325,S/256:POKE 329,S-INT(S/256)*256<br />
:rem 129<br />
540 POKE 333,E/256:POKE 337,E-INT(E/256)*256<br />
:rem 86<br />
545 FOR J=828 TO 854:READ X:POKE J,X:NEXT :rem 80<br />
546 FOR J=855 TO 922:POKE J,PEEK(J-l49):NEXT<br />
:rem 210<br />
547 POKE 919,167:POKE 920,2 :rem 158<br />
550 SYS 320:REM WRITE BLOCK TO TAPE :rem 15<br />
560 RUN :rem 144<br />
996 REM FOLLOWING ML IS SET UP FOR HEADER :rem 147<br />
997 REM LINE 1000'S 105 = LENGTH OF TONE; :rem 139<br />
998 REM LINE 1000'S 3 AND 60 SET START=$033C;<br />
:rem 76<br />
999 REM LINE 1010'S 3 AND 252 SET END =$03FC.<br />
:rem 208<br />
1000 DATA 169,105,133,171,169,3,133,194,169,60,133<br />
,193 :rem 103<br />
1010 DATA 169,3,133,175,169,252,133,174,169,1,133,<br />
184,133 :rem 251<br />
483
Tape Storage<br />
1020 DATA 186,169,0,133,183,169,255,133,185,76,107<br />
,248 :rem 119<br />
2000 DATA 169,234,141,40,3,169,131,141,2 :rem 159<br />
2010 DATA 3,169,1<strong>64</strong>,141,3,3,169,131,141 :rem 114<br />
2020 DATA 119,2,169,1,133,198,108,0,160 :rem 117<br />
When <strong>the</strong> header is written, prepare to write data. Use a starting address of 828<br />
and an ending address of 921. A short ML program is put at <strong>the</strong> start of <strong>the</strong> buffer,<br />
which will be loaded and run at $02A7. It disables RUN/STOP, restores ($0302) to<br />
normal, puts #$83 (SHIFT-RUN/STOP) into <strong>the</strong> keyboard buffer, and executes a<br />
JMP ($A000). This is enough to force <strong>the</strong> next program to load, <strong>the</strong>n run. <strong>The</strong> final<br />
bytes of <strong>the</strong> buffer hold <strong>the</strong> modified address, $02A7, which will be force-loaded<br />
into ($0302) and hence start <strong>the</strong> whole process.<br />
Many tapes using this sort of copy protection load into <strong>the</strong> entire area of RAM<br />
from, for example, $0300 up. This makes loading times ra<strong>the</strong>r long, as <strong>the</strong> area from<br />
$0400 to $0FFF is usually wasted.<br />
<strong>The</strong>re is considerable scope for ingenuity in using encoding routines, nonstandard<br />
6510 instructions, programs without headers, and overlays. If <strong>the</strong><br />
RUN/STOP and RESTORE keys are disabled, a reset switch leaves most memory in<br />
tact but erases all of RAM from $0000 to $0400 except <strong>the</strong> stack ($0100-$01FF).<br />
Thus, if key parts of a program are left in this area, <strong>the</strong> result can be all but<br />
impenetrable.<br />
484
Chapter 15<br />
Using Disk<br />
Storage<br />
Introduction to Disk Storage<br />
Basic Disk Commands<br />
Handling Disk Files with BASIC<br />
Summary of Disk Commands and<br />
Messages<br />
<strong>Commodore</strong> Utility Programs<br />
Hardware Notes<br />
Disk Data Storage<br />
Machine Language Disk<br />
<strong>Programming</strong>
Chapter 15<br />
Using Disk Storage<br />
Disk storage is more expensive than tape, but it is also more versatile. It can be used<br />
to store a selection of programs for rapid loading, but it also gives you access to<br />
large amounts of data.<br />
This chapter begins with a discussion of straightforward disk commands and<br />
progresses through more advanced material. By <strong>the</strong> end of <strong>the</strong> chapter you'll be able<br />
to handle most disk programming tasks.<br />
Introduction to Disk Storage<br />
<strong>The</strong> <strong>Commodore</strong> <strong>64</strong>'s serial port (next to <strong>the</strong> video port) is a design unique to Com<br />
modore. It accommodates single-disk 1540 and 1541 disk drives. <strong>The</strong> earlier 1540<br />
model was designed specifically for <strong>the</strong> VIC-20; <strong>the</strong> main difference between it and<br />
<strong>the</strong> 1541 is a single ROM chip in <strong>the</strong> 1541 that makes it compatible with both <strong>the</strong><br />
VIC and <strong>Commodore</strong> <strong>64</strong>.<br />
<strong>The</strong> <strong>64</strong>'s disk units store data on 5-1/4-inch diskettes, and a demonstration<br />
diskette should be packed with each disk drive. <strong>The</strong> usual advice is to switch on <strong>the</strong><br />
disk drive first, <strong>the</strong>n <strong>the</strong> <strong>64</strong>, and <strong>the</strong>n any printer, but <strong>the</strong> order usually doesn't<br />
matter.<br />
A diskette is inserted label up, with <strong>the</strong> read/write slot nearest <strong>the</strong> disk drive.<br />
<strong>The</strong> drive door, when closed, clamps <strong>the</strong> disk firmly and permits reading and writing<br />
to take place.<br />
Disks are faster than tape, but <strong>the</strong> <strong>64</strong>'s system (with data transferred one bit at a<br />
time) isn't fast by today's standards. Generally, you should allow about 10 seconds<br />
per 4000 bytes, plus about 5 seconds overhead—roughly 25 seconds for an 8K<br />
program.<br />
<strong>The</strong> non-CBM tape operating systems described in <strong>the</strong> previous chapter are just<br />
as fast; however, disks allow for random access. <strong>The</strong> disk lets you, choose from a<br />
whole range of programs or files on a single disk, giving a versatility unavailable<br />
with <strong>64</strong> tape systems.<br />
So-called black boxes are available to allow several <strong>64</strong>s to access <strong>the</strong> same disk<br />
drive. This saves money where a group of people must use <strong>the</strong> same programs (in<br />
some teaching situations, for example), and <strong>the</strong> serial connector is reliable over dis<br />
tances up to about 12 meters (approximately 40 feet).<br />
<strong>The</strong> <strong>64</strong>'s disk drive is autonomous. In o<strong>the</strong>r words, it is largely independent of<br />
<strong>the</strong> <strong>64</strong>. In fact, it has as much ROM as <strong>the</strong> <strong>64</strong> itself. When <strong>the</strong> drive receives a com<br />
mand from <strong>the</strong> <strong>64</strong>, that command is stored in <strong>the</strong> disk drive's RAM and carried out<br />
only when <strong>the</strong> disk drive decides to do so. Similarly, results are typically stored in a<br />
buffer, waiting for <strong>the</strong> <strong>64</strong> to read <strong>the</strong>m. This explains how it is possible for <strong>the</strong> <strong>64</strong> to<br />
print READY, even when <strong>the</strong> disk drive is still obviously working. It also permits<br />
disk functions to be changed by switching ROMs within <strong>the</strong> drive.<br />
One side effect of this arrangement is that errors can occur ei<strong>the</strong>r in <strong>the</strong> <strong>64</strong> or in<br />
<strong>the</strong> disk drive. For example, if <strong>the</strong>re's no diskette present, <strong>the</strong> disk drive can't read<br />
data and an error condition is present in <strong>the</strong> drive. <strong>Commodore</strong> has a special chan<br />
nel to allow transfer of information from and to <strong>the</strong> disk, as you'll see.<br />
487
Using Disk Storage<br />
<strong>The</strong> keyboard is affected by disk operations. For example, while data is being<br />
read from disk, <strong>the</strong> interrupt is mostly off. This means <strong>the</strong> keyboard cannot be pro<br />
cessed normally, and keys pressed when <strong>the</strong> drive is active may not show up when<br />
it stops. If you're using a disk system to store data, bear this in mind. Clear <strong>the</strong> key<br />
board queue (POKE 198,0) before INPUT or GET to insure that incomplete data isn't<br />
written to disk.<br />
Disk commands are more explicit than tape commands. Unless o<strong>the</strong>rwise in<br />
structed, <strong>the</strong> <strong>64</strong> assumes that LOAD (or whatever) applies to tape. Thus, disk com<br />
mands always include <strong>the</strong> device number, which is normally 8. Also, because disks<br />
can store many programs, <strong>the</strong> bare command LOAD is disallowed. Instead, quotation<br />
marks and names are used. For example, to simply read <strong>the</strong> disk directory you must<br />
type in LOAD "$",8 <strong>the</strong>n LIST. <strong>The</strong> DOS 5.1 wedge (on <strong>the</strong> demo disk) offers lim<br />
ited help with this.<br />
Serious disk drive users, who are using disks to store valuable data and writing<br />
<strong>the</strong>ir own programs, too, should take note of a few points to keep from losing data.<br />
First, duplicating disks for security purposes isn't easy with only one disk drive. Sec<br />
ond, <strong>the</strong>re are potential problems when <strong>the</strong> process of writing to a disk is interrupted<br />
(for example, by a SYNTAX ERROR) because incomplete information is left on <strong>the</strong><br />
disk and may corrupt o<strong>the</strong>r files. Subsequent parts of this chapter will discuss <strong>the</strong>se<br />
areas in more detail.<br />
Basic Disk<br />
Commands<br />
This section will take you through <strong>the</strong> steps needed to store a program on a new,<br />
blank disk. You will <strong>the</strong>n see how channel 15 allows communication between your<br />
<strong>64</strong> and <strong>the</strong> disk drive.<br />
Formatting a Disk<br />
Switch on <strong>the</strong> disk drive and <strong>the</strong> <strong>64</strong>, insert a new, blank disk (or one that contains<br />
information you no longer wish to keep) in <strong>the</strong> drive, and close <strong>the</strong> drive door.<br />
You're now ready to format <strong>the</strong> diskette. Formatting gives <strong>the</strong> diskette a name and a<br />
two-character identifier; it also writes data on <strong>the</strong> disk to identify it as a 1541-format<br />
disk.<br />
Every time you format a disk, all programs and any data that it contains will be<br />
wiped out. Don't format a disk more than once unless you no longer need its con<br />
tents and prefer an empty disk.<br />
To format a disk, type in <strong>the</strong> following command and press RETURN:<br />
OPEN 15A15/'NEW0:M4ME/ID":CLOSE 15<br />
<strong>The</strong> red light on <strong>the</strong> disk remains on for about a minute and a half; <strong>the</strong> drive<br />
should first move to <strong>the</strong> outer track (with some noise), <strong>the</strong>n click gently as it writes<br />
to <strong>the</strong> disk. After 35 clicks, <strong>the</strong> drive will stop and <strong>the</strong> red light will go off.<br />
<strong>The</strong> disk's name can have up to 16 characters (for example, DISK TESTS 1) and<br />
<strong>the</strong> identifier up to 2 characters (for example, 00 through 99). Avoid using <strong>the</strong> sym<br />
bols ?#*,:" or @ in names sent to <strong>the</strong> disk, since <strong>the</strong>y may be interpreted as<br />
separators or special operators.<br />
<strong>The</strong> identifier is written to <strong>the</strong> disk nearly 700 times. It helps to check that data<br />
is in its expected position and that <strong>the</strong> disk hasn't been inadvertently changed. It's<br />
488
Using Disk Storage<br />
thus advisable to give your disks individual IDs; o<strong>the</strong>rwise, swapping disks to load<br />
from one and save to <strong>the</strong> o<strong>the</strong>r may scramble data if <strong>the</strong> IDs happen to match.<br />
If you want to change <strong>the</strong> disk's name, you can use a shorter formatting com<br />
mand. Omit ,ID from <strong>the</strong> formatting command; <strong>the</strong> name will be changed and <strong>the</strong><br />
data apparently will all be deleted, exactly like full formatting, but <strong>the</strong> ID isn't<br />
changed. This takes about ten seconds.<br />
Inspecting a Disk's Directory<br />
Any disk's directory or catalog is recoverable with <strong>the</strong> following command:<br />
LOAD "$",8<br />
Type in this command, press RETURN, and <strong>the</strong>n LIST <strong>the</strong> directory.<br />
A newly formatted diskette's directory has its name and ID in reverse video, fol<br />
lowed by 2A, which shows <strong>the</strong> type of <strong>Commodore</strong> disk format. <strong>The</strong> message 6<strong>64</strong><br />
BLOCKS FREE shows that 6<strong>64</strong> blocks of 256 bytes each are available for storage (but<br />
not quite all are usable). <strong>The</strong> directory is held as BASIC, as you may have inferred<br />
from LIST, and that explains <strong>the</strong> leading zero at <strong>the</strong> start of <strong>the</strong> directory. It is a<br />
dummy line number and can be ignored.<br />
Note that inspecting <strong>the</strong> disk with LOAD"$",8 will erase any program you have<br />
in memory. Conversely, without NEW, a program typed in after reading <strong>the</strong> direc<br />
tory may contain odd lines left over from <strong>the</strong> directory. Subsequent sections give <strong>the</strong><br />
full syntax of LOAD"$", allowing parts of <strong>the</strong> directory to be listed and processed.<br />
Saving a Program<br />
To see how to save a program to disk, first type NEW, press RETURN, and <strong>the</strong>n<br />
type in any short program. <strong>The</strong>n type in SAVE "PROGRAM",8 and press RETURN<br />
to save <strong>the</strong> program to disk with <strong>the</strong> name (up to 16 alphanumeric characters) you<br />
gave it. Don't include ? # * , : or @ in <strong>the</strong> name. A null name (SAVE "",8) is re<br />
jected with 7MISSING FILE NAME ERROR.<br />
If you wish, you can VERIFY, with ei<strong>the</strong>r VERIFY "PROGRAM",8 or VERIFY<br />
"*",8. In ei<strong>the</strong>r case, you should see <strong>the</strong> following display as <strong>the</strong> program is com<br />
pared with <strong>the</strong> version in memory.<br />
SEARCHING FOR PROGRAM<br />
VERIFYING<br />
OK<br />
Disks are generally reliable enough to make this unnecessary. <strong>The</strong> version with *<br />
uses <strong>Commodore</strong>'s pattern-matching technique, explained below. <strong>The</strong> same idea al<br />
lows LOAD "*",8 to load <strong>the</strong> first program it finds, when <strong>the</strong> drive is turned on.<br />
When using disks, SAVE won't work if a program with <strong>the</strong> same name already<br />
exists on a given disk. This is a security measure. An error is generated by <strong>the</strong> disk<br />
drive, and <strong>the</strong> red light flashes, but no error message is displayed on <strong>the</strong> screen.<br />
You'll soon see how to read <strong>the</strong> disk drive's message.<br />
SAVE's syntax has an optional form causing SAVE with replace. It allows a pro<br />
gram to overwrite ano<strong>the</strong>r program with <strong>the</strong> same name. <strong>The</strong> command is SAVE<br />
"@:PROGRAM",8 where <strong>the</strong> added @: is interpreted by <strong>the</strong> disk as a command to<br />
overwrite. So, if you modify your program and <strong>the</strong>n enter SAVE "@:PROGRAM",8,<br />
you'll find <strong>the</strong> newer version present on loading later. It's only fair to note that disk<br />
489
Using Disk Storage<br />
errors of <strong>the</strong> kind caused by unclosed files (corrupted programs and/or data) have<br />
been associated with this command, so if you're <strong>the</strong> cautious sort, it's better to<br />
scratch <strong>the</strong> old file before saving.<br />
After saving a program, LOAD and LIST <strong>the</strong> disk directory. <strong>The</strong> diskette's name<br />
and ID remain <strong>the</strong> same, but a program (PRG on <strong>the</strong> line after <strong>the</strong> name shows it's a<br />
program) is present. If it's a short program it will probably occupy only one block,<br />
leaving 663 blocks free.<br />
Loading a Program<br />
To load a program, type in LOAD "PROGRAM",8 and press RETURN. <strong>The</strong> disk<br />
drive will run for a few seconds, and <strong>the</strong> READY prompt will appear. <strong>The</strong>n LIST or<br />
RUN your program. LOAD "PR*",8 or LOAD "*",8 will have <strong>the</strong> same effect, if<br />
PROGRAM is <strong>the</strong> first name in <strong>the</strong> directory.<br />
LOAD "filename",8,1 is necessary for a nonrelocatable LOAD. Machine lan<br />
guage, graphics definitions, VIC-II chip registers, and any data which needs to be re<br />
placed at <strong>the</strong> point from which it was saved uses this syntax.<br />
You can also use LOAD in program mode. LOAD from within a program pro<br />
duces <strong>the</strong> same chaining effect that you get with tape; however, it is much faster.<br />
<strong>The</strong> new program, presumed to be BASIC, runs from <strong>the</strong> start. It retains all <strong>the</strong> old<br />
variables if <strong>the</strong> new program is no longer than <strong>the</strong> old one and if strings and func<br />
tions held within BASIC are redefined. See CHAIN and OLD in Chapter 6 for fur<br />
<strong>the</strong>r discussion.<br />
ML and memory dumps can also be loaded successfully. Use 10 X=X+1:IF<br />
X=l THEN LOAD "GRAPHICS",8,1:REM ONLY LOADS FIRST TIME.<br />
Scratching a Program<br />
Scratch is a strange computerese word meaning to remove or erase a program. With<br />
tape, it's simple to rewind and obliterate a program by recording over it. Disks need<br />
a specific command, however, because <strong>the</strong> disk drive can't know which program to<br />
scratch unless it's told.<br />
SCRATCH has this syntax:<br />
OPEN 15A15/'SCRATCH:///£?«am^':CLOSE 15<br />
Pattern-matching abbreviations are also usable, so OPEN 15,8,15,"SCRATCH:N*":<br />
CLOSE 15 scratches anything beginning with N, while OPEN<br />
15,8,15,"SCRATCH:*": CLOSE 15 scratches everything and leaves <strong>the</strong> diskette<br />
empty. <strong>The</strong> number of files scratched is reported in channel 15. You can use S as <strong>the</strong><br />
abbreviation for SCRATCH.<br />
Copying Programs from One Disk to Ano<strong>the</strong>r<br />
Both BASIC and machine language programs can be transferred from disk to disk.<br />
However, BASIC programs are easier to transfer because <strong>the</strong> system keeps track of<br />
where <strong>the</strong>y start and end.<br />
First, though, it is helpful to look at <strong>the</strong> disk operation called initialization. With<br />
CBM disks this means forcing <strong>the</strong> drive to read <strong>the</strong> current diskette's directory infor<br />
mation into its own memory. This process is often automatic (for example, when a<br />
directory is loaded from disk), but to be on <strong>the</strong> safe side you can use this command<br />
490
Using Disk Storage<br />
to guarantee that <strong>the</strong> disk to be copied to is correctly set up. INITIALIZE has this<br />
syntax:<br />
OPEN 15,8,15,"INITIALIZE":CLOSE 15<br />
or<br />
OPEN 15,8,15,"I":CLOSE 15<br />
or<br />
OPEN 15,8,15:PRINT#15,"I":CLOSE 15<br />
To actually copy a BASIC program, first acquire two disks. Call <strong>the</strong>m source and<br />
destination. <strong>The</strong>n follow <strong>the</strong>se steps:<br />
1. LOAD "filename"$ from <strong>the</strong> source diskette.<br />
2. Remove <strong>the</strong> source disk, replace it with <strong>the</strong> destination disk, and close <strong>the</strong> drive<br />
door.<br />
3. Enter OPEN 15,8,15/T' to initialize <strong>the</strong> destination disk.<br />
4. SAVE "filename",% to save <strong>the</strong> program onto <strong>the</strong> destination diskette.<br />
5. Replace <strong>the</strong> source diskette, enter PRINT#15/T' to initialize it, and return to step 1.<br />
Repeat <strong>the</strong> process until you've moved as many programs as you want.<br />
Copying Machine Language and Memory Dumps<br />
To copy machine language or memory dumps, you'll need to know <strong>the</strong> start and end<br />
address. Finding <strong>the</strong> end address is simple: Enter LOAD "filename"',8,1 <strong>the</strong>n PRINT<br />
PEEK (45),PEEK (46). <strong>The</strong> end pointers are thus set, but <strong>the</strong> beginning is lost. To lo<br />
cate <strong>the</strong> starting address, you can read <strong>the</strong> start address as a program file.<br />
Once you've located those addresses, <strong>the</strong> process is similar to that for BASIC.<br />
Have source and destination diskettes ready. <strong>The</strong>n follow <strong>the</strong>se steps:<br />
1. LOAD "filename",8,1 from <strong>the</strong> source diskette.<br />
2. Exchange diskettes. Type NEW.<br />
3. Enter OPEN 15,8,15/T" to initialize <strong>the</strong> destination diskette.<br />
4. POKE <strong>the</strong> vector at (43-44) with <strong>the</strong> low byte and high byte of <strong>the</strong> starting ad<br />
dress, and POKE <strong>the</strong> vector at (45-46) with <strong>the</strong> low byte and high byte of <strong>the</strong> end<br />
address.<br />
5. SAVE "filename",,8.<br />
6. Exchange diskettes, enter PRINT#15/T', and repeat from step 1.<br />
General Copier for Programs<br />
For straightforward programs, <strong>the</strong> copying methods above are fine. Where <strong>the</strong>se<br />
methods fail, or if you simply want an easy copying method, use something like Pro<br />
gram 15-1, which reads a program from one disk, storing it above BASIC in RAM,<br />
<strong>the</strong>n writes it back to ano<strong>the</strong>r disk. Program 15-1 does not work properly, however,<br />
if <strong>the</strong> program to be copied is written in BASIC and ends with a semicolon. (Speed<br />
can be improved by reading and writing in ML.)<br />
491
Using Disk Storage<br />
Program 15-1. General Program Copier<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader/'Appendix C.<br />
10 POKE 55,0: POKE 56,12: CLR: REM FREE RAM STARTS<br />
$0C00 :rem 204<br />
20 PRINT "NAME OF PROGRAM TO BE COPIED" :rem 223<br />
30 INPUT N$ 2rem 101<br />
40 PRINT "{DOWN}NAME OF PROGRAM AFTER COPYING"<br />
:rem 159<br />
50 INPUT M$ :rem 102<br />
60 OPEN 15,8,15 srem 244<br />
100 PRINT "{DOWN}INSERT DISK HOLDING ORIGINAL PROG<br />
RAM" :rem 229<br />
110 PRINT" THEN PRESS RETURN" srem 255<br />
120 GET X$:IF X$CHR$(13) GOTO 120 :rem 48<br />
130 PRINT "{DOWNjOK... READING " N$:PRINT :rem 205<br />
200 OPEN 1,8,2,N$+",P,R" :rem 190<br />
300 FOR J=3072 TO 40960 :rem 115<br />
310 GET#1,X$:IF ST>0 GOTO 400 Srem 27<br />
320 POKE J,ASC(X$+CHR$(0)) :rem 139<br />
330 NEXT:PRINT "TOO LONG":END srem 19<br />
400 CLOSE 1:PRINT "{DOWN}INSERT DESTINATION DISK"<br />
:rem 153<br />
410 PRINT " THEN PRESS RETURN" srem 2<br />
420 GET X$:IF X$CHR$(13) GOTO 420 :rem 54<br />
430 PRINT "{DOWNjOK... WRITING " M$ :rem 50<br />
440 PRINT#15,"I" srem 103<br />
500 OPEN 1,8,2,M$+",P,W" :rem 197<br />
600 FOR K=3072 TO J srem 190<br />
610 PRINT#1,CHR$(PEEK(K)); srem 242<br />
620 NEXT :rem 215<br />
700 CLOSE 1:CLOSE 15:PRINT "COPY COMPLETED":rem 55<br />
Quite a number of copy utilities are on <strong>the</strong> market. Single-drive copiers need<br />
only one drive. <strong>The</strong>re are two basic types: Some copy an entire disk, reading as<br />
much as possible into RAM, <strong>the</strong>n copying an exact image onto a new disk. More<br />
sophisticated versions copy only those parts of <strong>the</strong> disk on which data is stored. One<br />
disk typically takes 15 minutes and six disk changes. <strong>The</strong> o<strong>the</strong>r type copies individ<br />
ual programs or files, allowing <strong>the</strong> user to select only those worth copying. This sec<br />
ond type (<strong>the</strong> program above is like this) is fine when small numbers of programs<br />
are to be copied, but tedious with large numbers of programs, because <strong>the</strong> disks<br />
typically are changed between each read/write operation.<br />
Two-drive copiers use two daisychained drives. <strong>The</strong> technique is to switch on<br />
one drive, load and run a disk-device number-change program to reassign it as drive<br />
9 (unless <strong>the</strong> drive's been changed in hardware—see below), turn <strong>the</strong> o<strong>the</strong>r drive on,<br />
and load <strong>the</strong> copy program. "COPY/ALL" on <strong>the</strong> demo disk is this type of program.<br />
Again, disks can be copied in entirety or copied one file at a time by utilities.<br />
492
Using Disk Storage<br />
Communicating with <strong>the</strong> Disk Drive: Using Channel 15<br />
Channel 15 is variously known as <strong>the</strong> error channel, <strong>the</strong> command channel, or <strong>the</strong><br />
information channel. <strong>The</strong> number 15 refers to its secondary address, <strong>the</strong> third<br />
parameter of <strong>the</strong> OPEN statement. Disk drives use this third parameter to identify<br />
<strong>the</strong> channel number, and generally it makes sense to use <strong>the</strong> same number for <strong>the</strong><br />
file where possible. To understand this better, enter and run <strong>the</strong> following one-line<br />
BASIC program:<br />
10 OPEN15,8,15:INPUT#15/E/E$,T/S:PRINT E;E$;T;S:CLOSE15:END<br />
If <strong>the</strong> disk drive has no current error stored in it, <strong>the</strong> result will be 0 OK 0 0,<br />
where <strong>the</strong> first zero is <strong>the</strong> error number, OK is <strong>the</strong> message from disk, and <strong>the</strong> track<br />
and sector of <strong>the</strong> error (both zero) mean <strong>the</strong>re's no problem. This is a long-winded<br />
way to discover <strong>the</strong> disk status. It can be tedious to enter and run it just to discover<br />
<strong>the</strong> reason for a disk error or problem. Note that direct mode can't be used; <strong>the</strong> line<br />
must be entered as part of a program. <strong>The</strong>refore, when developing disk programs, it<br />
makes sense to include this as, say, line 40000 so that RUN 40000 is ready and wait<br />
ing if needed.<br />
Note that reading <strong>the</strong> channel clears it, so a subsequent read will say OK even if<br />
<strong>the</strong>re's a major problem (like an open disk drive door). <strong>The</strong> message remains until<br />
ei<strong>the</strong>r <strong>the</strong> channel is read or disk activity forces in ano<strong>the</strong>r message.<br />
You'll see later <strong>the</strong> circumstances in which <strong>the</strong> flashing error light, which is apt<br />
to alarm newcomers, can be ignored. First, though, deliberately generate some errors<br />
and watch <strong>the</strong> effect of running line 10 above:<br />
1. Enter LOAD //o/o",8. This program doesn't exist on disk. RUN gives:<br />
62 FILE NOT FOUND 0 0<br />
2. Enter LOAD "1:HELLO",8. It tries to load a program from a nonexistent drive.<br />
Your drive is drive 0; since it's a single drive, <strong>the</strong>re's no drive 1. <strong>The</strong> message is:<br />
74 DRIVE NOT READY 0 0<br />
3. Enter SAVE "PROGRAM",8. (Assuming PROGRAM is still present on <strong>the</strong> disk.)<br />
RUN yields:<br />
63 FILE EXISTS 0 0<br />
4. Enter OPEN 15,8,15,"S:PROGRAM":CLOSE 15. This scratches PROGRAM from<br />
<strong>the</strong> disk. (<strong>The</strong> initial is sufficient.) Now RUN yields:<br />
1 FILES SCRATCHED 1 0<br />
which, translated, means that message 1 (which always deals with scratched files)<br />
reports that just one file was scratched by <strong>the</strong> command. More than one file may<br />
be scratched if pattern matching (with "S:*") is used.<br />
5. Turn <strong>the</strong> disk drive off. Open <strong>the</strong> disk drive door if <strong>the</strong>re's a disk present; this in<br />
sures that no magnetic glitch can occur on <strong>the</strong> disk. Turn on <strong>the</strong> disk drive and<br />
immediately enter RUN. Your message is something like this:<br />
73 CBM DOS V2.6 1541 0 0<br />
which tells you what type of ROM your disk unit has.<br />
If you want to experiment more, try <strong>the</strong> DOS 5.1 wedge from <strong>the</strong> demo disk,<br />
which modifies BASIC so that just pressing @ prints <strong>the</strong> message.<br />
493
Using Disk Storage<br />
Sending Messages to <strong>the</strong> Disk Drive<br />
You've seen how to read channel 15, but how are messages sent to <strong>the</strong> disk? <strong>The</strong><br />
syntax has two forms, both based on <strong>the</strong> syntax of OPEN:<br />
OPEN 15fi,l5,"command"<br />
or<br />
FRlNT#15,"command" (assuming OPEN 15,8,15 has been carried out).<br />
Formatting a disk and scratching a file are two examples we've seen so far. A sub<br />
sequent section includes a comprehensive list of eight disk commands that use this<br />
channel.<br />
Handling Disk Files with BASIC<br />
Files on disk are more complicated, and thus more difficult to understand, than tape<br />
files. If you're a newcomer to disks, you may find <strong>the</strong> concept of a file hard to grasp.<br />
However, after working through <strong>the</strong> examples which follow, it should become clear.<br />
<strong>The</strong>re are two essential aspects of any computer filing system. One is that an ex<br />
ternal storage device (like a disk drive) must be able to store and retrieve data in a<br />
reliable way; <strong>the</strong> o<strong>the</strong>r is that <strong>the</strong> computer must have commands available to<br />
handle <strong>the</strong> output and input of that data. To illustrate <strong>the</strong> second condition, consider<br />
<strong>the</strong> fact that <strong>the</strong> <strong>64</strong>'s disk drives can be programmed to store data almost anywhere<br />
on <strong>the</strong> disk surface. Although this can be a very useful feature, it does not provide a<br />
file in <strong>the</strong> true sense, because specially written commands have to be used to process<br />
<strong>the</strong> data.<br />
Disk files, unlike tape files, aren't always exclusively read or write files. <strong>The</strong><br />
versatility of disks enables files to be open for writing and reading at <strong>the</strong> same time.<br />
Ano<strong>the</strong>r example of disk versatility is that several disk files can be open at once.<br />
For example, a sequential file—identical to a tape file—can be read, updated, and<br />
<strong>the</strong>n written to a second sequential file. This is not possible with <strong>the</strong> <strong>64</strong>'s tape unit.<br />
<strong>The</strong> tape system can use only a single track of tape, whereas a disk uses a multitrack<br />
system.<br />
Types of File Organization<br />
<strong>The</strong> <strong>64</strong>'s disk system supports four types of files, shown on <strong>the</strong> directory as PRG,<br />
SEQ, REL, and USR (program files, sequential files, relative files, and user files). A<br />
user file allows programmers to build <strong>the</strong>ir own type of file by writing data directly<br />
to <strong>the</strong> disk and <strong>the</strong> directory, but all <strong>the</strong> work of arranging <strong>the</strong> data on disk and<br />
reading it back must be done by <strong>the</strong> programmers. <strong>The</strong> subsequent section on disk<br />
storage explains how this is done; meanwhile, USR can be ignored, since it is not a<br />
true file system.<br />
Program Files<br />
<strong>The</strong>se are simply programs or memory dumps which can be loaded and run (if<br />
<strong>the</strong>y're programs) in <strong>the</strong> usual way. However, <strong>the</strong> disk system also allows <strong>the</strong>m to be<br />
read from and written to, and that makes several nice programming techniques<br />
possible.<br />
494
Using Disk Storage<br />
Sequential Files<br />
Next to program files, sequential files are <strong>the</strong> easiest to understand. Data is written<br />
to <strong>the</strong>m from a buffer, in sequence, without restriction on <strong>the</strong> type of data or its<br />
length. Thus, <strong>the</strong> file can be of any length, regardless of <strong>the</strong> computer's RAM. Be<br />
cause sequential file data isn't ordered, it is usually read back in sequence starting at<br />
<strong>the</strong> beginning. As a result, long sequential files can be slow to handle.<br />
In practice, <strong>64</strong> sequential files—whe<strong>the</strong>r on tape or disk—aren't usually quite so<br />
free from structure. This is because it's easiest to use PRINT# to write data to a file<br />
and INPUT# to read it back, and both of those commands have certain restrictions<br />
on length and type of character that <strong>the</strong>y can handle.<br />
Relative Files<br />
Relative files do not have to be read from <strong>the</strong> beginning. Any record in <strong>the</strong> file can<br />
be read by number; thus, random access is a name sometimes given to such files.<br />
With <strong>the</strong> <strong>64</strong>, this is made possible by defining a record length when <strong>the</strong> file is ini<br />
tially opened, and diskette space is assigned as it's needed. For example, if record<br />
number 200 is to be written to a new relative file, <strong>the</strong> disk's operating system allo<br />
cates space on <strong>the</strong> diskette for 200 records of <strong>the</strong> desired length before writing <strong>the</strong><br />
data of record number 200.<br />
Relative filing is more ordered than sequential filing; later, you'll see exactly<br />
how that is accomplished. For <strong>the</strong> moment, note that <strong>the</strong> records are <strong>the</strong> same<br />
length. This wastes disk space if some records are far longer than o<strong>the</strong>rs. Obviously,<br />
a shorter maximum record length allows more records to be filed.<br />
Note also that accessing records by number may not be what you really want.<br />
For instance, you may find yourself using extra files, or arrays, to convert JONES<br />
into number 93. Never<strong>the</strong>less, this is <strong>the</strong> most advanced form of filing offered by<br />
most microcomputers.<br />
Direct access files may also be used. <strong>Commodore</strong>'s manuals refer to <strong>the</strong> system<br />
of storing data at certain sectors on <strong>the</strong> diskette as random access, which is explained<br />
in <strong>the</strong> section on data storage. More usually, direct access filing refers to a system<br />
allowing access to records by a single key. This is a fascinating system of file<br />
organization, easily implemented on <strong>the</strong> <strong>64</strong>.<br />
To take an actual example: You want to be able to read from disk, as fast as pos<br />
sible, information on any one person out of a group of 400 by entering <strong>the</strong> person's<br />
name. <strong>The</strong> <strong>64</strong>'s relative file system requires a number between 1 and 400, and <strong>the</strong><br />
idea of direct access is to convert <strong>the</strong> name into a number within that range. This<br />
could be done by converting some of <strong>the</strong> name's characters into ASCII, <strong>the</strong>n generat<br />
ing a key from 0 to 1 and using RND(— key)H00+l to generate a repeatable value<br />
in <strong>the</strong> required range. A good algorithm will, of course, evenly spread <strong>the</strong> coded val<br />
ues of <strong>the</strong> keys. With this kind of organization, records are held in <strong>the</strong> file in a jumbled<br />
sequence, but can be recovered by applying <strong>the</strong> coding algorithm to <strong>the</strong> key.<br />
Direct access has several drawbacks, however. First, <strong>the</strong>re's no easy way to print<br />
a sequential list of <strong>the</strong> records. In o<strong>the</strong>r words, it's difficult to check what's on file.<br />
Second, many keys will inevitably generate <strong>the</strong> same record number, so it's nec<br />
essary when writing to <strong>the</strong> file to check that <strong>the</strong> record number isn't used (if it is, try<br />
<strong>the</strong> next one). It's also necessary, when reading, to read until <strong>the</strong> correct record is<br />
495
Using Disk Storage<br />
found. For that reason, <strong>the</strong> file has to be longer than <strong>the</strong> number of records by at<br />
least 30 percent. In this case, about 35 percent of <strong>the</strong> records are synonyms, but this<br />
drops to 25 percent if all keys are tested first and all synonyms are stored in <strong>the</strong> file<br />
in a second pass. If <strong>the</strong> most frequently used records are entered first, efficiency im<br />
proves again.<br />
Inverted files are used in data base programming, where <strong>the</strong>re are huge<br />
amounts of data in a main file, and where you want a list of items conforming to<br />
several stringent criteria.<br />
Instead of reading <strong>the</strong> entire file, a large number of smaller files are established,<br />
with each holding keys to a subset of <strong>the</strong> original data. As a result, fewer files have<br />
to be read, but only at <strong>the</strong> expense of extra file space being taken up and extra work<br />
being required to add new records to a number of files. For example, 26 subsidiary<br />
files for initials A-Z plus a full-length relative file works well in some applications.<br />
Writing and Reading Disk Files<br />
OPEN. You can OPEN a disk file by using this syntax:<br />
OPEN file number, device number, disk channel, command string<br />
For example, OPEN 2,8,2,"0:ORDINARY FILE,S,W" opens file 2 to disk drive 8, and<br />
uses channel 2 in <strong>the</strong> disk drive. (This is relevant with random access storage and<br />
with relative files.) <strong>The</strong> command string begins with 0:, which is a construction from<br />
<strong>Commodore</strong> drives which have two disk drives, instead of only one. <strong>The</strong> o<strong>the</strong>r drive<br />
used <strong>the</strong> prefix 1:. This chapter ignores 0:, but it is often suggested that <strong>the</strong> prefix<br />
should be used. Regardless, readers with access to PET/CBM machines should keep<br />
this syntax in mind.<br />
<strong>The</strong> o<strong>the</strong>r part of <strong>the</strong> command string uses commas as separators and causes a<br />
sequential (S) file, called ORDINARY FILE, to be set up for writing (W). PRINT#2<br />
will now write to this file, and CLOSE 2 safely completes all <strong>the</strong> housekeeping.<br />
You'll see fur<strong>the</strong>r examples shortly in <strong>the</strong> demonstration programs on file handling.<br />
Note that <strong>the</strong> file number cannot be 0. Ordinarily, use any number from 1<br />
through 127. File numbers 128 through 255 should usually be avoided, because<br />
PRINT# to <strong>the</strong>m sends linefeed (CHR$(10)) with carriage return. This is useful with<br />
some printers, but not generally helpful with disk files.<br />
<strong>The</strong> device number is 8 unless changed by hardware or software. It's possible to<br />
connect two drives at once, one with device number 8 and <strong>the</strong> o<strong>the</strong>r with device<br />
number 9, and open several files to each (OPEN 3,9,3,"ORDINARY FILE,S,R"),<br />
allowing reading from 9 and writing back to 8.<br />
<strong>The</strong> channel number should generally not be 0, 1, or 15. This is because 0 and 1<br />
are related to <strong>the</strong> directory, and 15 is <strong>the</strong> command channel. <strong>The</strong> command string<br />
syntax varies with <strong>the</strong> type of file. See <strong>the</strong> demonstration programs.<br />
PRINT* is one of three BASIC commands (<strong>the</strong> o<strong>the</strong>rs are INPUT# and GET#)<br />
that let you send output to a file and read it back, ei<strong>the</strong>r as a batch of characters (IN-<br />
PUT#) or as individual characters (GET#). PRINT# outputs string and number ex<br />
pressions to <strong>the</strong> file in just <strong>the</strong> same way that output is sent to <strong>the</strong> screen. <strong>The</strong><br />
organization of relative files is identical to that of sequential files, as far as <strong>the</strong> stored<br />
data is concerned. PRINT# treats a colon or end-of-BASIC line as requiring a<br />
carriage-return character. <strong>The</strong> semicolon causes PRINT# to print no extra characters.<br />
496
Using Disk Storage<br />
<strong>The</strong> comma outputs ten spaces (in effect, tabulating across). Numbers appear in <strong>the</strong><br />
file with a leading minus or space, and with a trailing space, too.<br />
<strong>The</strong> effect of OPEN 2,8,2,"TEST,S,W" followed by PRINT#2,"HELLO"; 12345;<br />
"HELLO","HI" is shown in Figure 15-1.<br />
Figure 15-1. Using PRINT#<br />
|H|E|L|L|O| H2j3|4|5| |h|e|l|l|o| | | | | | | | I I |H|Tiq<br />
PRINT# can output individual characters for GET# to read back later. In this<br />
case, <strong>the</strong>re are no restrictions on character types. <strong>The</strong> two program lines,<br />
PRINT#2,CHR$(N); and GET#1,X$: N=ASC(X$+CHR$(O)), are exact mirror im<br />
ages. One writes a single character (<strong>the</strong> semicolon prevents unwanted RETURNs)<br />
and <strong>the</strong> o<strong>the</strong>r reads <strong>the</strong> character back, also converting it back into its ASCII value,<br />
allowing for <strong>the</strong> null-character bug in <strong>the</strong> <strong>64</strong>'s ASC command.<br />
If you're reading data with INPUT#, remember not to write strings longer than<br />
88 characters. INPUT# generates 7STRING TOO LONG if this happens. If long strings<br />
appear to be unavoidable, it's always possible (though slower) to evade this problem<br />
by replacing INPUT# with something like X$="": FOR J=l TO 100: GET#1,Y$:<br />
X$=X$+Y$: NEXT.<br />
Remember to include RETURN when estimating <strong>the</strong> lengths of records. Relative<br />
files in particular need a RETURN character if data is read back by INPUT#, and this<br />
adds 1 to <strong>the</strong> maximum record length.<br />
INPUT#. Using INPUT# is <strong>the</strong> most convenient way to fetch information from<br />
files. <strong>The</strong> point to understand is that PRINT# and INPUT# are largely mirror images<br />
of each o<strong>the</strong>r. PRINT#1,X$:PRINT#1,Y writes a string, <strong>the</strong>n a numeral, to a file;<br />
INPUT#1,X$,Y will interpret this correctly, reconstructing X$ and Y. If <strong>the</strong> variable<br />
types match, <strong>the</strong>re should be few problems.<br />
<strong>The</strong>re are several small complications, all of which have been mentioned al<br />
ready, but <strong>the</strong>y are worth going over again.<br />
INPUT# cannot input a string more than 88 characters long.<br />
INPUT# looks for a separator, normally a RETURN or a comma. Thus,<br />
PRINT#1,X$;Y cannot be read back properly by INPUT#, because <strong>the</strong> semicolon<br />
causes <strong>the</strong> two variables to be output with no break. It's easiest to separate <strong>the</strong> vari<br />
ables by a RETURN (CHR$(13)), but PRINT#1,X$//,"Y works just as well.<br />
INPUT# cannot input a null string. PRINT#1,X$: PRINT#1,Y$ <strong>the</strong>n<br />
INPUT#1,X$,Y$ ordinarily works, but if Y$ is nothing, PRINT# puts two consecutive<br />
RETURNS on file, and INPUT# behaves as though RETURN were pressed on IN<br />
PUT at <strong>the</strong> keyboard and goes on to <strong>the</strong> next item.<br />
GET#. This reads individual characters from a file, with no exceptions. It will<br />
fetch null characters written as CHR$(0), quotation marks (ASCII 34), RETURNS<br />
(ASCII 13), plus any punctuation and screen-editing characters. If you are interested<br />
in <strong>the</strong> entire contents of a file, use this command; if not, <strong>the</strong> intelligence of INPUT#,<br />
which assigns all your variables for you, makes a better command.<br />
CLOSE. Closing a file is simple:<br />
CLOSE file number<br />
497
Using Disk Storage<br />
CLOSE operates on one file only; you need CLOSE 2: CLOSE 15 if files 2 and 15 are<br />
open. Unclosed files can cause problems. See <strong>the</strong> section "When to Ignore <strong>the</strong> Red<br />
Warning Light" below for a full discussion.<br />
ST and Disk Errors and Messages<br />
ST has several applications in disk file handling. When INPUT# reads to <strong>the</strong> end of<br />
a file, ST is set to <strong>64</strong>. ST can be tested for this condition if <strong>the</strong> file length is un<br />
certain. After <strong>64</strong>, ST becomes 66, which means <strong>the</strong> device isn't responding.<br />
Two o<strong>the</strong>r possibilities are ST= -128 (usually accompanied by 7DEVICE NOT<br />
PRESENT) and ST=1 (if writing is slow). <strong>The</strong> o<strong>the</strong>r four bits of ST don't apply to<br />
disk. ST isn't usually important, because an end-of-file marker makes ST=<strong>64</strong> super<br />
fluous, and <strong>the</strong> o<strong>the</strong>r errors are generally obvious. However, a command like IF<br />
ST>0 THEN GOTO EXIT provides an easy exit mechanism when testing files. If you<br />
do this, remember that ST is reset after every input or output, so put <strong>the</strong> test im<br />
mediately after <strong>the</strong> relevant command.<br />
Disk messages nearly always indicate that a program can't run. <strong>The</strong> exceptions<br />
are message 1, <strong>the</strong> number of files scratched, and message 50, RECORD NOT<br />
PRESENT, which always occurs when a relative file is set up. <strong>The</strong> error may not be<br />
serious—for example, a syntax error in a command string—but it's good practice to<br />
follow each disk command with a subroutine call to read channel 15 and exit if <strong>the</strong><br />
message number is 20 or more. <strong>The</strong> subroutine should print <strong>the</strong> message number<br />
and its message, and close all open files, as <strong>the</strong> following example shows:<br />
10000 INPUT#15,E,E$,T,S: IF E
Using Disk Storage<br />
You can ignore <strong>the</strong> flashing red light if you are reading from disk. Suppose<br />
you've typed LOAD "PROGARM",8 in error; <strong>the</strong> red light flashes, and <strong>the</strong> message<br />
is ?FILE NOT FOUND. Type <strong>the</strong> correct version, LOAD "PROGRAM",8, and loading<br />
will proceed normally with no problems. <strong>The</strong> same sort of thing applies in a pro<br />
gram: 10 OPEN 2/8,2//FIEL,S,R" might generate an error, but if <strong>the</strong> line is edited<br />
and <strong>the</strong> program rerun, no harm will result.<br />
Take <strong>the</strong> red light seriously if you are writing to disk and a write file is still<br />
open. An unclosed file can cause problems with storage to disk, because <strong>the</strong> normal<br />
system of chaining between sectors is disturbed; o<strong>the</strong>r programs and files can become<br />
corrupted. This isn't likely to be a major problem. However, if you are using a file<br />
system for a serious purpose, you should be aware of this possibility, since <strong>the</strong>re will<br />
almost inevitably be program crashes during testing. When programs are finally com<br />
pleted, it is good practice to transfer <strong>the</strong>m to new disks to avoid any chance of error.<br />
<strong>The</strong> steps to take and danger signs to watch for are listed in <strong>the</strong> next section's notes.<br />
Handling Program Files<br />
Program files are marked PRG in <strong>the</strong> directory. <strong>The</strong>y are used for storing BASIC pro<br />
grams in tokenized form tfnd $AL or graphics as simple consecutive bytes. <strong>The</strong>re's no<br />
way of telling from <strong>the</strong> directory whe<strong>the</strong>r PRG is BASIC or not; if LOAD "NAME",8<br />
and RUN works, <strong>the</strong>n it is BASIC, at least in part. ML programs usually need a SYS<br />
call to run.<br />
PRG files can be opened for read or write. If such a file is read, <strong>the</strong> first two<br />
bytes are invariably <strong>the</strong> LOAD address, and <strong>the</strong> rest is <strong>the</strong> data. LOAD "NAME",8,1<br />
always loads into this LOAD address, but LOAD "NAME",8 allows relocation (and<br />
also relinks <strong>the</strong> program, assuming it to be BASIC). <strong>The</strong>re's no way to force a pro<br />
gram file to load where you want with LOAD "NAME",8.<br />
OPEN 2,8/2,//NAME,PRG,WRITE" opens a program file for write, while OPEN<br />
2,8,2,//NAME,PRG,READ" opens <strong>the</strong> same file for read. <strong>The</strong>re are, of course, vari<br />
ations on this. For instance, <strong>the</strong> file numbers and channel numbers needn't be 2; <strong>the</strong><br />
device number may not be 8; and <strong>the</strong> command string can be made up of string ex<br />
pressions. In addition, <strong>the</strong> command string can be abbreviated such as ,P,W for<br />
,PRG,WRITE.<br />
Program 15-2 is a short program that reads any program byte by byte, printing<br />
out <strong>the</strong> results in ASCII.<br />
Program 15-2. Reading Programs Byte by Byte<br />
1 OPEN 15,8,15,"I":REM INITIALIZE DISKETTE<br />
2 OPEN 2,8,2,"PROGRAM FILE DEMO,P,R":REM OPEN PRG<br />
{SPACE}FILE FOR READ<br />
3 GET#2,X$:REM GET A FILE CHARACTER<br />
4 IF ST>0 THEN CLOSE 2:END<br />
5 PRINT ASC(X$+CHR$(0));:REM PRINT ASCII VALUE<br />
6 GOTO 3<br />
Line 2 must include <strong>the</strong> name of <strong>the</strong> program to be examined. Alternative forms<br />
of <strong>the</strong> command string such as OPEN 2,8/2//NAME,PROGRAM/READ// or OPEN<br />
2,8,2^$+",P,R" are perfectly acceptable.<br />
499
Using Disk Storage<br />
Run this with a BASIC program as its PRG file, and you'll get BASIC in its<br />
tokenized form. For instance, if you save a one-line program (10 PRINT'HELLO")<br />
and <strong>the</strong>n look at it using this program, you'll get something like Figure 15-2.<br />
Figure 15-2. Tokenized BASIC<br />
LOAD<br />
Address<br />
1 8<br />
Link<br />
Address<br />
14 8<br />
Line<br />
Number<br />
10 0 153<br />
Tokenized Line<br />
PRINT "HELLO"<br />
34 72 69 76 76 79 34 0<br />
End of<br />
Program<br />
0 0<br />
Uses for Program File Processing<br />
Analyzing BASIC. Provided allowance is made for link addresses, line numbers,<br />
and <strong>the</strong> tokenized form of keywords, BASIC programs can be read, perhaps to see if<br />
<strong>the</strong>y're identical. Hidden code can be searched for. Appending, deleting, and similar<br />
manipulations are possible. <strong>The</strong> link address need not be correct, since LOAD will<br />
relink it.<br />
It's possible to write BASIC directly to a PRG file, by opening a program file for<br />
write, printing any two bytes as <strong>the</strong> start address (<strong>the</strong>y'll be overridden when <strong>the</strong><br />
program loads), and printing a fur<strong>the</strong>r series of CHR$(n) commands to make up <strong>the</strong><br />
program. This can be useful in some antilisting techniques, and BASIC lines longer<br />
than 88 characters can be written in this way, too.<br />
Finding ML or memory dump LOAD addresses. This can't be done in direct<br />
mode. Instead, you may use Program 15-3.<br />
Program 15-3. Finding ML or Memory Dump LOAD Addresses<br />
10 INPUT "PROGRAM NAME";N$<br />
20 OPEN 2,8,2,N$+",P,R"<br />
30 GET#2,X$,Y$<br />
40 PRINT ASC(X$+CHR$(0))+256*ASC(Y$+CHR$(0))<br />
50 CLOSE 2<br />
^.Analyzing ML programs. You've seen how to read <strong>the</strong> two LOAD address<br />
bytes. If you wish to load ML into a different area, you can change <strong>the</strong> LOAD ad<br />
dress by rewriting <strong>the</strong> two leading bytes using <strong>the</strong> routine in Program 15-4.<br />
Program 15-4. Changing <strong>the</strong> LOAD Address<br />
10 OPEN 2,8,2,"ML FILE1,P,R"<br />
20 OPEN 3,8,3,"ML FILE2,P,W"<br />
30 GET#2,X$,X$<br />
40 PRINT#3,CHR$(0)CHR$(192);<br />
50 GET#2,X$:IF X$="" THEN X$=CHR$(0)<br />
60 S=ST:PRINT#3,X$;<br />
70 IF S=0 GOTO 50<br />
80 CLOSE 2:CLOSE 3<br />
500
Using Disk Storage<br />
Lines 50 through 70 transfer <strong>the</strong> entire file, except for <strong>the</strong> first two bytes. <strong>The</strong><br />
old LOAD address is thrown away; <strong>the</strong> new is set at $C000. Note in line 50 how a<br />
character which GET# regards as null must be converted into CHR$(0); o<strong>the</strong>rwise,<br />
zero bytes will be lost. Line 60 preserves ST, which is reset by <strong>the</strong> PRINT# com<br />
mand, for <strong>the</strong> end-of-file test in line 70.<br />
If you change disks, you may need to initialize <strong>the</strong> new disk (or add line 0<br />
OPEN 15,8,15/T': CLOSE 15 to <strong>the</strong> program).<br />
PET/CBM programs load at $0401, so <strong>64</strong> programs can be made to load into<br />
<strong>the</strong>se machines with this program. Occasionally, you may even want to use <strong>the</strong> pro<br />
gram to restore LOAD addresses to programs which have lost <strong>the</strong>m through incorrect<br />
copying.<br />
Writing machine code or graphics definitions directly onto disk. As with<br />
BASIC, <strong>the</strong>re's no problem in opening a program file, writing a two-byte LOAD ad<br />
dress, and following this with bytes. For example, where RAM is already occupied<br />
by machine language or BASIC, or in tricky areas like zero page or <strong>the</strong> screen, this<br />
technique allows any area of RAM to be saved to disk. An autorun routine, analo<br />
gous to those used for tape, provides an illustration.<br />
Autorunning program. This is trickier with disk than with tape. If <strong>the</strong> start of<br />
BASIC is fixed, extra ML can be added to <strong>the</strong> start of <strong>the</strong> program to cause it to run<br />
automatically after loading. Alternatively, and for greater versatility, you can use a<br />
loader which calls <strong>the</strong> program by name, and so allows for variations in starting<br />
address.<br />
Program 15-5 autoruns ML programs, which <strong>the</strong>refore need no SYS call. <strong>The</strong><br />
forced LOAD address is $02A7. <strong>The</strong> autorun feature is caused by changing <strong>the</strong> vector<br />
at $0302-$0303 to $02A7, where <strong>the</strong> ML program is loaded by name (in effect, with<br />
LOAD "ML",8,1) and <strong>the</strong>n jumped to.<br />
To use this program, you need a BASIC program on disk, Program 15-5 (below)<br />
in memory, a new name for <strong>the</strong> program, plus a two-line message. When run, <strong>the</strong><br />
program adds ML from $02A7 to <strong>the</strong> start of BASIC. In o<strong>the</strong>r words, LOAD<br />
"NEWNAME",8,1 puts BASIC into its normal position, but prefaced by about five<br />
blocks of ML which autoruns. It also straddles <strong>the</strong> screen, so <strong>the</strong> message which is<br />
input appears before <strong>the</strong> program runs (it's timed to stay for about five seconds and<br />
appears black-on-white). RUN/STOP and RUN/STOP-RESTORE are automatically<br />
disabled. Some of <strong>the</strong> ML simulates plug-in ROM at $8000, which means that even<br />
a hardware reset cannot break into <strong>the</strong> program as it runs.<br />
Autorun assumes BASIC starts at $0801, as it normally does; VIC-20 is more<br />
awkward to autorun than <strong>the</strong> <strong>64</strong> is, because its starting position varies with memory<br />
expansion. Test <strong>the</strong> new program, <strong>the</strong>n delete <strong>the</strong> pure BASIC version from <strong>the</strong> disk;<br />
now you have an autorunning BASIC program.<br />
Program 15-5. ML Autorun<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
20 REM DATA MARKED WITH ASTERISKS PROTECTS AGAINST<br />
21 REM HARDWARE RESET; IF THIS ISN'T REQUIRED,<br />
:rem 132<br />
:rem 12<br />
22 REM OMIT THE DATA. ALSO OMIT IF BASIC USES<br />
:rem 67<br />
501
Using Disk Storage<br />
23 REM RAM AFTER $8000. :rem 181<br />
24 REM irem 74<br />
100 PRINT "MESSAGE, STATEMENT, OR TITLE" :rem 151<br />
110 INPUT M$ :rem 147<br />
120 INPUT "PRESENT PROGRAM NAME";N$ :rem 110<br />
130 INPUT "AUTORUN PROGRAM NAME";P$ :rem 126<br />
140 PRINT "{CLR}{2 DOWN}": PRINT M$ :rem 83<br />
200 OPEN 15,8,15,"I" :rem 217<br />
210 OPEN 1,8,3,N$+",P,R" :rem 192<br />
220 OPEN 2,8,4,P$+",P,W" :rem 202<br />
230 FOR J=679-2 TO 9999 :rem 143<br />
240 READ X: IF X
Using Disk Storage<br />
stored in similar sets—for instance, name followed by four address lines and a<br />
phone number—so <strong>the</strong>re are no problems in interpreting data when it is read back<br />
from <strong>the</strong> file.<br />
Sequential files, once written, aren't readily changed, but new records can easily<br />
be added onto <strong>the</strong> end. <strong>The</strong> disk operating system (DOS) has a built-in Append<br />
command. Files can be updated only by reading, correcting records as <strong>the</strong>y are read,<br />
<strong>the</strong>n writing back <strong>the</strong> edited version (with old records removed and new ones in<br />
serted) as a new file with a different name. This process, which is impossible on <strong>64</strong><br />
tape, is easy with disks.<br />
<strong>The</strong> DOS has ano<strong>the</strong>r command, Copy, which copies a sequential file onto <strong>the</strong><br />
same disk and optionally concatenates ano<strong>the</strong>r file on <strong>the</strong> end. This is more useful<br />
with CBM's double-disk units, but still has a few uses with <strong>the</strong> <strong>64</strong>.<br />
Use OPEN 2,8,2,'7i7enflme,SEQ,WRITE" to open a sequential file for write op<br />
erations. OPEN 2/8,2,/y*7ename,SEQ,READ// opens <strong>the</strong> same file for read; OPEN<br />
2,8,2,7*7ename,SEQ,APPEND" opens an existing file for Append.<br />
<strong>The</strong> file and channel numbers need not be 2, and <strong>the</strong> device number does not<br />
have to be 8; <strong>the</strong>re are alternative, similar forms. Sequential files are assumed by de<br />
fault, so if SEQ, or <strong>the</strong> shorter S, is omitted from any of <strong>the</strong>se commands, it makes<br />
no difference. READ is a fur<strong>the</strong>r default, so OPEN 2,8,2,"filename" assumes a<br />
sequential file will be read and reports an error if <strong>the</strong> file isn't found.<br />
A sequential file can be opened for write only once. <strong>The</strong>reafter, data can be ap<br />
pended, but an attempt to open it again for write using <strong>the</strong> same file number will<br />
cause a FILE EXISTS error message within <strong>the</strong> disk drive. However, using a different<br />
file number erases <strong>the</strong> file and starts over.<br />
Copy has <strong>the</strong> following syntax:<br />
OPEN 15,S,lS,"CO?\:new name=old name"<br />
or<br />
PRINT#15,"COPY:/H?a; name=old name" (after OPEN 15,8,15 has been carried out)<br />
This command writes ano<strong>the</strong>r copy of <strong>the</strong> file, under a different name (or you'll get<br />
FILE EXISTS), to <strong>the</strong> same disk.<br />
OPEN 15,8,15,"COPY:j!ac; name= first file,second file"<br />
<strong>The</strong> above combines two or more named files into ano<strong>the</strong>r; again, <strong>the</strong> combined file<br />
must have a new name.<br />
Program 15-6 is a simple example of a program that reads a sequential file and<br />
displays its contents onscreen. Note that ST in line 60 tests for <strong>the</strong> end-of-file con<br />
dition. If that line is omitted, nothing very terrible happens; however, line 40 will<br />
<strong>the</strong>n repeatedly fetch a meaningless character.<br />
Program 15-6. Reading and Displaying a Sequential File<br />
10 PRINT "NAME OF SEQ FILE TO BE DISPLAYED"<br />
20 INPUT N$<br />
30 OPEN 1,8,2,N$:REM OPEN SEQ FILE (DEFAULT= READ)<br />
40 GET#1,X$<br />
50 PRINT X$;<br />
503
Using Disk Storage<br />
60 IF ST>0 THEN CLOSE l:END:REM STOP AT END OF FIL<br />
E<br />
70 GET X$sIF X$=MM THEN 70<br />
80 GOTO 40<br />
You may find that <strong>the</strong> disk warning light flashes if you misspell <strong>the</strong> file's name<br />
or try to read a program ra<strong>the</strong>r than a sequential file. However, incorporating a test<br />
for <strong>the</strong>se messages is straightforward. First, add <strong>the</strong> following line:<br />
5 OPEN 15,8,15<br />
It is good practice to close file 15 last; if it is closed during a program, o<strong>the</strong>r disk<br />
files will close, too. <strong>The</strong>n add this subroutine:<br />
10000 INPUT#15,E,E$,T,S: IF E
Using Disk Storage<br />
<strong>the</strong> red light remains on because a file is open. Enter PRINT#1,"HELLO" and note<br />
<strong>the</strong> absence of activity. CLOSE 2 writes this data to disk and closes <strong>the</strong> file. <strong>The</strong> pre<br />
vious program will read back <strong>the</strong> five letters of HELLO plus a final RETURN<br />
character.<br />
Repeating <strong>the</strong> same command causes a FILE EXISTS disk error. If <strong>the</strong> file isn't<br />
important, OPEN 2,8,2/"@:SEQ TEST,W" will open a new file with <strong>the</strong> same name.<br />
Appending Sequential<br />
Files<br />
Appending means adding new data onto an already existing file. To append to a<br />
sequential file, use OPEN 2,8,2//SEQ TEST,A" which reopens <strong>the</strong> file, leaving <strong>the</strong><br />
red light on. PRINT#1, "GOODBYE":CLOSE 1 writes an extra record; again this can<br />
be checked by reading.<br />
Sample Uses for Sequential File Processing<br />
Storing records. If a file is to be written once only, open it for write, use INPUT<br />
from <strong>the</strong> keyboard, <strong>the</strong>n PRINT# to write to <strong>the</strong> file and CLOSE <strong>the</strong> file. Where a<br />
file is to have records added from time to time, but none removed or altered, it's<br />
easiest to set up <strong>the</strong> file first, <strong>the</strong>n open it for Append whenever it's needed, INPUT<br />
<strong>the</strong> new data, and PRINT# to <strong>the</strong> file.<br />
Where a file is to be pHitpH/ hnwpypr; use two files—perhaps "NAME" +<br />
STR$(N) followed by "NAME"+STR$(N+1). With this scheme, <strong>the</strong>re will always be<br />
a file called something like "NAMES/PHONES 33" on disk. <strong>The</strong> file-editing pro<br />
gram will ask for <strong>the</strong> update number (33 in this case) and open <strong>the</strong> earlier version<br />
for read and <strong>the</strong> later version for write. Alternatively, you may prefer to rename <strong>the</strong><br />
existing file OLD and write to NEW.<br />
<strong>The</strong> file OPEN commands have <strong>the</strong> following form:<br />
OPEN 2,8,2,"OLD":OPEN 3,8,3,"NEW,W"<br />
INPUT#2 takes data from OLD, while PRINT#3 writes it to NEW.<br />
For security, add a channel-reading subroutine which closes files if an error is<br />
detected.<br />
Dealing with BASIC. <strong>The</strong>re's a close connection between SEQ and PRG files.<br />
Commands to Append, Concatenate, and Copy all work with program files, al<br />
though <strong>the</strong> results don't always appear similar because BASIC uses three zero bytes<br />
as terminators. Thus, appending like this can work only if two of <strong>the</strong>se bytes are<br />
thrown away. BASIC can be written as a sequential file by opening a write file (for<br />
instance, file 1) and using CMD 1: LIST, followed by CLOSE 1, to print <strong>the</strong> program<br />
to <strong>the</strong> file. BASIC stored in this way is not tokenized and is generally longer than its<br />
normal equivalent. But this storage method allows for fairly easy program analysis.<br />
Cross-reference tables of variables by line numbers are a typical application.<br />
As you'll see, <strong>the</strong> directory track can be read as a file, and this gives a lot of<br />
information about <strong>the</strong> way files are stored. Using SAVE "PROGRAM,S,W",8 it's<br />
even possible to save programs as SEQ files.<br />
Copying files. SEQ files can be copied for security ei<strong>the</strong>r with a CBM 4040 disk<br />
drive or by reading <strong>the</strong> data, storing it in RAM, and writing it back onto a new disk.<br />
If <strong>the</strong> file is long, of course, this method is impossible; in such a case <strong>the</strong> best com<br />
promise is to write to a tape file, which obviously has no space restrictions, <strong>the</strong>n<br />
read back and write to <strong>the</strong> new disk.<br />
505
Using Disk Storage<br />
SEQ files occupy 254-byte sectors. Add toge<strong>the</strong>r <strong>the</strong> lengths of all <strong>the</strong> strings of<br />
data, including RETURNS, divide by 254, and round up to estimate <strong>the</strong> storage<br />
requirement of any SEQ file.<br />
Handling Relative<br />
Files<br />
Because of <strong>the</strong>ir highly structured format, relative filgs (REL in <strong>the</strong> directory) allow<br />
bothreading and writing in <strong>the</strong> same openlile.lijyery record is assigned a set lengtK,<br />
which cannot be exceeded! Shorter records are automaticalljrgaddedjvitii nulT<br />
characterslThus, whgSi^g^SL^1^0101^^ ** *s importsj£*j? wr*te kack~<strong>the</strong> entire<br />
j-gcord, or <strong>the</strong> finaTpart will Beie^segXPor^an^pI^WlLLlOMS must be printed<br />
back as WILLIAMS, not as an A at <strong>the</strong> sixth position.<br />
Relative files are referred to by number. <strong>The</strong> DOS uses a P parameter to transfer<br />
<strong>the</strong> record number to disk.<br />
Whenever a record is written beyond <strong>the</strong> present end of file, message 50,<br />
RECORD NOT PRESENT, is generated. <strong>The</strong> first time around, this can be ignored.<br />
You've seen already that it's a good idea to format <strong>the</strong> entire file right at <strong>the</strong> start,<br />
assuming <strong>the</strong> number of records needed in <strong>the</strong> complete file is known. This sets up<br />
each record as CHR$(255), so reading back an empty file lists each record as a *<br />
symbol.<br />
A relative file's data is stored like a sequential file (with ASCII characters sepa<br />
rated by RETURNs), but has an extra file of pointers. A maximum of three disk reads<br />
is needed to read a record with this system, which is <strong>the</strong>refore often slower than a<br />
sequential file, which never uses pointers. Of course, for random access, relative files<br />
are faster than any but <strong>the</strong> shortest sequential files. Records are stored in a disk<br />
buffer, so reading or writing adjacent numbered records often requires no disk access<br />
time.<br />
Use OPEN 2,9>,2l"filename,U" + CHR$(L) to open a relative file. As usual, <strong>the</strong><br />
file number and channel may take a range of values, and <strong>the</strong> device7 number may<br />
not be_8. Using L is compulsory when <strong>the</strong> file is set up for <strong>the</strong> first time; it is fol<br />
lowed by <strong>the</strong> record length, which must allow for a RETURN character. For example,<br />
use £BR&(21) if <strong>the</strong> longest record has length 20,<br />
<strong>The</strong> record Tength parameter is stored on <strong>the</strong> diskette. If you attempt to reopen<br />
<strong>the</strong> file with a different record length, error 50, RECORD NOT PRESENT, shows. <strong>The</strong><br />
maximum record length is 254. Anything beyond this gives error 51, OVERFLOW IN<br />
RECORD.<br />
Once <strong>the</strong> file has been opened, <strong>the</strong> L and parameter are optional and a simple<br />
OPEN statement with <strong>the</strong> name is sufficient. It makes sense to use <strong>the</strong> full version,<br />
though, in case you forget <strong>the</strong> record length.<br />
Obviously, <strong>the</strong> number of <strong>the</strong> record which is about to be read or written must<br />
be sent to disk. <strong>The</strong> syntax is tricky: PRINT#15/P" + CHR$(channel) + CHR$(/ow;<br />
byte) + CHR$(high byte) + CHR$(position) assuming OPEN 15,8,15. <strong>The</strong> channel<br />
parameter is identical to <strong>the</strong> channel used in OPEN, 2 in <strong>the</strong> example above. <strong>The</strong><br />
low/high byte format is familiar. Thus, record number 200 needs PRINT#15,<br />
"P"+CHR$(2)+CHR$(200) + CHR$(0)+CHR$(l).<br />
<strong>The</strong> final parameter is a pointer, with 1 representing <strong>the</strong> start of <strong>the</strong> record. It al<br />
lows writing or reading to take place a set distance within a record. Obviously, it<br />
shouldn't exceed <strong>the</strong> record length. It is usually 1. Don't omit it.<br />
506
Using Disk Storage<br />
<strong>The</strong> pointer allows records to be subdivided, so that a 200-byte record might<br />
have several fields (for instance, starting at 1, 40, and 100). In practice, each field can<br />
be written within its record sequentially, without bo<strong>the</strong>ring with this, because<br />
PRINT#, INPUT#, and GET# each advance <strong>the</strong> pointer as <strong>the</strong>y write or read into<br />
<strong>the</strong> file buffer. In any case, writing to such a record requires that everything up to<br />
<strong>the</strong> final record be written. If only <strong>the</strong> field starting at 40 were written, <strong>the</strong> field<br />
starting at 100 would be erased.<br />
Program 15-7 is an example of relative file handling. It asks for record numbers,<br />
<strong>the</strong>n for <strong>the</strong> data to be input, and writes <strong>the</strong> record to disk. It does not include a<br />
read of <strong>the</strong> error channel. To end <strong>the</strong> program, enter a record number of 99999.<br />
Program 15-7. Handling Relative Files<br />
10 OPEN 15,8,15<br />
20 PRINT "REL. FILENAME":INPUT N$<br />
30 PRINT "RECORD LENGTH":INPUT L<br />
40 OPEN 1,8,2,N$+",L,"+CHR$(L+1)<br />
50 INPUT "RECORD#";R<br />
60 IF R=99999 THEN CLOSE 1:END<br />
70 RH%=R/256:RL=R-RH%*256<br />
v-80 PRINT "RECORD":INPUT R$<br />
^90<br />
R$=LEFT$(R$,L)<br />
100 PRINT#15,"P"+CHR$(2)+CHR$(RL)+CHR$(RH%)+CHR$(1<br />
)<br />
110 PRINT#1,R$<br />
120 GOTO 50<br />
Line 40 opens a relative file, named by <strong>the</strong> user, and assigns a record length one<br />
greater than <strong>the</strong> value entered (to allow tor a RETURNjinhe end). Line 60 allows ^<br />
record number 99999 to act as an indicator that no more data is to be entered. Lines<br />
80 and 90 take in <strong>the</strong> material to be written to disk and check that it isn't too long.<br />
Line 100 sets <strong>the</strong> record number parameters from <strong>the</strong> channel number and record<br />
number. Line 110 finally puts <strong>the</strong> record onto disk. This is <strong>the</strong> simplest case, where<br />
<strong>the</strong> record starts at <strong>the</strong> beginning of its allotted space.<br />
Channel 15 can be read as usual. A subroutine call in a new line 45 could check<br />
<strong>the</strong> OPEN, and lines 105 and 115 can also be added to test <strong>the</strong> command and <strong>the</strong><br />
print.<br />
Remember that message 50 signals that <strong>the</strong> file is being extended, and <strong>the</strong>refore<br />
it should be expected when <strong>the</strong> file is being set up.<br />
Reading <strong>the</strong> file. <strong>The</strong> easiest way to read <strong>the</strong> file you've just created is to mod<br />
ify <strong>the</strong> write program, delete lines 80 and 90, and alter 110 to INPUT#1,R$: PRINT<br />
R$. Line 30 can be removed, and 40 OPEN 1,8,2,N$ is sufficient.<br />
<strong>The</strong> same file is usable for ei<strong>the</strong>r reading or writing. Typically, a program will<br />
have a menu allowing ei<strong>the</strong>r mode to be selected.<br />
Copying. Use OPEN 15,8,15,"C:new name=old name" to copy this type of file<br />
onto <strong>the</strong> same disk with a new name. This provides some security. Apart from direct<br />
disk copying, or putting data into RAM (where <strong>the</strong>re may not be enough available<br />
room), copying records to tape by reading <strong>the</strong>m in sequence, <strong>the</strong>n writing <strong>the</strong>m back<br />
to a different diskette, is <strong>the</strong> easiest method.<br />
507
Using Disk Storage<br />
Storage. <strong>The</strong> total file length is <strong>the</strong> L parameter multiplied by <strong>the</strong> largest record<br />
number, plus one (since record 0 exists). One thousand records of length 21 occupy<br />
21,000 bytes; <strong>the</strong>se are stored in 254-byte sectors, so <strong>the</strong> data occupies 83 sectors.<br />
(<strong>Commodore</strong> <strong>64</strong> disks have 6<strong>64</strong> free sectors.) Additionally, <strong>the</strong> side sectors contain<br />
ing <strong>the</strong> pointers occupy from 1 to 6 sectors, depending on <strong>the</strong> file length. <strong>The</strong> actual<br />
number is <strong>the</strong> file length in sectors divided by 120; so our example file needs 1 side<br />
sector only, and <strong>the</strong> entire file takes 84 sectors.<br />
A relative file which fills <strong>the</strong> entire diskette takes about five minutes to set up<br />
when first written.<br />
Note that some versions of CBM DOS aren't reliable in validating disks with rel<br />
ative files; however, 1540/1541 DOS apparently does not have this fault.<br />
Summary of<br />
Disk Commands and Messages<br />
<strong>The</strong> following list summarizes <strong>the</strong> <strong>64</strong> disk drive's file and program commands. All<br />
<strong>the</strong> syntax examples are illustrations only; modifying <strong>the</strong>m to your own requirements<br />
where necessary isn't much work.<br />
Append. Used mainly for sequential files, Append opens an already existing<br />
sequential file for write. New records are added to <strong>the</strong> end of <strong>the</strong> existing file. <strong>The</strong><br />
syntax is OPEN 2,8,2,7/tettame,A" <strong>the</strong>n PRINT#2 and CLOSE 2.<br />
Copy and Concatenate. Used mainly for sequential files, <strong>the</strong>se commands create<br />
a new file with a new name, consisting of a copy of just one file or of several con<br />
catenated files. OPEN 15,8,15,"C:NEW=FIRST,SECOND,THIRD" combines three<br />
files in sequence in <strong>the</strong> new sequential file called NEW.<br />
Directory. This command lists any diskette's contents. Its form is LOAD "$",8<br />
<strong>the</strong>n LIST. Getting a directory in this way will destroy any BASIC program in mem<br />
ory, so during program development, never read <strong>the</strong> directory unless you've saved<br />
<strong>the</strong> current version. <strong>The</strong> DOS wedge program on <strong>the</strong> demo disk reads <strong>the</strong> directory<br />
directly into <strong>the</strong> screen and can <strong>the</strong>refore be used during program development.<br />
Initialize. Usually automatic, this command reads <strong>the</strong> present diskette's storage<br />
details into disk RAM. If diskettes are changed, it's always safest to initialize <strong>the</strong> disk<br />
drive with OPEN 15,8,15/T':CLOSE 15. <strong>The</strong> command is necessary in some pro<br />
grams reading directly from <strong>the</strong> diskette and provides a means to get <strong>the</strong> drive work<br />
ing again in occasional anomalous situations. <strong>The</strong> <strong>64</strong>'s RAM is unaffected.<br />
New. New, used to format all new disks, has been explained earlier. <strong>The</strong> syntax<br />
is OPEN 15,8,15/'N:name,ID" for a brand-new diskette. Be careful with this<br />
command.<br />
OPEN, CLOSE. OPEN and CLOSE were discussed in <strong>the</strong> sections on program,<br />
sequential, and relative files, and in <strong>the</strong> discussion of channel 15. Typical BASIC is<br />
as follows:<br />
OPEN 15,8,15,"COMMAND STRING" to <strong>the</strong> command channel<br />
OPEN 2,8,2,"SEQFILE,S,W" to open SEQFILE for write<br />
OPEN 3,8A"@:PROGRAM,P,W" to scratch and reopen PROGRAM for write<br />
OPEN 4,8,4,"RELFILE,L,"+CHR$(101) to open RELFILE<br />
CLOSE 2: CLOSE 15 to close two files.<br />
Record#. For relative files only, this sets <strong>the</strong> record number and position within<br />
<strong>the</strong> record from which write or read will take place. Typical syntax is<br />
508
Using Disk Storage<br />
PRINT#15/T//+CHR$(4)+CHR$(LO)+CHR$(HI)+CHR$(l) where OPEN 15,8,15<br />
is assumed and <strong>the</strong> relative file uses channel 4.<br />
Rename. Rename changes <strong>the</strong> name of any type of file. It has <strong>the</strong> syntax OPEN<br />
15,8,15/'R:NEW=OLD//:CLOSE 15 where <strong>the</strong> file previously called OLD is now<br />
called NEW. Only <strong>the</strong> name is changed; a duplicate file is not created.<br />
Scratch. Scratch deletes any type of file by name, using pattern matching. Typi<br />
cally, it uses <strong>the</strong> form OPEN 15,8,15//S:///enflme":CLOSE 15.<br />
Validate. This command checks disk integrity. It tests and collects toge<strong>the</strong>r all<br />
<strong>the</strong> disk sectors' chaining. This is safe with any type of closed file but will erase un<br />
closed files; its syntax is OPEN 15,8,15,"V":CLOSE 15.<br />
Use Validate whenever disk writes have been interrupted, for example, by a syn<br />
tax error in BASIC. However, if you have an incomplete file you wish to save, first<br />
follow <strong>the</strong> instructions below. Validate is also useful for cleaning up a heavily used<br />
disk. If you have scratched and resaved programs many times (for example, during<br />
program development), <strong>the</strong> disk may contain more free blocks than are shown by<br />
LISTing <strong>the</strong> directory. Validate will reorganize such a disk and free up all of <strong>the</strong> un<br />
used blocks.<br />
Pattern Matching<br />
Disk commands involving loading or opening for reading generally can be abbre<br />
viated using * and ? as pattern-matching symbols. For example, LOAD "A*",8 loads<br />
<strong>the</strong> first PRG-type file in <strong>the</strong> directory which begins with A. However, LOAD"*",8<br />
and VERIFY "*",8 assume <strong>the</strong> last loaded program applies, unless no program has<br />
been loaded, in which case <strong>the</strong> first program loads. Similarly, OPEN 2,8,2,"S:X*":<br />
CLOSE 2 scratches all files beginning with X.<br />
<strong>The</strong> question mark (?) allows wild-card matching, but <strong>the</strong> exact positions have to<br />
match; LOAD "????BON*",8 loads TROMBONE, but not BONZO.<br />
Because of <strong>the</strong> possibility of sending spurious disk commands, you should not<br />
include symbols like * ? # : , in filenames.<br />
Problems with Disk Drives<br />
Unresponsive drive. Sometimes you'll get ?FILE NOT FOUND, even with LOAD<br />
"$",8. Try again. If that doesn't work, open <strong>the</strong> disk door and close it, <strong>the</strong>n try<br />
again.<br />
You may also get 7DEVICE NOT PRESENT when <strong>the</strong> disk is switched on and<br />
ready. Try initializing, or if <strong>the</strong> red light is lit, OPEN 15,8,15: CLOSE 15. Sometimes<br />
a printer causes this hang-up. Try turning your printer off.<br />
See Table 15-1 for a summary of <strong>the</strong> messages generated by <strong>the</strong> disk drive.<br />
File problems. Unclosed files are signaled with an asterisk in <strong>the</strong> directory entry<br />
(for example, *SEQ). However, some aborted files don't have this. You may have a<br />
situation where a program occupies two sectors, even though its file is reported as<br />
occupying only one, and <strong>the</strong>re are only 618 blocks free instead of 661 as expected. In<br />
each case it's ultimately best to validate, but you could ei<strong>the</strong>r leave <strong>the</strong> disk alone,<br />
using it only for reading, or recover some of <strong>the</strong> file data, using OPEN<br />
2,%,2,"fHename,M" which enables unclosed files of all types to be read as far as<br />
possible.<br />
509
CJ1<br />
o<br />
Table 15-1. Summary of Disk Drive Messages<br />
Message<br />
Type<br />
Information<br />
(not an error)<br />
<strong>Programming</strong> Mistake or<br />
Simple Mechanical Error<br />
0 Everything OK<br />
1 Files scratched<br />
(gives number)<br />
2-19 Undocumented.<br />
Not important.<br />
Input/<br />
Output<br />
Errors<br />
at<br />
Disk<br />
Level<br />
Initialization<br />
29 Disk ID/BAM mismatch<br />
Syntax<br />
Errors<br />
30 Syntax error<br />
31 Unrecognized command<br />
32 Overlength command<br />
33 Wrongly used ? or * in name<br />
34 File name omitted<br />
39 Unrec. command to channel 15<br />
Relative<br />
and Seq.<br />
Files<br />
50 Expand relative<br />
file size<br />
50 Relative file parameter error<br />
51 Relative record too long<br />
52 Relative file too big for disk<br />
File<br />
Errors<br />
60 Attempt to read a write file<br />
61 File not open<br />
62 File doesn't exist<br />
63 File does exist<br />
<strong>64</strong> File type mismatch<br />
Track<br />
and<br />
Sector<br />
Errors<br />
65 Block-Allocate error: gives<br />
next available track & sector<br />
66 Track or sector out of range<br />
67 System track or sector error<br />
DOS<br />
Errors<br />
70 Channel to disk unavailable<br />
71 Error in directory<br />
72 Disk (or directory) full<br />
73 DOS type message<br />
74 Drive not ready<br />
Hard Error<br />
20 Sector header not found<br />
21 Sync mark not found<br />
22 Sector not found<br />
23 Checksum error in byte<br />
24 Byte read error<br />
25 Readback compare error<br />
26 Write-protected diskette<br />
27 Checksum error in header<br />
28 Next sync mark not found<br />
s<br />
q<br />
W<br />
o<br />
3<br />
(Q<br />
(D
Using Disk Storage<br />
To avoid this sort of problem, simply make a point of closing write files if a pro<br />
gram crashes before <strong>the</strong>y are closed properly. Enter CLOSE 2 in direct mode, for ex<br />
ample, and include a CLOSE statement for channel 15.<br />
OPEN 15,8,15:CLOSE 15 will generally close all disk files successfully.<br />
Program problems. Sometimes <strong>the</strong> final sector of programs becomes corrupted;<br />
on LOAD <strong>the</strong> program loads, but READY never appears. <strong>The</strong> best solution is to press<br />
RUN/STOP-RESTORE, <strong>the</strong>n POKE zeros into memory after <strong>the</strong> end of <strong>the</strong> program,<br />
using BASIC'S pointers at locations 45 and 46 to locate <strong>the</strong> end.<br />
<strong>Commodore</strong> Utility<br />
Programs<br />
<strong>Commodore</strong>'s demo disks contain a number of programs. Those listed below are<br />
typical of what you will find.<br />
CHECK DISK<br />
Tests a diskette by writing to and reading from every sector.<br />
COPY/ALL<br />
A BASIC program, written by Jim Butterfield, to copy an entire disk from one drive<br />
to ano<strong>the</strong>r. Two drives are necessary. One drive can be reassigned device number 9<br />
with DISK ADDR CHANGE.<br />
DIR<br />
Reads <strong>the</strong> directory of device 8 from BASIC. No advantage over ordinary directories.<br />
DISK ADDR CHANGE<br />
Writes a new device number through <strong>the</strong> command channel, usually 9, to permit<br />
interdrive copying.<br />
DISPLAY T&S<br />
Displays any track and sector on <strong>the</strong> diskette. Very useful for examining <strong>the</strong> disk's<br />
entire storage system or (in extreme cases) for reading programs or files directly.<br />
DOS 5.1<br />
<strong>The</strong> <strong>64</strong> wedge. Use this to make direct mode disk commands simpler. <strong>The</strong> wedge<br />
will not coexist with some o<strong>the</strong>r utilities. It adds <strong>the</strong>se direct mode commands:<br />
@ alone reads <strong>the</strong> disk status and prints it,<br />
@$ reads and displays <strong>the</strong> directory, without affecting BASIC,<br />
/PROGRAM loads PROGRAM.<br />
Some versions allow a LOAD and RUN option, and an abbreviated SAVE command.<br />
All versions allow > (a wedge) as an alternative to (5).<br />
PERFORMANCE TEST<br />
Formats a disk, writes, and reads, but doesn't exhaustively test ei<strong>the</strong>r diskette or<br />
drive.<br />
511
Using Disk Storage<br />
PRINTER TEST<br />
For CBM printers only.<br />
VIC-20 WEDGE<br />
This program simplifies disk commands for <strong>the</strong> VIC-20, as DOS 5.1 does for <strong>the</strong> <strong>64</strong>.<br />
VIEW BAM<br />
Prints a diagram of <strong>the</strong> Block Availability Map.<br />
Hardware Notes<br />
1540/1541 Disk Drive Units<br />
<strong>The</strong>se drives contain a transformer to supply power, a printed circuit board contain<br />
ing <strong>the</strong> ROM, RAM, and interface chips which hold <strong>the</strong> disk operating system, and a<br />
drive unit, which is positioned away from <strong>the</strong> heat-generating components. Some<br />
models have metal shielding over <strong>the</strong> printed circuit board to reduce radio frequency<br />
emission. Both <strong>the</strong> shielding and <strong>the</strong> top half of <strong>the</strong> outer casing are easily removed<br />
(for example, to exchange a 1540 ROM for a 1541 ROM or to change <strong>the</strong> device<br />
number from 8). <strong>The</strong> design is similar to earlier <strong>Commodore</strong> disk drives, <strong>the</strong> 2031<br />
single disk and 4040 double disk.<br />
<strong>The</strong> device number can be set to any number from 8 to 15. At least four drives<br />
can be daisychained toge<strong>the</strong>r, so in principle, a four-drive system would be feasible.<br />
Given <strong>the</strong> right hardware, a single <strong>64</strong> can also share a disk drive with o<strong>the</strong>r <strong>64</strong>s.<br />
<strong>The</strong> read/write head faces up, so <strong>the</strong> underside of <strong>the</strong> diskette is <strong>the</strong> active side.<br />
Closing <strong>the</strong> door brings a pressure pad down on <strong>the</strong> head, keeping it in close contact<br />
with <strong>the</strong> diskette. During read/write operations, <strong>the</strong> diskette is rotated by <strong>the</strong> spindle<br />
motor at about 300 revolutions per minute, and centrifugal force gives <strong>the</strong> diskette<br />
some rigidity. <strong>The</strong> head itself is mounted on rails and can move, along with <strong>the</strong><br />
pressure pad, a maximum of about one inch. Movement is handled by a stepper<br />
motor. Each step moves <strong>the</strong> head about 1/30 inch.<br />
<strong>The</strong>se drives use 35 tracks. <strong>The</strong> actual magnetized zones are about 1/60 inch<br />
wide; <strong>the</strong> clutch mechanism which grips <strong>the</strong> diskette has to position it within that<br />
tolerance.<br />
Head alignment problems sometimes occur, in which diskettes work on one disk<br />
drive but not on ano<strong>the</strong>r, because <strong>the</strong> heads aren't quite in <strong>the</strong> same place relative to<br />
<strong>the</strong> disk center. Special alignment diskettes, having very slightly elliptical tracks,<br />
allow a disk drive head to be accurately repositioned. Realigning disk drive heads is<br />
specialized work.<br />
Diskettes<br />
1540/1541 disk drives use 5-1/4-inch floppy disks. Any good-quality, single-sided,<br />
single-density diskettes are fine. Soft-sectored diskettes are generally used, but hardsectored<br />
disks will also work well, as <strong>the</strong>ir index hole isn't used by <strong>the</strong> drives.<br />
Write-protection is readily implemented with 1540/1541 drives. An adhesive tab<br />
over <strong>the</strong> notch prevents writing to <strong>the</strong> disk. Attempting to write to such a disk re<br />
turns 26 WRITE PROTECT ON.<br />
512
Using Disk Storage<br />
Figure 15-3. A Typical Diskette<br />
Stress-Reducing Notches<br />
4, *,<br />
Read/Write<br />
Slot<br />
—><br />
-Track 1<br />
-Directory Track<br />
-Track 35<br />
Index<br />
Hole<br />
Write<br />
r-Protect<br />
Notch<br />
Label.<br />
Diskettes are inserted label up, read/write slot foremost. Diskette labels are<br />
deliberately positioned away from <strong>the</strong> slot, to reduce <strong>the</strong> chance of fingerprint dam<br />
age and to allow <strong>the</strong> label to be read when <strong>the</strong> diskette is in its dust cover. Writing<br />
on <strong>the</strong> label with a sharp implement—for instance, a ballpoint pen—may damage<br />
<strong>the</strong> diskette surface below. Always write on <strong>the</strong> label before putting it on <strong>the</strong> disk.<br />
It is good practice to open <strong>the</strong> drive door when drives are turned on or off.<br />
<strong>The</strong>re's some small chance of magnetic "glitch" damage to a diskette that's left in a<br />
drive with <strong>the</strong> door closed when power is turned on.<br />
It's easy to modify diskettes so that both sides are usable. <strong>The</strong> index hole isn't a<br />
factor; all that's needed is to cut a notch in <strong>the</strong> diskette opposite <strong>the</strong> write-protect<br />
notch. <strong>The</strong> diskette <strong>the</strong>n works on ei<strong>the</strong>r side. However, that may not be desirable.<br />
<strong>The</strong> standard argument against this practice is that small particles of dust, smoke,<br />
and o<strong>the</strong>r debris, which become trapped by <strong>the</strong> self-cleaning wiper which lines <strong>the</strong><br />
diskette, may be dislodged when <strong>the</strong> direction of rotation is reversed. In addition,<br />
some single-sided diskettes have defects on <strong>the</strong> back side. None<strong>the</strong>less, quite a num<br />
ber of people do this successfully.<br />
Diskette life is typically quoted as several million passes per track. At 300 rpm<br />
this represents about a week's continuous running.<br />
Track and Sector Storage System<br />
All 1540/1541 units use 35 tracks, defined by <strong>the</strong> head positions. Track 18 is exactly<br />
midway between <strong>the</strong> edge and center of <strong>the</strong> disk, and it stores all <strong>the</strong> directory infor<br />
mation, thus minimizing delays due to head movement. When a disk is formatted,<br />
<strong>the</strong> head moves to <strong>the</strong> outer track (track 1) end stop, <strong>the</strong>n counts in, one track at a<br />
time, to 35. <strong>The</strong> same head movement to track 1 (making a rapid clicking sound)<br />
513
Using Disk Storage<br />
happens whenever <strong>the</strong>re is a read error. This occurs because <strong>the</strong> head counts in until<br />
it arrives at its correct track, <strong>the</strong>n tries reading again in case its position was wrong<br />
before.<br />
A track is not a solid block of data. Instead, it is broken into 256-byte blocks<br />
called sectors. Any program or file is stored in sectors, and <strong>the</strong> first two bytes are al<br />
ways pointers to <strong>the</strong> next sector.<br />
Sector storage tolerates some, but not much, variation in disk rotation speed. If<br />
<strong>the</strong> disk spins too fast, sectors will overlap and data will be lost. Typically, <strong>the</strong>re's at<br />
least a one-second delay between starting <strong>the</strong> disk motor and writing or reading<br />
data. For this reason, <strong>the</strong> motor is left on for some time after an access, so if ano<strong>the</strong>r<br />
access follows shortly, no time is lost waiting for <strong>the</strong> speed to build up.<br />
<strong>Commodore</strong>'s system uses more sectors on <strong>the</strong> outer tracks than <strong>the</strong> inner. This<br />
takes advantage of <strong>the</strong> fact that <strong>the</strong> outer circumference is greater than <strong>the</strong> inner, in<br />
<strong>the</strong> same way that o<strong>the</strong>r recording media usually give better resolution at <strong>the</strong> edge<br />
than near <strong>the</strong> middle. However, because <strong>the</strong> angular speed is constant, outer tracks<br />
must be written and read more rapidly than inner tracks, so hard sectoring is<br />
impossible.<br />
Sectors are not written in sequence around <strong>the</strong> disk. If an entire track is filled<br />
with data from a single file or program, it's more efficient to chain sectors which are<br />
far apart on <strong>the</strong> disk, so that only half a revolution (ra<strong>the</strong>r than a whole revolution)<br />
is lost between reads or writes. A typical sequence on <strong>the</strong> outer tracks is 8, 18, 6, 16,<br />
4, 14, 2, 12, 0, 10, 20, 9, 19, 7, 17, 5, 15, 3, 13, 1, and finally 11.<br />
Sectors are stored with a short header, followed by data. Each part begins with a<br />
so-called sync field and ends with a checksum. <strong>The</strong> header contains 08, a two-byte<br />
ID, and <strong>the</strong> track and sector number. <strong>The</strong> data is preceded by 07. Messages 20-29<br />
from <strong>the</strong> disk may indicate that some aspect of this elaborate error-checking system<br />
has failed. For example, if a magnet is held near <strong>the</strong> edge of a diskette, <strong>the</strong> outer sec<br />
tors become unreadable. This technique can be used to protect disks from being<br />
copied.<br />
<strong>The</strong> conversion of bytes into magnetic patterns on disk, and vice versa, is an an<br />
alog hardware function, relying on cross-over detectors, amplifiers, and pulse<br />
shapers.<br />
Changing <strong>the</strong> Disk Device Number from 8<br />
Device number 8 is set by hardware, and many programs using disk assume drive 8.<br />
<strong>The</strong>refore, it is generally better to use software to alter <strong>the</strong> device number, even<br />
though <strong>the</strong> process has to be repeated whenever <strong>the</strong> drive is turned off. <strong>The</strong> excep<br />
tional case, where hardware change is desirable, occurs with a fairly permanent<br />
setup with two drives. In such a case, <strong>the</strong> change can be made permanent, or <strong>the</strong><br />
disk unit can be fitted with a switch to select its device number.<br />
Software conversion is easily done using CHANGE DISK ADDR on <strong>the</strong> demo<br />
disk. This program, which works with any <strong>Commodore</strong> disk, writes <strong>the</strong> new device<br />
number into two disk RAM locations. <strong>Commodore</strong> disks vary a great deal internally,<br />
so <strong>the</strong> program also has to work out <strong>the</strong> type of disk drive. With <strong>the</strong> <strong>64</strong>, use OPEN'<br />
15,8,15:PRINT#15/'M-W"CHR$(119)CHR$(0)CHR$(2)CHR$(32+9)CHR$(<strong>64</strong>+9):<br />
CLOSE 15 to convert from 8 to 9; <strong>the</strong> analogous statement will work for any o<strong>the</strong>r<br />
conversions within <strong>the</strong> range 8-15.<br />
514
Using Disk Storage<br />
When two drives are used, <strong>the</strong>y must be turned on separately. Typically, one<br />
drive is turned on, <strong>the</strong>n <strong>the</strong> <strong>64</strong> is turned on, <strong>the</strong>n <strong>the</strong> live disk's number is changed,<br />
and <strong>the</strong> second drive is turned on. In that way <strong>the</strong> system isn't confused. LOAD<br />
"$",8 and LOAD "$",9 load directories from <strong>the</strong> respective drives.<br />
Hardware conversion involves cutting jumpers. <strong>The</strong>se jumpers are not wires, but<br />
round spots of solder on <strong>the</strong> circuit board, separated into halves, with a thin strand<br />
of solder connecting each half. You cut <strong>the</strong> jumpers by scraping away, or breaking,<br />
<strong>the</strong> connecting strand with a sharp knife.<br />
<strong>The</strong> actual board layouts vary. <strong>The</strong> jumpers in <strong>the</strong> 1540 and early 1541 disk<br />
drives are located on <strong>the</strong> left side of <strong>the</strong> circuit board as you face <strong>the</strong> front of <strong>the</strong><br />
disk drive. On <strong>the</strong> newer 1541 drives, <strong>the</strong> jumpers are in <strong>the</strong> center of <strong>the</strong> board.<br />
<strong>The</strong> early 1541 drives can be identified by <strong>the</strong>ir white cases, while <strong>the</strong> newer 1541<br />
drives have brown cases. In both versions, jumper number 1 is nearest <strong>the</strong> front, and<br />
just behind it is jumper 2. Figure 15-4 shows <strong>the</strong> layout.<br />
Figure 15-4. Changing Drive Numbers by Hardware Modification<br />
Jumper 2<br />
Jumper 1<br />
Front of drive<br />
I<br />
Cutting only jumper 1 changes <strong>the</strong> device number to 9. Cutting only jumper 2<br />
changes it to 10. Cutting both jumpers changes it to 11. Note that opening <strong>the</strong> drive<br />
case to do this will probably void your warranty. To avoid severe electrical shock, do<br />
not attempt any such operation until you have turned <strong>the</strong> drive off and unplugged<br />
every connector. If you're not sure what's involved, get help from someone who<br />
understands electronics.<br />
Disk ROM<br />
<strong>Commodore</strong> disk drives have internal ROM from $C000 to $FFFF and RAM from $0<br />
to $07FF. It's easy to disassemble disk ROM, because disk memory can be read with<br />
<strong>the</strong> following command:<br />
PRINT#15/'M-R//CHR$(/ow;)CHR$^W:GET#15/X$:X=ASC(X$+CHR$(0))<br />
That assumes, of course, that OPEN 15,8,15 has been performed. <strong>The</strong> value X is <strong>the</strong><br />
result of using GET#, which in this case is equivalent to PEEKing <strong>the</strong> disk's memory.<br />
<strong>The</strong> low and high bytes of <strong>the</strong> location should be used in place of low and high. You<br />
can disassemble <strong>the</strong> ROM by replacing PEEK in a BASIC disassembler with this<br />
routine.<br />
515
Using Disk Storage<br />
Disk ROM has <strong>the</strong> conventional 6502 features, including NMI, Reset, and IRQ<br />
vectors at <strong>the</strong> top of memory. It also has tables of error messages and tables of com<br />
mands, some of which are undocumented.<br />
Minimizing<br />
Errors<br />
To minimize errors, <strong>the</strong> general rules are simple: Keep <strong>the</strong> disk drive free of dust,<br />
smoke, and o<strong>the</strong>r contaminants; store and treat <strong>the</strong> diskettes properly; keep copies of<br />
programs and data; and so on. It's worth having a standby system if your <strong>64</strong> is used<br />
for any serious purpose.<br />
Hardware errors are rare; one bad bit in 1011 is typical of quoted figures. Errors<br />
caused by unclosed files are far more likely. With some systems, programs to vali<br />
date data may be used. Such systems can be written to minimize disk use, favoring<br />
RAM where possible to minimize <strong>the</strong> probability of a mistake.<br />
Disk Data Storage<br />
As stated earlier, <strong>Commodore</strong> disks have 35 tracks. Of those tracks, 17 have 21 sec<br />
tors each, 7 have 19 sectors each, 6 have 18 sectors each, and 5 have 17 sectors<br />
each. That gives a total of 683 sectors. Track 18 holds <strong>the</strong> directory information.<br />
Subtracting 19 for <strong>the</strong> directory gives 6<strong>64</strong> blocks free, as reported by <strong>the</strong> directory<br />
for an empty disk. And 6<strong>64</strong> blocks of 254 bytes (excluding <strong>the</strong> track and sector<br />
pointers) gives 168,656 usable bytes. Relative files, as you've seen, require slightly<br />
more space; an entire diskette filled with a single relative file can occupy 658 blocks<br />
(167,132 bytes at most). Table 15-2 shows how <strong>the</strong> sectors are arranged on a disk.<br />
Table 15-2. Number of Sectors per Traok<br />
Track Number<br />
1-17<br />
18-24<br />
25-30<br />
31-35<br />
Sectors<br />
0-20<br />
0-18<br />
0-17<br />
0-16<br />
<strong>The</strong> directory track, track 18, is diagrammed in Figure 15-5 and has 19 sectors.<br />
Sector 0 holds <strong>the</strong> disk name, as well as a bitmap of every sector on <strong>the</strong> disk, show<br />
ing whe<strong>the</strong>r <strong>the</strong> sector is used or not. Sectors 1-18 store file type, filename, and<br />
pointers to <strong>the</strong> actual data. Each of <strong>the</strong>se sectors can store eight filenames, giving a<br />
maximum of 144 directory entries.<br />
Figure 15-5. Traok 18, <strong>the</strong> Directory Traok<br />
| Sector 0 | Sectors 1 through 18 |<br />
t<br />
Disk Name ** Directory Entries (up to 144) ►<br />
and BAM<br />
516
Using Disk Storage<br />
Each time a file is written, <strong>the</strong> BAM (Block Availability Map) is updated, so <strong>the</strong><br />
system knows which sectors are free for subsequent recording. VIEW BAM on <strong>the</strong><br />
demo disk prints a diagram of this map. To see how this works, load DISPLAY T&S<br />
and inspect track 18, sector 0. Its layout is described in Table 15-3.<br />
Table 15-3. Track 18, Sector 0 (BAM)<br />
Byte Numbers<br />
0,1<br />
2,3<br />
4-143<br />
144-159<br />
162-163<br />
165-166<br />
Track 18, Sector 0 (Directory Track)<br />
Pointer to directory entries—track 18, sector 1<br />
Disk format A<br />
BAM (Block Availability Map):<br />
35 sets of 4 bytes each<br />
Diskette Name (16 characters maximum)<br />
Diskette ID<br />
2A (version of disk operating system)<br />
(Omitted bytes are SHIFT-spaces, $A0, or spaces, $20. Remember, DISPLAY T&S prints values in hex.)<br />
BAM<br />
Each of <strong>the</strong> 35 tracks is represented by four bytes in <strong>the</strong> BAM, as shown in Table 15-4.<br />
Table 15-4. BAM Organization<br />
First Byte<br />
Second Byte<br />
Third Byte<br />
Fourth Byte<br />
Number of sectors<br />
free in this track<br />
[From 0 to 21]<br />
Bits for sectors<br />
7,6,5,4,3, 2, 1, 0<br />
[0=used, 1=free]<br />
Bits for sectors<br />
15,14,13,12,11,10,9, 8<br />
[0=used, 1=free]<br />
Bits for sectors<br />
X, X, X, 20,19,18,17,16<br />
[0=used or unavailable, 1=free]<br />
For example, <strong>the</strong> first track may appear as:<br />
04: 15 FF FF IF<br />
<strong>The</strong> value of <strong>the</strong> first byte is 21 ($15), which means that all 21 sectors of track 1 are<br />
free. <strong>The</strong> hex value in <strong>the</strong> second and third bytes is $FF (bit pattern 11111111),<br />
showing that sectors 0-7 and 8-15 are all free. <strong>The</strong> hex value of <strong>the</strong> fourth byte is<br />
$1F (bit pattern 0001 1111), meaning sectors 16 through 20 are unused as well.<br />
VIEW BAM picks through and displays <strong>the</strong>se bit patterns. Note <strong>the</strong> way information<br />
is preferentially stored near <strong>the</strong> middle track to minimize head movement time.<br />
Directory Entries<br />
Directory entries are fairly straightforward. Use DISPLAY T&S on track 18, sector 1;<br />
you'll find it split into eight sets of 32 bytes each. Except for <strong>the</strong> first 2 bytes of <strong>the</strong><br />
sector, which serve as a link to <strong>the</strong> next directory entry, <strong>the</strong> interpretation is shown<br />
in Table 15-5.<br />
517
Using Disk Storage<br />
Table 15-5. Contents of a Directory Entry<br />
BYTES<br />
0-1<br />
2<br />
3-4<br />
5-20<br />
21-22<br />
23<br />
24-27<br />
28-29<br />
30-31<br />
Contents of a Directory Entry<br />
Track and sector pointer in first entry. O<strong>the</strong>rwise unused.<br />
File Type. $ 0=Scratched/Not yet used<br />
$80=DELeted<br />
$81=SEQuentialfile<br />
$82=PRG, program file<br />
$83=USR, user file<br />
$84=RELative file<br />
$l-$4 signals an unclosed file. Such files are removed by Validate.<br />
$80 is a scratched unclosed file, a type to be avoided.<br />
Track and sector pointer to first block of file<br />
Filename + shifted spaces ($A0 characters)<br />
Track and sector pointer to relative file's first side sector<br />
Record size of relative file (i.e., parameter following L on opening file)<br />
Unused<br />
Replacement track and sector pointer for OPEN®<br />
Low and high byte of no. of blocks in file, as shown on <strong>the</strong> directory<br />
<strong>The</strong> first directory entry in track 18, sector 1 is as follows:<br />
00: 12 04 82 11 Track 18, sector 4 next entry. File is PRG. It starts track 17<br />
04: 00 48 4F 57 Sector 0. Name is: HOW<br />
08: 20 54 4F 20 TO<br />
0C: 55 53 45 A0 USE<br />
10: A0 A0 A0 A0 Name padded with SHIFT-space characters to length 16<br />
14: A0 00 00 00<br />
18: 00 00 00 00<br />
1C: 00 00 0D 00 Occupies 13 sectors<br />
Relative files have slightly more detail than o<strong>the</strong>r file types because of <strong>the</strong>ir in<br />
dex system. A track and sector pointer points to <strong>the</strong> first side sector (of a possible<br />
six), which is linked like any o<strong>the</strong>r file and treated as a separate file by <strong>the</strong> operating<br />
system. <strong>The</strong> record length parameter is also stored here. If you've forgotten it this is<br />
<strong>the</strong> place to look.<br />
<strong>The</strong> side sectors have <strong>the</strong> structure shown in Table 15-6.<br />
Table 15-6. Side Sectors in Relative Files<br />
Bytes<br />
0-1<br />
2<br />
3<br />
4-15<br />
16-255<br />
Contents<br />
Track and sector pointer to next side sector<br />
Side sector number, 0-5<br />
Record length of relative file<br />
6 pairs of pointers to every side sector<br />
120 pairs of pointers to consecutive sectors of data<br />
518
Using Disk Storage<br />
Up to 120 sectors can be stored in one of <strong>the</strong>se blocks. <strong>The</strong> system calculates <strong>the</strong><br />
effect of <strong>the</strong> record it is asked to read or write, by multiplying record length by<br />
record number, <strong>the</strong>n calculates which sector <strong>the</strong> start of <strong>the</strong> record must appear in.<br />
In <strong>the</strong> worst case, a new side sector has to be loaded, a track and sector looked up in<br />
it, <strong>the</strong>n finally <strong>the</strong> correct track and sector read. (If a record straddles two blocks, a<br />
fourth disk movement occurs.)<br />
Six side sectors can cover 720 blocks; this, of course, is enough for a file cover<br />
ing <strong>the</strong> whole diskette. However, in this case an extra channel (for a total of three)<br />
needs to be kept open within <strong>the</strong> disk: one for a side sector, one for a data sector,<br />
and a third for <strong>the</strong> data itself. A sequential file needs only two, one for <strong>the</strong> current<br />
sector and one for <strong>the</strong> correct data. Since 1541 disk drives allow five channels, two<br />
sequential files or one relative file or one of each type can be open at <strong>the</strong> same time.<br />
File and Program Storage<br />
DISPLAY T&S allows any file or program to be examined byte by byte. First, <strong>the</strong><br />
directory entry must be found in track'18. Bytes 3 and 4, immediately after <strong>the</strong> file<br />
type indicator and before <strong>the</strong> filename, show <strong>the</strong> track and sector of <strong>the</strong> first block.<br />
DISPLAY T&S outputs hex numbers and has been modified from earlier versions to<br />
automatically read chained blocks when desired.<br />
Program files (type $82) can be ei<strong>the</strong>r BASIC or ML dumps. <strong>The</strong> first two bytes<br />
are <strong>the</strong> LOAD address (for example, 01 08, $0801, for <strong>64</strong> BASIC). BASIC includes to<br />
kens, link addresses, and line numbers in coded form; though it looks ra<strong>the</strong>r strange,<br />
<strong>the</strong> messages are legible. ML, however, generally needs disassembly since it appears<br />
as a collection of seemingly random characters.<br />
Relative files are stored like sequential files, with <strong>the</strong> addition of side sectors,<br />
which are largely a list of track/sector combinations allotted to <strong>the</strong> relative file and<br />
noted in BAM as allocated.<br />
This may appear complex at first. However, DISPLAY T&S and o<strong>the</strong>r, more<br />
sophisticated disk examination programs will allow you to explore, and <strong>the</strong> system<br />
concepts will soon be easier to understand.<br />
<strong>The</strong> Disk Directory<br />
Both <strong>the</strong> entire disk directory track and <strong>the</strong> directory program can be read from<br />
BASIC. <strong>The</strong> information here will help you examine or modify disk programs, files,<br />
or directory entries by writing directly onto <strong>the</strong> disk.<br />
LOAD"$",8 doesn't load a conventional file. Instead, it processes <strong>the</strong> directory<br />
track, taking <strong>the</strong> diskette name, ID, and DOS version from sector 1, and taking file<br />
type, filename, and file length from <strong>the</strong> directory entries in <strong>the</strong> sequence <strong>the</strong>y are re<br />
corded. Because of this processing, diskettes with many files are slower than fairly<br />
empty diskettes. It is not possible to write to a file called $.<br />
<strong>The</strong> number of blocks free is calculated from <strong>the</strong> individual directory entries. If<br />
file storage has gone awry, <strong>the</strong> computed figure may include files which don't ap<br />
pear in <strong>the</strong> directory; in such a case, validation is desirable. <strong>The</strong> blocks-free figure<br />
sometimes differs from <strong>the</strong> total calculable from <strong>the</strong> BAM entries.<br />
519
Using Disk Storage<br />
Extending <strong>the</strong> Simple Directory<br />
<strong>The</strong> $ directory has its own pattern-matching rules.<br />
LOAD "$:<strong>64</strong>*",8 lists all programs and files beginning with <strong>64</strong>.<br />
LOAD "$:??ML*",8 lists all programs and files with ML at third and fourth positions<br />
LOAD "$:*=S",8 lists only sequential files.<br />
LOAD "$:MUS*=P",8 lists only programs beginning MUS.<br />
LOAD "$:NAME" lists only NAME'S entry.<br />
Reading <strong>the</strong> Directory Within BASIC<br />
<strong>The</strong> directory can be read from within a BASIC program without overwriting <strong>the</strong><br />
program by using OPEN 1,8,0,"$". Use of <strong>the</strong> zero channel is essential. GET#1 <strong>the</strong>n<br />
fetches two bytes (<strong>the</strong> LOAD address), <strong>the</strong>n four bytes (link pointers and line num<br />
bers) followed by a directory line and terminated by a null byte, and so on, until a<br />
link pointer of 0 is found. Program 15-8 shows how this works:<br />
Program 15-8. Reading <strong>the</strong> Directory<br />
10 OPEN 1,8,0,"$"<br />
20 GET#1,X$,X$<br />
30 GET#1,X$,X$,X$,X$<br />
40 IF ST THEN CLOSE 1:END<br />
50 GET#1,X$: IF X$=H" THEN PRINT:GOTO 30<br />
60 IF X$=CHR$(34) THEN Q=NOT Q<br />
70 IF Q THEN PRINT X$;<br />
80 GOTO 50<br />
Sorted<br />
Directory<br />
Program 15-9 prints a directory in <strong>the</strong> usual format, except that <strong>the</strong> names are sorted<br />
alphabetically. That makes it particularly useful if you have lots of programs. It can<br />
be modified for use with a printer and can process any number of disks, one after<br />
ano<strong>the</strong>r.<br />
Program 15-9. Sorted Directory<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
0 DATA 32,115,0,133,97,169,128,133,98,32,115,0,240<br />
,7,9,128,133,98,32,115 :rem 213<br />
1 DATA 0,165,47,133,99,165,48,133,100,160,0,165,97<br />
,209,99,208,7,200,165,98 •rem 79<br />
2 DATA 209,99,240,20,24,160,2,177,99,101,99,72,200<br />
,177,99,101,100,133 :rem 71<br />
3 DATA 100,104,133,99,144,221,160,5,177,99,133,102<br />
,200,177,99,133,101,208 :rem 3<br />
4 DATA 2,198,102,198,101,24,165,99,105,7,133,99,16<br />
5,100,105,0,133,100,165,101 :rem 192<br />
5 DATA 208,2,198,102,198,101,208,4,165,102,240,18,<br />
133,105,162,0,134,103,134 :rem 82<br />
6 DATA 104,165,99,133,106,165,100,133,107,240,224,<br />
240,114,24,165,106,105 :rem 198<br />
520
Using Disk Storage<br />
7 DATA 3,133,106,165,107,105,0,133,107,230,103,208<br />
,2,230,104,160,2,177,106 :rem 17<br />
8 DATA 153,109,0,136,16,248,160,5,177,106,153,109,<br />
0,136,192,2,208,246,170 :rem 7<br />
9 DATA 56,229,109,144,2,166,109,160,(2 SPACES}5,23<br />
2,200,202,208,8,165,112,197,109 :rem 169<br />
10 DATA 144,10,176,34,177,113,209,110,240,238,16,2<br />
6,160,2,185,112,0,145 :rem 142<br />
11 DATA 106,136,16,248,160,5,185,106,0,145,106,136<br />
,192,2,208,246,169,0,133 :rem 49<br />
12 DATA 105,165,101,197,103,208,152,165,102,197,10<br />
4,208,146,165,105,240,138,96 :rem 1<br />
15 REM *** SORT DIRECTORY *** (SEE LINE 40000 FOR<br />
{SPACE}OUTPUT)(2 SPACES}*** :rem 227<br />
20 POKE 56, PEEK(56)-1: CLR :rem 130<br />
30 T = PEEK(55) + 256*PEEK(56) :rem 167<br />
100 FOR J=T TO T+242: READ X: POKE J,X: NEXT<br />
:rem 107<br />
1000 PRINT "INSERT DISK; PRESS(5 SPACES}RETURN"<br />
:rem 58<br />
1002 GET X$: IF ASC(X$+CHR$(0))13 GOTO 1002<br />
srem 19<br />
1004 OPEN 15,8,15,"10": OPEN 1,8,0,"$0" :rem 93<br />
1006 PRINT "OK" srem 50<br />
1008 N=2: GOSUB 10000 :rem 49<br />
1010 N=32: GOSUB 10000: IF ST=0 THEN D=D+l: GOTO 1<br />
010 . srem 191<br />
1012 CLOSE 1: DIM D$(D) :rem 124<br />
1014 T = PEEK(55) + 256*PEEK(56) :rem 10<br />
1100 OPEN 1,8,0,"$0" srem 169<br />
1110 N=6: GOSUB 10000 srem 47<br />
1120 FOR J=l TO 25s GET#l,X$s D$(0)=D$(0)+X$s NEXT<br />
srem 236<br />
2000 N=3s GOSUB 10000: K=K+1s GET#l,Nl$s GET#1,N2$<br />
s IF ST>0 GOTO 20000 srem 24<br />
2010 D$(K) = STR$(ASC(N1$+CHR$(0)) + 256*ASC((N2$)<br />
+CHR$(0))) + " " srem 21<br />
2020 FOR J=l TO 27s GET#1,X$ srem 133<br />
2030 D$(K)=D$(K)+X$s NEXT srem 42<br />
2040 GOTO 2000 srem 193<br />
10000 FOR J=l TO Ns GET#l,X$s NEXTs RETURN srem 42<br />
20000 CLOSE Is CLOSE 15 srem 175<br />
30000 SYSTsD srem 196<br />
40000 OPEN 4,3s REM OR OPEN 4,4 TO DISPLAY TO PRIN<br />
TER srem 190<br />
40005 PRINT#4,CHR$(147) srem 247<br />
40010 FOR J=0 TO K-ls PRINT#4,"{10 SPACES}" D$(J)s<br />
NEXT srem 233<br />
40020 FOR J=l TO 10s PRINT#4s NEXT srem 48<br />
40030 CLOSE 4 srem 161<br />
40040 CLRs GOTO 1000 srem 13<br />
521
Using Disk Storage<br />
Counting Blocks Free Within BASIC<br />
Program 15-10 prints <strong>the</strong> number of blocks free, as reported by <strong>the</strong> directory.<br />
Program 15-10. Number of Blocks Free<br />
20000 OPEN 100,8,0,"$:U=U"<br />
20010 FOR J=l TO 35:GET#100,X$:NEXT<br />
20020 GET#100,Y$:CLOSE 100<br />
20030 BF=ASC(X$+CHR$(0)) + 256*ASC(Y$+CHR$(0))<br />
20040 PRINT BF"BLOCKS FREE"<br />
Reading BAM and <strong>the</strong> Directory Entries<br />
<strong>The</strong> command OPEN 2,8,2/'$" (channel is nonzero) allows <strong>the</strong> BAM track and direc<br />
tory entries to be read directly. In o<strong>the</strong>r words, <strong>the</strong> whole of track 18 is read as<br />
though it were a file, and 254 characters (not including <strong>the</strong> track and sector num<br />
bers) from each block can be read with GET#. This is a convenient way to look at<br />
<strong>the</strong> directory's internal information.<br />
Program 15-11. Reading <strong>the</strong> BAM<br />
10 Z$=CHR$(0):OPEN 2,8,2,"$"<br />
20 GET#2,X$,X?<br />
30 FOR J=l TO 35:GET#2,A$,B$,C$,D$<br />
40 PRINTJ ASC(A$+Z$) ASC(B$+Z$) ASC(C$+Z$) ASC(D$+<br />
Z$)<br />
50 NEXT:CLOSE 2:END<br />
Program 15-11 prints all 35 tracks of BAM information, arranged in sets of four,<br />
preceded by <strong>the</strong> track number. For example, 35 17 255 255 1 means that track 35<br />
has 17 free sectors, and all bits 0-16 are on. <strong>The</strong> number of free blocks can be cal<br />
culated from BAM; this number is usually <strong>the</strong> same as <strong>the</strong> directory's blocks-free<br />
figure.<br />
Knowing that, you can write a directory to use information from <strong>the</strong> directory<br />
entries, for example, <strong>the</strong> first track and sector. Program 15-12 reads <strong>the</strong> directory<br />
track and reports <strong>the</strong> LOAD address of every PRG type file; this is often helpful if<br />
you're trying to remember whe<strong>the</strong>r a program is BASIC or ML, or where a memory<br />
dump belongs in RAM.<br />
Program 15-12. Reading <strong>the</strong> Directory Track<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader/'Appendix C.<br />
10 DIM X$(30) jrem 107<br />
20 OPEN 15,8,15,"I" :rem 169<br />
30 OPEN 3,8,3,"#" :rem 30<br />
40 OPEN 2,8,2,"$" :rem 30<br />
50 FOR J=l TO 254:GET#2,X$:NEXT :rem 210<br />
100 FOR J=l TO 8 srem 11<br />
110 FOR K=l TO 30:GET#2,X$(K):NEXT :rem 100<br />
120 IF X$(1)CHR$(130) GOTO 200 :rem 75<br />
522
Using Disk Storage<br />
130 FOR K=4 TO 19:PRINT X$(K);:NEXT :rem 215<br />
140 PRINT#15#"U1:I1;3;07ASC(X$(2)+CHR$(0));ASC(X$(3<br />
)+CHR$(0)) srem 211<br />
150 GET#3,X$,X$,L$,H$ :rem 80<br />
160 PRINT ASC(L$+CHR$(0)) + 256*ASC(H$+CHR$(0))"<br />
{LEFT}" :rem 125<br />
200 IF J
Using Disk Storage<br />
Table 15-7. U Commands<br />
Command<br />
Ul or UA<br />
U2 or UB<br />
U3 or UC<br />
U4 or UD<br />
U5orUE<br />
U6 or UF<br />
U7 or UG<br />
U8 or UH<br />
U9 or UI<br />
ui-<br />
UI+<br />
U: orUJ<br />
Function<br />
Block Read<br />
Block Write<br />
Jump to $0500<br />
Jump to $0503<br />
Jump to $0506<br />
Jump to $0509<br />
Jump to $050C<br />
Jump to $050F<br />
Jump to ($FFFA)<br />
Set 1541 for VIC<br />
Set 1541 for <strong>64</strong><br />
Jump to ($FFFC)<br />
Block Commands<br />
Block read and block write (unlike all o<strong>the</strong>r commands) need an extra channel in<br />
which to store <strong>the</strong>ir data. OPEN 1,8,2,"#" opens a buffer, which BASIC refers to by<br />
its channel number (2) and file number (1). An alternative system is typically OPEN<br />
1,8,2,"#3" where, if <strong>the</strong> channel isn't available, error 70 (NO CHANNEL) is returned.<br />
You can use this to experiment with channels.<br />
For this discussion, assume OPEN 15,8,15 has been entered. Remember: If you<br />
are writing data, be sure to close <strong>the</strong>se files so that <strong>the</strong> final buffer is written. <strong>The</strong><br />
syntax for block read is PRINT#15,'Vl'';channel;0;track;sector.<br />
Program 15-13 is an example of how block read works. It follows a chain of sec<br />
tors. Try inputting track 18, sector 0 at <strong>the</strong> start. Note <strong>the</strong> use of two files, <strong>the</strong> com<br />
mand channel to load sectors in line 40, and <strong>the</strong> file to input characters in line 50.<br />
<strong>The</strong> program ends when a sector has a link set to track 0.<br />
Program 15-13. Using Block Read<br />
10 OPEN 15,8,15:OPEN 1,8,2,H#"<br />
20 INPUT "STARTING T & SM;T,S<br />
30 PRINT "TRACK11 T ",SECTOR" S<br />
40 PRINT#15,"U1";2;0;T;S<br />
50 GET#1,T$,S$: IF T$="" THEN CLOSE 1:CLOSE 15:END<br />
60 T=ASC(T$):S=ASC(S$+CHR$(0)):GOTO 30<br />
Program 15-14 demonstrates block write. It reads, alters, and writes back <strong>the</strong><br />
first directory entry block, on track 18, sector 1. Note <strong>the</strong> use of block pointer, or B-<br />
P, in line 30, which is analogous to <strong>the</strong> P parameter used with relative files.<br />
Program 15-14. Using Block Write<br />
10 OPEN 15,8,15:OPEN 1,8,2,"#"<br />
20 PRINT#15,IIU1";2;0;18;1<br />
524
Using Disk Storage<br />
30 PRINT#15,MB-P";2;2<br />
40 PRINT#1,CHR$(130+<strong>64</strong>);<br />
50 PRINT#15,"U2";2;0;18;1<br />
60 CLOSE 1:CLOSE 15:END<br />
This program assumes <strong>the</strong> directory has a PRG file first; by setting bit 6 to 1, <strong>the</strong><br />
file is locked and cannot be scratched. It appears as file type PRG
Using Disk Storage<br />
where. Its syntax is PRINT#15,"M-E"; CHR$(low byte); CHR$(high byte). <strong>The</strong> ML<br />
can be a routine in ROM, or a routine in RAM written (with M-W) by <strong>the</strong><br />
programmer.<br />
M-R (Memory Read). This command sends an address to disk, and returns <strong>the</strong><br />
value at that location along channel 15. Its syntax is PRINT#15,"M-R"; CHR$(/ow;<br />
byte); CHR$(high byte): GET#15,M$.<br />
To disassemble disk ROM, use a BASIC disassembler and add <strong>the</strong> following sub<br />
routine, replacing X=PEEK(P) in <strong>the</strong> disassembler.<br />
10000 PRINT#15//M-R";CHR$(P-256*INT(P/256));CHR$(P/256)<br />
10010 GET#15,X$:X=ASC(X$+CHR$(0)):RETURN<br />
M-W (Memory Write). Memory write puts data into disk RAM or interface<br />
chips. Each M-W command can write 35 bytes at most. <strong>The</strong> syntax is PRINT#15,"M-<br />
W"; CHR$(/ou; byte) CHR$(high byte) CHR$(length) X$, where X$ is a string of not<br />
more than 35 bytes and <strong>the</strong> o<strong>the</strong>r parameters are <strong>the</strong> starting address in RAM and<br />
<strong>the</strong> number of bytes.<br />
Machine Language Disk <strong>Programming</strong><br />
LOAD and SAVE<br />
BLOCK LOAD and SAVE are discussed in Chapter 6. <strong>The</strong>se work from within a pro<br />
gram without disturbing its sequence of operations.<br />
<strong>The</strong> autorunning loader in <strong>the</strong> section on program files uses Kernal subroutines,<br />
as shown below. Note that a name is necessary with disks, even if it's only "*".<br />
LDA<br />
LDX<br />
LDY<br />
JSR<br />
LDA<br />
LDX<br />
LDY<br />
JSR<br />
LDA<br />
STA<br />
JSR<br />
JMP<br />
#$01<br />
#$08<br />
#$00<br />
$FFBA<br />
#LENGTH<br />
#LOW<br />
#HIGH<br />
$FFBD<br />
#$00<br />
$0A<br />
$FFD5 -^<br />
START<br />
;FILE NUMBER<br />
;DEVICE NUMBER<br />
SECONDARY ADDRESS<br />
;SETLFS<br />
;NAME LENGTH<br />
;START OF NAME<br />
;SETNAM<br />
;LOAD/VERIFY FLAG 0$<br />
;LOAD £<br />
SAVE is similar, except that ISR $FFD8 is SAVE, and <strong>the</strong> start and end addresses<br />
must be specified. <strong>The</strong> X and Y registers hold <strong>the</strong> low and high bytes of <strong>the</strong> final ad<br />
dress + 1. <strong>The</strong> accumulator holds <strong>the</strong> zero page address of a pointer to <strong>the</strong> start ad<br />
dress. In addition, <strong>the</strong> setup for <strong>the</strong> Kernal routine SETLFS is slightly different. <strong>The</strong><br />
parameters for SETLFS are summarized in Table 15-8.<br />
File Handling<br />
OPEN and CLOSE can be done in ML, though it's often easier to OPEN files in<br />
BASIC and save <strong>the</strong> hassle of setting up a name or command string in RAM.<br />
As an example, consider <strong>the</strong> process of copying sequential or program files in<br />
order to change a program's LOAD address. That can be done in BASIC with OPEN<br />
1,8,2 "ORIGINAL,P,R" and OPEN 2,8,3//NEW,P,W" followed by<br />
526
Using Disk Storage<br />
GET#1,X$:PRINT#2,X$; with any necessary alterations. However, <strong>the</strong> ML equivalent<br />
of GET#1 and PRINT#1 is as follows:<br />
LOOP<br />
LDX<br />
ISR<br />
JSR<br />
PHA<br />
LDY<br />
LDX<br />
JSR<br />
PLA<br />
JSR<br />
CPY<br />
BEQ<br />
LDA<br />
JSR<br />
LDA<br />
JSR<br />
JSR<br />
RTS<br />
#$01<br />
$FFC6<br />
$FFCF<br />
$90<br />
#$02<br />
$FFC9<br />
$FFD2<br />
#$00<br />
LOOP<br />
#$01<br />
$FFC3<br />
#$02<br />
$FFC3<br />
$FFCC<br />
;OPEN FILE 1 FOR INPUT<br />
;INPUT A BYTE (LIKE GET#)<br />
;STOREIT<br />
;LOAD ST<br />
;OPEN FILE 2 FOR OUTPUT<br />
;RECOVER BYTE<br />
;OUTPUT IT (LIKE PRINT*)<br />
;CONTINUE IF ST IS 0<br />
;CLOSE 1<br />
;CLOSE 2<br />
;BACK TO NORMAL —<br />
;RETURN<br />
<strong>The</strong> demonstration uses CHKIN and CHKOUT (from <strong>the</strong> Kernal) to signal file<br />
numbers, rind CHRIN and CHROUT to get and print a character. CLOSE is easy to<br />
use, as <strong>the</strong> example shows. CLRCHN ($FFCC) returns I/O to normal operation.<br />
Program 15-15 gives ano<strong>the</strong>r, shorter example. It is POKEd from BASIC, so try<br />
it if you're inexperienced in ML. It displays 256 bytes from an open file 1 on a Com<br />
modore <strong>64</strong>. Try OPEN 1,8,2,"*,P,R": SYS 828 which will open <strong>the</strong> first file on disk<br />
(assumed to be a program) and display 256 bytes in black. More SYS 828 commands<br />
read fur<strong>the</strong>r, and <strong>the</strong> end is marked by RETURN characters, appearing as m. Enter<br />
CLOSE 1 to finish. You can also use this technique to examine sequential files, with<br />
OPEN 1,8,2,"filename": SYS 828.<br />
Table 15-8. SETLFS Summary<br />
LOAD "NAME",8<br />
A = 0<br />
X = 8<br />
Y = 0<br />
LOAD "NAME",8,1<br />
A = 1<br />
X = 8<br />
Y = 0<br />
SAVE "NAME",8<br />
A = 0<br />
X = 8<br />
Y = 1<br />
SAVE "NAME",8,1<br />
Secondary Address Irrelevant<br />
527
Using Disk Storage<br />
Program 15-15. ML File Reader<br />
10 FOR J=828 TO 851:READ X:POKE J,X:NEXT<br />
100 DATA 162,1,32,198,255,160,0,32<br />
110 DATA 207,255,153,0,4,169,1,153,0<br />
120 DATA 216,200,208,242,76,204,255<br />
OPEN and CLOSE in ML<br />
OPEN uses SETLFS to set <strong>the</strong> parameters for logical, first, and secondary addresses,<br />
typified by 1, 8, and 2 in OPEN \,%,2"filename". <strong>The</strong>se three parameters are often<br />
referred to as file number, device number, and channel number, respectively. Use <strong>the</strong><br />
following:<br />
LDA # file number<br />
LDX #S<br />
LDY # channel number<br />
JSR $FFBA<br />
<strong>The</strong> Kernal SETNAM routine at $FFBD uses <strong>the</strong> name, or command string,<br />
pointers, and length exactly like LOAD or SAVE. <strong>The</strong> Kernal OPEN routine is at<br />
$FFC0.<br />
<strong>The</strong> Kernal CLOSE routine is easier. <strong>The</strong> file number is stored in <strong>the</strong> accu<br />
mulator, <strong>the</strong>n JSR $FFC3 closes <strong>the</strong> file.<br />
Channel 15 and ML<br />
OPEN 15,8,15 is just a special case of OPEN. Messages from channel 15 consist of<br />
ASCII numbers and <strong>the</strong> message separated by commas and terminated by return.<br />
Thus, message 0 is this string:<br />
48 48 44 32 79 75 44 48 48 44 48 48 13<br />
00, OK,00,00 RETURN<br />
Thus, to check channel 15 from disk, open file 15, input two bytes, and check<br />
that each is 48. If not, <strong>the</strong> message can be printed by inputting fur<strong>the</strong>r characters<br />
and outputting <strong>the</strong>m, using $FFD2, in a loop until RETURN is received.<br />
<strong>The</strong> following routine performs <strong>the</strong> equivalent of OPEN 15,8,15:<br />
INPUT#15,E,E$,T,S: PRINT E,E$,T,S: CLOSE 15:<br />
528
Using Disk Storage<br />
;OPEN 15,8,15<br />
LDA #$0F<br />
LDX #$08<br />
LDY #$0F<br />
JSR $FFBA ;SET 15,8,15<br />
LDA #$00 ;SET LENGTH OF NAME^O<br />
JSR $FFBD POINTERS IRRELEVANT<br />
JSR $FFC0 ;OPEN 15,8,15<br />
LDX #$0F<br />
JSR $FFC6 ;OPEN 15 FOR INPUT<br />
;INPUT#15<br />
LOOP JSR $FFCF ;GET A BYTE<br />
CMP #$0D ;EXIT IF RETURN<br />
BEQ EXIT<br />
JSR $FFD2 ;PRINT TO SCREEN<br />
BNE LOOP<br />
;CLOSE 15<br />
EXIT LDA #$0F<br />
JSR $FFC3<br />
JSR<br />
RTS<br />
$FF^C<br />
;CLOSE 15<br />
;FILES NORMAL<br />
This routine can be used from BASIC or ML. In ML programming, as well as in<br />
BASIC, it is often useful to keep file 15 open while <strong>the</strong> program runs. Use <strong>the</strong> seg<br />
ment marked OPEN 15,8,15 to open. To test <strong>the</strong> error number, input two bytes using<br />
<strong>the</strong> portion marked INPUT#15 2nd check that both equal $30 (decimal 48).<br />
It's almost as easy to send a command to channel 15. Simply open <strong>the</strong> channel<br />
for output (with $FFC9) and send bytes, finishing with RETURN. CLOSE will not<br />
work immediately after this; use JSR $FFCC (CLRCHN) or make <strong>the</strong> disk unlisten.<br />
For example, LDA #$49, JSR $FFD2, LDA #$0D, JSR $FFD2, JSR $FFCC initializes<br />
<strong>the</strong> disk, if channel 15 is OPEN for output, by sending I <strong>the</strong>n RETURN (exactly like<br />
PRINT#15,"I").<br />
529
Chapter 16<br />
<strong>The</strong> Control<br />
Ports<br />
• Joysticks<br />
• Paddles and O<strong>the</strong>r Analog Devices
Chapter 16<br />
<strong>The</strong> Control<br />
Ports<br />
This chapter explains how to program controllers which fit <strong>the</strong> <strong>64</strong>'s control ports.<br />
Fast ML routines are supplied which can be used to write more efficient programs.<br />
Joysticks<br />
<strong>The</strong> <strong>64</strong> has two control ports, commonly called joystick ports, each with <strong>the</strong> stan<br />
dard nine-pin D-connector. Joysticks are <strong>the</strong> most popular external controller, and<br />
<strong>the</strong> <strong>64</strong> can use two. <strong>The</strong> four- or eight-direction control plus fire button is fine for<br />
games, but less suitable for complicated inputs like graphics and words. However,<br />
<strong>the</strong> <strong>64</strong> keyboard can be used along with joysticks, subject to a few restrictions. Com<br />
modore joysticks are interchangeable with those for Atari, Coleco, and several mod<br />
els of videogame machines.<br />
Joysticks are based on a simple principle. <strong>The</strong> central stick is connected to elec<br />
trical ground, so moving it makes contact with <strong>the</strong> sensors positioned up, down, left,<br />
or right. <strong>The</strong> button grounds ano<strong>the</strong>r wire when pressed. So, <strong>the</strong> cable to <strong>the</strong> <strong>64</strong> conr<br />
tains six lines, one of <strong>the</strong>m ground and <strong>the</strong> o<strong>the</strong>r five normally high but capable of<br />
being grounded. <strong>The</strong> <strong>64</strong> tests for one or more wires being grounded. Most joysticks<br />
are designed so that intermediate positions (nor<strong>the</strong>ast or up and right, and so on)<br />
ground two wires at once. Thus, <strong>the</strong> <strong>64</strong> may detect up to three wires of ei<strong>the</strong>r joy<br />
stick low simultaneously, counting <strong>the</strong> fire button. Some combinations aren't nor<br />
mally possible, of course, like north and south at <strong>the</strong> same time.<br />
Internally, <strong>the</strong> most common arrangement is a grounded metal ring and<br />
pressure-sensitive, dimpled-metal switches which give way and make contact when<br />
<strong>the</strong> stick moves. Heavy-duty models have o<strong>the</strong>r arrangements; some even use<br />
microswitches. Some models have two fire buttons, and/or a button on top of <strong>the</strong><br />
stick, so <strong>the</strong>y can be used in ei<strong>the</strong>r hand (converting some types for left-hand opera<br />
tion isn't hard). Joysticks tend to break down easily, often because <strong>the</strong> cable contains<br />
<strong>the</strong> thinnest possible strands of wire, which may break just inside <strong>the</strong> casing. To test<br />
a joystick, try it with one of my programs to verify that all eight directions can be lo<br />
cated easily.<br />
<strong>Programming</strong> Information<br />
Two locations are relevant when programming <strong>the</strong> control ports for use with joy<br />
sticks. <strong>Programming</strong> is easy if you know how to manipulate bits with AND and OR.<br />
Bit #: 7 6 5 4<br />
3<br />
2<br />
1<br />
0<br />
JOY1 (front)<br />
Keyboard Row:<br />
$DC01 (56321)<br />
Fire<br />
East<br />
West<br />
South<br />
North<br />
JOY2 (rear)<br />
Keyboard Column:<br />
$DC00 (56320)<br />
Fire<br />
East<br />
West<br />
South<br />
North<br />
PEEKing locations 56320 and 56321 reads <strong>the</strong> joysticks. Nothing else is needed.<br />
Bits 0-4 are normally set to 1. <strong>The</strong> joystick's grounding action sets <strong>the</strong>m to 0. <strong>The</strong><br />
order of <strong>the</strong> registers is reversed from what might seem natural. Joystick 1 is read<br />
533
<strong>The</strong> Control Ports<br />
from a register with a higher address than joystick 2. It's easy to incorrectly assume<br />
53260 is joystick 1.<br />
Keyboard interference. <strong>The</strong> control ports are wired to <strong>the</strong> keyboard circuitry,<br />
which is an economical arrangement. Generally, <strong>the</strong> keyboard won't be used with a<br />
joystick. But if it is, <strong>the</strong>re are a few side effects to watch for.<br />
<strong>The</strong> keyboard scan routine sets <strong>the</strong> column (a bit in location 56320), <strong>the</strong>n reads<br />
<strong>the</strong> row (location 56321); so joystick 1 causes spurious characters to appear on <strong>the</strong><br />
screen occasionally because it mimics a row. In fact, <strong>the</strong> 1 key, back arrow, CTRL, 2<br />
key, and space bar are interchangeable with S, N, W, E, and fire for joystick 1. Thus,<br />
if you press joystick 1 in <strong>the</strong> east direction, it outputs a 2; if you press it to <strong>the</strong> west,<br />
it slows screen scrolling, like <strong>the</strong> CTRL key does. Actually, locations 53261 and 145<br />
($91) can be interchanged in joystick programming, because 145 holds a copy of <strong>the</strong><br />
default row, for testing <strong>the</strong> RUN/STOP key.<br />
Joystick 2 doesn't generate spurious characters and is usually <strong>the</strong> better one to<br />
use with programs requiring only one joystick. But it does alter <strong>the</strong> keyscan. Pressing<br />
joystick 2 west while typing <strong>the</strong> X key has <strong>the</strong> same effect as SHIFT-RUN/STOP,<br />
for example.<br />
Hardware. Looking at <strong>the</strong> <strong>64</strong>, pins 1-5 are on top and 6-9 underneath, in leftto-right<br />
order. Joysticks use pins 1 (north), 2 (south), 3 (west), 4 (east), 6 (fire), and 8<br />
(ground). It's easy to experiment with <strong>the</strong>se ports, but be careful with pin 7, which<br />
carries 5 volts.<br />
Using WAIT. WAIT loops until a bit or bits at some location change, so this<br />
command is useful for starting or restarting games. For example, WAIT 56320,16,16<br />
waits until joystick 2's fire button is pressed, and WAIT 56321,16 waits until joystick<br />
l's button is not pressed.<br />
Program Examples<br />
BASIC routines to handle joysticks tend to be long, which is hard to avoid since all<br />
possible directions must be separated out for processing. Program 16-1 demonstrates<br />
<strong>the</strong> method of combining bits into one value.<br />
Program 16-1. BASIC Joystick Routine<br />
1 REM RETURNS P=-41 TO +41<br />
10000 PP=PEEK(56320)<br />
10010 P=((PPAND4)=0) - ((PPAND8)=0) + 40*((PPAND1)<br />
=0) - 40*((PPAND2)=0)<br />
10020 RETURN<br />
Add a line 100015 PRINT P:GOTO 10000 to change <strong>the</strong> subroutine to a<br />
demonstration you can simply run. Program 16-1 checks only joystick 2 and doesn't<br />
check <strong>the</strong> fire button.<br />
Using ML speeds joystick reading greatly, but <strong>the</strong> results often still need to be<br />
PEEKed by BASIC. <strong>The</strong> following ML routine reads both sticks; if ei<strong>the</strong>r is active, ST<br />
is set to a nonzero value. Program 16-2 returns <strong>the</strong> joystick information in locations<br />
2 and 3.<br />
534
<strong>The</strong> Control Ports<br />
Program 16-2. ML Joystick Routine<br />
1 REM ML JOYSTICK READER FOR THE <strong>64</strong><br />
2 REM<br />
3 REM USE SYS 828. ST0 MEANS JOY PRESSED<br />
4 REM PEEK(2) RETURNS 1,2,4,8,16 (OR MIX)<br />
5 REM FOR N,S,W,E,FIRE OF JOY 1<br />
6 REM PEEK(3) SAME FOR JOY 2<br />
7 REM<br />
10 FOR J=828 TO 848:READ XrPOKE J,X:NEXT<br />
20 DATA 173,0,220,41,31,73,31,133,3,173<br />
30 DATA 1,220,73,255,133,2,5,3,133,144,96<br />
40 SYS 828:PRINT ST PEEK(2) PEEK(3):GOTO 40<br />
Where all eight directions are needed, <strong>the</strong>y can be combined toge<strong>the</strong>r by a rou<br />
tine similar to <strong>the</strong> following, which uses <strong>the</strong> ML routine above. Delete line 40 in<br />
Program 16-2 and add <strong>the</strong> lines shown in Program 16-3. Replace <strong>the</strong> FIREBUTTON<br />
in line 1010 and <strong>the</strong> direction indicators in line 1020 with <strong>the</strong> line numbers of <strong>the</strong><br />
routines in your program that process those joystick operations. Program 16-3 will not<br />
run properly unless you replace <strong>the</strong>se with valid line numbers.<br />
Program 16-3. ML Joystick Interpreter<br />
1000 SYS 828:IF ST=0 GOTO 1000<br />
1010 IF (PEEK(3) AND 16)>0 THEN FIREBUTTON<br />
1020 ON PEEK(3) GOTO N,S,,W,NW,SW,,E,NE,SE<br />
O<strong>the</strong>r machine language techniques include using interrupts to read joysticks<br />
and process <strong>the</strong> results. For example, it is possible to retain a previous value even<br />
when <strong>the</strong> joystick is back in <strong>the</strong> neutral position, or to allow optional keyboard or<br />
joystick operation.<br />
Paddles and O<strong>the</strong>r Analog Devices<br />
Game paddles are less popular than joysticks, because, for many purposes <strong>the</strong><br />
simple style of joystick movement is easier to use than <strong>the</strong> rotating knobs on <strong>the</strong><br />
paddles. <strong>Commodore</strong>'s paddles consist of two separate handheld units, each with a<br />
knob and fire button, which plug toge<strong>the</strong>r into <strong>the</strong> same control port. Since <strong>the</strong> <strong>64</strong><br />
has two game ports, two pairs of paddles could be used between four people, but<br />
this is relatively unusual. This discussion deals mainly with using one pair of<br />
paddles.<br />
Counterclockwise rotation of <strong>the</strong> paddle knob increases <strong>the</strong> value read by <strong>the</strong><br />
<strong>64</strong>, and vice versa. It may be worth labeling <strong>the</strong> paddles since one (<strong>the</strong> X paddle) is<br />
read by SID chip location $D419 (54297), while <strong>the</strong> o<strong>the</strong>r (<strong>the</strong> Y paddle) is read at<br />
$D41A (54298). <strong>The</strong>se registers are each eight bits wide, so <strong>the</strong>re's maximum resolu<br />
tion of 1 in 256.<br />
If both ports are used, POKEs into bits 6 and 7 of location 56320 ($DC00) select<br />
whe<strong>the</strong>r port 1 or port 2 is to be read. Obviously, something like this is necessary to<br />
enable both ports (four paddles) to be used, since <strong>the</strong> SID chip has only two analogto-digital<br />
conversion registers.<br />
535
<strong>The</strong> Control Ports<br />
Paddles are analog devices: <strong>the</strong>y sweep through a continuous range of values.<br />
Differences between devices may cause slight compatibility problems because of this,<br />
unlike joysticks, where grounding is a simple on-or-off alternative. Pin 7 of each<br />
control port is connected to <strong>the</strong> 5-volt power supply. Pins 5 (Y) and 9 (X) are con<br />
nected to this, via <strong>the</strong> paddles. Rotating <strong>the</strong> paddles alters <strong>the</strong> resistance of <strong>the</strong> in<br />
ternal potentiometers (variable resistors), each connected to <strong>the</strong> knob so that, as it<br />
turns, <strong>the</strong> resistance changes. <strong>The</strong> SID chip's POT X and POT Y registers measure<br />
<strong>the</strong> changing voltage levels produced by this changing resistance, performing an<br />
analog-to-digital (A/D) conversion which relates <strong>the</strong> voltage level (0-5 volts) to a<br />
number 0-255.<br />
It's simple to use <strong>the</strong> same principle with o<strong>the</strong>r resistances. <strong>Commodore</strong> paddles<br />
are rated at 470K ohms (<strong>the</strong> K stands for thousand), and <strong>the</strong>ir minimum resistance is<br />
a few hundred ohms. <strong>The</strong>y are approximately linear, changing in even steps of about<br />
1000 ohms, so <strong>the</strong> overall range is large. To avoid damaging <strong>the</strong> SID chip while<br />
experimenting, keep minimum resistances of several hundred ohms between <strong>the</strong> 5-<br />
volt line and <strong>the</strong> POT inputs of <strong>the</strong> SID chip. <strong>Commodore</strong> documentation suggests<br />
hardware smoothing with a lOOOpF capacitor between <strong>the</strong> POT inputs and ground,<br />
but this is already built in on <strong>the</strong> <strong>64</strong>. <strong>Commodore</strong> paddles have a fire button on each<br />
unit, wired to pins 3 and 4 of <strong>the</strong> controller port for <strong>the</strong> X and Y paddles, respec<br />
tively, which is connected to ground (pin 8) when pressed, just like joystick contacts<br />
for <strong>the</strong> west and east directions.<br />
Atari paddles can also be used with a <strong>Commodore</strong> <strong>64</strong>, but since <strong>the</strong> maximum<br />
resistance of <strong>the</strong> potentiometers in <strong>the</strong> Atari unit is about twice that of <strong>Commodore</strong><br />
paddles—1M (1 million ohms) versus 470K ohms—<strong>the</strong> Atari paddles are very sen<br />
sitive. A half turn of an Atari paddle is approximately equal to a full turn of a Com<br />
modore paddle.<br />
<strong>Programming</strong> Information<br />
Paddles can be read in BASIC or ML. <strong>The</strong> following locations are important:<br />
Bit #: 7 6 5 4 3 2 1 0<br />
POTX<br />
POTY<br />
$D419 (54297)<br />
$D41A (54298)<br />
All Bits<br />
All Bits<br />
Port 1 Buttons<br />
$DC01 (56321)<br />
Y<br />
X<br />
Port 2 Buttons<br />
$DC00 (56320)<br />
Y<br />
X<br />
Note that <strong>the</strong> same location reads <strong>the</strong> paddle potentiometer values irrespective<br />
of <strong>the</strong> port, but <strong>the</strong> fire button locations vary, and <strong>the</strong>re's no hardware switch be<br />
tween <strong>the</strong>m. Note also that <strong>the</strong> default is port 1, so if you want to use <strong>the</strong> simplest<br />
approach, use this port and simply PEEK <strong>the</strong> POT X and POT Y registers.<br />
Program 16-4 selects and reads <strong>the</strong> paddle connected to port 2. Add a line 80<br />
PRINT X,Y,FB to see <strong>the</strong> values on <strong>the</strong> screen.<br />
536
<strong>The</strong> Control Ports<br />
Program 16-4. Reading Paddle 2<br />
10 POKE 56333,127:REM IRQS OFF<br />
20 POKE 56322,192:REM SET PINS 6,7 FOR OUTPUT<br />
30 POKE 56320,128:REM SELECT PORT 2 (<strong>64</strong>=PORT 1)<br />
40 X=PEEK(54297)<br />
50 Y=PEEK(54298):FB=PEEK(56320)AND 12:REM USE 5632<br />
1 FOR PORT 1<br />
60 POKE 56322,255:POKE 56333,129<br />
100 RUN<br />
<strong>The</strong> program selects port 2 in line 30. Interrupts have to be turned off to prevent<br />
<strong>the</strong> keyscan routine from altering values. After reading <strong>the</strong> registers, line 60 restores<br />
normal keyboard operation. If you don't need <strong>the</strong> keyboard, line 60 can be omitted.<br />
Paddle buttons generate spurious characters when connected to port 1. Pressing<br />
<strong>the</strong> button on paddle X is equivalent to pressing <strong>the</strong> CTRL key, which slows process<br />
ing. In port 2, <strong>the</strong> keyscan is altered (try X with fire button). As noted, port 2 cannot<br />
be read at <strong>the</strong> same time as <strong>the</strong> keyboard because of <strong>the</strong> conflict over <strong>the</strong> use of<br />
$DC00. This means that, with paddles in port 1, PEEKing values is easy, but <strong>the</strong> fire<br />
buttons will have to be avoided in some circumstances.<br />
ML programming is fast enough to disable <strong>the</strong> keyboard without noticeable ef<br />
fect. Program 16-5 allows four paddles and four buttons to be read with virtually no<br />
problems. <strong>The</strong> fire buttons register in <strong>the</strong> status variable ST, so IF ST>0 is a simple<br />
test for a button press.<br />
Program 16-5. ML Paddle Reader<br />
10 DATA 120,162,2,169,192,141,2,220,41,128,141,0<br />
11 DATA 220,160,208,136,208,253,173,25,212,149,2<br />
12 DATA 173,26,212,149,3,169,<strong>64</strong>,202,202,16,232,173<br />
13 DATA 1,220,74,74,9,252,133,144,173,0,220,9,243<br />
14 DATA 37,144,73,255,133,144,169,255,141,2,220,96<br />
15 FOR J=49152 TO 49211:READ X:POKE J,X:NEXT<br />
To activate <strong>the</strong> routine, use SYS 49152. <strong>The</strong> results will be left in <strong>the</strong> following<br />
locations:<br />
Paddle X<br />
Paddle Y<br />
FireX<br />
Fire Y<br />
Port 1<br />
PEEK(2)<br />
PEEK(3)<br />
ST Bit 0 Set<br />
STBit 1 Set<br />
Port 2<br />
PEEK(4)<br />
PEEK(5)<br />
ST Bit 2 Set<br />
STBit 3 Set<br />
<strong>The</strong> POT registers are usually updated about every 500 processor cycles. Extra<br />
time is necessary when <strong>the</strong> ports are shifted, so in ML it's best to allow a loop like<br />
LDX #$D0 : LOOP DEX : BNE LOOP before reading from <strong>the</strong> SID paddle registers.<br />
Accuracy. <strong>The</strong> <strong>64</strong>'s A/D conversion is better than <strong>the</strong> VIC-20's. <strong>The</strong>re's less<br />
crosstalk and no need to correct for <strong>the</strong> o<strong>the</strong>r paddle's value. With paddles, resolu<br />
tion is limited, and you may find intermediate values can't be read. Occasionally,<br />
values returned are actually very different from o<strong>the</strong>r recent values. Generally, don't<br />
537
<strong>The</strong> Control Ports<br />
try to aim for accuracy beyond <strong>the</strong> eight-bit limit imposed by <strong>the</strong> SID chip. It's pos<br />
sible to smooth values, but use a moving average method, taking a running total at<br />
regular intervals. With devices o<strong>the</strong>r than paddles, smoothing isn't suitable: graphics<br />
pads, where a stylus can jump from point to point at will, need a good estimate of<br />
each point's coordinate, not an average value between unrelated points.<br />
O<strong>the</strong>r Analog Devices<br />
O<strong>the</strong>r devices that make use of variable resistances may be interfaced to <strong>the</strong> <strong>64</strong><br />
through <strong>the</strong> paddle inputs. One of <strong>the</strong> most common is <strong>the</strong> graphics tablet. Contact<br />
of a stylus on a carbon film pad reduces resistance in both <strong>the</strong> X and Y direction.<br />
Well-designed graphics tablets should return X and Y values which reflect <strong>the</strong><br />
position of <strong>the</strong> stylus on <strong>the</strong> pad accurately. <strong>Programming</strong> is identical to that for<br />
paddles. Generally, <strong>the</strong>se are used to draw on <strong>the</strong> screen or to select from a menu by<br />
positioning a cursor at some option and pressing a button.<br />
Light Pens<br />
A light pen is a pen-shaped device, fitted with a cable, which plugs into control port<br />
1. <strong>The</strong> line carrying <strong>the</strong> light pen's signal is tied to <strong>the</strong> keyboard, so keys B, C, M, Z,<br />
fl, <strong>the</strong> left SHIFT key, and period won't work while a light pen is plugged in, unless<br />
<strong>the</strong> light pen has a switch on it. Watch for this if you want to input from <strong>the</strong> key<br />
board and use a light pen simultaneously.<br />
<strong>The</strong> internal electronics include a light-sensitive component, usually a phototransistor,<br />
which allows current to pass only when exposed to light. Light pens use<br />
<strong>the</strong> 5-volt and ground lines, plus a line into <strong>the</strong> VIC-II chip. When fairly intense<br />
light—such as <strong>the</strong> electron beam that creates <strong>the</strong> video display in a television or<br />
monitor—is detected, this line is grounded and two VIC chip registers are frozen, or<br />
latched, and remain unaltered until <strong>the</strong> next exposure to light. <strong>The</strong> two registers hold<br />
<strong>the</strong> horizontal and vertical positions of <strong>the</strong> pen inferred from <strong>the</strong> distance of <strong>the</strong><br />
electron beam from its starting position.<br />
Whenever a range of alternatives is to be selected from a screen display, a light<br />
pen may be useful. Selecting alternative answers to multiple-choice questions and<br />
selecting options from a menu are examples. Also, games like chess can make good<br />
use of light pens. Numbers can be input with a numeric 0-9 pad on <strong>the</strong> screen, and<br />
a light pen can help sketch on a screen.<br />
<strong>The</strong> drawbacks to using light pen input are <strong>the</strong> limited accuracy of <strong>the</strong> pens,<br />
computer limitations, and <strong>the</strong> fact that more people own joysticks than light pens.<br />
<strong>The</strong> glass in front of <strong>the</strong> TV tube and general lack of precision make accuracy and<br />
repeatability not as good as with analog devices like graphics tablets. Ano<strong>the</strong>r diffi<br />
culty is that some colors, such as black, won't trigger <strong>the</strong> pen.<br />
<strong>The</strong> light pen programming registers are read-only (<strong>the</strong>y cannot be POKEd):<br />
$D013 (53267) is <strong>the</strong> horizontal position register and $D014 (53268) is <strong>the</strong> vertical<br />
position register. <strong>The</strong> following one-line program displays both registers:<br />
10 PRINT PEEK(53267): PRINT PEEK(53268): PRINT "{CLR}": GOTO 10<br />
As you move <strong>the</strong> pen across <strong>the</strong> screen, <strong>the</strong> first value varies, increasing as you<br />
move <strong>the</strong> pen from left to right. As you move it down, <strong>the</strong> second reading increases.<br />
<strong>The</strong> readings are taken from <strong>the</strong> VIC chip as it monitors and controls <strong>the</strong> TV. Values<br />
538
<strong>The</strong> Control Ports<br />
across vary from about 30 to 190, ignoring <strong>the</strong> border area, and values down range<br />
from about 50 to 250 (on U.S. televisions). <strong>The</strong> monitor scans each picture twice,<br />
interlacing <strong>the</strong> pictures, which is why <strong>the</strong>re are only 200 separately distinguished<br />
raster lines. To convert <strong>the</strong>se ranges into 0-39 and 0-24 for column and row equiva<br />
lents is easy. Just subract 30, <strong>the</strong>n divide by 4, to get <strong>the</strong> horizontal position, and<br />
subtract 50 and divide by 8 for <strong>the</strong> vertical. Resolution is in principle two dots hori<br />
zontally and one dot vertically.<br />
Program 16-6. BASIC Light Pen Program<br />
20 X=PEEK(53267)<br />
30 Y=PEEK(53268)<br />
40 X=(X-30)/l60*40<br />
50 Y=(Y-50)/200*25<br />
60 PRINT M{HOME}":FOR J=l TO X.-PRINT " {RIGHT}" ; :NE<br />
XT<br />
70 FOR J=l TO Y:PRINT "{DOWN}"; :NEXT:PRINT "QM;:GO<br />
TO 20 ~~<br />
This simple program will draw a small ball on <strong>the</strong> screen when it detects a light<br />
pen reading. You'll probably find your pen's readings show quite a bit of jitter, even<br />
when held still, removing any chance of serious high-resolution work.<br />
Program 16-7 uses an ML subroutine to read <strong>the</strong> pen and convert its readings to<br />
a screen position. It POKEs a character into <strong>the</strong> screen, <strong>the</strong>n loops back for more. As<br />
you'll see, this is much faster than BASIC. Color change and character change<br />
demonstrations are included, and you can modify <strong>the</strong>se to suit your requirements.<br />
Program 16-7. ML Light Pen Draw<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader/' Appendix C.<br />
2 REM SYS 49152 READS, PLOTS UNTIL KEYPRESS:rem 73<br />
3 REM DEMO SETS WHITE BALL; USES KEYS :rem 206<br />
4 REM 1-8 AND SHIFT 1-8 TO CHANGE COLOR :rem 93<br />
5 REM & F7 TO SWITCH TO PLOTTING BLOCKS :rem 3<br />
6 REM (CHAR IS IN 49215, COLOR IN 49230) :rem 5<br />
10 FOR J=49152 TO 49236:READ X:POKEJ,X:NEXT<br />
:rem 221<br />
20 PRINT "{CLR}11 :rem 198<br />
100 SYS 49152 :rem 149<br />
110 A=PEEK(780):REM PEEK (197) ALSO OK :rem 35<br />
120 A$=CHR$(A) :rem 200<br />
130 IF CHR$(A)>=M1M AND CHR$(A)
<strong>The</strong> Control Ports<br />
502 DATA 48,144,230,201,200,176,226,41,248,168,169<br />
:rem 162<br />
503 DATA 0,133,3,132,2,6,2,38,3,6,2,38,3,152,101<br />
:rem 18<br />
504 DATA 2,133,2,169,4,101,3,133,3,138,74,74,168<br />
:rem 50<br />
505 DATA 169,81,145,2,165,2,133,4,165,3,24,105,212<br />
:rem 146<br />
506 DATA 133,5,169,1,145,4,208,173,240,171 :rem 16<br />
<strong>The</strong> ML checks to see that <strong>the</strong> light pen values are in range. If so, it calculates<br />
<strong>the</strong> position of <strong>the</strong> screen character, and plots. You may prefer to read <strong>the</strong> screen<br />
and detect a character as <strong>the</strong> pen points to it. Modify Program 16-7 as follows. Re<br />
place each of <strong>the</strong> first six numbers in line 500 with 234, <strong>the</strong>n replace line 505 with<br />
505 DATA 169,81,177,2,96, and delete line 506. RUN and PRINT J to find <strong>the</strong> cor<br />
rect upper limit to use after TO in <strong>the</strong> FOR-NEXT loop in line 10, and replace line<br />
100 with 100 SYS 49152: PRINT "{HOME}" PEEK(780): GOTO 100. Now <strong>the</strong><br />
screen PEEK value (32 for space, 1 for A, 2 for B, etc.) of <strong>the</strong> character selected by<br />
<strong>the</strong> light pen appears in <strong>the</strong> top left. <strong>The</strong> program's calculations are unaltered, but<br />
<strong>the</strong> screen is no longer POKEd. Values 32, 160, 48, and 200 in <strong>the</strong> DATA check <strong>the</strong><br />
limits, subtracting 32 and checking to see that <strong>the</strong> result doesn't exceed 160 hori<br />
zontally; <strong>the</strong>se can be fine-tuned.<br />
Light pens and o<strong>the</strong>r devices which ground <strong>the</strong> appropriate line can be pro<br />
grammed to generate interrupts (<strong>the</strong> end of Chapter 12 explains how). This has <strong>the</strong><br />
advantage of signaling every activation of <strong>the</strong> device, but o<strong>the</strong>rwise isn't very useful.<br />
<strong>The</strong> Control Port Socket<br />
Figure 16-1 shows <strong>the</strong> assignments of <strong>the</strong> nine pins of <strong>the</strong> control port sockets of <strong>the</strong><br />
<strong>64</strong>. <strong>The</strong> two ports are identical, except that <strong>the</strong> light pen input (shown at pin 6) is<br />
available only for port 1. Remember that control port devices will affect each o<strong>the</strong>r<br />
just as <strong>the</strong>y affect <strong>the</strong> keyboard. A light pen program may not work if paddles are<br />
connected, for example.<br />
Figure 16-1. Typical Control Port Plnout<br />
Joy W<br />
JoyE<br />
or<br />
or<br />
Paddle Y<br />
Paddle X<br />
Joy N<br />
JoyS<br />
Button<br />
Button<br />
Paddle Y<br />
o<br />
2<br />
O<br />
3<br />
O<br />
4<br />
V<br />
6<br />
O<br />
7<br />
O<br />
8<br />
O<br />
9<br />
O<br />
V<br />
Light Pen<br />
+5V<br />
GND<br />
Paddle X<br />
or<br />
Joy Button<br />
540
Chapter 17<br />
Peripherals<br />
• Printers<br />
• Plotters<br />
• Modems<br />
• <strong>The</strong> RS-232 Interface<br />
• <strong>The</strong> Serial Port
Chapter 17<br />
Major Peripherals<br />
This chapter covers printers, plotters, modems, and <strong>the</strong> <strong>64</strong>'s interfaces. Simple pro<br />
gram examples are included for quick reference.<br />
Printers<br />
Simple Commands<br />
<strong>Commodore</strong> printers designed for <strong>the</strong> <strong>64</strong> plug into <strong>the</strong> serial port, <strong>the</strong> round port at<br />
<strong>the</strong> back of <strong>the</strong> <strong>64</strong> next to <strong>the</strong> video output, or at <strong>the</strong> back of <strong>the</strong> disk unit when<br />
daisychaining. At <strong>the</strong> simplest level, printers are controlled with OPEN 4,4 (which<br />
opens file number 4 to <strong>the</strong> printer), PRINT#4,"HELLO" (which prints a message to<br />
file 4), and CLOSE 4 (which closes <strong>the</strong> file). Any number of PRINT#4 statements<br />
can be issued. PRINT statements can still be used to send output to <strong>the</strong> screen.<br />
<strong>The</strong> <strong>64</strong> has no LIST#4 statement. To LIST programs to <strong>the</strong> printer, use OPEN<br />
4,4: CMD 4: LIST (which opens file 4, directs output to that file instead of to <strong>the</strong><br />
screen, and lists). Follow this with PRINT#4: CLOSE 4 (which disengages CMD and<br />
closes <strong>the</strong> file). <strong>The</strong> PRINT#4 is needed to close <strong>the</strong> file properly, so get in <strong>the</strong> habit<br />
of using it before CLOSE whenever you use CMD.<br />
With machine language monitors, all output can be sent to a printer with OPEN<br />
4,4: CMD 4: followed by a SYS to <strong>the</strong> entry point of <strong>the</strong> monitor. <strong>The</strong>n enter M<br />
1000 1200, for example, and output for <strong>the</strong> desired memory dump will be made to<br />
<strong>the</strong> printer. <strong>The</strong> commands may have to be typed in blind, since <strong>the</strong>y may not echo<br />
to <strong>the</strong> screen, but this isn't a big problem. Enter X or E to exit <strong>the</strong> monitor, <strong>the</strong>n<br />
PRINT#4: CLOSE 4 to redirect output to <strong>the</strong> screen.<br />
Non-<strong>Commodore</strong> Printers<br />
For most applications, many non-<strong>Commodore</strong> printers use commands identical to<br />
those for <strong>Commodore</strong> printers. Also, many interfaces are available which emulate<br />
<strong>Commodore</strong> printer features in addition to allowing you to use <strong>the</strong> special features<br />
of <strong>the</strong> non-<strong>Commodore</strong> printer. Printers which use <strong>the</strong> RS-232 port at <strong>the</strong> back left<br />
of <strong>the</strong> <strong>64</strong>, usually with an RS-232 converter cartridge, use device number 2, instead<br />
of 4. To use such a device, <strong>the</strong> following sequence is typical:<br />
OPEN 2,2,0,CHR$(6):PRINT#2,"HELLO"<br />
This opens file 2 with baud rate 300, <strong>the</strong>n sends HELLO to <strong>the</strong> device. PRINT#2:<br />
CLOSE 2 closes <strong>the</strong> file. OPEN 2,2,0: CMD 2: LIST will list to such a printer. See<br />
<strong>the</strong> notes later in this chapter for more on RS-232 printers, which may not always<br />
work with <strong>the</strong> <strong>64</strong>.<br />
Easy Printing<br />
PRINT# statements are similar to ordinary PRINT statements, but <strong>the</strong>re is a distinc<br />
tion between <strong>the</strong> carriage-return character, CHR$(13), and <strong>the</strong> linefeed character,<br />
CHR$(10), which advances <strong>the</strong> paper in <strong>the</strong> printer. <strong>Commodore</strong> printers are de<br />
signed to treat PRINT# followed by a semicolon as an instruction to remain on <strong>the</strong><br />
same line. PRINT# followed by a colon or end-of-line marker is treated as a com<br />
bined carriage return and linefeed, so PRINT# behaves just like PRINT to <strong>the</strong> screen.<br />
543
Major Peripherals<br />
Not all printers have an automatic linefeed. If your non-<strong>Commodore</strong> printer over<br />
prints lines on top of each o<strong>the</strong>r, use a file number of 128 or greater (OPEN 128,4,<br />
for example, with PRINT#128,//HELLO//) to cause <strong>the</strong> <strong>64</strong> to output <strong>the</strong> linefeed.<br />
Control characters to <strong>the</strong> printer (to print reversed text, lowercase, and so on)<br />
are sent as special characters which <strong>the</strong> printer recognizes, typically as PRINT#4,<br />
CHR$(27) or as PRINT#4,A$ (where A$ is a string of CHR$ values). Printer pro<br />
grams are likely to contain a number of PRINT# statements which are meaningful<br />
only with reference to <strong>the</strong> printer in use.<br />
Choosing and Using<br />
Printers<br />
A printer is simply a device to convert a stream of bytes into text or graphics. Unlike<br />
o<strong>the</strong>r <strong>Commodore</strong> devices, non-<strong>Commodore</strong> printers can often substitute for Com<br />
modore equipment. This is worthwhile where special faster or higher quality print is<br />
required (it is best to see <strong>the</strong> product in action), or where a user needs to be able to<br />
print in foreign languages. In all <strong>the</strong>se cases, some sort of interface will be necessary,<br />
because nei<strong>the</strong>r of <strong>the</strong> <strong>64</strong>'s printer ports is standard.<br />
<strong>Commodore</strong> printers for <strong>the</strong> <strong>64</strong> include <strong>the</strong> 1515, <strong>the</strong> 1525, <strong>the</strong> MPS-801 (which<br />
is quite similar to <strong>the</strong> 1525 except that it uses cartridge ribbon instead of a reel), and<br />
<strong>the</strong> more versatile 1526. <strong>The</strong>ir features are summarized in Table 17-1. <strong>The</strong> 1515,<br />
1525, and MPS-801 are designed to be compatible with both <strong>the</strong> VIC and <strong>the</strong> <strong>64</strong>; <strong>the</strong><br />
1526 is designed specifically for <strong>the</strong> <strong>64</strong> and is not completely compatible with <strong>the</strong><br />
VIC. <strong>The</strong>y are made by Seikosha. (<strong>Commodore</strong> doesn't make any of its own<br />
printers.)<br />
Table 17-1. Selected <strong>Commodore</strong> Printers for <strong>the</strong> <strong>64</strong><br />
1515<br />
1525<br />
1526<br />
Dot Resolution<br />
of Characters<br />
7 up X 6 across<br />
7X6<br />
8X8<br />
Characters per Inch<br />
12<br />
10<br />
11<br />
Paper Widths (inches)<br />
Up to 8<br />
Up to 10<br />
Up to 10<br />
True Descenders<br />
ong,j,p,q,y?<br />
No<br />
No<br />
Partly<br />
Approx. Speed,<br />
Characters per Sec.<br />
30<br />
30<br />
60<br />
Separation Between<br />
Lines<br />
2 dots<br />
2 dots<br />
Programmable<br />
Programmable<br />
Formatting of Output?<br />
No<br />
No<br />
Yes<br />
Programmable Top-of-<br />
Form Feed?<br />
No<br />
No<br />
Yes<br />
Ribbon Type<br />
Cloth<br />
Cloth<br />
Carbon Film<br />
544
Major Peripherals<br />
Each of <strong>the</strong>se printers has <strong>the</strong> complete range of ROM graphics, although none<br />
prints characters that are identical to <strong>the</strong> <strong>64</strong>'s characters. <strong>The</strong> 1515's reverse charac<br />
ters, for example, lack <strong>the</strong> solid underline of <strong>the</strong> screen characters. In addition,<br />
lowercase letters like g lack true descenders, so <strong>the</strong>y seem to float up on <strong>the</strong> line.<br />
<strong>The</strong> printers have built-in ROM to process incoming commands and store graphics<br />
patterns, as well as RAM to act as a buffer, storing data while <strong>the</strong> printer deals<br />
with it.<br />
ROMs may be changed by <strong>Commodore</strong> without warning, so <strong>the</strong>re's no guar<br />
antee that one model won't differ from o<strong>the</strong>rs. <strong>Commodore</strong>'s printers haven't consis<br />
tently used identical commands in <strong>the</strong> past, ei<strong>the</strong>r. <strong>The</strong>se commands are discussed<br />
fur<strong>the</strong>r in <strong>the</strong> next section.<br />
Each printer allows 80 normal-width characters to <strong>the</strong> line (except that <strong>the</strong> 1515<br />
uses a smaller typeface and nonstandard 8-inch-wide paper). It is possible to use 8-<br />
1/2-inch paper on <strong>the</strong> 1515 by loosening <strong>the</strong> paper guide, removing <strong>the</strong> lid and <strong>the</strong><br />
guide, and taking out <strong>the</strong> bar so that only <strong>the</strong> paper holders touch <strong>the</strong> paper. How<br />
ever, <strong>the</strong> result is a very noisy printer.<br />
<strong>The</strong> number of lines per page has to be counted with <strong>the</strong> earlier printers.<br />
Usually, a total of 66 lines, including linefeeds, has to be arranged per page if neat<br />
output is desired. Six lines per inch is standard.<br />
In addition to <strong>the</strong> standard <strong>Commodore</strong> <strong>64</strong> graphics characters, <strong>the</strong> 1515, 1525,<br />
and MPS-801 printers have a graphic mode in which individual columns of dots can<br />
be programmed. This is demonstrated in <strong>the</strong> manuals by reproducing <strong>the</strong> Com<br />
modore symbol. A page of graphics can be printed continually redefining <strong>the</strong> dot<br />
pattern. This makes it easy to print high-resolution graphics. <strong>The</strong> 1526 lacks a<br />
graphic mode, but a similar effect can be achieved using this printer's single userdefined<br />
character. However, only one redefined character is allowed per line printed,<br />
so multiple overprints must be made to reproduce a complete line of graphics. Thus,<br />
<strong>the</strong> 1526 is less suitable for high-resolution graphics printing than <strong>the</strong> o<strong>the</strong>r models.<br />
Most software assumes device 4 for a printer. However, that can be switched to<br />
device 5, so two printers can be used simultaneously, with PRINT#4 selecting one<br />
printer and PRINT#5 selecting <strong>the</strong> o<strong>the</strong>r.<br />
<strong>The</strong> <strong>Commodore</strong> printers have a self-testing facility, a loop in internal ROM<br />
which outputs <strong>the</strong> character set (except for reverse characters, which may cause<br />
overheating if used excessively). <strong>The</strong>y also have a power-on sequence. <strong>The</strong> older<br />
1515 can jam and appear completely dead when turned on, because <strong>the</strong> cam driving<br />
<strong>the</strong> ribbon stuck. If this happens, lightly flick <strong>the</strong> pivoting part of <strong>the</strong> cam to loosen it.<br />
O<strong>the</strong>r <strong>Commodore</strong> printers include a series of printers for <strong>the</strong> earlier PET/CBM<br />
machines. All PET/CBM printers require an IEEE interface connected to <strong>the</strong> <strong>64</strong>'s<br />
normal printer port to operate. <strong>The</strong> 4022 is <strong>the</strong> main PET/CBM printer; it has a<br />
considerable number of features, including ten secondary addresses. A heavy-duty<br />
German printer and a very slow modified Olympia daisywheel are sometimes en<br />
countered, too.<br />
O<strong>the</strong>r Printers<br />
Most printers have a Centronics interface, which is a parallel interface using<br />
multiwire flat-ribbon cable. RS-232 serial interfaces are also common. IEEE interfaces<br />
545
Major Peripherals<br />
are rarer, and current loop interfaces are ano<strong>the</strong>r relatively uncommon type. All of<br />
<strong>the</strong>se can be connected to <strong>the</strong> <strong>64</strong>, with <strong>the</strong> proper interfacing.<br />
It is always advisable to test <strong>the</strong> compatibility of equipment before purchasing,<br />
particularly if packaged software is to be used. Most good word processors make<br />
allowances for printer type, but o<strong>the</strong>r programs may not work correctly with all<br />
printers, particularly with features like margin and tab.<br />
Printer Types<br />
Several different kinds of printers are now available for <strong>the</strong> <strong>64</strong>. <strong>The</strong>y are described<br />
below.<br />
Teletypes. <strong>The</strong>se are old-fashioned terminals, uppercase only, which commu<br />
nicate with computers via RS-232. Since <strong>the</strong>y have been replaced by video terminals<br />
in industry, <strong>the</strong>y can sometimes be found very cheaply. Of course, <strong>the</strong>y may cost<br />
you more in <strong>the</strong> long run, and are severely limited in <strong>the</strong>ir capability.<br />
Modified typewriters. Many typewriter manufacturers are now including inter<br />
face sockets on <strong>the</strong>ir machines, so daisywheel machines with this dual function are<br />
likely to become popular. Ball typewriters with interfaces are slower, though <strong>the</strong> im<br />
pression is often slightly better.<br />
<strong>The</strong>rmal and spark printers. <strong>The</strong>se printers make up characters from columns<br />
of dots, like dot-matrix printers, but use methods that are less demanding mechani<br />
cally. <strong>The</strong>rmal printers use short bursts of high temperature, while spark printers use<br />
short bursts of high voltage. <strong>The</strong>se printers are inexpensive, but <strong>the</strong> paper <strong>the</strong>y use is<br />
relatively costly and generally not <strong>the</strong> best quality.<br />
Dot-matrix printers. <strong>The</strong>se are by far <strong>the</strong> most widely used computer printers.<br />
<strong>The</strong> print head typically has seven to nine wires arranged vertically, and each wire is<br />
separately controlled by its own solenoid which drives <strong>the</strong> wire briefly into contact<br />
with ribbon and paper. Higher quality machines have more dots, so <strong>the</strong> image qual<br />
ity is better, although <strong>the</strong> delicacy of serifs and o<strong>the</strong>r features of typefaces are lost.<br />
An advantage of this method is that any characters within <strong>the</strong> limits imposed by <strong>the</strong><br />
dot resolution can be generated, so dot-matrix printers often have internal switches<br />
for assorted international alphabets, as well as <strong>the</strong> ability to print graphics. Some<br />
dot-matrix printers have double strike, emphasized, and correspondence-quality<br />
modes, and are able to print in several type widths.<br />
Daisywheel printers. A daisywheel has approximately 100 radial spokes, each<br />
of which holds a character at <strong>the</strong> tip. <strong>The</strong> wheels have low rotational inertia so <strong>the</strong>y<br />
can be spun rapidly, and common letters are clustered toge<strong>the</strong>r to reduce search<br />
time. A solenoid drives <strong>the</strong> letter against ribbon and paper. Commonly used spokes<br />
will eventually wear and <strong>the</strong> wheel will need replacing. Wheels and ribbons aren't<br />
standardized to any extent. <strong>The</strong>se printers are usually more expensive than dotmatrix<br />
units and are often slower, but <strong>the</strong> print quality is very good. Some<br />
daisywheel printers offer double strike, proportional, and shadow printing.<br />
General Remarks<br />
Printers normally use continuous fanfold paper. Letterhead stationery is available in<br />
continuous fanfold paper. Pin feed or sprocket feed usually implies that <strong>the</strong> printer<br />
feed mechanism has fixed sprockets. Tractor feed often implies that variable-width<br />
paper is usable. Friction feed indicates that rolls or sheets of unperforated paper are<br />
accepted.<br />
546
Major Peripherals<br />
Most printers use endless-loop cartridge ribbons or, for higher quality, fixedlength<br />
carbon film ribbons. Ribbon cartridges are not standardized, so be sure that<br />
you have access to a reliable supplier. Some of <strong>the</strong>se printers use standard typewriter<br />
ribbons (like <strong>the</strong> popular Gemini printers), which makes <strong>the</strong> ribbon less costly to<br />
replace.<br />
External switches can range from simple paper control (linefeed, formfeed, and<br />
online buttons) up to complete control over baud rate, parity, horizontal and vertical<br />
spacing, and so on. Some printers, like Epson's RX-80, have an automatic linefeed<br />
switch inside <strong>the</strong> machine. <strong>The</strong> switch is inaccessible without removing <strong>the</strong> lid and<br />
can be a liability if a printer is shared between computers.<br />
Maintenance generally requires return of <strong>the</strong> machine to <strong>the</strong> manufacturer, often<br />
via a dealer. Fortunately, most printers are quite reliable. But it is still a good idea to<br />
be sure that some maintenance is possible and that it is not too costly.<br />
<strong>The</strong> speed of a printer is usually quoted in characters per second or lines per<br />
minute, nei<strong>the</strong>r of which is a very satisfactory description. A lot depends on <strong>the</strong> den<br />
sity of <strong>the</strong> text to be output. Moreover, <strong>the</strong> figures quoted are often inaccurate. As<br />
usual it is best to test <strong>the</strong> printer in <strong>the</strong> conditions you plan to use it before you buy.<br />
<strong>Commodore</strong> <strong>64</strong> compatibility is difficult with regard to graphics and upper/<br />
lowercase switching. Few printers offer <strong>the</strong> entire <strong>64</strong> character set, and interfaces<br />
may not handle <strong>the</strong> <strong>64</strong> upper/lowercase switch. However, in some cases, <strong>the</strong> inter<br />
faces <strong>the</strong>mselves are programmable to allow for this or contain <strong>the</strong>ir own ROM<br />
character definitions for <strong>the</strong> <strong>Commodore</strong> graphics and reverse-field characters.<br />
<strong>Programming</strong> for Printers<br />
<strong>The</strong> following discussion is not intended to replace printer manuals, since <strong>the</strong>re are<br />
too many possible variations to cover each one completely. Instead, it offers sugges<br />
tions and hints on using printers correctly.<br />
<strong>Commodore</strong> printers are controlled in two ways, by <strong>the</strong> secondary address or by<br />
special characters with an ASCII value usually below 32. <strong>The</strong> table of ASCII codes in<br />
Appendix G shows <strong>the</strong> conventional meanings of codes 0-31, most of which are<br />
more relevant to Teletypes than to printers. <strong>The</strong> ESCape code, CHR$(27), is widely<br />
used with non-<strong>Commodore</strong> printers. Anything following ESCape is treated by a spe<br />
cial routine independent of <strong>the</strong> rest and can be used to set <strong>the</strong> features of <strong>the</strong> printer.<br />
In that respect, it works much like channel 15 of <strong>Commodore</strong> disk drives. Com<br />
modore printers could have used this method ra<strong>the</strong>r than secondary addresses.<br />
Some <strong>64</strong> control characters, like {CLR} and {DEL}, mean nothing to <strong>Commodore</strong><br />
printers and may cause <strong>the</strong>m to hang up. A number of <strong>the</strong> ASCII control characters<br />
are irrelevant. <strong>The</strong> special characters controlling <strong>64</strong> printers <strong>the</strong>refore are chosen<br />
from those characters. <strong>The</strong> characters and <strong>the</strong>ir functions are given in Table 17-2.<br />
Table 17-2. Common Printer Control Characters<br />
CHR$(10)<br />
CHR$(13)<br />
CHR$(17)<br />
CHR$(18)<br />
CHR$(145)<br />
CHR$(146)<br />
Linefeed<br />
Return<br />
Lowercase<br />
Reverse characters<br />
Uppercase<br />
Normal characters<br />
547
Major Peripherals<br />
All o<strong>the</strong>r controls have varied among different models of <strong>Commodore</strong> printers,<br />
and it is risky to assume <strong>the</strong>y will remain <strong>the</strong> same as <strong>the</strong>y are on your model. For<br />
example, <strong>the</strong> user-definable single character is CHR$(8), but in <strong>the</strong> past has been<br />
CHR$(254). Secondary addresses have varied as well: <strong>the</strong> 1515 uses OPEN 4,4,7 to<br />
set lowercase mode; earlier models used this for uppercase.<br />
To avoid such problems, you should let <strong>the</strong> <strong>64</strong> do <strong>the</strong> work of formatting and so<br />
on as much as possible. O<strong>the</strong>rwise, if you give your program to ano<strong>the</strong>r user or<br />
change printers for some reason, you may be faced with <strong>the</strong> irritating job of rewrit<br />
ing PRINT# statements.<br />
PRINT# and CMD<br />
<strong>The</strong>se two commands often cause confusion, since <strong>the</strong>y have almost identical effects.<br />
For example, after OPEN 4,4, <strong>the</strong> commands PRINT#4/'HELLO" and CMD4:<br />
PRINT "HELLO" each print HELLO to file 4. <strong>The</strong> difference is that CMD leaves <strong>the</strong><br />
printer in a listening mode, so future PRINT statements are output to <strong>the</strong> printer.<br />
However, CMD isn't really implemented properly. Although it works well with<br />
LIST (OPEN 4,4: CMD4: LIST), if CMD 4 is followed by a program with PRINT<br />
statements, it isn't reliable. GET, for example, makes <strong>the</strong> printing revert to <strong>the</strong><br />
screen. Thus, it's usually best to use PRINT#. If you wish to divert some output to<br />
<strong>the</strong> screen, use something like <strong>the</strong> routine shown below.<br />
10 PRINT "OUTPUT TO PRINTER OR SCREEN (P/S)";: INPUT X$<br />
20 IF X$="P" THEN D=4<br />
30 IF X$ ="S" THEN D=3 :REM SCREEN IS DEVICE 3<br />
40 OPEN D,D :REM NOW USE PRINT#D<br />
<strong>The</strong> same method can select device 5 ra<strong>the</strong>r than device 4, if appropriate, and<br />
OPEN 128+D,D with PRINT#128+D can add an extra linefeed which some print<br />
ers may require.<br />
As noted above, you should use PRINT#4 after CMD4 to "unlisten" <strong>the</strong> printer,<br />
and return everything to normal, followed by CLOSE4. Note that CMD4; and<br />
PRINT#4; each output nothing and can be used if it is important not to linefeed<br />
when <strong>the</strong>se commands are executed.<br />
Upper- and Lowercase<br />
CBM printers don't generally behave like VIC and <strong>64</strong> printers, since <strong>the</strong>y remain in<br />
ei<strong>the</strong>r uppercase or lowercase mode until changed. <strong>The</strong> CBM models revert to upper<br />
case unless specifically told o<strong>the</strong>rwise. After a RETURN, <strong>the</strong> lowercase mode is can<br />
celed. <strong>The</strong>refore, PRINT#4,CHR$(17); has to precede lowercase material, and<br />
PRINT#4,CHR$(145); must precede uppercase, if <strong>the</strong> two are mixed on a line (for<br />
example, lowercase letters mixed with graphics).<br />
Formerly, LISTing a program in lowercase was difficult, but secondary address 7<br />
allows this with some printers—use OPEN 4,4,7:CMD4,"TITLE": LIST.<br />
Formatting<br />
PRINT USING in Chapter 6 can format numbers, inserting leading spaces and trail<br />
ing zeros (as in 100.00). Alternatively, in BASIC, it's best to use something like<br />
SP$ = "{10 SPACES}": PRINT#4,RIGHT$(SP$+X$,10) instead of TAB. This right<br />
548
Major Peripherals<br />
justifies a string (or numeral held as a string) by padding with spaces, <strong>the</strong>n selecting<br />
a fixed length.<br />
<strong>The</strong> simplest way to truncate numerals is to use an expression like PRINT#4,<br />
INT(X*100 + .5)/100 which rounds to <strong>the</strong> nearest hundredth. Some CBM printers<br />
have formatting, typically allowing one format at a time to be defined (for instance,<br />
OPEN 2,4,2: PRINT#2,"S$$$$$9.99" and OPEN 1,4,1). PRINT#1 <strong>the</strong>n prints in a<br />
format defined by secondary address 2, so that 123.456 prints as +$123.45.<br />
User-Defined Graphics/Screen Dump<br />
<strong>The</strong> 1525 and MPS-801 use CHR$(8) to enter graphic mode, in which <strong>the</strong> dot pat<br />
tern of <strong>the</strong> printed character can be defined. Since <strong>the</strong>se printers form characters in a<br />
6X7 matrix, six columns of seven dots have to be defined. It's also necessary to<br />
add 128 to <strong>the</strong> value for each column. All that's needed is PRINT#4, CHR$(8) fol<br />
lowed by <strong>the</strong> bytes which define <strong>the</strong> columns. You can use CHR$ to define <strong>the</strong> bytes<br />
for <strong>the</strong> column values—for example, PRINT#4,CHR$(8) CHR$(150) CHR$(182)<br />
CHR$(224) CHR$(224) CHR$(182) CHR$(150). You can also use PRINT#4,X$ where<br />
X$ is built from values in a DATA statement, starting with 8. Remember to PRINT#4,<br />
CHR$(15) after graphic printing to return <strong>the</strong> printer to normal text mode.<br />
<strong>The</strong> 1526 has a single definable character, CHR$(254), specified as eight col<br />
umns of eight dots by opening a file to <strong>the</strong> printer with a secondary address of 5.<br />
Thus <strong>the</strong> 1526 equivalent for <strong>the</strong> example above would be OPEN 5,4,5: PRINT#5,<br />
CHR$(0) CHR$(22) CHR$(54) CHR$(96) CHR$(96) CHR$(54) CHR$(22) CHR$(0):<br />
CLOSE 5 to define <strong>the</strong> character, followed by OPEN 4,4:PRINT#4,CHR$(254):<br />
CLOSE 4 to print it.<br />
An interesting use for this definable character capability is to dump a highresolution<br />
screen to <strong>the</strong> printer. Multicolor mode is not as easy, since <strong>the</strong> printer<br />
can't distinguish <strong>the</strong> four colors. Program 17-1 slowly dumps a bitmap screen start<br />
ing at 8192 ($2000). It will not work with all printers, due to differences in <strong>the</strong><br />
printer commands and features. (It cannot be used with <strong>the</strong> 1526 printer.)<br />
Program 17-1. Graphics Screen Dump<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
100 BA=8192:REM BASE ADDRESS MAY DIFFER :rem 251<br />
105 OPEN 4,4 :rem 92<br />
110 FOR J=0 TO 7:P(j)=2fJ:NEXT :rem 191<br />
115 FOR X=0 TO 319 STEP 7 :rem 246<br />
120 X$=IMI :rem 144<br />
125 FOR Y=199 TO 0 STEP-1:V=0 :rem 34<br />
130 FOR BT=0 TO 6 :rem 87<br />
140 X1=X+BT:X2=X1 AND 7:IF BT AND X2 THEN 150<br />
:rem 137<br />
145 BY=BA + (Y AND 248)*40 + (XI AND 504) + (Y AND<br />
7) :rem 25<br />
150 V=V + P(BT)*SGN(PEEK(BY) AND P(7-X2)) :rem 183<br />
155 NEXT :rem 218<br />
160 PRINT V; :rem 181<br />
165 X$=X$ + CHR$(V+128) :rem 106<br />
170 NEXT :rem 215<br />
175 PRINT#4,CHR$(8)X$ :rem 179<br />
180 NEXT:CLOSE4 :rem 188<br />
549
Major Peripherals<br />
Printing a copy of a normal text screen is considerably less complicated than<br />
reproducing graphics. Program 17-2 is an ML screen dump which works with most<br />
printers, as long as <strong>the</strong> screen display uses only <strong>the</strong> ordinary ASCII characters with<br />
no <strong>64</strong> graphics. It includes tests for <strong>the</strong> screen start position, and for lower- or upper<br />
case mode. Use OPEN 4,4: CMD 4: SYS 830: PRINT#4: CLOSE 4 to activate <strong>the</strong><br />
routine.<br />
Program 17-2. ML Character Screen Dump<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
1 REM <strong>64</strong> SCREEN DUMP :rem 117<br />
2 REM USE OPEN 4,4:CMD4:SYS 830:PRINT#4:CLOSE4<br />
:rem 225<br />
3 REM ALLOWS FOR U/L CASE & SCREEN POSN :rem 226<br />
4 REM :rem 24<br />
5 FOR J=830 TO 932:READ X:POKE J,X:NEXT :rem 221<br />
10 DATA 169,0,133,3,133,4,133,5,173,136,2,133,6,16<br />
9 :rem 188<br />
11 DATA <strong>64</strong>,133,2,230,3,165,3,201,41,208,15,230,4<br />
:rem 23<br />
12 DATA 165,4,201,24,240,67,169,1,133,3,32,215,170<br />
:rem 137<br />
13 DATA 162,0,161,5,41,127,36,2,208,6,36,129,240<br />
srem 41<br />
14 DATA 1*,208,33,36,129,208,9,72,169,2,44,24,208<br />
jrem 116<br />
15 DATA 208,13,104,169,35,208,16,72,169,2,44,24,20<br />
8 :rem 207<br />
16 DATA 208,5,104,9,<strong>64</strong>,208,3,104,9,96,32,210,255<br />
:rem 54<br />
17 DATA 230,5,208,177,230,6,208,173,76,215,170<br />
:rem 218<br />
<strong>The</strong> above routine substitutes <strong>the</strong> dummy character # for any graphics found in<br />
<strong>the</strong> screen display. Alter <strong>the</strong> 35 in line 15 to change <strong>the</strong> dummy character to some<br />
o<strong>the</strong>r symbol.<br />
Repeat<br />
Some printers allow repetition of characters, notably of a single column of dots to<br />
build up a horizontal bar. A command like PRINT#4, CHR$(8) CHR$(26) CHR$(X)<br />
CHR$(255) CHR$(15); turns graphics on, turns repeat mode on, specifies <strong>the</strong> number<br />
of repetitions (X, which must be in <strong>the</strong> range 1-255), specifies character definition<br />
(255 gives a solid column of dots), and returns to normal graphics.<br />
Printer Presence<br />
Some programmers find this useful as a reminder to users to switch on <strong>the</strong> printer.<br />
In its simplest form, <strong>the</strong> command is OPEN 4,4: POKE 154,4: SYS 65490: POKE<br />
154,3: CLOSE 4: S=ST. When <strong>the</strong> printer is on, ST should be 0; when off, ST is<br />
-128, corresponding to 7DEVICE NOT PRESENT. SYS 65490 is <strong>the</strong> output routine<br />
550
Major Peripherals<br />
at $FFD2, and <strong>the</strong> above routine in effect tries to output to file 4, but avoids crashing<br />
in <strong>the</strong> way that PRINT#4 does.<br />
Spooling<br />
<strong>The</strong> idea of spooling is that a file can be read from disk and printed while <strong>the</strong> <strong>64</strong> is<br />
left free to run programs normally, except that accessing <strong>the</strong> serial bus is prohibited.<br />
In principle this seems easy, since <strong>the</strong> disk can talk and <strong>the</strong> printer can listen, but<br />
<strong>the</strong>re is no simple way to accomplish it. Some printers and some interfaces include a<br />
printer buffer, which accepts data from <strong>the</strong> computer and holds it until <strong>the</strong> printer<br />
can process it. Once all <strong>the</strong> data has been sent to <strong>the</strong> buffer, <strong>the</strong> computer returns to<br />
o<strong>the</strong>r operations.<br />
Plotters<br />
Plotters are most commonly used commercially for technical drawings. Plotters have<br />
two stepper motors controlling pen or paper movement (or both) vertically and hori<br />
zontally, with a mechanism to lift <strong>the</strong> pen off <strong>the</strong> paper. Typically, eight directions of<br />
motion can be selected. Small step sizes make for finer drawings, if <strong>the</strong> pen itself is<br />
fine enough, but tend to be slow. <strong>The</strong> fastest rate of plotting with inexpensive plot<br />
ters is roughly three inches per second, so be prepared for long delays, particularly if<br />
<strong>the</strong> interface is slow and if commands are sent with BASIC.<br />
<strong>Commodore</strong>'s 1520 plotter uses 4-1/2-inch-wide unsprocketed paper and has<br />
four pens (typically black, red, blue, and green). It has built-in alphanumerics which<br />
can be scaled to four sizes; <strong>the</strong> smallest draws 22 characters per inch. <strong>The</strong> pens<br />
move across <strong>the</strong> paper, and vertical motion is provided by a roller that moves <strong>the</strong><br />
paper itself. It connects to <strong>the</strong> serial port as device 6.<br />
<strong>The</strong>se plotters can be used to draw perspective pictures, including colorseparation<br />
pairs in red and green, and can also draw geometrical patterns. Yellow,<br />
magenta, and cyan pens could give an imitation of color-separation printing.<br />
Lines<br />
Program 17-3 is a plotter drawing subroutine that assumes a line, having a slope be<br />
tween zero and one, is to be drawn from left to right. (O<strong>the</strong>r slopes, including verti<br />
cal lines, are treated by analogous routines.) XD and YD are <strong>the</strong> distances to be<br />
plotted in <strong>the</strong> X and Y directions, M is <strong>the</strong> slope, and XP and YP keep track of <strong>the</strong><br />
current X and Y positions relative to <strong>the</strong> start of <strong>the</strong> plot. Line 120 plots nor<strong>the</strong>ast<br />
whenever that gives a better approximation than east.<br />
Program 17-3. Line Plotter<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
100 M=YD/XD:YP=0:REM M IS THE GRADIENT. Y-POSITION<br />
STARTS 0. :rem 113<br />
110 FOR XP=1 TO XD: PRINT#N,EAST: REM EXACT FORM V<br />
ARIES WITH PLOTTER srem 86<br />
120 IF M*XP>YP THEN PRINT#N,NORTHEAST: YP=YP+1:XP=<br />
XP+1:IF XP
Major Peripherals<br />
130 NEXT :rem 211<br />
140 IF YP-KYD THEN PRINT#N,NORTHEAST: YP=YP+1:GOT<br />
0 140:REM FINISH :rem 33<br />
Circles<br />
<strong>The</strong>re are several methods to plot circles. One useful circle plotting routine is eiven<br />
in Program 17-4. a<br />
Program 17-4. Circle Plotter<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
500 REM Q=DEGREES SUBTENDED BY EACH STRAIGHT LINE<br />
{SPACE}SEGMENT. •rem 31<br />
505 REM Q=10 PLOTS A 36-SIDED FIGURE :rem 1<br />
510 G=R:H=0: REM R=RADIUS. G AND H ARE INTERMEDIAT<br />
E VALUES :rem 131<br />
520 N=360/Q: REM N=NUMBER OF SIDES=NUMBER OF REPET<br />
ITIONS OF LOOP :rem 122<br />
530 F=COS(Q*_t/l80): I=SIN(Q*t/l80) :REM TRIG PARAME<br />
TERS :rem 24<br />
540. FOR J=0 TO N rrem 40<br />
550 C=G*F-H*I:A=G*I+H*F: REM THESE ARE THE X AND Y<br />
COORDINATES OF THE NEXT PT. :rem 1<strong>64</strong><br />
560 REM DRAW THE STRAIGHT LINE SEGMENT TO THE POIN<br />
T X=C,Y=A :rem 24<br />
570 G=C:H=A .rem 99<br />
580 NEXT J :rem 38<br />
Program 17-5 demonstrates <strong>the</strong> <strong>Commodore</strong> 1520 plotter's ability to draw<br />
graphs.<br />
Program 17-5. Plotter Demo<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C<br />
10 OPEN 4,6 rrem 41<br />
20 OPEN 6,6,1 :rem 137<br />
30 PRINT#6,"H" srem i<br />
40 FOR X=20 TO 460 STEP 2 .rem 237<br />
50 Y=SIN((X-20)*4*J7440) :rem 188<br />
60 Y=Y+(l/3)*SIN((X-20)*4*3*T/440) 2rem 172<br />
70 Y=Y+(l/5)*SIN((X-20)*4*5*T/440) -rem 177<br />
80 PRINT#6,"D\X,Y*70 " srem 156<br />
90 NEXT .rem 16Q<br />
100 PRINT#6,"M",479,0 \rem 96<br />
110 PRINT#6,"D",0,0 :rem 228<br />
120 PRINT#6,"D",0,110 .rem 71<br />
130 PRINT#6,"M",0,0 :;em 239<br />
140 PRINT#6,"R",0,-100 srem 131<br />
150 PRINT#4,"WAVEFORM WITH 2 ODD HARMONICS ADDED"<br />
160 PRINT#4:CLOSE 4:PRINT#6:CLOSE 6 irem 101<br />
552
Major Peripherals<br />
Modems<br />
Most <strong>64</strong> modem users have ei<strong>the</strong>r <strong>the</strong> model 1600 VICModem or <strong>the</strong> 1650<br />
Automodem. Both are designed for <strong>the</strong> U.S. phone system. <strong>The</strong> 1650 plugs into <strong>the</strong><br />
base of <strong>the</strong> phone, while VICModem requires that you use a phone with a modular<br />
plug handset in order to connect correctly.<br />
When <strong>the</strong> <strong>64</strong> is used to communicate with ano<strong>the</strong>r computer, <strong>the</strong> users must de<br />
cide which computer will originate <strong>the</strong> communication and which will answer. For<br />
example, when a bulletin board system like CompuServe is to be accessed, <strong>the</strong> <strong>64</strong> is<br />
always set to originate while talking to <strong>the</strong> system.<br />
To use a modem, first connect <strong>the</strong> modem to <strong>the</strong> computer with <strong>the</strong> <strong>64</strong> turned<br />
off. Plug <strong>the</strong> <strong>Commodore</strong> modem into <strong>the</strong> user port (and connect it to <strong>the</strong> phone, if<br />
it is a direct-connect model like <strong>the</strong> 1650), <strong>the</strong>n load and run <strong>the</strong> terminal software.<br />
Terminal software is <strong>the</strong> program that facilitates computer-to-computer talking via<br />
<strong>the</strong> modems. It may be on cartridge, tape, or disk. For <strong>the</strong> VICModem, you may use<br />
<strong>64</strong> Term and for <strong>the</strong> Automodem, use Term <strong>64</strong>, provided with <strong>the</strong> modem package.<br />
You may want to use your own terminal software. BASIC, although slow, is<br />
about as fast as <strong>the</strong> modem, so this is often a useful thing to do, notably when talk<br />
ing to computers with slightly unusual characteristics or when trying out unusual<br />
maneuvers like transferring files of data.<br />
Once <strong>the</strong> software has been loaded and run, dial <strong>the</strong> number (ei<strong>the</strong>r by dialing<br />
yourself or by inputting <strong>the</strong> number into <strong>the</strong> <strong>64</strong> and allowing <strong>the</strong> 1650 to dial for<br />
you). Wait for <strong>the</strong> carrier signal (a high-pitched tone). You may of course get a<br />
wrong number, outdated number, no reply, or a reply from a system operator<br />
(sysop). With some modems, you'll need to set <strong>the</strong> voice/data switch.<br />
Now, wait for <strong>the</strong> carrier-detected indicator (red light) to come on. Your soft<br />
ware may print something like <strong>64</strong> CONNECTED. Ei<strong>the</strong>r signifies that your modem<br />
has recognized <strong>the</strong> incoming frequency. Again, <strong>the</strong> actual procedure varies between<br />
modems; it's automatic with <strong>the</strong> Automodem, but acoustic modems require you to<br />
put <strong>the</strong> handset into <strong>the</strong> cups of <strong>the</strong> modem and switch to data (online) mode.<br />
Wait for <strong>the</strong> system's first welcome frame to appear. Public systems often ask<br />
for a password. This allows <strong>the</strong>m to charge for access to <strong>the</strong> system. Use <strong>the</strong> menu<br />
to select an item from what's available. CompuServe and o<strong>the</strong>r systems provide a<br />
large directory to help you with this: GO CBM 310 is a shortcut command with<br />
CompuServe.<br />
Notes on Modems<br />
It's helpful to know something about how modems work before looking at program<br />
ming. Modems and <strong>the</strong>ir software are designed around phone systems. This has sev<br />
eral consequences. Data must be transmitted serially, as bits ra<strong>the</strong>r than as bytes, so<br />
each end of <strong>the</strong> line needs a way to convert between parallel and serial transmission.<br />
In addition, <strong>the</strong> system needs some means of identifying <strong>the</strong> start of a byte; it also<br />
needs timing conventions so that it can reliably detect individual bits.<br />
Certain technical parameters are also important. Phone companies maintain con<br />
trol over certain technical details of <strong>the</strong>ir lines. <strong>The</strong>y do not permit excessive voltages<br />
to get to exchanges, and some tones and signals are reserved for diagnostic use.<br />
Such standards vary internationally. As a result, modems in different countries are<br />
553
Major Peripherals<br />
likely to be incompatible. It may, in fact, be illegal to attach modems made in one<br />
country to phone lines in some o<strong>the</strong>r country.<br />
Usually, this isn't a problem. Direct-connect modems are designed to be isolated<br />
from <strong>the</strong> phone line so high voltages cannot pass ei<strong>the</strong>r way. Acoustic modems,<br />
which generate and receive sounds and communicate <strong>the</strong>m through telephone hand<br />
sets, also present no voltage problems but face international compatibility problems.<br />
<strong>The</strong> most common American modem convention is <strong>the</strong> Bell 103, which is used<br />
by <strong>Commodore</strong> modems and by many computers. However, this is slow, as charac<br />
ters are generally transmitted at 300 bits per second. In practice, this amounts to 30<br />
characters per second at most. If <strong>the</strong> phone connection is weak, <strong>the</strong> transmission rate<br />
drops, since characters have to be retransmitted. Even 30 characters a second takes<br />
half a minute to fill a 40-column screen, and some characters are likely to be used<br />
for information on color, screen format, and so on. Still, this is faster than many<br />
people can read or talk.<br />
Bell 103 uses a system called frequency shift keying. It means that an on bit is<br />
transmitted with one frequency tone, while an off bit is transmitted with a tone of<br />
ano<strong>the</strong>r frequency. <strong>The</strong> tone of <strong>the</strong> signal carries information. In order that both<br />
ends of <strong>the</strong> line can talk, Bell 103 uses four tones altoge<strong>the</strong>r, which means messages<br />
can be simultaneously sent both ways.<br />
<strong>The</strong> receiving equipment at ei<strong>the</strong>r end has <strong>the</strong> job of sorting out which fre<br />
quency is being received. All frequencies are relatively high pitched, in order to carry<br />
as much information as possible while still being within <strong>the</strong> frequency range<br />
handled by <strong>the</strong> phone system. <strong>The</strong> frequencies in originate mode are 1270 Hz to<br />
transmit a mark signal, 1070 Hz to transmit space, 2225 Hz to receive mark, and<br />
2025 Hz to receive space. In answer mode, <strong>the</strong> frequencies are <strong>the</strong> o<strong>the</strong>r way<br />
around. Note that <strong>the</strong> mark signal is <strong>the</strong> idle or carrier signal, present when nothing<br />
is happening, but <strong>the</strong> system is ready and waiting.<br />
When a modem is in operation, <strong>the</strong>se tones are exchanged and deciphered.<br />
Conversion of bits into tones is called modulation, and <strong>the</strong> reverse process is called<br />
demodulation. <strong>The</strong> term modem is thus an abbreviation of <strong>the</strong> words modulator and<br />
demodulator.<br />
At 300 baud (or, more properly, 300 bits per second), <strong>the</strong> <strong>64</strong>'s modem receives<br />
300 tones of 2225 or 2025 Hz every second. <strong>The</strong> VICModem handles all of this with<br />
a single chip, using some o<strong>the</strong>r components to filter <strong>the</strong> four frequencies, and draws<br />
its power from <strong>the</strong> user port. (Note that <strong>the</strong> <strong>64</strong>'s tape system is practically a modem.<br />
However, it sends square waves, not sine waves, which aren't suited to phone lines.<br />
But digital-to-analog converters make it feasible to run a modem from <strong>the</strong> cassette<br />
port.)<br />
Bytes or words can be formatted in different ways, and every pair of commu<br />
nicating modems must be set to <strong>the</strong> same convention. Standard RS-232 has one start<br />
bit, seven data bits, one even-parity bit, and one stop bit—a total of ten bits per byte<br />
sent. Even parity means that <strong>the</strong> eighth bit is set to 0 or 1 to make <strong>the</strong> sum of <strong>the</strong><br />
individual bit values even. For example, <strong>the</strong> ASCII pattern for lowercase a is<br />
1100001 (97 in decimal). For even parity, a parity bit of 1 is added, so <strong>the</strong> resulting<br />
pattern, 11100001, has an even number of l's. (Bits are transmitted from <strong>the</strong> low bit<br />
first, so <strong>the</strong> parity bit is calculated and sent last.)<br />
554
Major Peripherals<br />
This is a security measure. Any received byte that doesn't conform to this pat<br />
tern must be wrong and is retransmitted. Note that some seven-bit codes always<br />
send <strong>the</strong> same parity bit, ei<strong>the</strong>r a space or mark, ignoring <strong>the</strong> security aspect. <strong>The</strong><br />
start and stop bits are both signaled by transmitting a space (ra<strong>the</strong>r than a mark), so<br />
synchronization is always okay. OPENing an RS-232 file allows <strong>the</strong>se variables to be<br />
controlled by <strong>the</strong> programmer, within limits.<br />
Note that since <strong>Commodore</strong> <strong>64</strong> characters use eight bits, standard ASCII isn't<br />
enough. In fact, much software simply ignores parity bits, using o<strong>the</strong>r error-checking<br />
methods instead. Getting <strong>the</strong> baud rate, <strong>the</strong> number of bits per word, and <strong>the</strong> num<br />
ber of start and stop bits right is necessary to successful modem communication.<br />
Converting bytes into bits and sending <strong>the</strong>m, and <strong>the</strong> converse process of<br />
assembling bits into bytes, can be performed in software (as <strong>the</strong> <strong>64</strong>'s RS-232<br />
handling does) or by chips like <strong>the</strong> UART (Universal Asynchronous Receiver-<br />
Transmitter—asynchronous means it can process data by watching for a start bit).<br />
Error checking is a complex process, which basically uses hash totals sent after<br />
data as a check. With any system <strong>the</strong>re must be some chance of completely random<br />
data conforming to <strong>the</strong> check, and such events constitute undetected errors. Gen<br />
erally, note that data is sent in batches (called records) of 256 bytes each. Records<br />
with errors are retransmitted, and <strong>the</strong> overhead spent on this process can be as much<br />
as 50 percent of <strong>the</strong> ideal error-free transmission time, depending on <strong>the</strong> quality of<br />
<strong>the</strong> phone link. Error correction may be automatic, or software may use a recall fea<br />
ture if a frame is unacceptable.<br />
Bell 103 modems use full duplex, which means that ei<strong>the</strong>r terminal can commu<br />
nicate at any time. Half duplex is analogous to radio communication, where ei<strong>the</strong>r<br />
direction is available, but normally only one at a time. <strong>The</strong> half-duplex switch turns<br />
off <strong>the</strong> echo-plexing feature, a verification system which returns characters when<br />
<strong>the</strong>y're received. Thus, if characters appear double, use this switch or software which<br />
verifies <strong>the</strong> echoed characters. True half duplex requires a line like RS-232's second<br />
ary channel to be able to interrupt unwanted messages.<br />
Smart terminal software can download programs (load <strong>the</strong>m and ei<strong>the</strong>r run<br />
<strong>the</strong>m or store <strong>the</strong>m on disk or tape). Data files are more difficult to handle, because<br />
<strong>the</strong>y don't transfer as simply as programs, having RETURN characters and so on<br />
embedded in <strong>the</strong>m. <strong>The</strong>y are also liable to exceed RAM storage. Downloading files,<br />
<strong>the</strong>refore, generally refers to programs and frames from data bases.<br />
<strong>The</strong> two o<strong>the</strong>r common modem standards are <strong>the</strong> Bell 202 and 212A, which are<br />
faster than <strong>the</strong> 103 standard. <strong>The</strong> 212A can work with <strong>the</strong> 103, but <strong>the</strong> 202 and<br />
212A are currently less popular than <strong>the</strong> 103, mainly because <strong>the</strong> faster modems are<br />
significantly more expensive. Incidentally, <strong>the</strong> 103 system can operate at 600 bits per<br />
second, which may be worth trying.<br />
One problem with acoustic modems may be getting <strong>the</strong> two cups which are sup<br />
posed to fit <strong>the</strong> handset into place. A few modems forgo <strong>the</strong> rigid body in favor of a<br />
pair of cups on leads, so <strong>the</strong>y can fit many phone styles. Incidentally, over short dis<br />
tances it's not necessary to use a modem—two VICs or <strong>64</strong>s can be connected by<br />
three lines between <strong>the</strong>ir user ports, or with RS-232 adapters. As always, leave this<br />
work to someone with sufficient technical experience.<br />
555
Major Peripherals<br />
<strong>Programming</strong> Modems<br />
Programs for use with modems must allow for two things. First, <strong>the</strong> RS-232 file must<br />
be opened properly. Second, transmissions both to and from <strong>the</strong> <strong>64</strong> must be allowed<br />
for. Both are fairly straightforward, though <strong>the</strong>y may appear difficult.<br />
Opening an RS-232 file. Only one RS-232 file can be opened at any one time,<br />
and <strong>the</strong> syntax is typically OPEN 2,2,0,CHR$(6). <strong>The</strong> device number must be 2, so<br />
file number 2 is simplest, allowing PRINT#2 and GET#2 for output and input via<br />
<strong>the</strong> modem. <strong>The</strong> secondary address is irrelevant.<br />
<strong>The</strong> filename consists of one or two characters in a string; <strong>the</strong> example is<br />
equivalent to CHR$(6)+CHR$(0). <strong>The</strong>se parameters are explained fully in <strong>the</strong> next<br />
section. <strong>The</strong> value shown assigns eight bits of data per word, with one stop bit (and<br />
a start bit, implicit in <strong>the</strong> whole process), 300 baud transmission, no parity, and full<br />
duplex. Three-line handshaking is assumed.<br />
This is <strong>the</strong> most common combination. Use OPEN 2,2,0,CHR$(38)CHR$(96) to<br />
assign a seven-bit word and even parity instead.<br />
Transmitting and receiving characters. All that's needed is a loop to get<br />
characters from <strong>the</strong> keyboard and send <strong>the</strong>m through <strong>the</strong> modem using PRINT#2,<br />
and to get characters from <strong>the</strong> modem (using GET#2). BASIC may need delay loops<br />
in its output to avoid sending characters too fast. For most purposes, some characters<br />
have to be converted, and BASIC provides an adaptable and quite easy means to do<br />
this. One reason for <strong>the</strong> conversion is that <strong>64</strong> ASCII is slightly different from true<br />
ASCII, so unless you're happy with strange-looking lettering, conversion is nec<br />
essary. <strong>The</strong> o<strong>the</strong>r reason is that it's useful to define some keys so that <strong>the</strong>y perform<br />
modem-specific functions.<br />
Program 17-6 is a good example of a program for use with a modem.<br />
Program 17-6. <strong>64</strong> Terminal Program<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
100 OPEN 2,2,0,CHR$(6):REM OPENS FILE 2; 300 BAUD,<br />
8 BIT, NO PARITY * :rem 39<br />
101 REM OPEN 2,2,0,CHR$(38)+CHR$(96) FOR ASCII 7 B<br />
ITS + EVEN PARITY :rem 84<br />
200 DIM F%(255),T%(255) :rem 86<br />
210 FOR J=32 TO <strong>64</strong>:T%(J)=J:NEXT :rem 193<br />
220 FOR J=65 TO 90:T%(J)=J+32:NEXT:REM LOWER-CASE<br />
:rem 71<br />
230 FOR J=91 TO 95:T%(J)=J:NEXT :rem 204<br />
240 FOR J=193 TO 218:T%(J)=J-128:NEXT:REM VIC UPPE<br />
R-CASE srem 202<br />
250 T%(133)=27:T%(134)=127:T%(135)=3:T%(136)=17<br />
251 REM THESE ARE TRUE ASCII:I.E.<br />
:rem 97<br />
ESC,DEL,CTRL-C B<br />
REAK,CTRL-Q .rem 139<br />
260 T%(137)=17:T%(138)=144 :rem 24<br />
300 FOR J=0 TO 255 .rem 112<br />
310 IF T%(J)>0 THEN F%(T%(j))=J srem 43<br />
320 NEXT srem 212<br />
400 PRINT CHR$(147) CHR$(14):REM CLEAR; LOWER-CASE<br />
556<br />
:rem 88
Major Peripherals<br />
500 IF PEEK(669)"" THEN PRINT#2,CHR$(T%(ASC(O<br />
UT$)));:PRINT OUT$; :rem 239<br />
520 GET#2,IN$:IF IN$>M" THEN PRINT CHR$(F%(ASC(IN$<br />
))); :rem 161<br />
521 REM IN$=IN$ AND 127 FOR 7 BIT CODE, :rem 226<br />
530 GOTO 500 :rem 102<br />
Line 100 opens <strong>the</strong> file, while line 101 shows an alternative OPEN statement.<br />
Lines 200-260 allow for conversion between input and output characters. An alter<br />
native way to do this is to use several IF-THEN range comparisons; however, arrays<br />
are faster, since <strong>the</strong> correct value can simply be looked up. Integer arrays save space.<br />
Lines 300-320 convert <strong>the</strong> from array into <strong>the</strong> inverse of <strong>the</strong> to array. Line 500 veri<br />
fies that output data has actually been sent.<br />
<strong>The</strong> status byte, ST, can also be tested. <strong>The</strong> variable IN$ comes from <strong>the</strong><br />
modem; OUT$ is actually fetched from <strong>the</strong> keyboard but is called OUT$ because it is<br />
to be sent to <strong>the</strong> o<strong>the</strong>r computer. Remember that <strong>the</strong> <strong>64</strong> keyboard allows control<br />
characters to be typed without special programming.<br />
ML conversion to true ASCII. ML programmers may need this routine, which<br />
converts a <strong>64</strong> ASCII character into true ASCII. It interchanges upper- with lowercase:<br />
CMP #$41<br />
BCC END<br />
CMP #$5B<br />
BCS LABEL<br />
ORA #$20<br />
BCC END<br />
LABEL CMP #$61<br />
BCC END<br />
CMP #$7B<br />
BCS END<br />
AND #$DF<br />
END continue...<br />
<strong>The</strong> RS-232 Interface<br />
RS-232-C is a communications standard established by <strong>the</strong> Electronic Industries<br />
Association. Its voltage convention is as follows: A negative voltage indicates a mark<br />
(bit value 1), while a positive voltage indicates a space (bit value 0). <strong>The</strong> standard<br />
also establishes a 25-pin connector to be* used in RS-232 equipment. (This connector<br />
is not used by <strong>the</strong> <strong>64</strong>, which provides RS-232 communication through <strong>the</strong> user port.)<br />
Pin numbering is from 1 to 13 (top) and 14 to 25 (bottom). It is sometimes helpful to<br />
know <strong>the</strong> standard functions of <strong>the</strong> pins, which are listed in Table 17-3.<br />
557
Major Peripherals<br />
Table 17-3. RS-232 Pin Functions<br />
Pin Number<br />
Description<br />
1<br />
2<br />
3<br />
4<br />
5<br />
6<br />
7<br />
8<br />
9<br />
10<br />
11<br />
12<br />
13<br />
14<br />
15<br />
16<br />
17<br />
18<br />
19<br />
20<br />
21<br />
22<br />
23<br />
24<br />
25<br />
GND<br />
TX<br />
RX<br />
RTS<br />
CTS<br />
DSR<br />
GND<br />
CD<br />
CL +<br />
CL-<br />
DTR<br />
Protective Ground<br />
Transmitted Data<br />
Received Data<br />
Request to Send<br />
Clear to Send<br />
Data Set Ready<br />
Signal Ground (Common Return)<br />
Carrier Detector<br />
Direct Current Loop (+)<br />
Direct Current Loop (-)<br />
Unassigned<br />
Sec. Rec'd. Line Sig. Detector<br />
Sec. Clear to Send<br />
Secondary Transmitted Data<br />
Transmission Signal Element Timing (DCE Source)<br />
Secondary Received Data<br />
Receiver Signal Element Timing (DCE Source)<br />
Unassigned<br />
Secondary Request to Send<br />
Data Terminal Ready<br />
Signal Quality Detector<br />
Ring Indicator<br />
Data Signal Rate Selector (DTE/DCE Source)<br />
Transmit Signal Element Timing (DTE Source)<br />
Unassigned<br />
In <strong>the</strong> <strong>64</strong>'s RS-232 system, an OPEN to <strong>the</strong> RS-232 device initializes a number<br />
of RAM locations and prepares for NMI interrupts, which are used with RS-232.<br />
<strong>The</strong>se interrupts disturb disk and tape timing, which is one reason nei<strong>the</strong>r <strong>the</strong> disk<br />
drive nor <strong>the</strong> Datassette can be used during transmission.<br />
RS-232's OPEN routine (at $F409 in <strong>the</strong> <strong>64</strong>) sets <strong>the</strong> parameters indicated in<br />
Table 17-4. If you OPEN and <strong>the</strong>n PEEK, you'll see some of <strong>the</strong>m. Most are reason<br />
ably straightforward. Two points are worth noting: OPEN to RS-232 lowers <strong>the</strong> top<br />
of memory by 512 bytes, making room for two 256-byte FIFO (first-in, first-out)<br />
buffers. BASIC pointers are altered to clear variables (equivalent to a CLR state<br />
ment), so it's best to OPEN <strong>the</strong> RS-232 file early in <strong>the</strong> program to avoid losing vari<br />
able values. <strong>The</strong> baud rate is controlled by reference to tables in ROM, which in <strong>64</strong><br />
has ten usable values.<br />
ML programmers may want to alter <strong>the</strong> NMI vector into RAM so that <strong>the</strong> tables<br />
can be changed. Alter <strong>the</strong> first tabled value to generate <strong>the</strong> new baud rate. After<br />
OPEN, remember to POKE <strong>the</strong> vector at ($299) with twice that value, plus 200. To<br />
convert ROM values into equivalent baud rates, use 50*EXP(9.23308 —<br />
LOG(VALUE + 100)).<br />
558
Major Peripherals<br />
Table 17-4. Locations Set by OPEN to RS-232 Channel<br />
$A7<br />
$A8<br />
$A9<br />
$AA<br />
$AB<br />
$B4<br />
$B5<br />
$B6<br />
$F7/F8<br />
$F9/FA<br />
$0293<br />
$0294<br />
$0295/0296<br />
$0297<br />
$0298<br />
$0299/029A<br />
$029B<br />
$029C<br />
$029D<br />
$029E<br />
Location<br />
167<br />
168<br />
169<br />
170<br />
171<br />
180<br />
181<br />
182<br />
247/248<br />
249/250<br />
659<br />
660<br />
661/662<br />
663<br />
6<strong>64</strong><br />
665/666<br />
667<br />
668<br />
669<br />
670<br />
Explanation<br />
Receive bit storage<br />
RX bit count<br />
RX start bit flag<br />
RX byte shifts in here<br />
RX parity bit<br />
TX bit count<br />
Next bit for TX<br />
TX byte shifts out from here<br />
Pointer to start of input buffer<br />
Pointer to start of output buffer<br />
Control Register (e.g., 6)<br />
Command Register (e.g., 0)<br />
Two o<strong>the</strong>r unused parameters<br />
ST value for RS-232<br />
9, 8, 7, or 6 bits in word +1<br />
2*timer value+200<br />
End of Receive FIFO Buffer<br />
Start of Receive Buffer<br />
Start of Transmit Buffer<br />
End of Transmit Buffer<br />
Control Register and Command Register<br />
Values in <strong>the</strong>se registers control <strong>the</strong> way RS-232 is configured. <strong>The</strong>re are six param<br />
eters involved. As stated earlier, OPEN 2,2,2,CHR$(6)+CHR$(0) assumes one stop<br />
bit, eight bits per word, 300 baud, no parity bit, full duplex, and <strong>the</strong> usual three-line<br />
handshake. <strong>The</strong> control register is set by <strong>the</strong> first CHR$ value, and <strong>the</strong> command<br />
register is set by <strong>the</strong> second. Figures 17-1 and 17-2 give details on <strong>the</strong> control reg<br />
ister and command register.<br />
559
1<br />
1<br />
ON Figure 17-1. <strong>The</strong> Control Register<br />
o<br />
128 <strong>64</strong> 32<br />
Number of<br />
Stop Bits<br />
Word Length<br />
(Excluding Parity)<br />
Unused Baud Rate Control<br />
0=single 00 = 8 bits<br />
l = two 01 = 7 bits (e.g., ASCII)<br />
10=6 bits<br />
11 = 5 bits (e.g., Baudot)<br />
0001 =<br />
0010 =<br />
: 75<br />
0011 =<br />
0100 =<br />
0101 = 150<br />
0110 = 300<br />
0111 =<br />
1000=<br />
1001 =<br />
1010 =<br />
50<br />
110<br />
134.5<br />
600<br />
1200<br />
1800<br />
2400<br />
Figure 17-2. <strong>The</strong> Command Register<br />
128 <strong>64</strong> 32 16<br />
Parity Type<br />
Parity Bit/<br />
No Parity Bit<br />
Duplex Unused<br />
1 |<br />
00=Odd Parity<br />
01=Even Parity<br />
10=Mark Bit<br />
11 = Space Bit<br />
0 = None<br />
1 = Parity Bit<br />
0 = Full<br />
1 = Half<br />
Handshake<br />
Type<br />
0 = 3-Line<br />
1 = X-Line<br />
Q<br />
O"<br />
(D
Major Peripherals<br />
Finally, six bits of location 663 ($297) report conditions resulting from RS-232<br />
use. If bit 0 is set, <strong>the</strong>re is a parity-bit error. Bit 1 being set indicates an error in <strong>the</strong><br />
structure of received bits, perhaps due to noise. Bit 2 is set when <strong>the</strong> receiver buffer<br />
is full (that is, when data is coming in too fast). Bit 3 is set to indicate when <strong>the</strong> re<br />
ceiver buffer is empty. Bit 4 is set when <strong>the</strong> clear-to-send signal is off (when <strong>the</strong> re<br />
mote terminal is not ready to receive). Bit 5 is unused, and bit 6 is set when <strong>the</strong><br />
data-set-ready signal is missing (<strong>the</strong> remote terminal is not ready to send). When bit<br />
7 is set, a break has been detected.<br />
<strong>The</strong> Serial<br />
Port<br />
<strong>The</strong> <strong>64</strong>'s serial port, adapted from <strong>the</strong> IEEE-488 standard interface of PET/CBM ma<br />
chines, is peculiar to <strong>Commodore</strong>. <strong>The</strong> <strong>64</strong> uses a simplified, nonstandard version,<br />
which carries serial (as opposed to parallel) data and is comparatively slow. Figure<br />
17-3 shows <strong>the</strong> port's six connections as <strong>the</strong>y appear looking at <strong>the</strong> <strong>64</strong> from <strong>the</strong> back<br />
(<strong>the</strong> serial port is next to <strong>the</strong> cassette port).<br />
Figure 17-3. <strong>64</strong> Serial Port<br />
Pin 1 SRQ in FLAG of CIA 1<br />
Pin 2<br />
Ground<br />
Pin 3 ATN in Pin 9 of user port<br />
Pin3 ATN out PA3 of CIA 2<br />
Pin4 CLKin PA6 of CIA 2<br />
Pin4 CLKout PA4 of CIA 2<br />
Pin5 Data in PA7 of CIA 2<br />
Pin5 Data out PA5 of CIA 2<br />
Pin 6 RESET Connected to <strong>64</strong> reset line<br />
Pin 6 is connected to <strong>the</strong> <strong>64</strong>'s reset line, which is why <strong>the</strong> disk drive and printer<br />
reset when <strong>the</strong> <strong>64</strong> is switched on. Data is transferred in and out through pin 5. Pin<br />
1, <strong>the</strong> service request line, allows devices to request service from <strong>the</strong> <strong>64</strong>. CLK is a<br />
clock signal and ATN (attention) is described below.<br />
Both CIAs are used in processing. <strong>The</strong> part of ROM handling this can be in<br />
spected in detail by looking at <strong>the</strong> places where bit 7 of $DD00 (CIA 2's Port A) is<br />
used; this line inputs data bits. Data is transmitted from bit 5 of CIA 2 Port B, so<br />
$DD00 also controls data output. O<strong>the</strong>r important functions of that location are bits<br />
4 and 6, which provide input and output clock signals.<br />
Briefly, <strong>the</strong> serial bus is controlled by <strong>the</strong> <strong>64</strong>. Devices on <strong>the</strong> bus are talkers, lis<br />
teners, or both. For example, printers listen and disk drives both talk and listen. <strong>The</strong><br />
Kernal has routines to make devices talk, listen, untalk, and unlisten, meaning in ef<br />
fect that <strong>the</strong>y're on or off. BASIC handles all this itself, apart from a few special<br />
effects.<br />
Commands are sent to devices when ATN is low (<strong>the</strong> bit value is 0). When ATN<br />
is set high again, all <strong>the</strong> bytes sent are interpreted as data. When ATN is low, typi<br />
cally a single byte is sent as a command; that byte is interpreted by <strong>the</strong> device as fol<br />
lows: If it is in <strong>the</strong> range $20-$3E, it means listen; if it's $3F, it means unlisten; $40-<br />
$5E mean talk; $5F means untalk; and $60-$7F indicate secondary addresses. This is<br />
561
Major Peripherals<br />
why secondary addresses are stored in <strong>the</strong> <strong>64</strong> with 96 decimal added, and why <strong>the</strong><br />
Kernal LISTEN and TALK routines begin with ORA #$20 and ORA #$40.<br />
A printer can be made to print, without opening a file, by setting <strong>the</strong> device<br />
number to 4, calling Kernal LISTEN, setting ATN out high, sending characters with<br />
CHROUT, and finally unlistening with CLRCHN. Whenever files are open to a de<br />
vice, <strong>the</strong> device is first made a talker or a listener. <strong>The</strong>n <strong>the</strong> secondary address is<br />
sent (<strong>the</strong> Kernal has two routines for this purpose), so <strong>the</strong> device knows which file to<br />
address.<br />
562
Appendices
Appendix A<br />
A Beginner's Guide to Typing In<br />
Programs<br />
What Is a Program?<br />
A computer cannot perform any task by itself. Like a car without gas, a computer<br />
has potential. But without a program, it isn't going anywhere. Most of <strong>the</strong> programs<br />
published in this book are written in a computer language called BASIC. BASIC is<br />
easy to learn and is built into all <strong>Commodore</strong> <strong>64</strong>s.<br />
BASIC Programs<br />
Computers can be picky. Unlike <strong>the</strong> English language, which is full of ambiguities,<br />
BASIC usually has only one right way of stating something. Every letter, character,<br />
or number is significant. Common mistakes are substituting <strong>the</strong> letter O for <strong>the</strong> nu<br />
meral 0, a lowercase 1 for <strong>the</strong> numeral 1, or an uppercase B for <strong>the</strong> numeral 8. Also,<br />
be sure to enter all punctuation, such as colons and commas, just as <strong>the</strong>y appear in<br />
<strong>the</strong> book. Spacing can be important. To be safe, type in <strong>the</strong> listings exactly as <strong>the</strong>y<br />
appear.<br />
Braces and Special<br />
Characters<br />
<strong>The</strong> exception to this typing rule is when you see something inside braces, such as<br />
{DOWN}. Anything within a set of braces is a special character, or characters, that<br />
cannot easily be listed on a printer. When you come across such a special statement,<br />
refer to Appendix B, "How to Type In Programs."<br />
About DATA Statements<br />
Some programs contain a section, or sections, of DATA statements. <strong>The</strong>se lines pro<br />
vide information needed by <strong>the</strong> program. Some DATA statements contain programs<br />
in machine language, while o<strong>the</strong>rs contain graphics codes. <strong>The</strong>se lines are especially<br />
sensitive to errors.<br />
If a single number in any one DATA statement is mistyped, your machine could<br />
lock up, or crash. If this happens, <strong>the</strong> keyboard and RUN/STOP key may seem dead<br />
and <strong>the</strong> screen may go blank.<br />
But don't panic; no damage has been done. To regain control, you have to turn<br />
off your computer, <strong>the</strong>n turn it back on. This will erase whatever program was in<br />
memory, so always save a copy of your program before you run it If your computer<br />
crashes, you can reload <strong>the</strong> program and look for your mistake.<br />
Sometimes a mistyped DATA statement will cause an error message when <strong>the</strong> .<br />
program is run. <strong>The</strong> error message may refer to <strong>the</strong> program line that reads <strong>the</strong> data.<br />
However, <strong>the</strong> error may still be in <strong>the</strong> DATA statements.<br />
Get to Know Your Machine<br />
You should familiarize yourself with your computer before attempting to type in a<br />
program. Learn <strong>the</strong> statements you use to store and retrieve programs from tape or<br />
disk. You'll want to save a copy of your program so that you won't have to type it in<br />
565
Appendix A<br />
every time you want to use it. Learn to use your machine's editing functions. How<br />
do you change a line if you make a mistake? You can always retype <strong>the</strong> line, but at<br />
least you need to know how to backspace. Do you know how to enter reverse video,<br />
lowercase, and control characters? It's all explained in your <strong>64</strong>'s manual.<br />
A Quick Review<br />
1. Type in <strong>the</strong> program, a line at a time, in order. Press RETURN at <strong>the</strong> end of each<br />
line. Use <strong>the</strong> INST/DEL and cursor keys to correct mistakes.<br />
2. Check <strong>the</strong> line you've typed against <strong>the</strong> line in <strong>the</strong> book. You can check <strong>the</strong> entire<br />
program again if you get an error when you run <strong>the</strong> program.<br />
3. Make sure you've entered statements in braces using <strong>the</strong> appropriate control key<br />
(see Appendix B, "How to Type In Programs").<br />
566
Appendix B<br />
How to Type In Programs<br />
Many of <strong>the</strong> programs in this book contain special control characters (cursor control,<br />
color keys, reverse characters, and so on). To make it easy to know exactly what to<br />
type when entering one of <strong>the</strong>se programs into your computer, we have established<br />
<strong>the</strong> following listing conventions.<br />
Generally, <strong>Commodore</strong> <strong>64</strong> program listings contain words within braces which<br />
spell out any special characters: {DOWN} means to press <strong>the</strong> cursor-down key, while<br />
{5 SPACES} tells you to press <strong>the</strong> space bar five times.<br />
To indicate that a key should be shifted (hold down <strong>the</strong> SHIFT key while press<br />
ing <strong>the</strong> o<strong>the</strong>r key), <strong>the</strong> key would be underlined in our listings. For example, S<br />
would mean to type <strong>the</strong> S key while holding down <strong>the</strong> SHIFT key. This would ap<br />
pear on your screen as a heart symbol. If you find an underlined key enclosed in<br />
braces (for example, {10 N}), you should type <strong>the</strong> key as many times as indicated. In<br />
this case, you would enter ten SHIFTed NTs. One exception to this is that {SHIFT-<br />
SPACE} means to hold down <strong>the</strong> SHIFT key and type <strong>the</strong> space bar.<br />
If a key is enclosed in special brackets, f< >\, you should hold down <strong>the</strong> Com<br />
modore key while pressing <strong>the</strong> key inside <strong>the</strong> special brackets. (<strong>The</strong> <strong>Commodore</strong> key<br />
is <strong>the</strong> key in <strong>the</strong> lower-left corner of <strong>the</strong> keyboard.) Again, if <strong>the</strong> key is preceded by a<br />
number, you should press <strong>the</strong> key as many times as necessary.<br />
Occasionally, you will see a single character within braces. <strong>The</strong>se characters are<br />
entered by pressing CTRL while typing <strong>the</strong> letter indicated. For example, {A} is en<br />
tered by pressing CTRL-A.<br />
About <strong>the</strong> quote mode: You know that you can move <strong>the</strong> cursor around <strong>the</strong><br />
screen with <strong>the</strong> CRSR keys. Sometimes a programmer will want to move <strong>the</strong> cursor<br />
under program control. That's why you see all <strong>the</strong> {LEFT}'s, {HOME}'s, and<br />
{BLU}'s in our programs. <strong>The</strong> only way <strong>the</strong> computer can tell <strong>the</strong> difference between<br />
direct and programmed cursor control is <strong>the</strong> quote mode.<br />
Once you press <strong>the</strong> quote (<strong>the</strong> double quote, SHIFT-2), you are in <strong>the</strong> quote<br />
mode. For instance, if you type quote followed by a few characters, <strong>the</strong>n try to<br />
change it by moving <strong>the</strong> cursor left, you'll only get a bunch of reverse-video charac<br />
ters. <strong>The</strong>se are <strong>the</strong> symbols for cursor left. <strong>The</strong> only editing key that isn't pro<br />
grammable is <strong>the</strong> INST/DEL key, so you can still use INST/DEL to back up and edit<br />
<strong>the</strong> line. Once you type ano<strong>the</strong>r quote, you are out of quote mode.<br />
You also go into quote mode when you use INST/DEL to insert spaces into a<br />
line. In any case, <strong>the</strong> easiest way to get out of quote mode is simply to press RE<br />
TURN. You'll <strong>the</strong>n be out of quote mode and can cursor up to <strong>the</strong> mistyped line and<br />
fix it.<br />
Use <strong>the</strong> following table when entering cursor and color control keys:<br />
567
Appendix B<br />
When You<br />
Read:<br />
Press:<br />
See:<br />
When You<br />
Read:<br />
Press:<br />
See:<br />
{CLR}<br />
SHIFT |<br />
CLR/HOME<br />
{HOME}<br />
CLR/HOME<br />
i 2 g<br />
{UP}<br />
SHIFT | | f CRSR 1<br />
{DOWN}<br />
| CRSR i<br />
{LEFT}<br />
shift]<br />
Appendix C<br />
<strong>The</strong> Automatic Proofreader<br />
Charles Brannon<br />
"<strong>The</strong> Automatic Proofreader" will help you type in program listings without typing<br />
mistakes. It is a short error-checking program that hides itself in memory. When ac<br />
tivated, it lets you know if you have made a mistake immediately after typing a line<br />
from a program listing.<br />
Preparing <strong>the</strong><br />
Proofreader<br />
Please read <strong>the</strong>se instructions carefully before typing in any programs in this book.<br />
1. Using <strong>the</strong> listing below, type in <strong>the</strong> Proofreader. Be very careful when entering <strong>the</strong><br />
DATA statements—don't type an 1 instead of a 1, an O instead of a 0, extra com<br />
mas, etc.<br />
2. Save <strong>the</strong> Proofreader on tape or disk at least twice before running it for <strong>the</strong> first<br />
time. This is very important because <strong>the</strong> Proofreader erases part of itself when you<br />
first type RUN.<br />
3. After <strong>the</strong> Proofreader is saved, type RUN. It will check itself for typing errors in<br />
<strong>the</strong> DATA statements and warn you if <strong>the</strong>re's a mistake. Correct any errors and<br />
save <strong>the</strong> corrected version. Keep a copy in a safe place. You'll need it again and<br />
again, when entering a BASIC program from this book, COMPUTERS Gazette, or<br />
COMPUTE! magazine.<br />
4. When a correct version of <strong>the</strong> Proofreader is run, it activates itself and you are<br />
<strong>the</strong>n ready to enter a program listing. If you press RUN/STOP-RESTORE, <strong>the</strong><br />
Proofreader is disabled. To reactivate it, just type <strong>the</strong> command SYS 886 and press<br />
RETURN.<br />
Using <strong>the</strong> Proofreader<br />
Many listings in this book have a checksum number appended to <strong>the</strong> end of each line,<br />
for example, :rem 123. Don't enter this statement when typing in a program. It is just<br />
for your information. <strong>The</strong> rem makes <strong>the</strong> number harmless if someone does type it<br />
in. It will, however, use up memory if you enter it, and it will confuse <strong>the</strong> Proof<br />
reader, even if you entered <strong>the</strong> rest of <strong>the</strong> line correctly.<br />
When you type in a line from a program listing and press RETURN, <strong>the</strong> Proof<br />
reader displays a number at <strong>the</strong> top of your screen. This checksum number must match<br />
<strong>the</strong> checksum number in <strong>the</strong> printed listing. If it doesn't, it means you typed <strong>the</strong> line<br />
differently from <strong>the</strong> way it is listed. Immediately recheck your typing. Remember,<br />
don't type <strong>the</strong> rem statement with <strong>the</strong> checksum number; it is published only so you<br />
can check it against <strong>the</strong> number which appears on your screen.<br />
<strong>The</strong> Proofreader is not picky with spaces. It will not notice extra spaces or miss<br />
ing ones. This is for your convenience, since spacing is generally not important. But<br />
since proper spacing occasionally is important, be extra careful with spaces.<br />
One sort of error that <strong>the</strong> Proofreader will not catch is transposition. If you type<br />
PIRNT in a program line instead of PRINT, <strong>the</strong> Proofreader will not detect <strong>the</strong> error<br />
because all <strong>the</strong> proper characters are present (even if <strong>the</strong>y are in <strong>the</strong> wrong order). If<br />
a program fails to work even though <strong>the</strong> Proofreader says all <strong>the</strong> lines are correct,<br />
look for an error of this type.<br />
569
Appendix C<br />
Here's ano<strong>the</strong>r thing to watch out for: If you enter <strong>the</strong> line by using abbrevi<br />
ations for commands, <strong>the</strong> checksum will not match up. But <strong>the</strong>re is a way to make<br />
<strong>the</strong> Proofreader check it. After entering <strong>the</strong> line, LIST it. This eliminates <strong>the</strong> abbrevi<br />
ations. <strong>The</strong>n move <strong>the</strong> cursor up to <strong>the</strong> line and press RETURN. It should now<br />
match <strong>the</strong> checksum. You can check whole groups of lines this way.<br />
Special<br />
Tape SAVE Instructions<br />
When you're through typing a listing, you must disable <strong>the</strong> Proofreader before sav<br />
ing <strong>the</strong> program on tape. Disable <strong>the</strong> Proofreader by pressing RUN/STOP-RESTORE<br />
(hold down <strong>the</strong> RUN/STOP key and sharply tap <strong>the</strong> RESTORE key). This procedure<br />
is not necessary for disk SAVEs, but you must disable <strong>the</strong> Proofreader this way before a<br />
tape SAVE.<br />
SAVE to tape erases <strong>the</strong> Proofreader from memory, so you'll have to load and<br />
run it again if you want to type ano<strong>the</strong>r listing. SAVE to disk does not erase <strong>the</strong><br />
Proofreader.<br />
Hidden Perils<br />
<strong>The</strong> Proofreader's home in <strong>the</strong> <strong>64</strong> is not a very safe haven. Since <strong>the</strong> cassette buffer<br />
is wiped out during tape operations, you need to disable <strong>the</strong> Proofreader with RUN/<br />
STOP-RESTORE before you save your program. This applies only to tape use. Disk<br />
users have nothing to worry about.<br />
Not so for <strong>64</strong> owners with tape drives. What if you type in a program in several<br />
sittings? <strong>The</strong> next day, you come to your computer, load and run <strong>the</strong> Proofreader,<br />
<strong>the</strong>n try to load <strong>the</strong> partially completed program so you can add to it. But since <strong>the</strong><br />
Proofreader is trying to hide in <strong>the</strong> cassette buffer, it is wiped out.<br />
What you need is a way to load <strong>the</strong> Proofreader after you've loaded <strong>the</strong> partial<br />
program. <strong>The</strong> problem is that a tape LOAD to <strong>the</strong> buffer destroys what it's supposed<br />
to load.<br />
After you've typed in and run <strong>the</strong> Proofreader, enter <strong>the</strong> following three lines in<br />
direct mode (without line numbers) exactly as shown:<br />
A$="PROOFREADER•T":B$="{10 SPACES}":FOR X = 1 TO 4<br />
: A$=A$+B$: NEXT X<br />
FOR X = 886 TO 1018: A$=A$+CHR$(PEEK(X)): NEXT X<br />
OPEN 1,1,1,A$:CLOSE 1<br />
After you enter <strong>the</strong> last line, you will be asked to press RECORD and PLAY on<br />
your cassette recorder. Put this program at <strong>the</strong> beginning of a new tape; this gives<br />
you a new way to load <strong>the</strong> Proofreader. Anytime you want to bring <strong>the</strong> Proofreader<br />
into memory without disturbing anything else, put <strong>the</strong> cassette in <strong>the</strong> tape drive, re<br />
wind, enter OPEN1:CLOSE1, and press PLAY on <strong>the</strong> recorder. You'll get <strong>the</strong> mes<br />
sage FOUND PROOFREADER, but not <strong>the</strong> familiar LOADING. Don't worry; <strong>the</strong><br />
Proofreader is now in memory. You can <strong>the</strong>n start <strong>the</strong> Proofreader by typing SYS<br />
886. To test this, type in PRINT PEEK (886). It should return <strong>the</strong> number 173. If it<br />
570
Appendix C<br />
does not, repeat <strong>the</strong> steps above, making sure that A$ contains 13 characters<br />
(PROOFREADER.!) and that B$ contains ten spaces.<br />
You can now reload <strong>the</strong> Proofreader into memory whenever LOAD or SAVE de<br />
stroys it, restoring your personal typing helper.<br />
Automatic Proofreader for <strong>Commodore</strong> <strong>64</strong> and VIC<br />
100 PRINT"{CLR}PLEASE WAIT...":FORI=886TO1018:READ<br />
A:CK=CK+A:POKEI,A:NEXT<br />
110 IF CK17539 THEN PRINT"{DOWN}YOU MADE AN ERRO<br />
R":PRINT"IN DATA STATEMENTS.":END<br />
120 SYS886:PRINT"{CLR}{2 DOWN}PROOFREADER ACTIVATE<br />
D.":NEW<br />
886 DATA 173,036,003,201,150,208<br />
892 DATA 001,096,141,151,003,173<br />
898 DATA 037,003,141,152,003,169<br />
904 DATA 150,141,036,003,169,003<br />
910 DATA 141,037,003,169,000,133<br />
916 DATA 254,096,032,087,241,133<br />
922 DATA 251,134,252,132,253,008<br />
928 DATA 201,013,240,017,201,032<br />
934 DATA 240,005,024,101,254,133<br />
940 DATA 254,165,251,166,252,1<strong>64</strong><br />
946 DATA 253,040,096,169,013,032<br />
952 DATA 210,255,165,214,141,251<br />
958 DATA 003,206,251,003,169,000<br />
9<strong>64</strong> DATA 133,216,169,019,032,210<br />
970 DATA 255,169,018,032,210,255<br />
976 DATA 169,058,032,210,255,166<br />
982 DATA 254,169,000,133,254,172<br />
988 DATA 151,003,192,087,208,006<br />
994 DATA 032,205,189,076,235,003<br />
1000 DATA 032,205,221,169,032,032<br />
1006 DATA 210,255,032,210,255,173<br />
1012 DATA 251,003,133,214,076,173<br />
1018 DATA 003<br />
571
Appendix D<br />
Screen Location Table<br />
Row<br />
0 1024<br />
10<strong>64</strong><br />
1104<br />
1144<br />
-<br />
-<br />
1184<br />
D 1224<br />
12<strong>64</strong><br />
1304<br />
1344<br />
10 1424<br />
14<strong>64</strong><br />
1504<br />
1544<br />
15 1624<br />
16<strong>64</strong><br />
1704<br />
1744<br />
20 S<br />
18<strong>64</strong><br />
1904<br />
•<br />
•<br />
•<br />
•<br />
_ . 1944<br />
24 1984<br />
10 15 20<br />
Column<br />
25 30 35 39<br />
572
Appendix E<br />
Screen Color Memory Table<br />
Row<br />
0 55296<br />
55336<br />
55376<br />
55416<br />
55456<br />
5 55496<br />
55536<br />
55576<br />
55616<br />
^ 55656<br />
10 55696<br />
55736<br />
55776<br />
55816<br />
15 55896<br />
55936<br />
55976<br />
56016<br />
56056<br />
2U 56096<br />
56136<br />
56176<br />
o/t 56216<br />
24 56256<br />
10 15 20<br />
Column<br />
25 30 35 39<br />
573
Appendix F<br />
Screen Color Codes<br />
Color:<br />
Black<br />
White<br />
Red<br />
Cyan<br />
Purple<br />
Green<br />
Blue<br />
Yellow<br />
Code:<br />
0<br />
1<br />
2<br />
3<br />
4<br />
5<br />
6<br />
7<br />
Color:<br />
Orange<br />
Brown<br />
Light<br />
Red<br />
Dark<br />
Gray<br />
Medium<br />
Gray<br />
Light<br />
Green<br />
Light<br />
Blue<br />
light<br />
Gray<br />
Code:<br />
8<br />
9<br />
10<br />
11<br />
12<br />
13<br />
14<br />
15<br />
574
Appendix G<br />
ASCII Codes<br />
Dec Hex Meaning<br />
Dec Hex Meaning Dec Hex Meaning Dec Hex Meaning<br />
0 00 NUL - Null character<br />
1 01 SOH - Start heading<br />
32 20 SPACE<br />
33 21 !<br />
<strong>64</strong> 40<br />
65 41<br />
@<br />
A<br />
96<br />
97<br />
60<br />
61<br />
a<br />
2 02 STX - Start text<br />
34 22 "<br />
66 42<br />
B<br />
98<br />
62<br />
b<br />
3 03 ETX -End text<br />
35 23 #<br />
67 43<br />
C<br />
99<br />
63<br />
c<br />
4 04 EOT - End transmission<br />
5 05 ENQ - Enquiry<br />
36 24 $<br />
37 25 %<br />
68 44<br />
69 45<br />
D<br />
E<br />
100 <strong>64</strong><br />
101 65<br />
d<br />
e<br />
6 06 ACK -Acknowledge<br />
38 26 &<br />
70 46<br />
F<br />
102 66<br />
f<br />
7 07 BEL -Ring bell<br />
8 08 BS - Backspace<br />
9 09 HT - Horizontal tabulation<br />
39 27 '<br />
40 28 (<br />
41 29 )<br />
71 47<br />
72 48<br />
73 49<br />
G<br />
H<br />
I<br />
103 67<br />
104 68<br />
105 69<br />
g<br />
h<br />
i<br />
10 0A LF -Linefeed<br />
11 0B VT - Vertical tabulation<br />
12 0C FF -Formfeed<br />
42 2A *<br />
43 2B +<br />
44 2C ,<br />
74 4A<br />
75 4B<br />
76 4C<br />
J<br />
K<br />
L<br />
106 6A<br />
107 6B<br />
108 6C<br />
i<br />
k<br />
1<br />
13 0D CR - Carriage return<br />
45 2D -<br />
77 4D<br />
M<br />
109 6D<br />
m<br />
14 0E SO - Shift out<br />
46 2E .<br />
78 4E<br />
N<br />
110 6E<br />
n<br />
15 OF SI -Shift in<br />
47 2F /<br />
79 4F<br />
O<br />
111 ££<br />
o<br />
16 10 DLE - Data link escape<br />
17 11 DC1 - Device control 1<br />
18 12 DC2 - Device control 2<br />
48 30 0<br />
49 31 1<br />
50 32 2 -<br />
80 50<br />
81 51<br />
82 52<br />
P<br />
Q<br />
R<br />
112 70<br />
113 71<br />
114 72<br />
P<br />
q<br />
r<br />
19 13 DC3 - Device control 3<br />
51 33 3<br />
83 53<br />
S<br />
115 73<br />
s<br />
20 14 DC4 - Device control 4<br />
52 34 4<br />
84 54<br />
T<br />
116 74<br />
t<br />
21 15 NAK - Negative acknowledge<br />
53 35 5<br />
85 55<br />
U<br />
117 75<br />
u<br />
22 16 SYN - Synchronous idle<br />
54 36 6 '<br />
86 56<br />
V<br />
118 76<br />
v<br />
23 17 ETB - End transmission block<br />
55 37 7<br />
87 57<br />
w<br />
119 77<br />
w<br />
24 18 CAN -Cancel<br />
56 38 8<br />
88 58<br />
X<br />
120 78<br />
X<br />
25 19 EM - End medium<br />
26 1A SUB -Substitute<br />
57 39 9<br />
58 3A :<br />
89 59<br />
90 5A<br />
Y<br />
Z<br />
121 79<br />
122 7A<br />
y<br />
27 IB ESC -Escape<br />
28 1C FS - File separator<br />
29 ID GS - Group separator<br />
59 3B ;<br />
60 3C <<br />
61 3D =<br />
91 5B<br />
92 5C<br />
93 5D ]<br />
[<br />
\<br />
123 7B<br />
124 7C<br />
125 7D<br />
30 IE RS - Record separator<br />
62 3E ><br />
94 5E *<br />
126 7E<br />
AJ<br />
31 IF US - Unit separator<br />
63 3F ?<br />
95 5F -<br />
127 7F<br />
DEL<br />
ASCII (American Standard Code for Information Interchange) is largely followed by<br />
<strong>the</strong> <strong>64</strong>—more closely than in earlier CBM equipment; alphabetic characters, numer<br />
als, and punctuation are generally similar, although <strong>the</strong> <strong>64</strong> has uppercase and lower<br />
case letters switched with respect to standard ASCII. Standard ASCII, however, has a<br />
parity bit (bit 7) set to make <strong>the</strong> number of l's in <strong>the</strong> byte even. This is why ASCII<br />
has only 128 characters.<br />
575
Appendix H<br />
<strong>Commodore</strong> ASCII<br />
Codes<br />
Hex<br />
05<br />
08<br />
09<br />
0D<br />
0E<br />
11<br />
12<br />
13<br />
14<br />
1C<br />
ID<br />
IE<br />
IF<br />
20<br />
21<br />
22<br />
23<br />
24<br />
25<br />
26<br />
27<br />
28<br />
29<br />
2A<br />
2B<br />
2C<br />
2D<br />
2E<br />
2F<br />
30<br />
31<br />
32<br />
33<br />
34<br />
35<br />
36<br />
Dec<br />
5<br />
8<br />
9<br />
13<br />
14<br />
17<br />
18<br />
19<br />
20<br />
28<br />
29<br />
30<br />
31<br />
32<br />
33<br />
34<br />
35<br />
36<br />
37<br />
38<br />
39<br />
40<br />
41<br />
42<br />
43<br />
44<br />
45<br />
46<br />
47<br />
48<br />
49<br />
50<br />
51<br />
52<br />
53<br />
54<br />
Character<br />
WHITE<br />
DISABLE<br />
SHIFT-COMMODORE<br />
ENABLE<br />
SHIFT-COMMODORE<br />
RETURN<br />
LOWERCASE<br />
CURSOR DOWN<br />
REVERSE VIDEO ON<br />
HOME<br />
DELETE<br />
RED<br />
CURSOR RIGHT<br />
GREEN<br />
BLUE<br />
SPACE<br />
i<br />
#<br />
$ ■<br />
%<br />
&<br />
(<br />
)<br />
*<br />
+<br />
—<br />
/<br />
0<br />
1<br />
2<br />
3<br />
4<br />
5<br />
6<br />
Hex<br />
37<br />
38<br />
39<br />
3A<br />
3B<br />
3C<br />
3D<br />
3E<br />
3F<br />
40<br />
41<br />
42<br />
43<br />
44<br />
45<br />
46<br />
47<br />
48<br />
49<br />
4A<br />
4B<br />
4C<br />
4D<br />
4E<br />
4F<br />
50<br />
51<br />
52<br />
53<br />
54<br />
55<br />
56<br />
57<br />
58<br />
59<br />
5A<br />
5B<br />
5C<br />
Dec<br />
55<br />
56<br />
57<br />
58<br />
59<br />
60<br />
61<br />
62<br />
63<br />
<strong>64</strong><br />
65<br />
66<br />
67<br />
68<br />
69<br />
70<br />
71<br />
72<br />
73<br />
74<br />
75<br />
76<br />
77<br />
78<br />
79<br />
80<br />
81<br />
82<br />
83<br />
84<br />
85<br />
86<br />
87<br />
88<br />
89<br />
90<br />
91<br />
92<br />
Character<br />
7<br />
8<br />
9<br />
;<br />
<<br />
=<br />
><br />
7<br />
@<br />
A<br />
B<br />
C<br />
D<br />
E<br />
F<br />
G<br />
H<br />
I<br />
J<br />
K<br />
L<br />
M<br />
N<br />
O<br />
P<br />
Q<br />
R<br />
S<br />
T<br />
U<br />
V<br />
W<br />
X<br />
Y<br />
Z<br />
[<br />
f<br />
576
Hex Dec Character<br />
]<br />
I<br />
B<br />
B<br />
□<br />
D<br />
D<br />
a<br />
□<br />
□<br />
D<br />
S<br />
0<br />
n<br />
H<br />
P<br />
a<br />
m<br />
H<br />
ORANGE<br />
5D<br />
5E<br />
5F<br />
60<br />
61<br />
62<br />
63<br />
<strong>64</strong><br />
65<br />
66<br />
67<br />
68<br />
69<br />
6A<br />
6B<br />
6C<br />
6D<br />
6E<br />
6F<br />
70<br />
71<br />
72<br />
73<br />
74<br />
75<br />
76<br />
77<br />
78<br />
79<br />
7A<br />
7B<br />
7C<br />
7D<br />
7E<br />
7F<br />
81<br />
93<br />
94<br />
95<br />
96<br />
97<br />
98<br />
99<br />
100<br />
101<br />
102<br />
103<br />
104<br />
105<br />
106<br />
107<br />
108<br />
109<br />
110<br />
111<br />
112<br />
113<br />
114<br />
115<br />
116<br />
117<br />
118<br />
119<br />
120<br />
121<br />
122<br />
123<br />
124<br />
125<br />
126<br />
127<br />
129<br />
lex<br />
85<br />
86<br />
87<br />
88<br />
89<br />
8A<br />
8B<br />
8C<br />
8D<br />
8E<br />
90<br />
91<br />
92<br />
93<br />
94<br />
95<br />
96<br />
97<br />
98<br />
99<br />
9A<br />
9B<br />
9C<br />
9D<br />
9E<br />
9F<br />
A0<br />
Al<br />
A2<br />
A3<br />
A4<br />
A5<br />
A6<br />
A7<br />
A8<br />
A9<br />
Dec<br />
133<br />
134<br />
135<br />
136<br />
137<br />
138<br />
139<br />
140<br />
141<br />
142<br />
143<br />
145<br />
146<br />
UZ.<br />
148<br />
149<br />
150<br />
151<br />
152<br />
153<br />
154<br />
155<br />
156<br />
157<br />
158<br />
159<br />
160<br />
161<br />
162<br />
163<br />
1<strong>64</strong><br />
165<br />
166<br />
167<br />
168<br />
169<br />
Appendix H<br />
Character<br />
fl<br />
f3<br />
f5<br />
i7<br />
(2<br />
f4<br />
f6<br />
f8<br />
SHIFT-RETURN<br />
UPPERCASE<br />
BLACK<br />
CURSOR UP<br />
REVERSE VIDEO OFF<br />
CLEAR SCREEN<br />
INSERT<br />
BROWN<br />
LIGHT RED<br />
GRAY1<br />
GRAY 2<br />
LIGHT GREEN<br />
LIGHT BLUE<br />
GRAY 3<br />
PURPLE<br />
CURSOR LEFT<br />
YELLOW<br />
CYAN<br />
SHIFT-SPACE<br />
1<br />
u<br />
□<br />
□<br />
□<br />
a<br />
E<br />
577
Appendix H<br />
Hex<br />
Dec<br />
AA<br />
AB<br />
AC<br />
AD<br />
AE<br />
AF<br />
BO<br />
Bl<br />
B2<br />
B3<br />
B4<br />
B5<br />
B6<br />
B7<br />
B8<br />
B9<br />
BA<br />
BB<br />
BC<br />
BD<br />
BE<br />
BF<br />
CO<br />
Cl<br />
C2<br />
C3<br />
C4<br />
C5<br />
C6<br />
C7<br />
C8<br />
C9<br />
CA<br />
CB<br />
CC<br />
CD<br />
CE<br />
170<br />
171<br />
172<br />
173<br />
174<br />
175<br />
176<br />
177<br />
178<br />
179<br />
180<br />
181<br />
182<br />
183<br />
184<br />
185<br />
186<br />
187<br />
188<br />
189<br />
190<br />
191<br />
192<br />
193<br />
194<br />
195<br />
196<br />
197<br />
198<br />
199<br />
200<br />
201<br />
202<br />
203<br />
204<br />
205<br />
206<br />
Character<br />
□<br />
m<br />
a<br />
E<br />
B<br />
y<br />
H<br />
H<br />
ffl<br />
□<br />
□<br />
a<br />
u<br />
a<br />
■<br />
H<br />
H<br />
B<br />
H<br />
m<br />
B<br />
B<br />
□<br />
B<br />
D<br />
a<br />
□<br />
D<br />
S<br />
0<br />
Hex<br />
Dec<br />
CF<br />
DO<br />
Dl<br />
D2<br />
D3<br />
D4<br />
D5<br />
D6<br />
D7<br />
D8<br />
D9<br />
DA<br />
DB<br />
DC<br />
DD<br />
DE<br />
DF<br />
EO<br />
El<br />
E2<br />
E3<br />
E4<br />
E5<br />
E6<br />
E7<br />
E8<br />
E9<br />
EA<br />
EB<br />
EC<br />
ED<br />
EE<br />
EF<br />
FO<br />
Fl<br />
F2<br />
F3<br />
207<br />
208<br />
209<br />
210<br />
211<br />
212<br />
213<br />
214<br />
215<br />
216<br />
217<br />
218<br />
219<br />
220<br />
221<br />
222<br />
223<br />
224<br />
225<br />
226<br />
227<br />
228<br />
229<br />
230<br />
231<br />
232<br />
233<br />
234<br />
235<br />
236<br />
237<br />
238<br />
239<br />
240<br />
241<br />
242<br />
243<br />
Character<br />
□<br />
□<br />
Q<br />
D<br />
□<br />
m<br />
H<br />
SPACE<br />
E<br />
n<br />
D<br />
D<br />
n<br />
B<br />
□<br />
m<br />
a<br />
u<br />
B<br />
H<br />
H<br />
ffl<br />
578
Appendix H<br />
Hex<br />
F4<br />
F5<br />
F6<br />
F7<br />
F8<br />
F9<br />
FA<br />
FB<br />
FC<br />
FD<br />
FE<br />
FF<br />
Dec<br />
244<br />
245<br />
246<br />
247<br />
248<br />
249<br />
250<br />
251<br />
252<br />
253<br />
254<br />
255<br />
Character<br />
D<br />
C<br />
[]<br />
n<br />
D<br />
El<br />
H<br />
5]<br />
1. 0-4, 6-7, 10-12, 15-16, 21-27, 128, 130-132, and 143 have no effect.<br />
2. 192-223 same as 96-127, 224-254 same as 160-190, 255 same as 126.<br />
579
Appendix I<br />
Screen Character Codes<br />
Hex Dec Uppercase and Lower- and<br />
Full Graphics Set Uppercase<br />
a<br />
b<br />
c<br />
d<br />
e<br />
f<br />
g<br />
h<br />
00<br />
01<br />
02<br />
03<br />
04<br />
05<br />
06<br />
07<br />
08<br />
09<br />
0A<br />
OB<br />
OC<br />
OD<br />
OE<br />
OF<br />
10<br />
11<br />
12<br />
13<br />
14<br />
15<br />
16<br />
17<br />
18<br />
19<br />
1A<br />
IB<br />
1C<br />
ID<br />
IE<br />
0<br />
1<br />
2<br />
3<br />
4<br />
5<br />
6<br />
7<br />
8<br />
9<br />
10<br />
11<br />
12<br />
13<br />
14<br />
15<br />
16<br />
17<br />
18<br />
19<br />
20<br />
21<br />
22<br />
23<br />
24<br />
25<br />
26<br />
27<br />
28<br />
29<br />
30<br />
@<br />
A<br />
B<br />
C<br />
D<br />
E<br />
F<br />
G<br />
H<br />
I<br />
J<br />
K<br />
L<br />
M<br />
N<br />
O<br />
P<br />
Q<br />
R<br />
S<br />
T<br />
U<br />
V<br />
w<br />
X<br />
Y<br />
z<br />
[<br />
£<br />
]<br />
t<br />
m<br />
n<br />
o<br />
P<br />
q<br />
r<br />
s<br />
t<br />
u<br />
V<br />
w<br />
X<br />
y<br />
z<br />
Hex Dec Uppercase and Lower- and<br />
Full Graphics Set Uppercase<br />
-space-<br />
!<br />
#<br />
$<br />
&<br />
IF<br />
20<br />
21<br />
22<br />
23<br />
24<br />
25<br />
26<br />
27<br />
28<br />
29<br />
2A<br />
2B<br />
2C<br />
2D<br />
2E<br />
2F<br />
30<br />
31<br />
32<br />
33<br />
34<br />
35<br />
36<br />
37<br />
38<br />
39<br />
3A<br />
3B<br />
3C<br />
3D<br />
31<br />
32<br />
33<br />
34<br />
35<br />
36<br />
37<br />
38<br />
39<br />
40<br />
41<br />
42<br />
43<br />
44<br />
45<br />
46<br />
47<br />
48<br />
49<br />
50<br />
51<br />
52<br />
53<br />
54<br />
55<br />
56<br />
57<br />
58<br />
59<br />
60<br />
61<br />
—<br />
i<br />
»<br />
#<br />
$<br />
°/c<br />
&<br />
'<br />
(<br />
)<br />
•<br />
+<br />
_<br />
/<br />
0<br />
1<br />
2<br />
3<br />
4<br />
5<br />
6<br />
7<br />
8<br />
9<br />
<<br />
_<br />
0<br />
1<br />
2<br />
3<br />
4<br />
5<br />
6<br />
7<br />
8<br />
9<br />
580
Appendix<br />
Hex Dec Uppercase and Lower- and<br />
Full Graphics Set Uppercase<br />
Hex Dec Uppercase and Lower- and<br />
Full Graphics Set Uppercase<br />
3E 62<br />
3F 63<br />
40 <strong>64</strong><br />
41 65<br />
42 66<br />
43 67<br />
44 68<br />
45 69<br />
46 70<br />
47 71<br />
48 72<br />
49 73<br />
4A 74<br />
4B 75<br />
4C 76<br />
4D 77<br />
4E 78<br />
4F 79<br />
50 80<br />
51 81<br />
52 82<br />
53 83<br />
54 84<br />
55 85<br />
56 86<br />
57 87<br />
58 88<br />
59 89<br />
5A 90<br />
5B 91<br />
5C 92<br />
5D 93<br />
5E 94<br />
B<br />
a<br />
B<br />
B<br />
□<br />
□<br />
D<br />
O<br />
□<br />
□<br />
D<br />
S<br />
0<br />
□<br />
□<br />
□<br />
D<br />
Q<br />
a<br />
0<br />
c<br />
m<br />
B<br />
A<br />
B<br />
C<br />
D<br />
E<br />
F<br />
G<br />
H<br />
I<br />
]<br />
K<br />
L<br />
M<br />
N<br />
O<br />
P<br />
Q<br />
R<br />
S<br />
T<br />
U<br />
V<br />
w<br />
X<br />
Y<br />
z<br />
5F 95<br />
60 96<br />
61 97<br />
62 98<br />
63 99<br />
<strong>64</strong> 100<br />
65 101<br />
66 102<br />
67 103<br />
68 104<br />
69 105<br />
6A 106<br />
6B 107<br />
6C 108<br />
6D 109<br />
6E 110<br />
6F 111<br />
70 112<br />
71 113<br />
72 114<br />
73 115<br />
74 116<br />
75 117<br />
76 118<br />
77 119<br />
78 120<br />
79 121<br />
7A 122<br />
7B 123<br />
7C 124<br />
7D 125<br />
7E 126<br />
7F 127<br />
I<br />
U<br />
□<br />
D<br />
□<br />
a<br />
s<br />
B<br />
a<br />
E<br />
Q<br />
B<br />
H<br />
a<br />
B<br />
H<br />
H<br />
D<br />
C<br />
a<br />
□<br />
n<br />
□<br />
a<br />
D<br />
H<br />
H<br />
E<br />
B<br />
- -space- -<br />
II<br />
H<br />
□<br />
D<br />
O<br />
a<br />
a<br />
E<br />
a<br />
a<br />
B<br />
H<br />
H<br />
ffl<br />
□<br />
C<br />
a<br />
n<br />
n<br />
a<br />
H<br />
128-255 are reverse video of 0-127.<br />
581
00<br />
Hex<br />
Address<br />
D000<br />
D001<br />
D002<br />
D003<br />
D004<br />
D005<br />
D006<br />
D007<br />
D008<br />
D009<br />
DOOA<br />
DOOB<br />
DOOC<br />
DOOD<br />
DOOE<br />
DOOF<br />
D010<br />
D011<br />
D012<br />
D013<br />
D014<br />
D015<br />
D016<br />
Decimal<br />
Address<br />
53248<br />
53249<br />
53250<br />
53251<br />
53252<br />
53253<br />
53254<br />
53255<br />
53256<br />
53257<br />
53258<br />
53259<br />
53260<br />
53261<br />
53262<br />
53263<br />
532<strong>64</strong><br />
53265<br />
53266<br />
53267<br />
53268<br />
53269<br />
53270<br />
Offset<br />
0<br />
1<br />
2<br />
3<br />
4<br />
5<br />
6<br />
7<br />
8<br />
9<br />
10<br />
11<br />
12<br />
13<br />
14<br />
15<br />
16<br />
17<br />
18<br />
19<br />
20<br />
21<br />
22<br />
Function<br />
Sprite 0 X-Position (low 8 bits)<br />
Sprite 0 Y-Position<br />
Sprite 1 X-Position (low)<br />
Sprite 1 Y-Position<br />
Sprite 2 X-Position (low)<br />
Sprite 2 Y-Position<br />
Sprite 3 X-position (low)<br />
Sprite 3 Y-Position<br />
Sprite 4 X-Position (low)<br />
Sprite 4 Y-Position<br />
Sprite 5 X-Position (low)<br />
Sprite 5 Y-Position<br />
Sprite 6 X-Position (low)<br />
Sprite 6 Y-Position<br />
Sprite 7 X-Position (low)<br />
Sprite 7 Y-Position<br />
High Bit of Sprite X-Position<br />
Sprite 7 | Sprite 6<br />
Extended<br />
Color mode<br />
Bit 8 | 1 = on<br />
Raster Scan line and Write Reg<br />
Bit 7 | Bit 6<br />
light Pen Horizontal Position<br />
light Pen Vertical Position<br />
Enable Sprites (1 = on, 0 = ofi<br />
Sprite 7 | Sprite 6<br />
1 1 1<br />
iste<br />
f><br />
0<br />
Sprite 5<br />
Bitmap<br />
mode<br />
1 — on<br />
r for Raster I<br />
Bit 5<br />
Sprite 5<br />
Chip<br />
Reset<br />
= Normal<br />
Sprite 4<br />
Screen<br />
Blanking<br />
0 = Blank<br />
nterrupts<br />
Bit 4<br />
Sprite 4<br />
Multicolor<br />
Mode<br />
1 = Enable<br />
Sprite 3<br />
No. of Rows<br />
1 = 25<br />
0 = 24<br />
Bit 3<br />
Sprite 3<br />
No. of<br />
Columns<br />
1 = 40<br />
0 = 38<br />
Sprite 2<br />
Ver<br />
Bit 2<br />
Sprite 2<br />
HorL<br />
Sprite 1<br />
deal Screen Posi<br />
0-7 pixels<br />
Bitl<br />
Sprite 1<br />
zontal Screen Po<br />
0-7 Pixels<br />
Sprite 0<br />
tion<br />
BitO<br />
Sprite 0<br />
sition<br />
©<br />
O<br />
O<br />
■5"
I<br />
><br />
CJ1<br />
00<br />
Address Hex Address Decimal<br />
Offset<br />
D017<br />
53271<br />
23<br />
D018<br />
53272<br />
24<br />
D019<br />
53273<br />
25<br />
D01A<br />
53274<br />
26<br />
DO1B<br />
53275<br />
27<br />
D01C<br />
53276<br />
28<br />
D01D<br />
53277<br />
29<br />
DO1E<br />
53278<br />
30<br />
DO1F<br />
53279<br />
31<br />
D020<br />
53280<br />
32<br />
D021<br />
53281<br />
33<br />
D022<br />
53282<br />
34<br />
D023<br />
53283<br />
35<br />
D024<br />
53284<br />
36<br />
D025<br />
53285<br />
37<br />
D026<br />
53286<br />
38<br />
D027<br />
53287<br />
39<br />
Continued on next page.<br />
Function<br />
Sprite Vertical I<br />
Sprite 7<br />
Bit 13<br />
Interrupt Flags<br />
1 = Interrupts<br />
0 = No<br />
Interrupts<br />
Interrupt Enable<br />
1<br />
Sprite-Data Pric<br />
Sprite 7<br />
Sprite Multicolc<br />
Sprite 7<br />
Sprite Horizont<br />
Sprite 7<br />
Sprite-Sprite Cc<br />
Sprite 7<br />
Sprite-Data Col<br />
Sprite 7<br />
1<br />
1<br />
1<br />
1<br />
1<br />
1<br />
1<br />
1<br />
Ixpansion (1 =<br />
Sprite 6<br />
Screen Baj<br />
Bit 12<br />
and Write Regist<br />
1<br />
? ( 1 = enabled,<br />
1<br />
)rity (1 = data,<br />
Sprite 6<br />
r Mode (1 = m<br />
Sprite 6<br />
al Expansion (1<br />
Sprite 6<br />
►llision Register<br />
Sprite 6<br />
lision Register (c<br />
Sprite 6<br />
1<br />
1<br />
1<br />
1<br />
1<br />
1<br />
1<br />
1<br />
on, 0 = off)<br />
Sprite 5<br />
,e Address<br />
Bit 11<br />
er to Clear E ag*<br />
1<br />
0 = not enable<br />
1<br />
O = sprite)<br />
Sprite 5<br />
ulticolor, 0 = hi<br />
Sprite 5<br />
= on, 0 = off)<br />
Sprite 5<br />
cleared only wh<br />
Sprite 5<br />
leared only v /he<br />
Sprite 5<br />
1<br />
1<br />
1<br />
1<br />
1<br />
1<br />
1<br />
1<br />
Sprite 4<br />
Bit 10<br />
1<br />
d)<br />
1<br />
Sprite 4<br />
-res)<br />
Sprite 4<br />
Sprite 4<br />
en read)<br />
Sprite 4<br />
n read)<br />
Sprite 4<br />
1<br />
1<br />
1<br />
1<br />
1<br />
1<br />
1<br />
1<br />
Sprite 3<br />
Bit 13<br />
light Pen<br />
light Pen<br />
Sprite 3<br />
Sprite 3<br />
Sprite 3<br />
Sprite 3<br />
Sprite 3<br />
Sprite 2<br />
Sprite 1<br />
Base/ Character address<br />
Definitions<br />
Bit 12<br />
Bit 11<br />
Sprite-Sprite<br />
Collision<br />
Sprite-Data<br />
Collision<br />
Sprite-Sprite<br />
Collision<br />
Sprite-Data<br />
Collision<br />
Sprite 2<br />
Sprite 1<br />
Sprite 2<br />
Sprite 1<br />
Sprite 2<br />
Sprite 1<br />
Sprite 2<br />
Sprite 1<br />
Sprite 2<br />
Sprite 1<br />
Border Color (0-15)<br />
Background Color 0 (0-15)<br />
Background Color 1<br />
Background Color 2<br />
Background Color 3<br />
Sprite Multicolor 0<br />
Sprite Multicolor 1<br />
Sprite 0 Color<br />
Sprite 0<br />
(1)<br />
Raster<br />
Scan<br />
Raster<br />
Scan<br />
Sprite 0<br />
Sprite 0<br />
Sprite 0<br />
Sprite 0<br />
Sprite 0<br />
><br />
"D<br />
(D<br />
a<br />
x"
CJ1<br />
QO<br />
Hex Decimal<br />
Address Address<br />
D028<br />
53288<br />
D029<br />
53289<br />
D02A<br />
53290<br />
D02B<br />
53291<br />
D02C<br />
53292<br />
D02D<br />
53293<br />
D02E<br />
53294<br />
Offset<br />
40<br />
41<br />
42<br />
43<br />
44<br />
45<br />
46<br />
Function<br />
1<br />
1<br />
1<br />
1<br />
1<br />
1<br />
1<br />
1<br />
1<br />
1<br />
1<br />
1<br />
1<br />
1<br />
1<br />
1<br />
1<br />
1<br />
1<br />
1<br />
1<br />
1<br />
1<br />
1<br />
1<br />
1<br />
1<br />
1<br />
Sprite 1 Color<br />
Sprite 2 Color<br />
Sprite 3 Color<br />
Sprite 4 Color<br />
Sprite 5 Color<br />
Sprite 6 Color<br />
Sprite 7 Color<br />
57
1<br />
Hex Decimal<br />
Address Address Offset<br />
/^D400 54272 0<br />
( D407 54279 7<br />
1 D40E 54286 14<br />
I D401 54273 1<br />
1 D408 54280 8<br />
\ D40F 54287 15<br />
Voice 1 J<br />
Voice 2 \<br />
Voice 3<br />
I D402 54274 2<br />
I D409 54281 9<br />
/ D410 54288 16<br />
f D403 54275 3<br />
D40A 54282 10<br />
D411 54289 17<br />
D404 54276 4<br />
D40B 54283 11<br />
D412 54290 18<br />
D405 54277 5<br />
D40C 54284 12<br />
D413 54291 19<br />
D406 54278 6<br />
D40D 54285 13<br />
D414 54292 20<br />
D415 54293 21<br />
D416 54294 22<br />
D417 54295 23<br />
D418 54296 24<br />
D419 54297 25<br />
D41A 54298 26<br />
D41B 54299 27<br />
D41C 54300 28<br />
Function<br />
Frequency Control (low byte)<br />
Frequency Control (high byte)<br />
Pulse Width (bits 7-0)<br />
not used<br />
Control Reg ister (1 = o n, 0 = off)<br />
noise<br />
pulse sawtooth triangle<br />
Attack (0-15)<br />
Sustain Level (0-15)<br />
not used<br />
F liter Control<br />
FC10 FC9<br />
FC8<br />
FC7<br />
Resonance (0-15)<br />
Cutout<br />
Voice 3<br />
Filter Type<br />
(0=not cut high pass band pass low pass<br />
l=cut)<br />
Potentiometer X (0-255)<br />
Potentiometer Y (0-255)<br />
Read Waveform of Voice 3 (0-255)<br />
Read Envelope of Voice 3 (0-255)<br />
bit 11<br />
5ulse Width (bits 11-8<br />
bit 10 | bit 9 bit 8<br />
test<br />
ring | sync | gate<br />
Decay (0-15)<br />
Release (0-15)<br />
Filter Cor<br />
FC2<br />
itrol (bits 2-0)<br />
FC1 | FC0<br />
(bits 10-3)<br />
FC6<br />
FC5<br />
FC4<br />
FC3<br />
Filtered Voice(s) (1 = on, 0 = off)<br />
External | Voice 3 | Voice 2<br />
Voice 1<br />
Master Volume (0-15)<br />
Write-only<br />
Registers<br />
Read-only<br />
Registers<br />
D<br />
O<br />
(0<br />
a<br />
x"
Appendix L<br />
Device Numbers<br />
Table of second parameter in OPEN. Example: OPEN 5,4 opens file 5 to printer.<br />
0 Keyboard<br />
1 Tape (not used in SX-<strong>64</strong> models)<br />
2 RS-232, usually modem<br />
3 Screen<br />
4 Printer<br />
5 Printer—alternative setting<br />
6 Plotter<br />
8 Disk Drive<br />
9 Disk Drive—alternative<br />
10 Disk Drive—alternative<br />
11 Disk Drive—alternative<br />
586
CJ1<br />
00<br />
HAHAHAHAHAHAHA HAHAHAHAHAHAHAHAHA IA HA HA HA HA HA HA, HA -tAHAHAHAHAHAHAHA<br />
W U> U> W U> W Ul UJWOJWOJWWWW<br />
o Tl m D D CD > «> oo -j <<br />
«cl<br />
iw ui i r W N) -» O<br />
en ui in ui i<br />
K) «*j -- en «<br />
comoioj<br />
oo ui uiooomux<br />
C« W CD MO-» L _<br />
00 M WOtOONOOfii<br />
HA HA HA HA HA HA HA VH<br />
Zj Zj Zj vJ >vj «>J *-J •*J-<br />
Tim OOCD >«> eo<br />
•n m oocd :<br />
-(AHAHAHAHAHAHAHAHAHAHAHAHAHAHAHA<br />
UUIUIVIUIUIVIUIlnVIVIUIUIUI<br />
HAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHA<br />
_*-»oooooooooo(Oto *J * N> (O<br />
2 S o * » w-5<br />
ff» o -cr oo ro <br />
co vj sjs4Njc»a)ata)inuitnut*<br />
->(O O) W -' 00 O) U) -'COO WOC0UI<br />
0)OJ;i0W00N)0tlUIOC00W>l<br />
j:4:WWWWNJION)N)<br />
UOCOUIIOOvlUINJO'<br />
oo *J *«j "^J n| on en en<br />
sj (o *^i ^ nj (O en ^ ^ (O en jr >■■* oo en<br />
NCn-1UIOt0BW>JNWOUllOC<br />
- — rocno*oohjcnojrooioeno<br />
$88888888<br />
O) at at vi m » ui<br />
uiwoauiwo<br />
O*C?<br />
S o ui (O j<br />
monCD>*ooo^Jcnui4rujNj-<br />
C«««UIWUUIOMNJuiioo^iyiioo-J^K);<br />
■ H *: ^ *£<br />
cncnencnuiuiuiui.tS'&j;<br />
«WWWUI<br />
OMU1MO<<br />
*nrnrnrnrnmrTimmrnrnmmnirnm m oc<br />
AHAHAHAHA<br />
OOPO<br />
HA HA HA HA ■(<br />
K> K> rs> K> K> NJ<br />
SSSS2S<br />
CjJ ijj OU<br />
(DOOM<br />
N>NJ|OK)K»K>KIKirOhJrOK>N-.-<br />
NJKI-*—•—»-J---»-»-»—»—' O O<br />
O00JWU1*WW«OV000<br />
0010(0(0(0(0(<br />
J-'OtOCOMtnVl<br />
I UI UI UI UI UI UI<br />
iCOOlWOCOUIWOMWWOvJOIW<br />
rOOtOOICP<br />
(OUOOtom-'UiOCOSU^MOlOVI<br />
wmOtC0M0)O4:00KJfflOJr05'-<br />
b<br />
o r<br />
Si<br />
o r<br />
n o<br />
p 5<br />
73<br />
I<br />
a<br />
x
Appendix N<br />
Opcodes in<br />
Detail<br />
Opcode Description Flags<br />
N V B D I Z C<br />
ADC Add memory with carry to accumulator N V 2 C<br />
AND Logical AND memory with accumulator N Z<br />
ASL Shift memory or accumulator one bit left N Z C<br />
BCC Branch if carry bit clear ^<br />
BCS Branch if carry bit set ~~~~<br />
BEQ Branch if zero bit set<br />
BIT AND with A, storing Z and bits 6 and 7 M7M6 Z<br />
BMI Branch if N (negative) flag set<br />
BNE Branch if zero bit clear ~~<br />
BPL Branch if N bit is not set<br />
BRK Force break to IRQ 11<br />
BVC Branch on internal overflow bit clear<br />
BVS Branch on internal overflow bit set<br />
CLC Clear <strong>the</strong> carry bit 0<br />
CLD Clear decimal flag (for hex arithmetic) 0<br />
CLI Clear interrupt disable flag 0<br />
CLV Clear internal overflow flag 0<br />
CMP Compare memory to accumulator N Z C<br />
CPX Compare memory to X register N Z C<br />
CPY Compare memory to Y register ^__ N Z C<br />
DEC Decrement memory location N 2<br />
DEX Decrement X register N Z<br />
DEY Decrement Y register N Z<br />
EOR Logical exclusive OR memory with A N Z<br />
INC Increment memory location ' N 2<br />
I NX Increment X register N Z<br />
INY Increment Y register N Z<br />
JMP Jump to new address<br />
JSR Jump to new address, saving return<br />
LDA Load accumulator from memory N Z<br />
LDX Load X register from memory N Z<br />
LDY Load Y register from memory N Z<br />
LSR Shift memory or accumulator one bit right 0 Z C~<br />
NOP No operation<br />
ORA Logical inclusive OR memory with A N Z<br />
PHA Push accumulator onto stack<br />
PHP Push processor status flags onto stack<br />
PLA Pull stack into accumulator N Z<br />
PLP Pull stack into processor status flags N V B D I Z C<br />
ROL Rotate memory or A one bit left, inc. C N Z C<br />
ROR Rotate memory or A^one bit right, inc. C N Z C<br />
RTI Return from interrupt N V B D I Z C<br />
RTS Return from subroutine called by JSR<br />
SBC Subtract memory and C-complement from A N V Z C<br />
5EC Set <strong>the</strong> carry bit f<br />
SED Set <strong>the</strong> decimal flag (for BCD arithmetic) 1<br />
SEI Set <strong>the</strong> interrupt disable flag 1<br />
STA Store accumulator into memory<br />
STX Store X into memory<br />
STY Store Y into memory<br />
TAX Transfer accumulator to X register N Z<br />
TAY Transfer accumulator to Y register N Z<br />
TSX Transfer stack pointer to X register N Z<br />
TXA Transfer X register to A N Z<br />
TXS Transfer X register to stack pointer<br />
TYA Transfer Y register to A N Z<br />
588
Appendix N<br />
6D 4<br />
2D 4<br />
OE 6<br />
2C 4<br />
CD 4<br />
EC 4<br />
CC 4<br />
CE 6<br />
4D 4<br />
EE 6<br />
4C 3<br />
20 6<br />
AD 4<br />
AE 4<br />
AC 4<br />
4E 6<br />
OD 4<br />
2E 6<br />
6E 6<br />
ED 4<br />
8D 4<br />
* .<br />
2<br />
7D*4<br />
3D* 4<br />
1E 7<br />
DD*4<br />
DE 7<br />
5D*4<br />
FE 7<br />
BD*4<br />
BC*4<br />
5E 7<br />
1D*4<br />
3E 7<br />
7E 7<br />
FD*4<br />
9D 5<br />
79*4<br />
39*4<br />
D9*4<br />
59*4<br />
B9*4<br />
BE*4<br />
19*4<br />
F9*4<br />
99 5<br />
65 3<br />
25 3<br />
06 5<br />
24 3<br />
C5 3<br />
E4 3<br />
C4 3<br />
C6 5<br />
45 3<br />
E6 5<br />
A5 3<br />
A6 3<br />
A4 3<br />
46 5<br />
05 3<br />
26 5<br />
66 5<br />
E5 3<br />
85 3<br />
86 3<br />
84 3<br />
75 4<br />
35 4<br />
16 6<br />
D5 4<br />
D6 6<br />
55 4<br />
F6 6<br />
B5 4<br />
B4 4<br />
56 6<br />
15 4<br />
36 6<br />
76 6<br />
F5 4<br />
95 4<br />
94 4<br />
rt if i ndex c:rosses<br />
page<br />
+1 if b ranch is taken,<br />
hI more if p age cr ossed<br />
B6 4<br />
96 4<br />
00 7<br />
B8 2<br />
CA 2<br />
88 2<br />
EA 2<br />
48 3<br />
08 3<br />
68 4<br />
28 4<br />
40 6<br />
60 6<br />
38 2<br />
F8 2<br />
78 2<br />
AA 2<br />
A8 2<br />
BA 2<br />
8A 2<br />
9A 2<br />
98 2<br />
69 2<br />
29 2<br />
C9 2<br />
EO 2<br />
CO 2<br />
49 2<br />
A9 2<br />
A2 2<br />
AO 2<br />
09 2<br />
E9 2<br />
9022<br />
BO2 2<br />
F022<br />
3022<br />
DO2 2<br />
1022<br />
5022<br />
7022<br />
OA 2<br />
4A 2<br />
2A 2<br />
6A 2<br />
61 6<br />
21 6<br />
Cl 6<br />
41 6<br />
A1 6<br />
01 6<br />
El 6<br />
81 6<br />
71*5<br />
31*5<br />
D1*5<br />
51*5<br />
B1*5<br />
1 1 5<br />
F1*5<br />
91 6<br />
6C 5<br />
ADC<br />
AND<br />
ASL<br />
BCC<br />
BCS<br />
BEQ<br />
BIT<br />
BMI<br />
BNE<br />
BPL<br />
BRK<br />
BVC<br />
BVS<br />
CLC<br />
CLD<br />
CLI<br />
CLV<br />
CMP<br />
CPX<br />
CPY<br />
DEC<br />
DEX<br />
DEY<br />
EOR<br />
1 NC<br />
INX<br />
INY<br />
JMP<br />
JSR<br />
LDA<br />
LDX<br />
LDY<br />
LSR<br />
NOP<br />
ORA<br />
PHA<br />
PHP<br />
PLA<br />
PLP<br />
ROL<br />
ROR<br />
RTI<br />
RTS<br />
SBC<br />
SEC<br />
SED<br />
SEI<br />
STA<br />
STX<br />
STY<br />
TAX<br />
TAY<br />
TSX<br />
TXA<br />
TXS<br />
TYA<br />
589
CJl<br />
v£><br />
O Opcode Low Nybble<br />
0<br />
1<br />
2<br />
3<br />
4<br />
5<br />
6<br />
7<br />
8<br />
9<br />
A<br />
B<br />
C<br />
D<br />
E<br />
p<br />
0<br />
BRK<br />
BPL<br />
JSR<br />
BMI<br />
RTI<br />
BVC<br />
RTS<br />
BVS<br />
BCC<br />
LDY Inun<br />
BCS<br />
CPY Imm<br />
BNE<br />
CPX Inun<br />
BEQ<br />
ORA<br />
ORA<br />
AND<br />
AND<br />
EOR<br />
EOR<br />
ADC<br />
ADC<br />
STA<br />
STA<br />
LDA<br />
LDA<br />
CMP<br />
CMP<br />
SBC<br />
SBC<br />
1 2<br />
(Ind,X)<br />
(Ind),Y<br />
(Ind,X)<br />
(ind),Y<br />
(Ind.X)<br />
(Ind),Y<br />
(Ind.X)<br />
(Ind),Y<br />
(Ind.X)<br />
(Ind),Y<br />
(Ind,X)<br />
(Ind)pY<br />
(Ind.X)<br />
(Ind),Y<br />
(Ind.X)<br />
(Ind),Y<br />
LDX Imm<br />
BIT<br />
STY<br />
STY<br />
LDY<br />
LDY<br />
CPY<br />
CPX<br />
4<br />
Zer<br />
Zer<br />
Zer,X<br />
Zer<br />
Zer.X<br />
Zer<br />
Zer<br />
ORA<br />
ORA<br />
AND<br />
AND<br />
EOR<br />
EOR<br />
ADC<br />
ADC<br />
STA<br />
STA<br />
LDA<br />
LDA<br />
CMP<br />
CMP<br />
SBC<br />
SBC<br />
5<br />
Zer<br />
Zer,X<br />
Zer<br />
Zer,X<br />
Zer<br />
Zer.X<br />
Zer<br />
Zer.X<br />
Zer<br />
Zer,X<br />
Zer<br />
Zer,X<br />
Zer<br />
Zer,X<br />
Zer<br />
Zer,X<br />
ASL<br />
ASL<br />
ROL<br />
ROL<br />
LSR<br />
LSR<br />
ROR<br />
ROR<br />
STX<br />
STX<br />
LDX<br />
LDX<br />
DEC<br />
DEC<br />
INC<br />
INC<br />
6<br />
Zer<br />
Zer,X<br />
Zer<br />
Zer,X<br />
Zer<br />
Zer,X<br />
Zer<br />
Zer,X<br />
Zer<br />
Zer.Y<br />
Zer<br />
Zer.Y<br />
Zer<br />
Zer.X<br />
Zer<br />
Zer.X<br />
8<br />
PHP<br />
CLC<br />
PLP<br />
SEC<br />
PHA<br />
CLI<br />
PLA<br />
SEI<br />
DEY<br />
TYA<br />
TAY<br />
CLV<br />
INY<br />
CLD<br />
INX<br />
SED<br />
ORA<br />
ORA<br />
AND<br />
AND<br />
EOR<br />
EOR<br />
ADC<br />
ADC<br />
STA<br />
LDA<br />
LDA<br />
CMP<br />
CMP<br />
SBC<br />
SBC<br />
9<br />
Imm<br />
Abs,<br />
Imm<br />
Abs,<br />
Imm<br />
Abs,<br />
Imm<br />
Abs,<br />
Abs,<br />
Imm<br />
Abs,<br />
Imm<br />
Abs,<br />
Imm<br />
Abs<br />
Y<br />
Y<br />
Y<br />
Y<br />
Y<br />
Y<br />
Y<br />
Y<br />
A<br />
ASL<br />
ROL<br />
LSR<br />
ROR<br />
TXA<br />
TXS<br />
TAX<br />
TSX<br />
DEX<br />
NOP<br />
A<br />
A<br />
A<br />
A<br />
BIT<br />
JMP<br />
JMP<br />
STY<br />
LDY<br />
LDY<br />
CPY<br />
CPX<br />
C<br />
Abs<br />
Abs<br />
Ind<br />
Abs<br />
Abs<br />
Abs.X<br />
Abs<br />
Abs<br />
ORA<br />
ORA<br />
AND<br />
AND<br />
EOR<br />
EOR<br />
ADC<br />
ADC<br />
STA<br />
STA<br />
LDA<br />
LDA<br />
CMP<br />
CMP<br />
SBC<br />
SBC<br />
D<br />
Abs<br />
Abs.X<br />
Abs<br />
Abs.X<br />
Abs<br />
Abs.X<br />
Abs<br />
Abs,X<br />
Abs<br />
Abs,X<br />
Abs<br />
Abs,X<br />
Abs<br />
Abs,X<br />
Abs<br />
Abs,X<br />
E<br />
ASL<br />
ASL<br />
ROL<br />
ROL<br />
LSR<br />
LSR<br />
ROR<br />
ROR<br />
STX<br />
LDX<br />
LDX<br />
DEC<br />
DEC<br />
INC<br />
INC<br />
Abs<br />
Abs,<br />
Abs<br />
Abs<br />
Abs<br />
Abs<br />
Abs<br />
Abs<br />
Abs<br />
Abs<br />
Abs<br />
Abs<br />
Abs<br />
Abs<br />
Abs<br />
X<br />
X<br />
X<br />
X<br />
,Y<br />
,x<br />
,x<br />
XT<br />
5"<br />
o<br />
o*<br />
o<br />
(D<br />
g.<br />
x"<br />
o<br />
o<br />
o<br />
■o<br />
o<br />
a (ft
Appendix P<br />
6502/6510 Quasi-Opcodes<br />
Instruction<br />
Abs<br />
Abs,X<br />
Abs,Y<br />
Zer<br />
Zer,X<br />
Zer,Y<br />
(Ind,X)<br />
(Ind),Y<br />
Imm<br />
ASO (ASL,ORA)<br />
RLA (ROL,AND)<br />
LSE (LSR,EOR)<br />
RRA (ROR,ADC)<br />
AXS (STX,STA)<br />
LAX (LDX,LDA)<br />
DCM (DECCMP)<br />
INS (INQSBC)<br />
ALR (LSR,EOR)<br />
ARR (ROR,ADC)<br />
OAL (TAX,LDA)<br />
SAX (DEX,CMP)<br />
OF<br />
2F<br />
4F<br />
6F<br />
8F<br />
AF<br />
CF<br />
EF<br />
IF<br />
3F<br />
5F<br />
7F<br />
DF<br />
FF<br />
IB<br />
3B<br />
5B<br />
7B<br />
BF<br />
DB<br />
FB<br />
07<br />
27<br />
47<br />
67<br />
87<br />
A7<br />
C7<br />
E7<br />
17<br />
37<br />
57<br />
77<br />
B7<br />
D7<br />
F7<br />
97<br />
03<br />
23<br />
43<br />
63<br />
83<br />
A3<br />
C3<br />
E3<br />
13<br />
33<br />
53<br />
73<br />
B3<br />
D3<br />
F3<br />
0B<br />
2B<br />
4B<br />
6B<br />
AB<br />
CB<br />
NOP<br />
SKB<br />
SKW<br />
1A,<br />
80,<br />
0C,<br />
3A, 5A, 7A, DA, FA<br />
B2, C2, E2, 04,14, 34,44, 54, <strong>64</strong>, 74, D4, F4<br />
1C, 3C, 5C, 7C, DC, FC<br />
ASO ASL <strong>the</strong>n ORA <strong>the</strong> result with <strong>the</strong> accumulator<br />
RLA ROL <strong>the</strong>n AND <strong>the</strong> result with <strong>the</strong> accumulator<br />
LSE LSR <strong>the</strong>n EOR <strong>the</strong> result with <strong>the</strong> accumulator<br />
RRA ROR <strong>the</strong>n ADC <strong>the</strong> result from <strong>the</strong> accumulator<br />
AXS Store <strong>the</strong> result of A AND X<br />
LAX LDA and LDX with <strong>the</strong> same data<br />
DCM DEC memory and CMP <strong>the</strong> result with <strong>the</strong> accumulator<br />
INS INC memory <strong>the</strong>n SBC <strong>the</strong> result with <strong>the</strong> accumulator<br />
ALR AND <strong>the</strong> accumulator with data and LSR <strong>the</strong> result<br />
ARR AND <strong>the</strong> accumulator with data and ROR <strong>the</strong> result<br />
OAL ORA <strong>the</strong> accumulator with #$EE, AND <strong>the</strong> result with data, <strong>the</strong>n TAX<br />
SAX SBC data from A AND X and store result in X<br />
NOP No operation<br />
SKB Skip byte (that is, branch of +1)<br />
SKW Skip word of two bytes (that is, branch of +2)<br />
A number of bit patterns which do not appear in Appendices N and O will still<br />
be interpreted by <strong>the</strong> 6502/6510 as opcodes. <strong>The</strong>se commands are not part of <strong>the</strong><br />
6502/6510's specification. Types X3, X7, XB, and XF (and most of X2) aren't defined.<br />
Generally, <strong>the</strong>se quasi-opcodes arise from <strong>the</strong> processor attempting to execute two<br />
instructions simultaneously.<br />
591
Appendix P<br />
<strong>The</strong>re are many regularities in <strong>the</strong>se results. Codes ending in bits 11 execute two<br />
standard instructions ending with bits 01 and 10, simultaneously; if <strong>the</strong> addressing<br />
modes of <strong>the</strong> instructions don't match, <strong>the</strong> higher may be executed first. Those<br />
quasi-opcodes shown in <strong>the</strong> table in boldface seem likely to be more reliable than<br />
<strong>the</strong> o<strong>the</strong>rs.<br />
While <strong>the</strong>re are no guarantees that <strong>the</strong>se opcodes will continue to work with all<br />
revisions of <strong>the</strong> 6502/6510, it is a fact that some published software containing <strong>the</strong>se<br />
codes has given no problems. All 6502/6510s seem to be produced from <strong>the</strong> same<br />
masks, as is shown by <strong>the</strong> well-known bug in indirect JMP, where JMP ($01FF) takes<br />
its two-byte address from $01FF and $0100.<br />
Besides providing some programming shortcuts, quasi-opcodes allow some mea<br />
sure of concealment from disassembly, as no standard disassembler program will be<br />
able to interpret <strong>the</strong>m. For example:<br />
033C ASO $0342 ;Shift Left contents of $0324<br />
033F DCM $0345 ;Decrement contents of $0345<br />
0342 ML program<br />
shows on a monitor as:<br />
033C OF 42 03 CF 45<br />
0341 03 XX ?? ?? YY<br />
where XX, ??, and YY are parts of <strong>the</strong> ML program. Disassembly starting at 033C<br />
will produce at least ten bytes of garbage. However, <strong>the</strong> program will run properly,<br />
but only once. You must compensate for <strong>the</strong> first two instructions, which halve <strong>the</strong><br />
contents of $0345 and decrement <strong>the</strong> contents of $0345. If you set up a loop to<br />
change some o<strong>the</strong>r portion of <strong>the</strong> ML—for example, by EORing it with some set val<br />
ues—<strong>the</strong> whole of a large section of RAM ML can be made hard to decipher.<br />
592
Appendix Q<br />
Converting <strong>Commodore</strong> <strong>64</strong>, VIC-<br />
20, and CBM Programs<br />
Conversion is a deceptively simple word, hiding <strong>the</strong> reality that one machine's pro<br />
grams must often be rewritten for use on ano<strong>the</strong>r. First, you'll see how to transfer<br />
programs between machines. <strong>The</strong>n you'll see how to convert <strong>the</strong>m to run in <strong>the</strong>ir<br />
new environments. Generally, <strong>the</strong>se remarks apply only to BASIC; ML programs<br />
usually have to be rewritten.<br />
LOADing O<strong>the</strong>r Programs into <strong>the</strong> <strong>Commodore</strong> <strong>64</strong><br />
VIC-20 programs. VIC-20 disk programs should load without difficulty into <strong>the</strong><br />
<strong>Commodore</strong> <strong>64</strong>. However, tape programs may give problems, since recording speeds<br />
differ even though <strong>the</strong> format is <strong>the</strong> same. If loading is unsuccessful, try saving <strong>the</strong><br />
original as a file with OPEN1,1: CMD1: LIST: PRINT#1: CLOSE1 and using a<br />
MERGE to read it into <strong>the</strong> computer. This writes <strong>the</strong> program in smaller chunks, so<br />
loading is easier. If this fails, loading into a CBM/PET first (see below), and <strong>the</strong>n<br />
into <strong>the</strong> <strong>64</strong>, is likely to work. Alternatively, <strong>the</strong> program could even be transferred by<br />
modem.<br />
PET/CBM programs. PET/CBM disk programs should load into <strong>the</strong> <strong>64</strong>, but<br />
only if formatted with CBM's 4040 disk drive. Tape should be trouble-free; if <strong>the</strong>re<br />
are LOAD errors, try using <strong>the</strong> same recorder with both CBM and <strong>64</strong> to be sure <strong>the</strong><br />
head alignment isn't a factor.<br />
Note that <strong>the</strong> earliest (tiny keyboard) PETs don't operate in quite <strong>the</strong> same man<br />
ner; <strong>the</strong>y have an extra zero byte at <strong>the</strong> start which usually scrambles <strong>the</strong> first line<br />
after loading into <strong>the</strong> <strong>64</strong> (<strong>the</strong> rest of <strong>the</strong> program is fine).<br />
To load a program from one of <strong>the</strong>se very early PETs into <strong>the</strong> <strong>64</strong>, add a redun<br />
dant first line to <strong>the</strong> PET program and delete <strong>the</strong> meaningless line number at <strong>the</strong><br />
start when it's loaded into VIC. You can also load <strong>the</strong> program into a newer CBM<br />
and save it, giving a <strong>64</strong>-loadable program.<br />
Loading <strong>64</strong> Programs into O<strong>the</strong>r Computers<br />
Loading <strong>64</strong> programs into <strong>the</strong> VIC-20. This is no problem with disks. However,<br />
tape may be unsuccessful, because of timing differences. Use <strong>the</strong> same cures as you<br />
would when loading VIC programs into <strong>the</strong> <strong>64</strong>.<br />
Loading <strong>64</strong> programs into PET/CBMs. This is slightly tricky; <strong>the</strong> <strong>64</strong>'s screen is<br />
usually at <strong>the</strong> place PET/CBM BASIC starts. First move <strong>the</strong> screen, <strong>the</strong>n move <strong>the</strong><br />
start of BASIC down with POKE 44,4: POKE 1024,0: NEW; now load <strong>the</strong> <strong>64</strong> pro<br />
gram and save to tape or disk. <strong>The</strong> result will load successfully into PET/CBM<br />
machines.<br />
Alternatively, change <strong>the</strong> LOAD address to $0401 on disk or tape, by reading<br />
and writing back <strong>the</strong> program file (disk) or overwriting <strong>the</strong> header (tape is trickier!).<br />
O<strong>the</strong>r methods are possible, too. <strong>The</strong> following program, "Simulate CBM/PET,"<br />
modifies <strong>the</strong> <strong>64</strong> to resemble a CBM/PET.<br />
593
Appendix Q<br />
Simulate CBM/PET<br />
10 SIMPLE PET/CBM SIMULATOR<br />
20 POKE 792,193:POKE <strong>64</strong>6,5:POKE 53281,0:POKE 53280<br />
,0<br />
30 POKE 56576,5:POKE 53272,4:POKE <strong>64</strong>8,128<br />
40 POKE 1024,0:POKE 43,1:POKE 44,4:POKE 55,0:POKE<br />
{SPACE}56,128:PRINT"{CLR}":NEW<br />
Program Conversion<br />
Programs which are pure BASIC, even for non-CBM computers, can often be con<br />
verted to run on <strong>the</strong> <strong>64</strong>. Difficulties are likely, though, particularly if disk or tape ac<br />
cess is needed. <strong>The</strong> <strong>64</strong> can perform any <strong>Commodore</strong> disk operation, although CBM<br />
BASIC 4.0 requires translation into <strong>the</strong> lower level version, since it includes disk<br />
commands not available on <strong>the</strong> VIC or <strong>64</strong>. O<strong>the</strong>r computers' disk operations may<br />
well be rewritten to operate with <strong>the</strong> <strong>64</strong>.<br />
<strong>The</strong>re are often o<strong>the</strong>r subtle differences between computers, too. For instance,<br />
some interpret logical true as 1, ra<strong>the</strong>r than —1 as with CBM, so logical operations<br />
may work incorrectly. And some commands (like PRINT USING) are simply missing<br />
from CBM BASIC.<br />
CBM BASICs are all more or less transportable between machines. However, <strong>the</strong><br />
earliest PETs and latest CBMs are a bit different from <strong>the</strong> <strong>64</strong> in several small ways—<br />
GO TO isn't allowed as one word in <strong>the</strong> oldest PETs, for example, and DS is a re<br />
served variable in <strong>the</strong> most recent models. Pure BASIC (without SYS, PEEK, POKE,<br />
WAIT, or USR) is compatible to a very large extent; screen problems can occur, with<br />
related features like <strong>the</strong> bug in INPUT "LONG PROMPT";X$, differences with POS,<br />
SPC, and TAB, and cursor movements which may scroll <strong>the</strong> screen.<br />
You can expect that calculation programs and programs which print out results<br />
will work with little change; so will programs written without PEEKs or POKEs.<br />
With luck, programs which use <strong>the</strong> built-in graphics set may convert easily. A check<br />
ers program, with complicated logic and a simple board display, may need work on<br />
<strong>the</strong> display but can be expected to run properly if <strong>the</strong> graphics are right.<br />
POKE, PEEK, SYS, WAIT, and USR. <strong>The</strong>se are <strong>the</strong> problem areas when<br />
converting programs; very little ML is transportable between machines. Some ML<br />
has an exact equivalent in each CBM machine, for example, screen POKEs and<br />
POKEs into <strong>the</strong> keyboard buffer. But o<strong>the</strong>r ML is machine-specific. For example,<br />
sprites in <strong>the</strong> <strong>64</strong> have no equivalent in o<strong>the</strong>r CBM machines.<br />
If you're lucky and <strong>the</strong> BASIC program has many REMarks, conversion can be a<br />
simple matter of looking up <strong>the</strong> location in one memory map and finding <strong>the</strong><br />
equivalent in ano<strong>the</strong>r. Disabling <strong>the</strong> RUN/STOP key and manipulating <strong>the</strong> keyboard<br />
buffer are examples. You may be able to delete some commands; disabling<br />
RUN/STOP isn't very important. In addition, you may be able to replace some<br />
PEEKs amd POKEs. For instance, <strong>the</strong> <strong>64</strong>'s POKE 198,0 has a BASIC equivalent, FOR<br />
J=l TO 10: GET X$: NEXT, which clears a ten-character keyboard buffer. CBM's<br />
POKE 59468,14 to switch to lowercase is replaceable by PRINT CHR$(14) on <strong>the</strong><br />
VIC and <strong>64</strong>.<br />
Generally, POKEs, PEEKs, and WAITs involving locations 140-250 are likely to<br />
apply to <strong>the</strong> screen or keyboard. Low memory values often alter BASIC pointers.<br />
594
Appendix Q<br />
Most low RAM locations have <strong>the</strong> same sort of effect with VIC and <strong>the</strong> <strong>64</strong>. CBM is<br />
ra<strong>the</strong>r different, though as a rule BASIC 2.0's usage of locations up to 120 or so are<br />
just three addresses less than VIC/<strong>64</strong> values (a POKE to location 41 in a PET/CBM<br />
has <strong>the</strong> same effect as a POKE to location 44 on a VIC or <strong>64</strong>).<br />
SYS commands can be converted only if you have ML knowledge. A routine<br />
may call some Kernal addresses and be usable unchanged; more likely, disassembly<br />
will show up a few addresses which have to be changed. Without ML knowledge<br />
you can't be sure what ML POKEd to RAM does.<br />
POKE commands are usually <strong>the</strong> most difficult to convert, because <strong>the</strong>y can<br />
change <strong>the</strong> whole program configuration. Screen POKEs and <strong>the</strong> color RAM, graph<br />
ics definitions and sound, interface chip manipulations, and uses of multicolor mode<br />
illustrate this sort of thing. PEEKs (to read joysticks, for example) can be tricky as<br />
well, but <strong>the</strong>y can be routinized more easily in view of <strong>the</strong> narrower purposes <strong>the</strong>y<br />
serve.<br />
<strong>The</strong> following table gives relevant POKE and PEEK locations for a variety of<br />
functions. It should help you identify <strong>the</strong> purpose of a few of those mysterious<br />
POKEs in o<strong>the</strong>r people's programs.<br />
Equivalent Memory Locations<br />
VIC-20<br />
<strong>64</strong><br />
CBM BASIC 2 & 4<br />
7680-8185<br />
(unexpanded)<br />
32768-33767<br />
(40-column)<br />
Screen Memory<br />
4096-4591<br />
(with 8K or more<br />
expansion)<br />
1024-2023<br />
32768-34767<br />
(80-column)<br />
Color Memory<br />
37888-38393<br />
(unexpanded)<br />
38400-38905<br />
(with 8K or more<br />
55296-56295<br />
—<br />
expansion)<br />
Character ROM<br />
32768-36863<br />
53248-57343<br />
—<br />
Registers to Control<br />
Character Set Location<br />
36866, 36867, 36869<br />
53272<br />
—<br />
Sound Registers<br />
36874-36878<br />
54272-54300<br />
594<strong>64</strong>, 59466<br />
Joystick Registers<br />
37151, 37152<br />
56320, 56321<br />
—<br />
light Pen Registers<br />
36870, 36871<br />
53267, 53268<br />
—<br />
Paddle Registers<br />
36872, 36873<br />
54297, 54298<br />
—<br />
Interface Chip<br />
Registers<br />
VIA1 37136-37151<br />
VIA2 37152-37167<br />
CIA1 56320-56335<br />
CIA2 56576-56591<br />
PIA1 59408-59411<br />
PIA2 59424-59427<br />
VIA 59456-59471<br />
Start-of-BASIC Pointer<br />
43,44<br />
43,44<br />
40,41<br />
Top-of-BASIC Pointer<br />
55,56<br />
55,56<br />
52,53<br />
595
Appendix R<br />
Supermon <strong>64</strong><br />
Supermon is a relatively short monitor for <strong>the</strong> <strong>64</strong>. It is a public domain program, so it<br />
is free. It can be loaded like BASIC and run, and this puts it into <strong>the</strong> top of BASIC<br />
memory, leaving RAM from $C000 free for ML programs. Chapter 8 explains its op<br />
eration. This version prints your input in white, and <strong>the</strong> monitor's output in cyan, for<br />
good readability.<br />
SYS 38910 reenters Supermon after .X has been used to exit to BASIC, assuming<br />
Supermon is in its usual position in RAM and hasn't been disconnected by<br />
RUN/STOP-RESTORE. SYS to an address containing zero (SYS 13, for example)<br />
will reenable <strong>the</strong> monitor as well.<br />
<strong>The</strong> following instructions tell you how to enter and save Supermon. <strong>The</strong> first<br />
step is to switch on <strong>the</strong> <strong>64</strong> and type in <strong>the</strong> entire program (not necessarily all at<br />
once) and save "Supermon Data" to tape or disk. Note that complete accuracy is re<br />
quired in entering <strong>the</strong> data. For security, a simple checksum is included. It's often<br />
most efficient to have a friend call out <strong>the</strong> numbers as you type in programs.<br />
Next, turn <strong>the</strong> computer off, <strong>the</strong>n on again, and type in:<br />
POKE 43,1: POKE 44,18: POKE 18*256,0: NEW<br />
to move BASIC up out of <strong>the</strong> way. After this, LOAD "SUPERMON DATA",8 (or ,1<br />
for tape). Run <strong>the</strong> Supermon Data program (which takes about 25 seconds), <strong>the</strong>n<br />
SAVE "SUPERMON <strong>64</strong>",8 (or ,1 for tape). Now, "Supermon <strong>64</strong>" becomes <strong>the</strong> pri<br />
mary version of Supermon; just load and run it.<br />
Supermon Data<br />
For mistake-proof program entry, be sure to use <strong>the</strong> "Automatic Proofreader," Appendix C.<br />
10 FOR J=2048 TO 4587: READ X: POKE J,X: T=T+X: NE<br />
XT srem 24<br />
20 IF TO283295 THEN PRINT "CHECKSUM ERROR": END<br />
:rem 234<br />
30 POKE 43,1: POKE 44,8: POKE 45,235: POKE 46,17:<br />
{SPACE}CLR: LIST :rem 27<br />
400 DATA 0,26,8,100,0,153,34,147,18,29,29,29,29,83<br />
,85,80,69,82,32 :rem 134<br />
401 DATA 54,52,45,77,79,78,0,49,8,110,0,153,34,17,<br />
32,32,32,32,32 :rem 69<br />
402 DATA 32,32,32,32,32,32,32,32,32,32,0,75,8,120,<br />
0,153,34,17,32 srem 30<br />
403 DATA 46,46,74,73,77,32,66,85,84,84,69,82,70,73<br />
,69,76,68,0,102 srem 173<br />
404 DATA 8,130,0,158,40,194,40,52,51,41,170,50,53,<br />
54,172,194,40 srem 14<br />
405 DATA 52,52,41,170,49,50,55,41,0,0,0,170,170,17<br />
0,170,170,170 srem 255<br />
406 DATA 170,170,170,170,170,170,170,170,170,170,1<br />
70,170,170,170 srem <strong>64</strong><br />
407 DATA 170,170,170,170,165,45,133,34,165,46,133,<br />
35,165,55,133 srem 34<br />
596
Appendix R<br />
408 DATA 36,165,56,133,37,160,0,165,34,208,2,198,3<br />
5,198,34,177,34 :rem 150<br />
409 DATA 208,60,165,34,208,2,198,35,198,34,177,34,<br />
240,33,133,38 :rem 49<br />
410 DATA 165,34,208,2,198,35,198,34,177,34,24,101,<br />
36,170,165,38 :rem 43<br />
411 DATA 101,37,72,165,55,208,2,198,56,198,55,104,<br />
145,55,138,72 :rem 50<br />
412 DATA 165,55,208,2,198,56,198,55,104,145,55,24,<br />
144,182,201,79 :rem 102<br />
413 DATA 208,237,165,55,133,51,165,56,133,52,108,5<br />
5,0,79,79,79,79 :rem 163<br />
414 DATA 173,230,255,0,141,22,3,173,231,255,0,141,<br />
23,3,169,128,32 :rem 103<br />
415 DATA 144,255,0,0,216,104,141,62,2,104,141,61,2<br />
,104,141,60,2 :rem 238<br />
416 DATA 104,141,59,2,104,170,104,168,56,138,233,2<br />
,141,58,2,152 :rem 19<br />
417 DATA 233,0,0,141,57,2,186,142,63,2,32,87,253,0<br />
,162,66,169,42 :rem 72<br />
418 DATA 32,87,250,0,169,82,208,52,230,193,208,6,2<br />
$0,194,208,2,230 :rem 179<br />
419 DATA 38,96,32,207,255,201,13,208,248,104,104,1<br />
69,159,32,210 :rem 36<br />
420 DATA 255,169,0,0,133,38,162,13,169,46,32,87,25<br />
0,0,169,5,32,210 :rem 169<br />
421 DATA 255,32,62,248,0,201,46,240,249,201,32,240<br />
,245,162,14,221 :rem 106<br />
422 DATA 183,255,0,208,12,138,10,170,189,199,255,0<br />
,72,189,198,255 :rem 154<br />
423 DATA 0,72,96,202,16,236,76,237,250,0,165,193,1<br />
41,58,2,165,194 :rem 137<br />
424 DATA 141,57,2,96,169,8,133,29,160,0,0,32,84,25<br />
3,0,177,193,32 :rem 80<br />
425 DATA 72,250,0,32,51,248,0,198,29,208,241,96,32<br />
,136,250,0,144 :rem 74<br />
426 DATA 11,162,0,0,129,193,193,193,240,3,76,237,2<br />
50,0,32,51,248 ;rem 70<br />
427 DATA 0,198,29,96,169,59,133,193,169,2,133,194,<br />
169,5,96,152,72 :rem 177<br />
428 DATA 32,87,253,0,104,162,46,76,87,250,0,169,15<br />
9,32,210,255,162 :rem 189<br />
429 DATA 0,0,189,234,255,0,32,210,255,232,224,22,2<br />
08,245,160,59 :rem 21<br />
430 DATA 32,194,248,0,173,57,2,32,72,250,0,173,58,<br />
2,32,72,250,0 :rem 11<br />
431 DATA 32,183,248,0,32,141,248,0,240,92,32,62,24<br />
8,0,32,121,250 :rem 54<br />
432 DATA 0,144,51,32,105,250,0,32,62,248,0,32,121,<br />
250,0,144,40,32 :rem 77<br />
433 DATA 105,250,0,169,159,32,210,255,32,225,255,2<br />
40,60,166,38,208 srem 176<br />
597
Appendix R<br />
434 DATA 56,165,195,197,193,165,196,229,194,144,46<br />
,160,58,32,194 :rem 130<br />
435 DATA 248,0,32,65,250,0,32,139,248,0,240,224,76<br />
,237,250,0,32 :rem 14<br />
436 DATA 121,250,0,144,3,32,128,248,0,32,183,248,0<br />
,208,7,32,121 :rem 5<br />
437 DATA 250,0,144,235,169,8,133,29,32,62,248,0,32<br />
,161,248,0,208 :rem 76<br />
438 DATA 248,76,71,248,0,32,207,255,201,13,240,12,<br />
201,32,208,209 :rem 69<br />
439 DATA 32,121,250,0,144,3,32,128,248,0,169,159,3<br />
2,210,255,174 :rem 23<br />
440 DATA 63,2,154,120,173,57,2,72,173,58,2,72,173,<br />
59,2,72,173,60 :rem 80<br />
441 DATA 2,174,61,2,172,62,2,<strong>64</strong>,169,159,32,210,255<br />
,174,63,2,154 :rem 28<br />
442 DATA 108,2,160,160,1,132,186,132,185,136,132,1<br />
83,132,144,132 :rem <strong>64</strong><br />
443 DATA 147,169,<strong>64</strong>,133,187,169,2,133,188,32,207,2<br />
55,201,32,240 :rem 44<br />
444 DATA 249,201,13,240,56,201,34,208,20,32,207,25<br />
5,201,34,240,16 :rem 104<br />
445 DATA 201,13,240,41,145,187,230,183,200,192,16,<br />
208,236,76,237 :rem 79<br />
446 DATA 250,0,32,207,255,201,13,240,22,201,44,208<br />
,220,32,136,250 :rem 93<br />
447 DATA 0,41,15,240,233,201,3,240,229,133,186,32,<br />
207,255,201,13 srem 54<br />
448 DATA 96,108,48,3,108,50,3,32,150,249,0,208,212<br />
,169,159,32,210 srem 130<br />
449 DATA 255,169,0,0,32,239,249,0,165,144,41,16,20<br />
8,196,76,71,248 :rem 151<br />
450 DATA 0,32,150,249,0,201,44,208,186,32,121,250,<br />
0,32,105,250,0 srem 35<br />
451 DATA 32,207,255,201,44,208,173,32,121,250,0,16<br />
5,193,133,174 .rem 18<br />
452 DATA 165,194,133,175,32,105,250,0,32,207,255,2<br />
01,13,208,152 :rem 16<br />
453 DATA 169,159,32,210,255,32,242,249,0,76,71,248<br />
,0,165,194,32 :rem 43<br />
454 DATA 72,250,0,165,193,72,74,74,74,74,32,96,250<br />
,0,170,104,41 srem 34<br />
455 DATA 15,32,96,250,0,72,138,32,210,255,104,76,2<br />
10,255,9,48,201 srem 123<br />
456 DATA 58,144,2,105,6,96,162,2,181,192,72,181,19<br />
4,149,192,104 :rem 49<br />
457 DATA 149,194,202,208,243,96,32,136,250,0,144,2<br />
,133,194,32,136 :rem 133<br />
458 DATA 250,0,144,2,133,193,96,169,0,0,133,42,32,<br />
62,248,0,201,32 srem 108<br />
459 DATA 208,9,32,62,248,0,201,32,208,14,24,96,32,<br />
175,250,0,10,10 :rem 10'9<br />
598
Appendix R<br />
460 DATA 10,10,133,42,32,62,248,0,32,175,250,0,5,4<br />
2,56,96,201,58 srem 56<br />
461 DATA 144,2,105,8,41,15,96,162,2,44,162,0,0,180<br />
,193,208,8,180 srem 65<br />
462 DATA 194,208,2,230,38,214,194,214,193,96,32,62<br />
,248,0,201,32 :rem 31<br />
463 DATA 240,249,96,169,0,0,141,0,0,1,32,204,250,0<br />
,32,143,250,0 :rem 244<br />
4<strong>64</strong> DATA 32,124,250,0,144,9,96,32,62,248,0,32,121,<br />
250,0,176,222 :rem 11<br />
465 DATA 174,63,2,154,169,159,32,210,255,169,63,32<br />
,210,255,76,71 :rem 99<br />
466 DATA 248,0,32,84,253,0,202,208,250,96,230,195,<br />
208,2,230,196 : rem 32<br />
467 DATA 96,162,2,181,192,72,181,39,149,192,104,14<br />
9,39,202,208,243 :rem 206<br />
468 DATA 96,165,195,1<strong>64</strong>,196,56,233,2,176,14,136,14<br />
4,11,165,40,1<strong>64</strong> :rem 157<br />
469 DATA 41,76,51,251,0,165,195,1<strong>64</strong>,196,56,229,193<br />
,133,30,152,229 :rem 155<br />
470 DATA 194,168,5,30,96,32,212,250,0,32,105,250,0<br />
,32,229,250,0 :rem 5<br />
471 DATA 32,12,251,0,32,229,250,0,32,47,251,0,32,1<br />
05,250,0,144,21 :rem 81<br />
472 DATA 166,38,208,100,32,40,251,0,144,95,161,193<br />
,129,195,32,5 :rem 32<br />
473 DATA 251,0,32,51,248,0,208,235,32,40,251,0,24,<br />
165,30,101,195 :rem 52<br />
474 DATA 133,195,152,101,196,133,196,32,12,251,0,1<br />
66,38,208,61,161 :rem 181<br />
475 DATA 193,129,195,32,40,251,0,176,52,32,184,250<br />
,0,32,187,250 :rem 33<br />
476 DATA 0,76,125,251,0,32,212,250,0,32,105,250,0,<br />
32,229,250,0,32 :rem 85<br />
477 DATA 105,250,0,32,62,248,0,32,136,250,0,144,20<br />
,133,29,166,38 :rem 63<br />
478 DATA 208,17,32,47,251,0,144,12,165,29,129,193,<br />
32,51,248,0,208 :rem 134<br />
479 DATA 238,76,237,250,0,76,71,248,0,32,212,250,0<br />
,32,105,250,0 :rem 17<br />
480 DATA 32,229,250,0,32,105,250,0,32,62,248,0,162<br />
,0,0,32,62,248 :rem 44<br />
481 DATA 0,201,39,208,20,32,62,248,0,157,16,2,232,<br />
32,207,255,201 :rem 55<br />
482 DATA 13,240,34,224,32,208,241,240,28,142,0,0,1<br />
,32,143,250,0 :rem 243<br />
483 DATA 144,198,157,16,2,232,32,207,255,201,13,24<br />
0,9,32,136,250 :rem 75<br />
484 DATA 0,144,182,224,32,208,236,134,28,169,144,3<br />
2,210,255,32,87 :rem 135<br />
485 DATA 253,0,162,0,0,160,0,0,177,193,221,16,2,20<br />
8,12,200,232,228 :rem 141<br />
599
Appendix R<br />
486 DATA 28,208,243,32,65,250,0,32,84,253,0,32,51,<br />
248,0,166,38,208 :rem 178<br />
487 DATA 141,32,47,251,0,176,221,76,71,248,0,32,21<br />
2,250,0,133,32 :rem 62<br />
488 DATA 165,194,133,33,162,0,0,134,40,169,147,32,<br />
210,255,169,159 :rem 141<br />
489 DATA 32,210,255,169,22,133,29,32,106,252,0,32,<br />
202,252,0,133 srem 11<br />
490 DATA 193,132,194,198,29,208,242,169,145,32,210<br />
,255,76,71,248 :rem 112<br />
491 DATA 0,160,44,32,194,248,0,32,84,253,0,32,65,2<br />
50,0,32,84,253 :rem 65<br />
492 DATA 0,162,0,0,161,193,32,217,252,0,72,32,31,2<br />
53,0,104,32,53 srem 41<br />
493 DATA 253,0,162,6,224,3,208,18,1<strong>64</strong>,31,240,14,16<br />
5,42,201,232,177 srem 166<br />
494 DATA 193,176,28,32,194,252,0,136,208,242,6,42,<br />
144,14,189,42 srem 46<br />
495 DATA 255,0,32,165,253,0,189,48,255,0,240,3,32,<br />
165,253,0,202 srem 21<br />
496 DATA 208,213,96,32,205,252,0,170,232,208,1,200<br />
,152,32,194,252 srem 117<br />
497 DATA 0,138,134,28,32,72,250,0,166,28,96,165,31<br />
,56,1<strong>64</strong>,194,170 srem 147<br />
498 DATA 16,1,136,101,193,144,1,200,96,168,74,144,<br />
11,74,176,23,201 srem 177<br />
499 DATA 34,240,19,41,7,9,128,74,170,189,217,254,0<br />
,176,4,74,74,74 srem 160<br />
500 DATA 74,41,15,208,4,160,128,169,0,0,170,189,29<br />
,255,0,133,42 srem 19<br />
501 DATA 41,3,133,31,152,41,143,170,152,160,3,224,<br />
138,240,11,74 srem 253<br />
502 DATA 144,8,74,74,9,32,136,208,250,200,136,208,<br />
242,96,177,193 srem 94<br />
503 DATA 32,194,252,0,162,1,32,254,250,0,196,31,20<br />
0,144,241,162 srem 2<br />
504 DATA 3,192,4,144,242,96,168,185,55,255,0,133,4<br />
0,185,119,255 srem 45<br />
505 DATA 0,133,41,169,0,0,160,5,6,41,38,40,42,136,<br />
208,248,105,63 srem 57<br />
506 DATA 32,210,255,202,208,236,169,32,44,169,13,7<br />
6,210,255,32,212 srem 173<br />
507 DATA 250,0,32,105,250,0,32,229,250,0,32,105,25<br />
0,0,162,0,0,134 :rem 71<br />
508 DATA 40,169,159,32,210,255,32,87,253,0,32,114,<br />
252,0,32,202,252 2rem 162<br />
509 DATA 0.133,193,132,194,32,225,255,240,5,32,47,<br />
251,0,176,233 :rem 23<br />
510 DATA 76,71,248,0,32,212,250,0,169,3,133,29,32,<br />
62,248,0,32,161 :rem 109<br />
600
Appendix R<br />
511 DATA 248,0,208,248,165,32,133,193,165,33,133,1<br />
94,76,70,252,0 :rem 84<br />
512 DATA 197,40,240,3,32,210,255,96,32,212,250,0,3<br />
2,105,250,0,142 :rem 93<br />
513 DATA 17,2,162,3,32,204,250,0,72,202,208,249,16<br />
2,3,104,56,233 :rem 53<br />
514 DATA 63,160,5,74,110,17,2,110,16,2,136,208,246<br />
,202,208,237,162 :rem 158<br />
515 DATA 2,32,207,255,201,13,240,30,201,32,240,245<br />
,32,208,254,0 :rem 248<br />
516 DATA 176,15,32,156,250,0,1<strong>64</strong>,193,132,194,133,1<br />
93,169,48,157 :rem 51<br />
517 DATA 16,2,232,157,16,2,232,208,219,134,40,162,<br />
0,0,134,38,240 :rem 56<br />
518 DATA 4,230,38,240,117,162,0,0,134,29,165,38,32<br />
,217,252,0,166 :rem 67<br />
519 DATA 42,134,41,170,188,55,255,0,189,119,255,0,<br />
32,185,254,0,208 zrem 188<br />
520 DATA 227,162,6,224,3,208,25,1<strong>64</strong>,31,240,21,165,<br />
42,201,232,169 :rem 63<br />
521 DATA 48,176,33,32,191,254,0,208,204,32,193,254<br />
,0,208,199,136 :rem 85<br />
522 DATA 208,235,6,42,144,11,188,48,255,0,189,42,2<br />
55,0,32,185,254 :rem 137<br />
523 DATA 0,208,181,202,208,209,240,10,32,184,254,0<br />
,208,171,32,184 :rem 110<br />
524 DATA 254,0,208,166,165,40,197,29,208,160,32,10<br />
5,250,0,1<strong>64</strong>,31 zrem 73<br />
525 DATA 240,40,165,41,201,157,208,26,32,28,251,0,<br />
144,10,152,208 irem 58<br />
526 DATA 4,165,30,16,10,76,237,250,0,200,208,250,1<br />
65,30,16,246,1<strong>64</strong> :rem 162<br />
527 DATA 31,208,3,185,194,0,0,145,193,136,208,248,<br />
165,38,145,193 :rem 96<br />
528 DATA 32,202,252,0,133,193,132,194,169,159,32,2<br />
10,255,160,65 :rem 32<br />
529 DATA 32,194,248,0,32,84,253,0,32,65,250,0,32,8<br />
4,253,0,169,5 :rem 25<br />
530 DATA 32,210,255,76,176,253,0,168,32,191,254,0,<br />
208,17,152,240 :rem 73<br />
531 DATA 14,134,28,166,29,221,16,2,8,232,134,29,16<br />
6,28,40,96,201 srem 79<br />
532 DATA 48,144,3,201,71,96,56,96,<strong>64</strong>,2,69,3,208,8,<br />
<strong>64</strong>,9,48,34,69 :rem 61<br />
533 DATA 51,208,8,<strong>64</strong>,9,<strong>64</strong>,2,69,51,208,8,<strong>64</strong>,9,<strong>64</strong>,2,<br />
69,179,208,8,<strong>64</strong> :rem 162<br />
534 DATA 9,0,0,34,68,51,208,140,68,0,0,17,34,68,51<br />
,208,140,68,154 :rem 121<br />
535 DATA 16,34,68,51,208,8,<strong>64</strong>,9,16,34,68,51,208,8,<br />
<strong>64</strong>,9,98,19,120 :rem 107<br />
601
Appendix R<br />
536 DATA 169,0,0,33,129,130,0,0,0,0,89,77,145,146,<br />
134,74,133,157 srem 69<br />
537 DATA 44,41,44,35,40,36,89,0,0,88,36,36,0,0,28,<br />
138,28,35,93,139 :rem 187<br />
538 DATA 27,161,157,138,29,35,157,139,29,161,0,0,4<br />
1,25,174,105,168 :rem 193<br />
539 DATA 25,35,36,83,27,35,36,83,25,161,0,0,26,91,<br />
91,165,105,36 srem 37<br />
540 DATA 36,174,174,168,173,41,0,0,124,0,0,21,156,<br />
109,156,165,105 srem 113<br />
541 DATA 41,83,132,19,52,17,165,105,35,160,216,98,<br />
90,72,38,98,148 srem 153<br />
542 DATA 136,84,68,200,84,104,68,232,148,0,0,180,8<br />
,132,116,180,40 srem 122<br />
543 DATA 110,116,244,204,74,114,242,1<strong>64</strong>,138,0,0,17<br />
0,162,162,116 srem 10<br />
544 DATA 116,116,114,68,104,178,50,178,0,0,34,0,0,<br />
26,26,38,38,114 srem 116<br />
545 DATA 114,136,200,196,202,38,72,68,68,162,200,5<br />
8,59,82,77,71 srem 56<br />
546 DATA 88,76,83,84,70,72,68,80,44,65,66,249,0,53<br />
,249,0,204,248 srem 120<br />
547 DATA 0,247,248,0,86,249,0,137,249,0,244,249,0,<br />
12,250,0,62,251 srem 123<br />
548 DATA 0,146,251,0,192,251,0,56,252,0,91,253,0,1<br />
38,253,0,172,253 srem 160<br />
549 DATA 0,70,248,0,255,247,0,237,247,0,13,32,32,3<br />
2,80,67,32,32 srem 17<br />
550 DATA 83,82,32,65,67,32,88,82,32,89,82,32,83,80<br />
'0 :rem 21<br />
602
Index<br />
A (Assemble) Monitor command 203, 229-30<br />
ABS function 19<br />
accumulators 110<br />
ADC instruction 288<br />
address bus 108<br />
addressing modes, 6510 chip 208-10<br />
"ADSR Plotter" program 441<br />
algorithm 81<br />
AND instruction 221-22, 289-90<br />
AND operator 20<br />
animation 421-23<br />
"animation" program 421<br />
answer mode (modem) 553<br />
APPEND BASIC extension 168<br />
appending sequential files 505<br />
arrays 97-98<br />
Arrow tape operating system 473<br />
ASC function 16, 21, 87<br />
ASCII code 15, 93, 575<br />
ASL instruction 221, 290-91<br />
assemblers 233-38<br />
attack (sound) 433-34<br />
ATN function 21-22<br />
audio-video socket 9, 118<br />
AUTO BASIC extension 168<br />
"Auto" program 169<br />
"Automatic Proofreader, <strong>The</strong>" 569-71<br />
autostart cartridges 131-33<br />
BAM 517, 522<br />
bank selection 109<br />
bank switching 388<br />
"BASIC Autoloader" program 482-84<br />
BASIC joystick routine 534<br />
"BASIC Light Pen Program" 539<br />
"BASIC Line Peeker" program 141<br />
"BASIC list" program 179-80<br />
BASIC ROM routines 250-51<br />
"BASIC Wedge Demonstration" program<br />
2<strong>64</strong>-65<br />
BASIC programming language 3, 105<br />
advanced 137-200<br />
effective programming 77-101<br />
error messages 68-73<br />
extensions 167-200<br />
graphics and 359-60<br />
mixing ML with 277-84<br />
modifying in RAM 257-58<br />
modifying through vectors and wedges<br />
262-69<br />
moving into RAM 257, 260<br />
reference guide 15-73<br />
BCC instruction 291<br />
BCS instruction 292<br />
Bell 103 standard 554<br />
BEQ instruction 292-93<br />
binary notation 105-6<br />
binary search 95<br />
bit instruction 293-94<br />
"Bitmap Draw Routine" program 400<br />
"Bitmap Drawing with a Joystick" program<br />
402-3<br />
bitmapped graphics 383-84, 396-405<br />
block commands, disk 524-25<br />
BLOCK LOAD BASIC extension 169-70<br />
BLOCK SAVE BASIC extension 169-70<br />
BMI instruction 294-95<br />
BNE instruction 295-96<br />
Boolean expression. See logical expression<br />
BRK opcode 203, 207, 213<br />
vector 269<br />
"BASIC Screen" program 142<br />
BPL instruction 296<br />
BRK instruction 297<br />
buffer 110, 154-56<br />
BVC instruction 298<br />
BVS instruction 299<br />
C (Compare Memory) Monitor command 230<br />
calculations, ML and 251-56<br />
cartridge 114-17, 131-33, 437<br />
cartridge socket 9, 118-20<br />
cassette port 9, 120<br />
CBM MON monitor 226, 227-28<br />
CHAIN BASIC extension 171<br />
"Change Color Ram" program 371<br />
"Change Vertical Position" program 381<br />
"Changing <strong>the</strong> LOAD Address" program<br />
500-501<br />
channel fifteen 493-94, 528-29<br />
"Character Editor" program 392-95<br />
character ROM 359, 389-90<br />
characters 359-62<br />
characters, ML and 248<br />
CHECK DISK <strong>Commodore</strong> disk utility<br />
program 511<br />
CHR$ function 16, 17, 22<br />
CHRGET Kernal routine 263, 2<strong>64</strong><br />
CHROUT Kernal routine 247-48, 365<br />
CIA (Complex Interface Adapter) 109, 121-29,<br />
259, 269<br />
"Circle Plotter" program 552<br />
CLOSE statement 12, 22-23, 87<br />
tape 469, 470<br />
disk 497-98<br />
ML 526-29<br />
CLC instruction 299<br />
CLD instruction 300<br />
CLI instruction 300<br />
CLR statement 23-24<br />
603
CLV instruction 301<br />
CMD statement 24, 543, 548<br />
CMP instruction 301-2<br />
COLOR BASIC extension 171<br />
color RAM 93, 375-79, 396<br />
changing with ML 370-71<br />
"Color Ram Motion7' program 371<br />
"Combine lines" program 198-99<br />
commas, INPUT and 155-56<br />
commercial software 131-34<br />
<strong>Commodore</strong> <strong>64</strong>, different models of 113<br />
<strong>Commodore</strong> ASCII codes 576-79<br />
<strong>Commodore</strong> <strong>64</strong> 105-36<br />
"Compare ROM" program 258-59<br />
comparisons, ML 207-8<br />
COMPILE BASIC extension 171-72<br />
CompuServe 553<br />
COMPUTE's Machine Language Routines for<br />
<strong>the</strong> <strong>Commodore</strong> <strong>64</strong> 167<br />
COMPUTE'S Second Book of Machine Language<br />
237<br />
"Computed GOTO and GOSUB" program<br />
173<br />
CONT command 25<br />
control port 9-10, 120, 533-40<br />
controllers 137<br />
copy protection, tape 481-82<br />
COPY/ALL <strong>Commodore</strong> disk utility 511<br />
COS function 25<br />
CPX instruction 303<br />
CPY instruction 304<br />
CRUNCH BASIC extension 173<br />
cursor 248<br />
D (Disassemble) Monitor command 203, 230<br />
daisywheel printers 546<br />
data files, tape 468-70<br />
"Data Maker" program 278<br />
DATA statement 26, 97, 278, 565<br />
data structures 97-99<br />
Datassette 465-66, 471-73<br />
troubleshooting 472<br />
"Date Validator" program 99-100<br />
"Day of <strong>the</strong> Week Calculator" program 100<br />
"Days Between Two Dates" program 100<br />
debugging BASIC programs 87<br />
DEC instruction 304-5<br />
decay (sound) 433-34<br />
decimal arithmetic 224<br />
"Decimal Input" program 88-89<br />
decimal notation 106-8<br />
DEEK BASIC extension 174<br />
DEF FN statement 26-27<br />
"Delete" program 174<br />
DEX instruction 305-6<br />
DEY instruction 306<br />
device number 12, 166, 514-15, 586<br />
"Dice" program 97<br />
diet calculator 990-91<br />
DIM statement 27-28, 101, 147<br />
DIR <strong>Commodore</strong> disk utility 511<br />
direct access commands, disk 523-26<br />
direct access files, disk 495-96<br />
direct mode 11, 151<br />
disk 3, 487-529<br />
command summary 508-9<br />
copying 490-92<br />
device number, changing 514-15<br />
directory 489, 517-18, 51-23<br />
error channel 493-94<br />
errors, 498<br />
file handling, ML 526-29<br />
formatting 488-89<br />
hardware notes 512<br />
ID 488-89<br />
loading program 490<br />
message summary 510<br />
scratching file 490<br />
saving program 489-90<br />
troubleshooting 509-11<br />
ROM 515-16<br />
DISK ADDR CHANGE <strong>Commodore</strong> disk<br />
utility 511<br />
"Disk Merge" program 181<br />
disk utility programs, <strong>Commodore</strong> 511-12<br />
diskette storage 513-14<br />
diskettes, physical characteristics of 512-13<br />
disk, commercial software and 133<br />
DISPLAY T&S <strong>Commodore</strong> disk utility 511<br />
DOKE BASIC extension 174<br />
DOS 5.1 <strong>Commodore</strong> disk utility 511<br />
dot-matrix printers 546<br />
"Double Density" program 369-70<br />
"Drawing Lines" program 400-02<br />
DUMP BASIC extension 174-76<br />
editing BASIC 11-12<br />
editor/assemblers 238<br />
END statement 2-29<br />
end-of-tape marker 466<br />
envelope, SID 433-35, 437<br />
EOR instruction 221-22, 307<br />
EPROM (erasable programmable read only<br />
memory) 109, 135,36<br />
"Equation Solver" program 91-92<br />
error message subroutine 85<br />
error messages, BASIC 69-73<br />
errors, in ML programming 224-25<br />
EXP function 17, 29<br />
expansion boards 135<br />
exponential notation 15<br />
expressions, BASIC 17-18<br />
extended background color mode 379-80<br />
"Extended Background Color Mode" program<br />
380<br />
"Fast Step" program 165<br />
604
1540 model disk drive 487<br />
1541 model disk drive 487<br />
files 12, 468-70, 494, 495-96, 499, 500,<br />
502-8, 509, 518-19, 526-29<br />
filters, sound 435-36<br />
"Finding ML or Memory Dump LOAD<br />
Address" program 500<br />
flags 110<br />
floating point accumulator 333<br />
flow chart 80-81<br />
FOR-NEXT structure 29-31, 87<br />
forced-load address 466-67<br />
"Fraction Maker" program 92<br />
FRE function 17, 31-32<br />
"Froggie Graphics" program 363<br />
full duplex communication 555<br />
function keys 157-59<br />
"Function Keys" program 157-58<br />
functions, BASIC 17, 146<br />
G (Go) Monitor command 203, 230<br />
game paddles 535-38<br />
gate bit 440<br />
"General Program Copier" 492<br />
GET statement 32, 89<br />
GET# statement 32-33, 470, 497<br />
GETIN Kernal routine 248<br />
GOSUB statement 34-35, 84<br />
GOSUB-RETURN structure 87<br />
GO dummy statement 33<br />
GOTO statement 35, 84<br />
graphics 3, 359-423<br />
cross-reference 368-69<br />
double-density 369-70<br />
"Graphics Screen Dump" program 549-50<br />
H (Hunt Memory) Monitor command 231<br />
half duplex communication 555<br />
"Handling Relative Files" program 507<br />
hardware schematic 112-13<br />
hardware vectors 356<br />
header, tape 481-82<br />
hex-to-decimal conversion subroutine 84<br />
hex-to-decimal conversion, ML 253-54<br />
hexadecimal notation 5,107-8<br />
"Histogram Demo" program 367<br />
"Horizontal Motion" program 423<br />
hybrid programs 279-80<br />
I (Interpret Memory) Monitor command 231<br />
IEEE communication 134-35<br />
IF-THEN statement 35-36<br />
index 205-6<br />
INC instruction 308<br />
input buffer 154-55<br />
INPUT statement 36-38<br />
INPUT# statement 38, 87, 470, 497<br />
INT function 39<br />
integer variables 16<br />
intercepting keys 162-63<br />
interfaces 134-35<br />
interrupt register, VIC II 415-16<br />
interrupts 127, 269-73, 415-21<br />
"Investigating <strong>the</strong> CIA" program 126-27<br />
INX instruction 206, 309<br />
INY instruction 309<br />
IOINIT Kernal routine 260<br />
IRQ interrupt 415, 466<br />
"IRQ Polling" program 415<br />
IRQ vector 213-14, 269<br />
"Jesu Joy" program 450-51<br />
jiffy clock 156<br />
JMP instruction 310<br />
joystick 9, 127, 402, 533-35<br />
joystick port. See control port<br />
JSR instruction 213, 311<br />
"Kaleidoscope" program 363<br />
Kernal ROM 333<br />
Kernal routines 241-49, 354-56<br />
I/O errors 241-42<br />
new languages and 259-61<br />
keyboard 10-11, 159-65, 457, 248<br />
decoding 160-61<br />
redefinition 1<strong>64</strong><br />
reading 159-60<br />
repeat keys 1<strong>64</strong><br />
keyboard buffer 155, 159<br />
keys 10-11<br />
intercepting 162-63<br />
keywords, BASIC 11-12, 16, 19-73<br />
L (Load ML) Monitor command 231<br />
labels 235<br />
LADS assembler 237<br />
languages, new 259-61<br />
LDA instruction 312<br />
LDX instruction 313<br />
LDY instruction 313-14<br />
LEFT$ function 17, 39, 92<br />
"Legible list" program 177-78<br />
LEN function 40<br />
LET statement 40-41<br />
"LET Vector Demo" program 267<br />
light pen 10, 127, 417, 538-40<br />
"Line Plotter" program 551-52<br />
linked lines, BASIC 140-41<br />
linking devices 135<br />
LIST BASIC extension 176-80<br />
LIST statement 41<br />
LIST Kernal routine 262<br />
listing conventions 4-5<br />
literals 15<br />
"Load Anywhere" program 479<br />
LOAD command 42-43, 465-68<br />
loading 151-52<br />
LOG function 17, 43-44<br />
logical expression 17<br />
logical file number 12<br />
605
logical line 12<br />
loops, ML 219-21, 206-7<br />
LSR instruction 221, 314-15<br />
M (Memory Display) Monitor command 203,<br />
231<br />
machine language. See ML<br />
"Machine Language Sort for String Arrays"<br />
program 193-94<br />
MAE editor/assembler 238<br />
making BASIC run faster 100-101<br />
"Maze Demo" program 365<br />
memory commands, disk 525-26<br />
memory configuration 114-18<br />
memory locations, equivalent 595<br />
memory map 109-11, 333-56<br />
bitmap merge BASIC extension 180<br />
MOD BASIC extension 181<br />
MICROMON-<strong>64</strong>" monitor 226, 228<br />
"MicroScope" program 111<br />
MID$ function 17, 44, 92, 93<br />
ML 3, 105, 203-38<br />
graphics and 365-75<br />
relocating 280-83<br />
tape routines 470-71, 478-81<br />
techniques 216-21<br />
"ML Autorun" program 501-2<br />
"ML Character Screen Dump" program 550<br />
"ML Clock" program 128-29<br />
"ML File Reader" program 528<br />
ML joystick interpreter 535<br />
ML joystick routine 535<br />
"ML light Pen Draw" program 539-40<br />
"ML Paddle Reader" program 537<br />
"ML Read-Only" program 447-48<br />
"ML Relocator" program 283<br />
"ML Reverse" program 366-67<br />
modems 137, 553-57<br />
monitors 112, 203-8, 226-33, 234, 543<br />
command dictionary 229-33<br />
"Mosaic" program 391<br />
"Multicolor Bitmap Draw Routine" program<br />
404-5<br />
multicolor mode 376-79<br />
"Multicolor Mode" program 378-79<br />
"Music Program" 451-47<br />
music <strong>the</strong>ory 448-49<br />
N (Number Adjuster) Monitor command 232<br />
NEW command 44-45<br />
NEXT statement 45<br />
NMI (Non-Maskable Interrupt) 124, 213-14,<br />
269-73, 415<br />
"NMI Demo" program 271<br />
noise 431-32<br />
NOP instruction 315<br />
NOT operator 45-46<br />
"Number Guessing Game" program 81-82<br />
"Number of Blocks Free" program 522<br />
number storage, tape and 470<br />
numbers 15, 152-54<br />
numeric expression 17<br />
object code 233-34<br />
"Oh, Zeros" program 93<br />
"OLD" program 181-82<br />
ON statement 46-47<br />
ONERR BASIC extension 182<br />
opcode, 6510 chip 235, 287-329, 588-90<br />
OPEN statement 12, 47-48<br />
control register 559-60<br />
disk 496<br />
ML 526-29<br />
printers 543<br />
RS-232 channel and 559<br />
tape 469, 470<br />
operand 235<br />
operators 16<br />
OR operator 48-49<br />
ORA instruction 221-22, 315-16<br />
"Organ Keyboard" program 457-49<br />
originate mode (modem) 553<br />
P (Printer Disassembly) Monitor command 232<br />
"Packing Numbers" program 93<br />
paddles 10<br />
pattern matching, filename 509<br />
PAUSE BASIC extension 182-83<br />
"Payroll Analyser" program 91<br />
PEEK function 18, 49<br />
PERFORMANCE TEST <strong>Commodore</strong> disk<br />
utility 511-12<br />
PET <strong>64</strong> computer 4<br />
"Pet Your <strong>64</strong>" program 260-61<br />
PHA instruction 213, 316-17<br />
PHP instruction 317<br />
physical line 12<br />
PLA (programmed logic array) 109, 113<br />
PLA instruction 213, 318<br />
PLOT Kernal routine 248<br />
"Plotter Demo" program 552<br />
plotters 551-52<br />
plotting 367-69<br />
PLP instruction 318-19<br />
pointers 110<br />
POKE statement 50<br />
BASIC graphics and 3<strong>64</strong>-65<br />
"POKEing BASIC to <strong>the</strong> Screen" program 142<br />
POP BASIC extension 183<br />
"POP" program 183-84<br />
POS function 50-51<br />
PRINT statement 51-52, 87, 360-63<br />
PRINT BASIC extension 184<br />
PRINT USING BASIC extension 184-87<br />
"PRINT USING Demo" program 186<br />
"PRINT USING" program 184-85<br />
PRINT# statement 52-53, 87, 470, 496-97,<br />
543-45, 548<br />
606
printers 109, 543-51<br />
<strong>Commodore</strong> 543-45<br />
control characters 547-48<br />
non-<strong>Commodore</strong> 545-7<br />
presence 550-51<br />
spooling 551<br />
program chaining 151-52, 467<br />
program conversion 593-95<br />
program counter 213<br />
program files, disk 494, 499, 500<br />
program mode 11, 151<br />
program recovery 129-31<br />
programming aids, music 462<br />
programming standards 83-86<br />
"<strong>Programming</strong> Sprites with User-Defined<br />
Characters" program 41-11<br />
programs (ROM) 110<br />
PROM (Programmable Read Only Memory)<br />
109<br />
pseudo-opcode 234, 235<br />
pulse wave 430-31<br />
quasi-opcodes 591-92<br />
"Queens" program 96-97<br />
R (Register Display) Monitor command 232<br />
Rabbit tape operating system 473<br />
RAM 108, 109<br />
free areas 165-66<br />
in disk drive 487-88<br />
ML manipulation 256-59<br />
RAM data storage 99<br />
BASIC and 277<br />
RAMTAS Kernal routine 260<br />
randomizing 96-97<br />
ML and 254-55<br />
range of byte, testing 219<br />
raster interrupt 417-19<br />
"Reading and Displaying a Sequential File"<br />
program 503-4<br />
READ statement 53-54<br />
"Reading Bytes from Tape" program 471<br />
"Reading Paddle 2" program 537<br />
"Reading Programs Byte by Byte" program<br />
499<br />
"Reading <strong>the</strong> Bam" program 522<br />
"Reading <strong>the</strong> Directory Track" program<br />
522-23<br />
RECONFIGURE BASIC extension 187-88<br />
"Reconfigure" program 187-88<br />
register 333<br />
registers, SID 438-41<br />
control 440-^1<br />
envelope shape 441, 442<br />
filter 443<br />
frequency control 439<br />
pulse width 439<br />
read-only 444<br />
voice 438<br />
volume 443<br />
relative files, disk 495-96, 506-8, 518-19<br />
release (sound) 434<br />
"Relocating Program Generator", program<br />
283-84<br />
REM BASIC extension 188-89<br />
REM statement 54, 84, 101<br />
ML and 278<br />
RENUMBER BASIC extension 19-91<br />
"Renumber" program 190<br />
RESET BASIC extension 191<br />
reset vector 213-14<br />
resetting <strong>the</strong> computer 129-31<br />
reset, hardware 130-31<br />
RESTORE statement 55<br />
RETURN statement 55-56<br />
RF modulator output jack 9<br />
"Rhythm Box" program 459-61<br />
RIGHTS function 17, 56, 92<br />
ring modulation 432-33, 440<br />
RND function 56-57<br />
ROL instruction 221, 319<br />
ROM 108-9<br />
ML manipulation 257-59<br />
upgrading 258-59<br />
ROM cartridge 109<br />
"ROM RAM" program 257<br />
ROM upgrade" program 259<br />
ROR instruction 221, 320<br />
"Rounding" program 89-90<br />
RS-232 interface 557-61<br />
OPEN and 559<br />
pin functions 558<br />
RS-232 processing 9, 124, 134, 545-46<br />
RTI instruction 213, 320-21<br />
RTS instruction 203, 321-22<br />
RUN command 57-58<br />
RUN/STOP 129-30<br />
RUN/STOP and RUN/STOP-RESTORE,<br />
disabling 156-57<br />
RUN/STOP-RESTORE 129-30<br />
RUN/STOP-RESTORE, RAM BASIC and 260<br />
S (Save ML) Monitor command 232<br />
"Save Anywhere" program 480-81<br />
SAVE command. 58-59, 465-68<br />
saving 151-52<br />
sawtooth wave 430<br />
SBC instruction 322-23<br />
screen 166-67, 247-48<br />
screen character codes 580-81<br />
screen color codes 574<br />
screen color memory table 573<br />
"Screen Dump" program 175<br />
screen location table 572<br />
screen RAM 396<br />
"Screen Save and Load" program 170-71<br />
"Scroll Down" program 372<br />
607
"Scroll Left" program 373<br />
"Scroll Right" program 373<br />
"Scroll Up" program 372<br />
scrolling 372-75<br />
smooth 380-82<br />
search algorithm, relative files 495<br />
"Search" program 191-92<br />
searching 95<br />
SEC instruction 323<br />
secondary address 12<br />
sector 513-14, 516-17<br />
SED instruction 324<br />
SEI instruction 324-25<br />
sequential files, disk 495, 496, 502-6<br />
serial port 9, 120, 487, 561-62<br />
series calculations 256<br />
SET BASIC extension 192<br />
SETLFS Kernal routine 527<br />
SGN function 59<br />
"Shell-Metzner Sort" program 192<br />
shift and rotate instructions 221<br />
"Shuffler" program 95-96<br />
shuffling 95-96<br />
SID chip 3, 109, 112, 259, 427-35, 437-48,<br />
535, 585<br />
side sectors 518-19<br />
"Simple Design" program 362<br />
"Simple Menu" program 86<br />
"Simple ML Output" program 366<br />
"Simple POKE" program 3<strong>64</strong><br />
"Simple PRINT Demo" program 362<br />
"Simple SIDMON" program 444-46<br />
"Simpler Shuffler" program 96<br />
SIN function 59-60<br />
sine waves 427-29<br />
"Single-Key Keyword Entry" program 163-<strong>64</strong><br />
1650 model AUTOMODEM 553<br />
1600 model VICMODEM 553<br />
6502 chip 108<br />
6510 chip 108, 287-329<br />
"<strong>64</strong> Terminal Program" 556-57<br />
"Smooth Scroll" program 382<br />
"Sorted Directory" program 520-21<br />
sorting 95, 192-95<br />
sound 3, 427-62<br />
source code 233<br />
SPC( output function 60<br />
speech syn<strong>the</strong>sis 437<br />
"sprite collision" program 409<br />
"Sprite Editor" program 412-15<br />
"Sprite-Data Collision" program 416-17<br />
sprites 405-15<br />
collision 409<br />
defining 406<br />
disabling 406<br />
enabling 406<br />
expansion 408<br />
interrupts and 416-20<br />
mapping 409-11<br />
modes 407-8<br />
positioning 406-7<br />
priority 408-9<br />
SQR function 17, 60-61<br />
square wave. See pulse wave<br />
ST reserved variable 16, 61, 470, 498<br />
STA instruction 325<br />
stack 110-11, 213<br />
statements 18<br />
status register 211-13<br />
STOP statement 62<br />
storage in memory, BASIC 139-51<br />
accuracy of numbers 152-54<br />
arrays 146-48<br />
BASIC bytes 143<br />
calculating 139-40, 150-51<br />
floating-point 153<br />
garbage collection and 148-50<br />
string 148-49<br />
variables 144-46<br />
STR$ function 16, 62-63<br />
"String and Integer Input" program 88<br />
string expression 17<br />
string handling 92-94<br />
string variables 16, 145-46<br />
STX instruction 326<br />
STY instruction 326<br />
subroutines, ML 207-8<br />
"SUPERMON" monitor 203, 226-28, 596-602<br />
sustain (sound) 434<br />
SX-<strong>64</strong> computer 4<br />
SYS statement 63<br />
systems 11, 79, 82-83<br />
T (Transfer Memory) Monitor command 232<br />
TAB( function <strong>64</strong><br />
tables (ROM) 110<br />
TAN function <strong>64</strong><br />
tape 3, 127, 465-84<br />
commercial software and 133-34<br />
headers 475-77<br />
type to purchase 472-73<br />
tape buffer 156<br />
"Tape Directory" program 476-77<br />
tape recorders, non-<strong>Commodore</strong> 473<br />
tape recorder, programming 474-76<br />
TAX instruction 326-27, 205<br />
TAY instruction 327<br />
Teletype printers 546<br />
terminal software 553<br />
<strong>the</strong>rmal and spark printers 546<br />
"Thirty-Two Sprites" program 419-21<br />
TI reserved variable 16, <strong>64</strong>-65<br />
TI$ reserved variable <strong>64</strong>-65<br />
timers 127-28<br />
timing 215-16<br />
608
'Tournament Sort" program 192-93<br />
"Trace" program 195-96<br />
track 513-14, 516-17<br />
triangle wave 429<br />
TSX instruction 327-28<br />
tunes 437<br />
turnkey systems 118<br />
TV 111-12<br />
two-byte operations 217-19<br />
twos complement arithmetic 222-23<br />
TXA instruction 328<br />
TXS instruction 328-29<br />
TYA instruction 329<br />
typewriters, modified 546<br />
typing in programs 565-68<br />
U commands, disk 523-24<br />
unclosed files 509<br />
UNCRUNCH BASIC extension 173<br />
UNLIST BASIC extension 196-99<br />
user port 9, 105, 120<br />
user-definable characters 376-77, 383-95, 421<br />
"Using a Quote Before Input" program 155<br />
"Using Block Read" program 524<br />
"Using Block Write" program 524-25<br />
"Using Files" program 470<br />
"Using <strong>the</strong> Input Buffer" program 155<br />
"USR Demonstration" program 252-53<br />
USR function 66, 251-53<br />
V (Verify) Monitor command 232<br />
VAL function 16, 66<br />
"Variable Dump" program 175-76<br />
variables, BASIC 11, 15-16, 101, 144-46, 157<br />
"VARPTR" program 199-200, 250<br />
vectors 110, 262-69<br />
VERIFY command 66-67, 467<br />
"Vertical Motion" program 422<br />
VIC II chip 3, 4, 100, 109, 112, 113-14, 259,<br />
375, 383, 384-87, 391, 397, 405, 538,<br />
582-84<br />
VIEW BAM <strong>Commodore</strong> disk utility 512<br />
voices, SID 438, 448<br />
wait statement 67-68, 534<br />
warm start 260<br />
warning light, disk 498-99<br />
wedges 263-65<br />
"Window List" program 176-77<br />
"Wordscore" program 94<br />
"Writing Bytes to Tape" program 471<br />
X (Exit to BASIC) Monitor command 233<br />
x register 205, 206-7<br />
zero flag 207<br />
zero page 209, 213,<br />
609
To order your copy of <strong>Programming</strong> <strong>The</strong> <strong>Commodore</strong> <strong>64</strong><br />
Disk, call our toll-free US order line: 1-800-334-0868 (in NC<br />
call 919-275-9809) or send your prepaid order to:<br />
<strong>Programming</strong> <strong>The</strong> <strong>Commodore</strong> <strong>64</strong> Disk<br />
COMPUTE! Publications<br />
P.O. Box 5058<br />
Greensboro, NC 27403<br />
All orders must be prepaid (check, charge, or money order). NC<br />
residents add 4.5% sales tax.<br />
Send copies of <strong>Programming</strong> <strong>The</strong> <strong>Commodore</strong> <strong>64</strong> Disk at<br />
$12.95 per copy.<br />
Subtotal<br />
$_<br />
Shipping & Handling: $2.00/disk $_<br />
Sales tax (if applicable) $_<br />
Total payment enclosed $_<br />
All payments must be in U.S. funds.<br />
□ Payment enclosed<br />
Charge a Visa □ MasterCard □ American Express<br />
Acct. No. Exp. Date<br />
(Required)<br />
Signature<br />
Name<br />
Address<br />
City State Zip<br />
Please allow 4-5 weeks for delivery.<br />
4595073
If you've enjoyed <strong>the</strong> articles in this book, you'll find<br />
<strong>the</strong> same style and quality in every monthly issue of<br />
COMPUTEI's Gazette for <strong>Commodore</strong>.<br />
For Fastest Service<br />
Call Our Toll-Free US Order Line<br />
800-334-0868<br />
In NC call 919-275-9809<br />
COMPUTED Gazette<br />
P.O. Box 5058<br />
Greensboro, NC 27403<br />
My computer is:<br />
□ <strong>Commodore</strong> <strong>64</strong> □ VIC-20 □ O<strong>the</strong>r.<br />
□ $24 One Year US Subscription<br />
□ $45 Two Year US Subscription<br />
□ $65 Three Year US Subscription<br />
Subscription rates outside <strong>the</strong> US:<br />
□ $30 Canada<br />
□ $65 Air Mail Delivery<br />
□ $30 International Surface Mail<br />
Name<br />
Address<br />
City<br />
Country<br />
State<br />
Zip<br />
Payment must be in US funds drawn on a US bank, international<br />
money order, or charge card. Your subscription will begin with <strong>the</strong><br />
next available issue. Please allow 4-6 weeks for delivery of first issue.<br />
Subscription prices subject to change at any time.<br />
□ Payment Enclosed<br />
□ MasterCard<br />
□ Visa<br />
□ American Express<br />
Acct. No. Expires /<br />
(Required)<br />
<strong>The</strong> COMPUTEI's Gazette subscriber list is made available to carefully screened<br />
organizations with a product or service which may be of interest to our readers. If you<br />
prefer not to receive such mailings, please check this box a<br />
759199
COMPUTE! Books<br />
Ask your retailer for <strong>the</strong>se COMPUTE! Books or order<br />
directly from COMPUTE!.<br />
Call toll free (fh US) 800-334-0868 (in NC 919-275-9809)<br />
or write COMPUTE! Books, P.O. Box 5058, Greensboro, NC<br />
27403.<br />
Quantity Title Price* Total<br />
SpeedScript: <strong>The</strong> Word Processor for <strong>the</strong><br />
<strong>Commodore</strong> <strong>64</strong> and VIC-20 (94-9) $ 9.95<br />
<strong>Commodore</strong> SpeedScript Book Disk $12.95<br />
COMPUTEI's <strong>Commodore</strong> <strong>64</strong>/128 Collection (97-3) $12.95<br />
All About <strong>the</strong> <strong>Commodore</strong> <strong>64</strong>, Volume Two (45-0) $16.95<br />
All About <strong>the</strong> <strong>Commodore</strong> <strong>64</strong> Volume One (40-X) $12.95<br />
<strong>Programming</strong> <strong>the</strong> <strong>Commodore</strong> <strong>64</strong>:<br />
<strong>The</strong> <strong>Definitive</strong> Guide (50-7) $24.95<br />
COMPUTEI's Data File Handler for <strong>the</strong><br />
<strong>Commodore</strong> <strong>64</strong> (86-8) $ 12.95<br />
Kids and <strong>the</strong> <strong>Commodore</strong> <strong>64</strong> (77-9) $12.95<br />
COMPUTEI's <strong>Commodore</strong> Collection, Volume 1 (55-8) $12.95<br />
COMPUTEI's <strong>Commodore</strong> Collection, Volume 2 (70-1) $12.95<br />
COMPUTEI's VIC-20 and <strong>Commodore</strong> <strong>64</strong><br />
Tool Kit: BASIC (32-9) $16.95<br />
COMPUTED VIC-20 and <strong>Commodore</strong> <strong>64</strong><br />
Tool Kit: Kernal (33-7) $16.95<br />
COMPUTEI's Telecomputing on <strong>the</strong><br />
<strong>Commodore</strong> <strong>64</strong> (009) $ 12.95<br />
COMPUTEI's VIC-20 Collection (007) $12.95<br />
<strong>Programming</strong> <strong>the</strong> VIC (52-3) $24.95<br />
VIC Games for Kids (35-3) $12.95<br />
COMPUTEI's First Book of VIC (07-8) $12.95<br />
COMPUTED Second Book of VIC (16-7) $12.95<br />
COMPUTEI's Third Book of VIC (43-4) $12.95<br />
Mapping <strong>the</strong> VIC (24-8) $14.95<br />
COMPUTED VIC-20 Collection (007) $12.95<br />
•Add $2.00 per book for shipping and handling.<br />
Outside US add $5.00 air mail or $2.00 surface mail.<br />
NC residents add 4.5% sales tax<br />
Shipping & handling: $2.00/book<br />
Total payment<br />
All orders must be prepaid (check, charge, or money order).<br />
All payments must be in US funds.<br />
□ Payment enclosed.<br />
Charge □ Visa □ MasterCard □ American Express<br />
Acct. No Exp. Date—<br />
Name_<br />
(Required)<br />
Addres<br />
City<br />
•Allow 4-5 weeks for delivery.<br />
Prices and availability subject to change.<br />
Current catalog available upon request.<br />
State.<br />
Zip-<br />
4595073