27.11.2014 Views

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

SHOW MORE
SHOW LESS

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

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!