"A few entry points, original/upgrade ROM" and "Plotting With the CBM 2022 Printer" were originally published in COMPUTE! Magazine, January/February 1980, copyright 1980, Small System Services, Inc. "BASIC 4.0 Memory Map" and" "PET 4.0 ROM Routines" were originally published in COMPUTE! Magazine, November/December 1980, copyright 1980, Small System Services, Inc. "More VIC Maps" was originally published in COMPUTE! Magazine, March 1982, copyright 1982, Small System Services, Inc. "Commodore 64 Memory Map" was originally published in COMPUTE! Magazine, October 1982, copyright 1982, Small System Services, Inc. "Shoot" was originally published in COMPUTE! Magazine, September 1981, copyright 1981, Small System Services, Inc. "SUPERMON: A Primary Tool For Machine Language Programming" was originally published in COMPUTE! Magazine, December 1981, copyright 1981, Small System Services, Inc. "MICROMON: An Enhanced Machine Language Monitor" was originally published in COMPUTE! Magazine, January 1982, copyright 1982, Small System Services, Inc. "VIC Micromon" was originally published in COMPUTE! Magazine, November 1982, copyright 1982, Small System Services, Inc. "Supermon 64" was originally published in COMPUTE! Magazine, January 1983, copyright 1983, Small System Services, Inc.

Copyright © 1983, Small System Services, Inc. All rights reserved.

Reproduction or translation of any part of this work beyond that permitted by Sections 107 and 108 of the United States Copyright Act without the permission of the copyright owner is unlawful.

Printed in the United States of America

ISBN 0-942386-11-6

10 9 8 7 6 5 4

~

~

~

~ ~

Table of Contents

Preface v

Introduction — Why Machine Language? vii

Chapter I: How To Use This Book 1

Chapter 2: The Fundamentals 7

Chapter 3: The Monitor 23

Chapter 4: Addressing 37

Chapter 5: Arithmetic 53

Chapter 6: The Instruction Set 63

Chapter 7: Borrowing from BASIC 91

Chapter 8: Building A Program 97

Chapter 9: ML Equivalents

Of BASIC Commands 121

Appendices

A: Instruction Set 149

B: Maps 167

C: Assembler Programs 223

D: Disassembler Programs 237

E: Number Charts 243

F: Monitor Extensions 253

G: The Wedge 335

Index 339

Preface

Something amazing lies beneath BASIC.

Several years ago I decided to learn to program in machine language, the computer's own language. I understood BASIC fairly well and I realized that it was simply not possible to accomplish all that I wanted to do with my computer using BASIC alone. BASIC is sometimes just too slow.

I faced the daunting (and exhilarating) prospect of learning to go below the surface of my computer, of finding out how to talk directly to a computer in its language, not the imitation-English of BASIC. I bought four books on 6502 machine language programming and spent several months practicing with them and puzzling out opcodes and hexadecimal arithmetic, and putting together small machine language programs.

Few events in learning to use a personal computer have had more impact on me than the moment that I could instantly fill the TV screen with any picture I wanted because of a machine language program I had written. I was amazed at its speed, but more than that, I realized that any time large amounts of information were needed on screen in the future — it could be done via machine language. I had, in effect, created a new BASIC "command" which could be added to any of my programs. This command — using a SYS or USR instruction to send the computer to my custom-designed machine language routine — allowed me to have previously impossible control over the computer.

BASIC might be compared to a reliable, comfortable car. It will get you where you want to go. Machine language is like a sleek racing car — you get there with lots of time to spare. When programming involves large amounts of data, music, graphics, or games — speed can become the single most important factor.

After becoming accustomed to machine language, I decided to write an arcade game entirely without benefit of

-

BASIC. It was to be in machine language from start to finish. I predicted that it would take about twenty to thirty hours. It was a space invaders game with mother ships, rows of aliens, sound . . . the works. It took closer to 80 hours, but I am probably more proud of that program than of any other I've written.

After I'd finished it, I realized that the next games would —-

be easier and could be programmed more quickly. The modules handling scoring, sound, screen framing, delay, and —-

player/enemy shapes were all written. I only had to write new sound effects, change details about the scoring, create new shapes. The essential routines were, for the most part, already written for a variety of new arcade-type games. When creating machine language programs you build up a collection of reusable subroutines. For example, once you find out how to make sounds on your machine, you change the details, but not the underlying procedures, for any new songs.

The great majority of books about machine language assume a considerable familiarity with both the details of microprocessor chips and with programming technique. This book only assumes a working knowledge of BASIC. It was designed to speak directly to the amateur programmer, the ^

part-time computerist. It should help you make the transition from BASIC to machine language with relative ease. -*

This book is dedicated to Florence, Jim, and Larry. I would also like to express my gratitude to Lou Cargile for his —

many helpful suggestions; to Tom R. Halfhill and Charles Brannon of the COMPUTE! Magazine editorial staff for their «^

contributions — both direct and indirect — to this book; and to Robert Lock and Kathleen Martinek for their encouragement, comments, and moral support. And special thanks to Jim Butterfield for his maps, programs, and constant encouragement to everyone who decides to learn 6502 machine language programming.

c o

o

9

»

3 W>

C

c

"I

z

£

to to

C -C >,

H *J to

o J2 ?? Q QJ

o B 1 - o

o53J5

s fr -B re

rC rr ;u oj QJ

2 3 8- So

re &,« 2 2

1 S3 S^

MH QJ _, >, OJ

g ">§".£ S Si 4! re -C

| «B -.» g

re „ boU ^

6b;-; 9 £ £

£<f ft|

g cq qj oi re

%.£«£

£ boX JS *

.. re y - O

vh ,-j re rH g

■3 I a £^

ill

g x S n C

o y

—

> *

bo re

-a -^

A ft u qj

^^ gH

"5 re > to

oj tr 1

O ft

x ^ a-

*i re

re »i -5 aj

«>U

re £

3 0

*- re P >

>. o

3 '

re £

oo £P <u en

^ re -m >~

g

o>

" - g re 2 y

3 C

5c-*

C 3

re o

3*3 o

2 « s

£ ! 5

SO "5 "ft

^ — -^

x C 3

. o

M -a to

— 3 XT 1 <v

C H

"2 ra

H ft

re oi

qj c

V 5

x cu

to 3

03 5

2 re '£ T3 5b o 5 § oj oj

C

re re

^a £

3 —

o 5

_C;

to

T3 qj qj —.

a re re u

fE oj re bO h re bo

3 2

" tn

£ =

5^ « 5!

re "5 2 "n T1

"re 5 w E

bO re .3 g +.43

•rug

£ £ re

^S3

£ |^

i2 tnU 3 <u "

£•"<

re g ,5

"go U u-o en • 2

< a £

S^ re . o 5 SiS

c -3 .2

QJ 'u ■3 Oh

-C en

y m

re re

s QJ

C 3

'" n »

U3 l-l "

3 oj f 3 i- a re 3 ;

5c £ c

a. ^c oTS .•3 a,,

* S 2£ ft

QJ ^

re O

QJ QJ

-*-» ft

■ QJ

QJ &

re fa

B o

W 0

c

£

QJ +j

en re

< H

bo

.£ ffl Td o

g. jn 7j re 3 tn

to 2 c n ■Sfi

3 3 £ re

-j

£ p2

a

ft" QJ

01 O re "

OJ ft

DO" 1 -C

£> V \J

3 o —

60 -

V t J

W OU H

C 01

of

C/j

<

CQ

QJ ^

-3 «

01

to

re

3

to

3

J3

OJ tB 0

OJ « to

ft

01

H

o

01 01

-C

re

01

— 0

T3 C

to 3 r

? -Q bmTl

bo.

T3

3 Q

(X

3

QJ «" <

"O -S *5 J2 PQ

to _^J

m o

3 —'

re o

3 ~

3 X „ >

3 K

•a qj

2 £

.£ c

£.SP

■*-■ >-i

3 QJ

* o

QJ 4-<

1 ' g

" QJ

QJ

S re

01

In C«n

3 re o P T3 o>

c 3 «

5 3 2 J o oj

o ^<

^45 £ >< oj >i o

£< £ ; .

^u g rc/j .2

°< 6

3 CQ 3

re

i qj s

ft -*-» .3 " O

O) ^

§1

U 01

-d

3

O

fN >^XZ

Uf ;

>7 3 N

o "^ r )

" uU

to" f^Z

2 ft £ £

£

ft ^o

U

Qj Xj

re -ft

a ^ w g w

y <c»g ~ •£

O QJ S

•S 3

&0 q J3

re M ~

3 1j bO S

£ s

|2£

OJ to

^ o 2

bO boH —

CX to

£ ro

e S £

S. 3 to

••-* re

O O

£ £

QJ OJ

£ 6 S

3 re

re 2 J-*

bO OJ

a b

3

re

< 3

o -r

l |

<o re

QJ

& e

QJ O

x; x!

bo 3

CN

<

01

QjU

re 2

( . ^ re qj

n3 QJ

^rS

^ +J tO ^^ .4-t

35 2

3 .S

£?j

bO bOtN O O O b £ "~>

w C ta

5 o oj

P u ?-

>^QJ £

>jh to o

•~ to u

*J » QJ

ll*J g g»a

C C3 [fi QJ

rv O — ft

~ ^,<n -a

oj re

3

£

re

re _q

OJ

£

OJ

H t_i

— J i— -

3S

QJ

Introduction

~ ~

Throughout the book we'll be learning how to handle a variety of ML instructions, and the "Simple Assembler" program will be of great help. You might want to familiarize yourself with it. Knowing «fe

what it does (and using it to try the examples in this book), you will gradually build your understanding of ML, hexadecimal numbers, and the new possibilities open to the computerist who knows ML.

Seeing It Work

Chapters 2 through 8 each examine a major aspect of ML where it

differs from the way BASIC works. In each chapter, examples and

exercises lead the programmer to a greater understanding of the

methods of ML programming. By the end of the book, you should be

able to write, in ML, most of the programs and subroutines you will

want or need. -^

Let's examine some advantages of ML, starting with the main one — ML runs extremely fast. -^

Here are two programs which accomplish the same thing. The first is in ML, and the second is in BASIC. They get results at very different speeds indeed, as you'll see:

Machine Language

169 1 160 0 153 0 128 153 0 129 153 0 ^ 130 153 0 131 200 208 241 96

BASIC

5 FOR 1 = 1 TO 1000: PRINT "A";: NEXT I "

These two programs both print the letter "A" 1000 times on the screen. The ML version takes up 28 bytes of Random Access Memory (RAM). The BASIC version takes up 45 bytes and takes about 30 times as long to finish the job. If you want to see how quickly the ML works, you can POKE those numbers somewhere into RAM and run the ML program with a SYS (Commodore computers) or USR (Atari) or CALL (Apple). In both BASIC and ML, many instructions are followed by an argument. The instructions SYS and CALL have numbers as their arguments. In these cases, the instruction is going to turn control of the computer over to the address given as the argument. There would be an ML program waiting there. To make it easy to see this ML program's speed, we'll load it into memory without yet knowing much about it. -^

A disassembly is like a BASIC program's LISTing. You can give the starting address of an ML program to a disassembler and it will >_

translate the numbers in the computer's memory into a readable series of ML instructions. See Appendix D for a disassembler that you ^^

can use to examine and study ML programs.

Introduction

Here's what the PET/CBM version looks like when it has been translated by a disassembler:

A Disassembly

Program I -1. Disassembly.

The following BASIC programs (called loaders) POKE the ML instructions (and their arguments) into memory for you:

Program 1-2. PET Version.

810 820 830 840 850

READ DA:POKE AD

1 REM PET VERSION

800 FOR AD=864T0883 ,DA:NEXT AD

PRINT"SYS 864 TO ACTIVATE" DATA169,01,160,0,153,0 DATA128,153,0,129,153,0 DATA130,153,0,131,200,208 DATA241,96

Program 1-3. VIC Version.

1 REM VIC VERSION

800 FOR AD=864T0885:READDA:POKEAD,D

A:NEXTAD

PRINT"SYS 864 TO ACTIVATE"

805 810 820 830 840

DATA DATA DATA DATA

160 30, 153 200

0, 153

0,

169

0. 31

150

153, 169,

208, 237, 96

153, 0, 151

Introduction

Program 1-4. 64 Version.

Neuter model 64's need to have the color registers set before running this program to see the effect on the full screen.

1 REM COMMODORE 64 VERSION _

800 FOR AD=40000TO40019:READDA:POKE

AD,DA:NEXTAD —.

805 PRINT"SYS 40000 TO ACTIVATE" 810 DATA169,1,160,0,153,0 820 DATA4,153,0,5,153,0

830 DATA6,153,0,7,200,208 —'

840 DATA241,96

Program 1-5. Apple Version.

100 FOR 1=1536 TO 1561:READ A:POKE I,A:NEXT I

110 PRINT "A=USR(1536) TO ACTIVATE "

120 DATA 165,88,133,0,165,89,133,1,169

130 DATA 33,162,4,160,0,145,0,200,208,251,230

140 DATA 1,202,208,244,104,96

After running this program, type the SYS or USR or CALL as instructed and the screen will instantly fill. From now on, when we mention SYS, Atari owners should mentally substitute USR and Apple owners should think CALL.

BASIC stands for Beginners All-purpose Symbolic Instruction Code. Because it is all-purpose, it cannot be the perfect code for any specific job. The fact that ML speaks directly to the machine, in the machine's language, makes it the more efficient language. This is because however cleverly a BASIC program is written, it will require extra running time to finish a job.

For example, PRINT involves BASIC in a series of operations which ML avoids. BASIC must ask and answer a series of questions. Where is the text located that is to be PRINTed? Is it a variable? Where

~

100 FOR I = 770 TO 789: READ A: POKE I,A: NE —*

XT 110 PRINT "CALL 770 TO ACTIVATE " —

120 DATA 169,129,162,0,157,0,4,157,0,5,157,0 ,6,157,0,7,202,208,241,96

Program 1-6. Atari Version.

~ ~

Introduction

is the variable located? How long is it? Then, it must find the proper location on the screen to place the text. However, as we will discover, ML does not need to hunt for a string variable. And the screen addresses do not require a complicated series of searches in an ML program. Each of these tasks, and others, slow BASIC down because it must serve so many general purposes. The screen fills slowly because BASIC has to make many more decisions about every action it attempts than does ML.

Inserting ML For Speed

A second benefit which you derive from learning ML is that your understanding of computing will be much greater. On the abstract level, you will be far more aware of just how computers work. On the practical level, you will be able to choose between BASIC or ML, whichever is best for the purpose at hand. This choice between two languages permits far more flexibility and allows a number of tasks to be programmed which are clumsy or even impossible in BASIC. Quite a few of your favorite BASIC programs would benefit from a small ML routine, "inserted" into BASIC with a SYS, USR, or CALL, to replace a heavily used, but slow, loop or subroutine. Large sorting tasks, smooth animation, and many arcade-type games must involve ML.

BASIC Vs. Machine Language

BASIC itself is made up of many ML programs stored in your computer's Read Only Memory (ROM) or sometimes loaded into RAM from disk. BASIC is a group of special words such as STOP or RUN, each of which stands for a cluster of ML instructions. One such cluster might sit in ROM (unchanging memory) just waiting for you to type LIST. If you do type in that word, the computer turns control over to the ML routine which accomplishes a program listing. The BASIC programmer understands and uses these BASIC words to build a program. You hand instructions over to the computer relying on the convenience of referring to all those pre-packaged ML routines by their BASIC names. The computer, however, always follows a series of ML instructions. You cannot honestly say that you truly understand computing until you understand the computer's language: machine language.

Another reason to learn ML is that custom programming is then possible. Computers come with a disk operating system (DOS) and BASIC (or other "higher-level" languages). After a while, you will likely find that you are limited by the rules or the commands available in these languages. You will want to add to them, to customize them. An understanding of ML is necessary if you want to add new words to BASIC, to modify a word processor (which was written in ML), or to personalize your computer — to make it behave precisely as you want it to.

Introduction

BASIC'S Strong Points

Of course, BASIC has its advantages and, in many cases, is to be preferred over ML. BASIC is easier to analyze, particularly because it often includes REM statements which reveal the functions of the program's parts. REMs also make BASIC easier to modify. This could make it the language of choice if the program must frequently be partially rewritten or updated to conform to changing conditions. For example, a program which calculates a payroll might well have at the beginning a series of data statements which contain the tax rates. BASIC DATA statements can be easily altered so that the program will reflect the current rates. If the payroll program runs fast enough in BASIC, there is no advantage to translating it into ML.

BASIC is also simpler to debug (to get all the problems ironed out so that it works as it should). In Chapter 3 we will examine some ML debugging techniques which work quite well, but BASIC is the easier of the two languages to correct. For one thing, BASIC often just comes out and tells you your programming mistakes by printing out error messages on the screen.

Contrary to popular opinion, ML is not necessarily a memory —

saving process. ML can use up about as much memory as BASIC does when accomplishing the same task. Short programs can be somewhat more compact in ML, but longer programs generally use up bytes fast in both languages. However, worrying about using up computer memory is quickly becoming less and less important. In a few years, programmers will probably have more memory space available than —

they will ever need. In any event, a talent for conserving bytes, like skill at trapping wild game, will likely become a victim of technology. It will always be a skill, but it seems as if it will not be an everyday necessity.

So, which language is best? They are both best — but for different purposes. Many programmers, after learning ML, find that they continue to construct programs in BASIC, and then add ML modules where speed is important. But perhaps the best reason of all for learning ML is that it is fascinating and fun.

~ ->

~

r

I

r

How To Use This Book

Although anyone wishing to learn 6502 machine language (ML) will likely find this book instructive and worthwhile, the specific example programs are written to work on five popular personal computers: Apple, Atari, VIC, Commodore 64, and the PET/CBMs. If your computer uses the 6502 microprocessor, but is not one of these machines, you will need to find a "memory map" for your particular machine. These maps — widely available in books and magazines, and from user groups — will allow you to follow and practice with the examples of 6502 machine language throughout this book.

In particular, there are several memory addresses which are used in many of the examples presented in this book. Their addresses are given for the five computers mentioned above, but if you have a different computer, you should look them up in a map of your machine:

1. "'Which key is pressed?" This is an address, usually somewhere in the first 256 addresses, which is always holding the value of the most recently pressed key on the keyboard.

2. Starting Address of RAM Screen Memory. This is the address in your computer where, if you POKEd something into it from BASIC, you would see the effect in the upper left-hand corner of your screen.

3. Print a Character. This address is within your BASIC ROM memory itself. It is part of the BASIC language, but written in ML. It is the starting address of a routine which will put a character on the screen.

4. Get a Character. Also part of BASIC in ROM memory, this ML routine accepts a character from the keyboard and stores it.

5. A safe place. You must know where, in your computer, you can construct ML programs without interfering with a BASIC program or anything else essential to the computer's normal operations. The best bet is often that memory space designed to serve the cassette player called the cassette buffer. While practicing, you won't be using the cassette player and that space will be left alone by the computer itself.

Here are the answers to give the Simple Assembler (Appendix C) when it asks for "Starting Address." These are hexadecimal numbers about which we'll have more to say in the next chapter. For now, if you've got an Atari, type in 0600. If you use a PET/CBM, answer 0360. For VIC or Commodore 64, type: 0340. If you have an

I How To Use This Book

Apple, use 0300. For other computers, you'll need to know where ^-*

there are about 100 RAM memory addresses that are safe.

All through this book, the examples will start at various arbitrary addresses (1000, 2000, 5000, for example). You should substitute the addresses which are safe in your computer. Just as it doesn't matter —

whether you start a BASIC program at line number 10 or line 100, it makes no difference whether a ML program starts at address 1000 or —»

0340, as long as you are putting it in a safe memory zone.

So, start all of the examples you assemble for practice in the —

same convenient, safe memory location for your machine. In fact, the Simple Assembler (SA) was designed to be modified and customized. See the introduction to Appendix C for more detailed instructions on customizing. Because you can make the SA conform to your needs, you might want to replace the line with the INPUT that requests the starting address (variable SA) with a specific address. In this way, you can work with the examples in the book without having to specify the safe address each time.

The First Step: Assembling

Throughout this book there are many short example ML programs. -—

They vary in length, but most are quite brief and are intended to

illustrate a ML concept or technique. The best way to learn something

new is most often to just jump in and do it. Machine language

programming is no different. Machine language programs are written ^

using a program called an assembler, just as BASIC programs are

written using a program called ' 'BASIC.'' ,,_

In Appendix C there is a program called the "Simple Assembler." Your first step in using this book should be to type in the Microsoft version; it will work correctly on all personal computers using Microsoft BASIC. (If you have an Atari, type in the Atari version.)

Once you've typed this program into your computer, you can save it to tape or disk and use it whenever you want to construct a ML program. The example ML routines in this book should be entered into your computer using the Simple Assembler and then modified, examined, and played with.

Frequently, the examples are designed to do something to the screen. The reason for this is that you can tell at once if things are working as planned. If you are trying to send the message "TEST STRING" and it comes out "test string" or "TEST STRIN" or "TEST STRING®" — you can go back and reassemble it with the SA until you get it right. More importantly, you'll discover what you did wrong. ■^

What you see on the screen when you POKE a particular number to the screen will differ from computer to computer. In fact, it can vary on different models of the same computer. For this reason,

How To Use This Book I

the examples in the book are usually given in standard ASCII codes (explained later).

Chances are that your computer uses a particular code for the alphabet which is not ASCII. The Commodores use what's called "PET ASCII" and the Atari uses ATASCII, for ATari ASCII. It's not that bad, however, since once you've found the correct number to show the letter "A" on screen, the letter "B" will be the next higher number. If you don't have a chart of the character codes for your computer's screen POKEs, just use this BASIC program and jot down the number which is used to POKE the uppercase and lowercase "A."

10 FOR I = 0 TO 255: POKE (your computer's start-of-screen-RAM address), I: NEXT

With that knowledge, you can easily achieve the exact, predicted results for the examples in the book by substituting your computer's code.

A Sample Example

The following illustrations will show you how to go about entering and testing the practice examples in the book. At this point, of course, you won't recognize the ML instructions involved. The following samples are only intended to serve as a guide to working with the examples you will come upon later in the text.

After you've typed in and saved the SA, you can RUN it (it's a BASIC program which helps you to write ML). The first thing it does is ask you where you want to start your ML program — where you want it stored in memory. This is why you need to know of a safe place to put ML programs in your computer.

Of course you use line numbers when creating a BASIC program. Line numbers are not used in ML programming. Instead, you can think of memory addresses as "line numbers." So, if you are using the Atari, you will tell the SA that you are going to start your ML program at 0600. It will then print 0600 on the screen as if it were a line number, and you enter a ML program instruction, one per line, like this:

0600 PLA (This PLA is always required in the Atari when

you use USR.)

0601 LDY #00 (Stay in the hexadecimal mode for this example.)

I How To Use This Book

The SA will automatically print each "line number" address when you are programming. You just type in those now mysterious ML instructions. This program will put the letter "A" on screen. After you are finished with an example, you type the word "END" and the SA will tell you the starting address of your ML program in RAM memory.

The next step is to try out the ML program you've written to see .—*.

that it will work as planned. On the Atari, you could type:

X = USR(1536) (and hit RETURN) —

and this will "RUN" your ML program. You will have sent control of the computer from BASIC to your new ML program via the USR command. Be sure to remember that the Atari requires the PLA as the first instruction of each ML program that you plan to go to from BASIC by using the USR command. In all the examples in this book, type in a PLA as the first instruction before continuing with the rest of the example if you use an Atari,

Most personal computers use Microsoft BASIC, and the PLA is not necessary. Here's how the same example would look on a PET/CBM after you answered 0360 as the starting address when the SA asked for it:

(The word "END" isn't a 6502 ML instruction; it's a special signal to the SA to stop constructing a program and exit the SA program. Such special words are called pseudo-ops.)

Then you could test it in direct mode (just typing in the instruction onto the screen with no line number and not as part of a BASIC program) by typing:

SYS 864 and you should see the "A" on the screen. —»

Notice that the Atari and PET versions are similar, but not identical. All 6502 based computers will work with the same "instruction set" of commands which the 6502 chip can understand. The major differences occur when you need to specify something «

which is particular to the design of your computer brand. An example would be the location in memory of your computer's screen. The instructions at 0605 in the Atari example and 0364 in the PET example send the code for the letter "A" to the different screen locations for these two computer brands. Also, the letter "A" itself is signified by the number 41 on a PET and by the number 21 on an Atari. .^

But we'll go into these things further on. The main thing to learn here is how to use the SA to practice the examples. If you type in 0600 ^^

r

How To Use This Book I

**

as the starting address as in the Atari example above, the SA will print the number 0600 on screen and wait for you to type in a 6502 instruction (PLA in this case) and hit RETURN. Then it will print the next memory address just as if you were using an automatic line numbering routine when programming in BASIC. After you hit RETURN, the SA will print 0601 and wait for you to type in LDY #00.

r

The Fundamentals

The difficulty of learning ML has sometimes been exaggerated. There are some new rules to learn and some new habits to acquire. But most ML programmers would probably agree that ML is not inherently more difficult to understand than BASIC. More of a challenge to debug in many cases, but it's not worlds beyond BASIC in complexity. In fact, many of the first home computerists in the 1970's learned ML before they learned BASIC. This is because an average version of the BASIC language used in microcomputers takes up around 12,000 bytes of memory, and early personal computers (KIM, AIM, etc.) were severely restricted by containing only a small amount of available memory. These early machines were unable to offer BASIC, so everyone programmed in ML.

Interestingly, some of these pioneers reportedly found BASIC to be just as difficult to grasp as ML. In both cases, the problem seems to be that the rules of a new language simply are "obscure" until you know them. In general, though, learning either language probably requires roughly the same amount of effort.

The first thing to learn about ML is that it reflects the construction of computers. It most often uses a number system (hexadecimal) which is not based on ten. You will find a table in Appendix E which makes it easy to look up hex, decimal, or binary numbers.

We count by tens because it is a familiar (though arbitrary) grouping for us. Humans have ten fingers. If we had eleven fingers, the odds are that we would be counting by elevens.

What's a Natural Number?

Computers count in groups of twos. It is a fact of electronics that the easiest way to store and manipulate information is by ON-OFF states. A light bulb is either on or off. This is a two-group, it's binan/, and so the powers of two become the natural groupings for electronic counters. 2, 4, 8, 16, 32, 64, 128, 256. Finger counters (us) have been using tens so long that we have come to think of ten as natural, like thunder in April. Tens isn't natural at all. What's more, twos is a more efficient way to count.

To see how the powers of two relate to computers, we can run a short BASIC program which will give us some of these powers. Powers of a number are the number multiplied by itself. Two to the

2 The Fundamentals

power of two (2 2 ) means 2 times 2 (4). Two to the power of three (2 3 ) means 2 times 2 times 2 (8).

10 FOR 1 = 0 to 16 20 PRINT 2 A I 30 NEXT I

ML programming can be done in decimal (based on ten-groupings), but usually is not. Most ML programming involves hex numbers. This means groups of 16 rather than 10.

Why not just program in the familiar decimal numbers (as BASIC does)? Because 16 is one of the powers of two. It is a convenient grouping (or base) for ML because it organizes numbers the way the computer does. For example, all computers work, at the most elementary level, with bits. A bit is the smallest piece of information possible: something is either on or off, yes or no, plus or minus, true or false. This two-state condition (binary) can be remembered by a computer's smallest single memory cell. This single cell is called a bit. The computer can turn each bit "on" or "off" as if it were a light bulb or a flag raised or lowered.

It's interesting that the word bit is frequently explained as a shortening of the phrase Binary digiT. In fact, the word bit goes back several centuries. There was a coin which was soft enough to be cut with a knife into eight pieces. Hence, pieces of eight. A single piece of this coin was called a bit and, as with computer memories, it meant that you couldn't slice it any further. We still use the word bit today as in the phrase two bits, meaning 25 cents.

Whatever it's called, the bit is a small, essential aspect of computing. Imagine that we wanted to remember the result of a subtraction. When two numbers are subtracted, they are actually being compared with each other. The result of the subtraction tells us which number is the larger or if they are equal. ML has an instruction, like a command in BASIC, which compares two numbers by subtraction. It is called CMP (for compare). This instruction sets "flags" in the CPU (Central Processing Unit), and one of the flags always remembers whether or not the result of the most recent action taken by the computer was a zero. We'll go into this again later. What we need to realize now is that each flag — like the flag on a mailbox — has two possible conditions: up or down. In other words, this information (zero result or not-zero) is binary and can be stored within a single bit. Each of the flags is a bit. Together they make up one byte. That byte is called the Status Register.

Byte Assignments

Our computers group these bits into units of eight, called bytes. This relationship between bits and bytes is easy to remember if you think of a bit as one of the "pieces of eight." Eight is a power of two also

The Fundamentals 2

(two, to the third power). Eight is a convenient number of bits to work with as a group because we can count from zero to 255 using only eight bits.

This gives us enough room to assign all 26 letters of the alphabet (and the uppercase letters and punctuation marks, etc.) so that each printed character will have its particular number. The letter "A" (uppercase) has been assigned the number 65. "B" is 66, and so on. Throughout this book, examples will follow the ASCII code for letters of the alphabet. Most microcomputers, however, do not adhere strictly to the ASCII code. If you get unexpected results when trying the example programs, check your BASIC manual to see if POKEing to the screen RAM uses a different code than ASCII. If that is the case, substitute your screen POKE code for the values given in the examples.

These "assignments" form the convention called the ASCII code by which computers worldwide can communicate with each other. Text can be sent via modems and telephone lines and arrive meaning the same thing to a different computer. It's important to visualize each byte, then, as being eight bits ganged together and able to represent 256 different things. As you might have guessed, 256 is a power of two also (two, to the power of eight).

So, these groupings of eight, these bytes, are a key aspect of computing. But we also want to simplify our counting from 0 to 255. We want the numbers to line up in a column on the screen or on paper. Obviously, the decimal number five takes up one space and the number 230 takes up three spaces.

Also, hex is easier to think about in terms of binary numbers — the on-off, single-bit way that the computer handles numbers:

2 The Fundamentals

~

~

See how hex $10 (hex numbers are usually preceded by a dollar sign to show that they are not decimal) looks like binary? If you split a hex number into two parts, 1 and 0, and the binary (it's an eight-bit group, a byte) into two parts, 0001 and 0000 — you can see the relationship. -^

The Rationale For Hex Numbers

ML programmers often handle numbers as hexadecimal digits, meaning groups of sixteen instead of ten. It is usually just called hex. You should read over the instructions to the Simple Assembler and remember that you can choose between working in hex or decimal with that assembler. You can know right from the start if you're working with hex or decimal, so the dollar sign isn't used with the Simple Assembler.

DECIMAL 0123456789 then you start over with 10

HEX 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E OF then you start over with 10

Program 2-1. Microsoft Hex-Decimal Converter.

1 HE$="0123456789ABCDEF"

2 PRINT"{CLEAR}{03 DOWNjPLEASE CHOOSE:

4 PRINT"{03 DOWN}{03 RIGHT}1-INPUT HEX &

GET DECIMAL BACK.

5 REM NEW LINE HERE

6 PRINT"{02 DOWN} 2-INPUT DECIMAL TO G

ET HEX BACK.

7 GETK:IFK=0THEN7 9 PRINT"{CLEAR}":ON KGOTO200,400

100 H$="":FORM=3TO0STEP--1:N%=DE/(16~M) : DE= DE-N%*16*M:H$=H$+MID$(HE$,N%+1,1) :NEXT

101 RETURN

102 D=0:Q=3:FORM=1TO4:FORW=0TO15:IFMID$(H? ,M,1)=MID$(HE$,W+l,1)THEN104

103 NEXTW

104 D1=W*(16*(Q)):D=D+D1:Q=Q-1:NEXTM

105 DE=INT(D)-.RETURN 200 INPUT"{02 DOWN}HEX";H$:GOSUB102:PRINTS

PC(11)"{UP}= {REV}"DE" {left} " 210 GOTO200 400 INPUT"{02 DOWN}DECIMAL";DE:GOSUB100:PR

r

r

The Fundamentals 2

INTSPC(14)"{UP}= {REV} "H$" " 410 GOTO400

Program 2-2. Atari Hex-Decimal Converter.

100 DIM H$(23),N$(9):OPEN#1,4,0, "K:"

130 GRAPHICS 0

140 PRINT" PLEASE CHOOSE:"

150 PRINT"1- INPUT HEX AND GET DECIMAL BAC

K." 160 PRINT"2- INPUT DECIMAL AND GET HEX BAC

K." 170 PRINT:PRINT"==>";:GET#1,K 180 IFK<49OR>50THEN170

190 PRINTCHR$(K):0NK-48 GOTO 300,400 300 H$="@ABCDEFGHII 111 I 1lJKLMNO" 310 PRINT"HEX";:INPUT N$:N=0 320 FORI=lTOLEN(N$) 330 N=N*16+ASC(H$(ASC(N$(i))-47))-64:NEXTI

340 PRINT"?";N$;"=";N:PRINT:PRINT:GOTO140

400 H$="0123456789ABCDEF"

410 PRINT"DECIMAL";:INPUTN:M=4096

420 PRINTN;"=$";

430 F0RI=1T04:J=INT(N/M)

440 PRINTH$(J+1,J+1);:N=N-M*J:M=M/16

450 NEXTI:PRINT:PRINT:GOTO140

The first thing to notice is that instead of the familiar decimal symbol 10, hex uses the letter "A" because this is where decimal numbers run out of symbols and start over again with a one and a zero. Zero always reappears at the start of each new grouping in any number system: 0, 10, 20, etc. The same thing happens with the groupings in hex: 0, 10, 20, 30, etc. The difference is that, in hex, the 1 in the "tens" column equals a decimal 16. The second column is nowa "sixteens"column. 11 means 17, and 21 means 33 (2 times 16 plus one). Learning hex is probably the single biggest hurdle to get over when getting to know ML. Don't be discouraged if it's not immediately clear what's going on. (It probably never will be totally clear — it is, after all, unnatural.) You might want to practice the

2 The Fundamentals

~

~

exercises at the end of this chapter. As you work with ML, hex will gradually seem less and less alien.

To figure out a hex number, multiply the second column by 16 and add the other number to it. So, 1A would be one times 16 plus 10 (recall that A stands for ten).

Hex does seem impossibly confusing when you come upon it for the first time. It will never become second nature, but it should be at least generally understood. What is more, you can program in ML quite easily by looking up the hex numbers in the table at the end of this book. You need not memorize them beyond learning to count from 1 to 16 — learning the symbols. Be able to count from 00 up to OF. (By convention, even the smallest hex number is listed as two digits as in 03 or OB. The other distinguishing characteristic is that dollar sign that is usually placed in front of them: $05 or $0E.) It is enough to know what they look like and be able to find them when you need them.

The First 255 -~

Also, most ML programming involves working with hex numbers only between 0 and 255. This is because a single byte (eight bits) can hold no number larger than 255. Manipulating numbers larger than 255 is of no real importance in ML programming until you are ready to work with more advanced ML programs. This comes later in the book. For example, all 6502 ML instructions are coded into one byte, all the "flags" are held in one byte, and many "addressing modes" use one byte to hold their argument.

To learn all we need know about hex for now, we can try some problems and look at some ML code to see how hex is used in the majority of ML work. But first, let's take an imaginary flight over computer memory. Let's get a visual sense of what bits and bytes and the inner workings of the computer's RAM look like.

The City Of Bytes

Imagine a city with a single long row of houses. It's night. Each house has a peculiar Christmas display: on the roof is a line of eight lights. The houses represent bytes; each light is a single bit. (See Figure 2-1.) If we fly over the city of bytes, at first we see only darkness. Each byte contains nothing (zero), so all eight of its bulbs are off. (On the horizon we can see a glow, however, because the computer has memory up there, called ROM memory, which is very active and contains built-in programs.) But we are down in RAM, our free user-memory, and there are no programs now in RAM, so every house is dark. Let's observe what happens to an individual byte when different numbers are stored there; we can randomly choose byte 1504. We hover over that house to see what information is "contained" in the light display. (See Figure 2-2.)

12

~ ~

The Fundamentals 2

f-

r

V-\V\lv

i\. 'go

rh^4

tf *!>?*

ii§

'■so c

2 The Fundamentals

Figure 2-2.

~

~

Like all the rest, this byte is dark. Each bulb is off. Observing this, we know that the byte here is "holding" or representing a zero. If someone at the computer types in POKE 1504,1 — suddenly the rightmost light bulb goes on and the byte holds a one instead of a

zero:

Figure 2-3.

^-4-M-«4L

W&Ut*A**t^*^r^, ^^A.{.U^ *^^ SZ3 —:

This rightmost bulb is in the l's column (just as it would be in our usual way of counting by tens, our familiar decimal system). But the next bulb is in a 2's column, so POKE 1504, 2 would be:

Figure 2-4.

I ''

~ ~

~ ~

And three would be one and two: Figure 2-5.

n"C0"'

In this way — by checking which bits are turned on and then adding them together — the computer can look at a byte and know what number is there. Each light bulb, each bit, is in its own special

The Fundamentals 2

position in the row of eight and has a value twice the value of the one just before it:

Figure 2-6.

fr fl "" J ^fiHr ' -1-'-" J ^ , -'^-*J -*-*^J -*JI —' L T

I2B* 64* ^ 16* g'< 4* # '*

Eight bits together make a byte. A byte can "hold" a number from 0 through 255 decimal. We can think of bytes, though, in any number system we wish — in hex, decimal, or binary. The computer uses binary, so it's useful to be able to visualize it. Hex has its uses in ML programming. And decimal is familiar. But a number is still a number, no matter what we call it. After all, five trees are going to be five trees whether you symbolize them by 5, $05, or 00000101.

A Binary Quiz

BASIC doesn't understand numbers expressed in hex or binary. The Simple Assembler contains two subroutines to translate a number from decimal to hex or vice versa. You might want to take a look at how it's done as a way of getting a better feel for these different numbers systems. The subroutines are located at lines 4000 and 5000. Binary, for humans, is very visual. It forms patterns out of zeros and ones. The following program will let you quiz yourself on these patterns.

Here is a game, for all computers, which will show you a byte as it looks in binary. You then try to give the number in decimal:

Program 2-3. Binary Quiz for Ail Computers.

100 REM BINARY QUIZ

110 C1=20:C0=111: REM FOR ATARI ONLY 120 C1=88:C0=79: REM FOR APPLE ONLY 130 C1=209:C0=215:REM FOR COMMODORE ONLY 140 X=INT(256*RND(1)): D = X: P = 128 150 PRINT CHR$(125);: REM ATARI ONLY 160 PRINT CHR$(147);: REM COMMODORE ONLY 170 HOME: REM APPLE ONLY 180 FOR I = 1 TO 8

190 IF INT(D/P) = 1 THEN PRINT CHR$(C1);: D = D-P: GOTO 210

2 The Fundamentals

200 PRINT CHR$(C0); —

210 P = P/2: NEXT I: PRINT

220 PRINT " WHAT IS THIS IN DECIMAL?"

230 INPUT Q: IF Q = X THEN PRINT

"CORRECT": GOTO 250 240 PRINT "SORRY, IT WAS";X 250 FOR T = 1 TO 1000: NEXT T 260 GOTO 140 _

This program will print out the entire table of binary numbers from 0 to 255: ^

Program 2-4.

100 REM COMPLETE BINARY TABLE

110 L=8:B=2:C=1

120 FORX=0TO255:PRINTX;

140 IFXAND1THENK(C)=49:GOTO160

150 K(C)=48 -

160 C=C+1:IFBANDXTHENK(C)=49:GOTO180

170 K(C)=48

180 B=B*2:IFO8THEN200 —.

190 GOTO160

200 FORI=0TO7:PRINTSTR$(K(L)-48);:L=L-1

210 NEXT ^

220 C=0:PRINT

260 L=8:B=2:C=1:NEXTX -

Examples And Practice

Here are several ordinary decimal numbers. Try to work out the hex equivalent:

The Fundamentals 2

9. 255 10. 254

We are not making an issue of learning hex or binary. If you needed to look up the answers in the table at the end of the book, fine. As you work with ML, you will familiarize yourself with some of the common hex numbers. You can write most ML programs without needing to worry about binary. For now, we only want to be able to recognize what hex is. There are even some pocket "programmer" calculators which change decimal to hex for you and vice versa. Another way to go about "hexing" is to use a BASIC program which does the translation. A problem with BASIC is that you will be working in ML and your computer will be tied up. It is often inconvenient to crank up a BASIC program each time you need to work out a hex number. However, the Simple Assembler will do the translations for you any time you need them.

One other reason that we are not stressing hex too much is that ML is generally not programmed without the help of an assembler. The Simple Assembler provided in this book will handle most of your input automatically. It allows you to choose whether you prefer to program in hex or decimal. You make this decision by changing line 10 before starting to assemble. After that, you can put in hex or decimal without worrying that there will be any confusion about your intentions.

This little BASIC program is good for practicing hex, but also shows how to change a small part and make it work for two-byte hex numbers. It will take decimal in and give back the correct hex. It is designed for Microsoft BASIC computers, so it will not work on the Atari.

10 H$= "0123456789 ABCDEF"

20 PRINT "ENTER DECIMAL NUMBER";:INPUTX

30 IF X > 255 GOTO 20: REM NO NUMBERS BIGGER

THAN 255 ALLOWED

40 FORI = lTO0STEP-l

50 N% = X/(16'M): X = X-N% * 16 4T

60 HE$ = HE$ + MID$(H$,N% + 1,1)

70 NEXT

80 PRINT HE$

90 GOTO 20

For larger hex numbers (up to two, to the power of 16 — which is 65536), we can just change the above program. Eliminate line 30 and change line 40 to: FOR 1 = 3 TO 0 STEP -1. This will give us four-place hex numbers (used only as addresses) but which will also become recognizable after some ML practice.

2 The Fundamentals

65535 is an interesting number because it represents the limit of our computers' memories. In special cases, with additional hardware, memory can be expanded beyond this. But this is the normal upper limit because the 6502 chip is designed to be able to address (put bytes in or take them out of memory cells) up to $FFFF.

Ganging Two Bytes Together To Form An Address

The 6502 often sets up an address by attaching two bytes together and looking at them as if they formed a unit. An address is most commonly a two-byte number. $FFFF (65535) is the largest number that two bytes can represent, and $FF (255) is the most that one byte can hold. Three-byte addressing is not possible for the 6502 chip. "Machine language" means programming which is understood directly by the 6502 chip itself. There are other CPU (Central Processing Unit) chips, but the 6502 is the CPU for VIC, Apple, 64, PET/CBM, and Atari. It's the one covered in this book.

Reading A Machine Language Program

Before getting into an in-depth look at "monitors," those bridges between you and your machine's language — we should first learn how to read ML program listings. You've probably seen them often enough in magazines. Usually, these commented, labeled, but very strange-looking programs are called source code. They can be examined and translated by an assembler program into an ML program. When you have an assembler program run through source code, it looks at the key words and numbers and then POKEs a series of numbers into the computer. This series is then called the object code.

Source programs contain a great deal of information which is of interest to the programmer. The computer only needs a list of numbers which it can execute in order. But for most people, lists of numbers are only slightly more understandable than Morse code. The solution is to replace numbers with words. The primary job of an assembler is to recognize an ML instruction. These instructions are called mnemonics, which means "memory aids." They are like BASIC words, except that they are always three letters long.

If you type the mnemonic ]MP, the assembler POKEs a 76 into RAM memory. It's easier to remember JMP than 76. The 76 is the number that clues the computer that it's supposed to perform a JMP. The 76 is called an opcode, for "operation code." The three-letter words we use in ML programming, the mnemonics, were designed to sound like what they do. JMP does a JUMP (like a GOTO in BASIC). Some deluxe assemblers also let you use labels instead of numbers — as long as you define your labels at the start of the source code. These labels can refer to individual memory locations, special values like the score in a game, or entire subroutines.

The Fundamentals 2

Four Ways To List A Program

Labeled, commented source code listings are the most elaborate kind of ML program representation. There are also three other kinds of ML listings. We can use a simple addition example program to show how it looks when represented in each of the four ML program listing styles. The first two styles are simply ways for you to type a program into the computer. The last two styles show you what to type in, but also illustrate what is going on in the ML program. First let's look at the most elementary kind of ML found in books and magazines: the BASIC loader.

Program 2-6. BASIC Loader.

10 FOR ADDRESS = 4096 TO 4103

20 READ BYTE

30 POKE ADDRESS, BYTE

40 NEXT ADDRESS

50 DATA 169,2,105,5,141,160,15,96

This is a series of decimal numbers in DATA statements which is POKEd into memory starting at decimal address 4096. When these numbers arrive in RAM, they form a little routine which puts the number 2 into the accumulator — a special location in the computer that we'll get to later — and then adds 5. The result of the addition is then moved from the accumulator to decimal address 4000. If you try this program out, you can SYS 4096 to execute ML program and then ? PEEK (4000) and you'll see the answer: seven. BASIC loaders are convenient because the user doesn't need to know how to enter ML programs. The loader POKEs them in and all the user has to do is SYS or USR or CALL to the right address and the ML transfers control back to BASIC when its job is done.

Getting even closer to the machine level is the second way you might see ML printed in books or magazines: the hex dump. On some computers (PET, Apple) there is a special "monitor" program in ROM which lets you list memory addresses and their contents as hex numbers. More than that, you can usually type over the existing values on the screen and change them. That's what a hex dump listing is for. You copy it into your computer's RAM by using your computer's monitor. How you enter the monitor mode differs on each computer and we'll get to monitors in the next chapter.

The hex dump, like the BASIC loader, tells you nothing about the functions or strategies employed within an ML program. Here's the hex dump version of the same 2 + 5 addition program:

2 The Fundamentals

Program 2-7.

1000 A9 02 69 05 8D A0 OF 60

The third type of listing is called a disassembly. It's the opposite of an assembly because another program called a disassembler takes machine language (the series of numbers, the opcodes in the computer's memory) and translates it into the words, the mnemonics, which ML programmers use. The instruction you use when you want to load the accumulator is called LDA, and you can store what's in the accumulator by using an STA. We'll get to them later. In this version of our example addition routine, it's a bit clearer what's going on and how the program works. Notice that on the left we have the hex numbers and, on the right, the translation into ML instructions. ADC means ADd with Carry and RTS means ReTurn from Subroutine.

The Deluxe Version

Finally we come to that full, luxurious, commented, labeled, deluxe

source code we spoke of earlier. It includes the hex dump and the

disassembly, but it also has labels and comments and line numbers

added, to further clarify the purposes of things. Note that the

numbers are all in hex. On the far left are the memory addresses

where this routine is located. Next to them are the hex numbers of the

instructions. (So far, it resembles the traditional hex dump.) Then

come line numbers which can be used the way BASIC line numbers

are: deleted, inserted, and so on. Next are the disassembled

translations of the hex, but you can replace numbers with —

labels (see Program 2-10). You could still use numbers, but if you've

defined the labels early on, they can serve as a useful reminder of

what the numbers represent. Last, following the semicolons, are the

comments. They are the same as REM statements. (See Programs 2-9

and 2-10.)

~

:>o

The Fundamentals 2

.6

S o U

O

u

3

o

DC

C

-Q

£

in i/>

<

I

M

So

o

k

0-

0J Xi

.s s

z

at xJ

>> en O JU

LTI o o o o o O O H CN ro **

OHrlrlrlH

o o o o o o

fa o

CM IT) O

o o <C

men Qo

<C ^D 00 IT)

I I I I

o cn ■>* r~

o o o o

o o o o

£

in i/i

<

_C0

"35

-O

(N

E

M

o

L,

a.

o

s

E-i

fa

o

OS < fa OS

a o

D Eh rt! CO

Eh Si < Eh CO

mooooooooo

OOOOOMr-HrHrHnH OOOOOOOOOO

o

< fa o II

fa

U

o

Eh CO

O

o o

Eh

BS

«c

Eh CO

2 The Fundamentals

Program 2-11. The Source Code By Itself.

; START ADDR $1000 (4096) ;DEFINE LABLE "TWO" AS 2, ;DEFINE "ADDER" AS A 5. ;DEFINE STORAGE ADDR.

LOAD A WITH 2

ADD 5

STORE AT DECIMAL 4000

RETURN

END OF ASSEMBLY

Program 2-11 illustrates just the source code part. The object code has not yet been generated from this source code. The code has not been assembled yet. You can save or load source code via an assembler in the same way that you can save or load programs via BASIC. When 2-11 is in the computer, you could type "ASSEMBLE" and the assembler would translate the instructions, print them on the screen, and POKE them into memory.

The Simple Assembler operates differently. It translates, prints, and POKEs after you hit RETURN on each line of code. You can save and load the object, but not the source code.

Before we get into the heart of ML programming, a study of the instruction mnemonics and the various ways of moving information around (called addressing), we should look at a major ML programming aid: the monitor. It deserves its own chapter.

~ ~

~ ~ ~

~

ANSWERS to quiz: 0A, OF, 05, 10, 11, 20, 80, 81, FF, FE

~ ~

The Monitor

A monitor is a program which allows you to work directly with your computer's memory cells. When the computer "falls below" BASIC into the monitor mode, BASIC is no longer active. If you type RUN, it will not execute anything. BASIC commands are not recognized. The computer waits, as usual, for you to type in some instructions. There are only a few instructions to give to a monitor. When you're working with it, you're pretty close to talking directly to the machine in machine language.

The PET and Apple II have monitors in ROM. This means that you do not need to load the monitor program into the computer; it's always available to you. (PETs with Original ROM sets do not have a ROM monitor; you must load in the monitor from a tape or disk.) Atari and VIC computers have a monitor as part of a larger "Assembler Editor" plug-in cartridge. The monitor on the Atari cartridge is called the "Debugger." That's a good name for it: debugging is the main purpose of a monitor. You use it to check your ML code, to find errors.

The various computers have different sets of instructions which their monitors recognize. However, the main functions are similar, so it is worth reading through all of the following descriptions, even if the discussion is not specifically about the monitor for your computer. On the PET/CBM, VIC, and 64 you can add many of these functions with a monitor "extension" program called Micromon or Supermon (about which more later). These monitors are included in Appendix F. The monitors on the Apple II and available in the Atari Assembler Editor Cartridge do not need "extending." They contain most of the significant features required of a monitor. However, the special extensions in Appendix F for the Commodore computers add considerably to the Commodore ML programmer's repertoire.

The Apple II

You enter the Apple monitor by typing CALL -151. You will see the "*" monitor prompt and the cursor immediately after it. Here are the monitor instructions:

1. Typing an address (in hex) will show you the number contained in that memory cell. *2000 (hit RETURN) will show 2000 — FF (if, in fact, 255 decimal ($FF, hex) is in that location).

2. You can examine a larger amount of memory in hex (this is

23

3 The Monitor

~ ~

called a memory dump or a hex dump). The Apple monitor remembers the address of the last number displayed. This can be used as a starting address for the dump. If you type the instruction in number one above, and then type *.2010, you will see a dump of memory between 2001 and 2010. The only difference between this and instruction one is the period (.) before the requested address.

3. You can directly cause a dump by putting the period between two addresses: *2000.2010 combines the actions of instructions one

and two above. ,_ k

4. Hitting RETURN will continue a dump, one line at a time.

5. The last displayed memory location can be changed by using the colon (:). This is the equivalent of BASIC'S POKE. If *2000 results in FF on the screen, you can change this FF to zero by typing *:00. To see the change, type *2000 again. Or you could type *2000:00 and make the change directly.

The Apple II reference manual contains excellent descriptions of the monitor instructions. We will list the rest of them only briefly here:

6. Change a series of locations at once: *2000: 00 69 15 65 12.

7. Move (transfer) a section of memory: *4000 < 2000.2010M will copy what's between 2000 and 2010 up to address 4000. (All these addresses are hex.)

8. Compare two sections of memory: *4000 < 2000.2010V. This looks like Move, but its job is to see if there are any differences between the numbers in the memory cells from 2000-2010 and those from 4000-4010. If differences are found, the address where the difference occurs appears on screen. If the two memory ranges are identical, nothing is printed on the screen. -^

9. Saving (writing) a section of ML to tape: *2000.2010W. This is how you would save an ML program. You specify the addresses of the start and end of your program.

10. Loading (reading) a section of memory (or an ML program) back into the computer from tape: *2000.2010R will put the bytes saved, in instruction nine, above, back where they were when you saved them.

An interesting additional feature is that you could send the bytes —.

to any address in the computer. To put them at 4000, you would just type *4000.4010R. This gives you another way to relocate subroutines or entire ML programs (in addition to the Move instruction, number seven above). If you move an ML program to reside at a different address from the one it was originally intended during assembly, any JMP or JSR (Jump To Subroutine, like BASIC'S GOSUB) instructions which point to within your program must be adjusted to point to the new addresses. If your subroutine contained an instruction such as 2000 JSR 2005, and you loaded at 4000, it would still say 4000 JSR 2005. You would have to change it to read 4000 JSR 4005. All the BNE,

The Monitor 3

BPL, BEQ, branching instructions, though, will make the move without damage. They are relative addresses (as opposed to the absolute addressing of JSR 2005). They will not need any adjusting. We'll go into this in detail later.

11. Run (go): *2000G will start executing the ML program which begins at address 2000. There had better be a program there or the machine is likely to lock up, performing some nonsense, an endless loop, until you turn off the power or press a RESET key. The program or subroutine will finish and return control of the computer to the monitor when it encounters an RTS. This is like BASIC'S SYS command, except the computer returns to the monitor mode.

12. Disassemble (list): *2000L will list 20 lines of ML on the screen. It will contain three fields (a field is a "zone" of information). The first field will contain the address of an instruction (in hex). The address field is somewhat comparable to BASIC'S line numbers. It defines the order in which instructions will normally be carried out.

Here's a brief review of disassembly listings. The second field shows the hex numbers for the instruction, and the third field is where a disassembly differs from a "memory" or "hex" dump (see numbers one and two, above). This third field translates the hex numbers of the second field back into a mnemonic and its argument. Here's an example of a disassembly:

2000 A9 41 LDA #$41

2002 8D 23 32 STA $3223

2005 A4 99 LDY $99

Recall that a dollar sign ($) shows that a number is in hexadecimal. The pound sign (#) means "immediate" addressing (put the number itself into the A register at 2000 above). Confusing these two symbols is a major source of errors for beginning ML programmers. You should pay careful attention to the distinction between LDA #$41 and LDA $41. The second instruction (without the pound sign) means to load A with whatever number is found in address $41 hex. LDA #$41 means put the actual number 41 itself into the accumulator. If you are debugging a routine, check to see that you've got these two types of numbers straight, that you've loaded from addresses where you meant to (and, vice versa, you've loaded immediately where you intended).

13. Mini-assembler. This is an assembler program, though it is not part of the monitor ROM. It is in the Integer BASIC ROM, so systems using firmware Applesoft II cannot use it although the Apple II Plus can, in the INT mode. Like the Simple Assembler, this miniassembler cannot use labels or calculate forward branches. (The Simple Assembler can be used for forward branches, however, as we'll see later.) You enter the Apple mini-assembler by typing the

3 The Monitor

address, mnemonic, and argument of your first instruction. The ! is printed by the computer:

!2000:LDA #15

This will be disassembled, and then you type in the next line, using spaces between each field:

! LDY#01 -^

14. Step and Trace. These are very useful ways to isolate and fix errors. Remember that ML does not have much in the way of error messages. In fact, unless you are using a very complex assembler program, the only error that an assembler can usually detect is an impossible mnemonic. If you mistyped LDA as LDDA, your assembler would print ??? or, in the Apple, sound a beep and put a circumflex (a) near the error. In any case, you are not going to get elaborate SYNTAX ERROR messages. The Simple Assembler will type the word ERROR on the screen. Try it.

Weil examine step and trace debugging methods under numbers 10 and 11 of the discussion of the Atari cartridge below. The Atari Assembler Cartridge and the Commodore Monitor Extension programs both allow step and trace, too.

15. Changing registers. *(CONTROL) E will display the contents of the Accumulator, the X and Y registers, the status register (P) and the stack pointer (S). You can then change the contents of these registers by typing them in on screen, following a colon. Note that to change the Y register, you must type in the A and X registers as well:

* (CONTROL) E

You'll see: A = 01 X = 05 Y = FF P = 30 S=FE (whatever's in the registers at the time).

To change the Y register to 00, you type in the A, X, and then the new version of Y:

*:01 05 00 (and hit RETURN)

16. Going back to BASIC. You can use * (CONTROL) B to go to BASIC (but it will wipe out any BASIC program that might have been there). Or you can use * (CONTROL) C to go back to BASIC, non-destructively.

The Atari Monitor

To enter the monitor on the Atari, you put the assembler cartridge into the left slot. The Atari does not have a monitor in ROM; you need the cartridge. As mentioned at the start of this chapter, the monitor mode in Atari is called DEBUG and is a part of the larger program within the assembler cartridge. There are three parts (or

26

~ ~

r

The Monitor 3

modes) within the cartridge: EDIT, ASM (assembler), and DEBUG. Before looking at the commands available in the DEBUG mode, let's briefly explore how an ML program is created using the EDIT mode followed by ASM. The cartridge provides the Atari with a more advanced assembler than the Simple Assembler or the miniassemblers available within the Apple II monitor or the Commodore monitor extension programs. The cartridge allows labels, comments, and line numbers.

Until now, we've discussed ML programming which uses three fields (zones). Here's an example program which shows these three simple fields. We will print ten "A's" on the screen (the numbers are decimal):

When you are in Atari's EDIT mode, you construct a program somewhat differently than you do with the Simple Assembler (or with mini-assemblers). Here's the same program using the Atari's additional fields:

Line# 100 110 120 130 140

Label START

LOOP

Instruction LDY LDA STA DEY BNE

Argument #10

#33 (88),Y

Comments

Set up counter for loop

"A"inATASCII

LOOP Loop until zero

Notice that labels allow us to use the word LOOP instead of the specific address we want to loop back to. In addition to all this, there are pseudo-ops which are instructions to the assembler to perform some task. A pseudo-op does not become part of the ML program (it's not a 6502 instruction), but it affects the assembly process in

3 The Monitor

some way. We would need two pseudo-ops in the above program to allow it to be assembled properly. Add these lines:

10 * = $0600 (tells the assembler that this program should be assembled starting at address $0600. The $ means hexadecimal.) 160 .END (tells the assembler that it should stop assembling here.)

The example above with line numbers and labels is called source code because it is the source from which the assembler gets its information when it assembles object code (object code is an actual ML program which could be run, or executed). You cannot run the program above as is. It must first be assembled into 6502 ML. For one thing, the label LOOP has to be replaced with the correct branch back to line 120. Source code does not put bytes into memory as you write it (as a more elementary assembler like the Simple Assembler does).

More Than A Monitor

To make this into object code which you can actually execute, you type ASM (for assemble), and the computer will put the program together and POKE the bytes into memory, showing you on screen what it looks like.

To test the program, type BUG to enter the DEBUG mode, clear the screen, and RUN it by typing G600 (for GO $0600). You'll see AAAAAAAAAA on screen. It works!

All this isn't, strictly speaking, a monitor. It's a full assembler. The part of the assembler cartridge program which is equivalent to the monitor programs on Apple II and PET is the DEBUG mode. There are a number of commands in DEBUG with which you can examine, test, and correct ML code. As on the other computers, the DEBUG (monitor) mode allows you to work closely with single bytes at a time, to see the registers, to trace program flow. All numbers you see on screen (or use to enter into the computer) are in hex. You enter the DEBUG mode by typing BUG when the Assembler Cartridge is in the Atari. (To go back to EDIT mode, type X.) Here are the commands of DEBUG:

1. Display the registers: type DR (RETURN) and you will see whatever is in the various registers.

A = 01 X = 05 Y = 0F P = 30 S = FE (P is the status register and S is the stack pointer.)

2. Change the registers: type CR < 6,2 (RETURN) and you will have put a six into the accumulator and a two into the X register. To put a five into the status register, you must show how far to go by using commas: CR<,,,5 would do it. CR<5 would put five into the accumulator.

3. Dump memory: type D2000 and you will see the eight hex numbers which start at address 2000 in memory.

The Monitor 3

D2000

2000 FF 02 60 20 FF D2 00 00

D2000,2020 (would dump out memory between these two addresses)

4. Change memory: type C2000 < 00,00 to put zeros into the first two bytes following address 2000.

5. Transfer (move) memory: type M1000< 2000,2010 and you will non-destructively copy what's between 2000-2010 down into 1000-1010.

6. Compare (verify) memory: type V1000< 2000,2010 and any mismatches will be printed out.

7. Disassemble (list): type L2000 and you will see 20 lines of instructions displayed, the mnemonics and their arguments.

8. Mini-assemble: the DEBUG mode allows you to enter mnemonics and arguments one at a time, but you cannot use labels. (The pseudo-ops BYTE, DBYTE, and WORD are available, though.) This is similar to the Simple Assembler and the mini-assemblers available to Apple II and PET monitor users.

You type 2000 < LDA $05 and the computer will show you the bytes as they assemble into this address. Subsequent instructions can be entered by simply using the less-than sign again: < INC $05. To return to the DEBUG mode, you can hit the RETURN key on a blank line.

9. Go (RUN a program): type G2000 and whatever program starts at address 2000 will run. Usually, you can stop the RUN by hitting the BREAK key. There are cases, though, (endless loops) which will require that you turn off the computer to regain control.

10. Trace: type T2000 and you will also RUN your program, but the registers, bytes of ML code, and the disassembled mnemonics and arguments are shown as each instruction is executed. This is especially useful since you can watch the changes taking place in the registers and discover errors. If you have an LDA $03 and you then expect to find the accumulator to have the number three in it — you'll notice that you made that very common mistake we talked about earlier. Following LDA $03, you will see that the accumulator has, perhaps, a ten in it instead of the three you thought you'd get. Why? Because you wanted to write LDA #03 (immediate). Instead, you mistakenly loaded A with the value in address three, whatever it is.

Seeing unexpected things like this happen during trace allows you to isolate and fix your errors. Trace will stop when it lands on a BRK instruction or when you press the BREAK key.

11. Step: type S2000 and you will "step" through your program at 2000, one instruction at a time. It will look like trace, but you move slowly and you control the rate. To see the following instruction, you type the S key again. Typing S over and over will bring you through

3 The Monitor

the program. —

12. Return to EDIT mode: type X.

PET, VIC, And Commodore 64 Monitors

The resident monitor on the PET/CBM computer is the simplest of monitors. You enter it from BASIC by typing SYS 4 when no program is RUNning. This lands on a BReaK instruction; address 4 always contains a zero which is the opcode for BRK. You are then in monitor mode. Original ROM PETs, the earliest models, do not have a monitor in ROM, but one is available on tape, called TIM. Everything is done with hexadecimal numbers.

There are only six monitor commands:

1. Go (RUN) : type G 2000 and the program starts at address 2000. It will continue until it lands on a BRK instruction. There is no key you can type to stop it.

2. LOAD (from tape or disk): type L "0:NAME",08 and a program called "name" on disk drive zero will be loaded at the address from which it was SAVEd. There is no provision to allow you to LOAD to a different address. L "NAME",01 will LOAD from tape.

3. SAVE (to a tape or disk): type S "0:NAME",08,2000,2009 and the bytes between hex 2000 and 2008 will be saved to disk drive zero

and called "name." Important note: you should always be aware that a *m

SAVE will not save the highest byte listed in your SAVE instruction. You

always specify one byte more than you want to save. In our example

here, we typed 2009 as our top address, but the monitor SAVEd only

up to 2008. S "NAME",01,2000,2009 will SAVE to tape. —

An interesting trick is to save the picture on your screen. Try this from the monitor (for a disk drive): S "0:SCREEN",08,8000,8400 (with a tape drive: S "SCREEN",01,8000,8400). Then, clear the screen and type: L "0:SCREEN",08 (tape: L "SCREEN",01). This illustrates that an ML SAVE or LOAD just takes bytes from within whatever range of memory you specify; it doesn't care what those bytes contain or if they make ML sense as a program.

4. See memory (memory dump): type M 2000 2009 and the bytes between these addresses will be displayed on screen. To change them, you use the PET cursor controls to move to one of these hex numbers and type over it. Hitting the RETURN key makes the change in the computer's memory (the same way you would change a line in BASIC).

Machine Language Registers —.

5. See the registers: type R and you will see something like this on screen (the particular numbers in each category will depend on what's going on in your computer whenever you type R):

PC IRQ SR AC XR YR SP

2000 E62E 30 00 05 FF FE

The Monitor 3

The PC is the program counter: above, it means that the next instruction the computer would perform is found at address 2000. If you typed G (for RUN), this is where it would start executing. The IRQ is the interrupt request. The SR is the status register (the condition of the flags). The AC is the accumulator, the XR and YR are the X and Y registers. The SP is the stack pointer. We'll get into all this later.

6. Exit to BASIC: type X.

That's it. Obviously, you will want to add trace, step, transfer, disassemble, and other useful monitor aids. Fortunately, they are available. Two programs, Supermon and Micromon, can be LOADed into your Commodore computer and will automatically attach themselves to your "resident" monitor. That is, when you're in the monitor mode, you can type additional monitor commands.

Both Micromon and Supermon are widely available through user groups (they are in the public domain, available to everyone for free). If there is no user group nearby, you can type them in yourself. Supermon appeared in COMPUTE! Magazine, December 1981, Issue #19, on page 134. Micromon appeared in COMPUTE!, January 1982, Issue #20, page 160. A Micromon for VIC can be found in COMPUTE!, November 1982. Because of their value, particularly when you are debugging or analyzing ML programs, you will want to add them to your program library. Several of these monitor extensions can be found in Appendix F.

Using The Monitors

You will make mistakes. Monitors are for checking and fixing ML programs. ML is an exacting programming process, and causing bugs is as unavoidable as mistyping when writing a letter. It will happen, be sure, and the only thing for it is to go back and try to locate and fix the slip-up. It is said that every Persian rug is made with a deliberate mistake somewhere in its pattern. The purpose of this is to show that only Allah is perfect. This isn't our motivation when causing bugs in an ML program, but we'll cause them nonetheless. The best you can do is try to get rid of them when they appear.

Probably the most effective tactic, especially when you are just starting out with ML, is to write very short sub-programs (subroutines). Because they are short, you can more easily check each one to make sure that it is functioning the way it should. Let's assume that you want to write an ML subroutine to ask a question on the screen. (This is often called a prompt since it prompts the user to do something.)

The message can be: "press any key." First, we'll have to store the message in a data table. We'll put it at hex $1500. That's as good a place as anywhere else. Remember that your computer may be using a different screen RAM POKE code to display these letters. POKE the

31

~

3 The Monitor

letter "A" into your screen RAM to see what number represents the start of your screen alphabet and use those numbers for any direct-to screen messages in this book.

ASCII ATAI

-^1W

150D 00

the signal that the message is finished. Atari must use something beside zero which is used to represent the space character.)

We'll put the subroutine at $1000, but be warned! This subroutine will not work as printed. There are two errors in this program. See if you can spot them:

1000 LDY #$00

1002 LDA $1500,Y 1005 CMP $00 1007 BNE $100A 1009 RTS

(is it the delimiter?)

(if not, continue on)

(it was zero, so quit and return to whatever JSRed, or called, this subroutine) 100A STA $8000,Y (for PET) 100D INY 100E JMP $1000 (always JMP back to $1000)

Make the following substitutions if you use one of these machines:

Atari: 1005 CMP $FF (That's hex for 255.)

Atari: 100A STA $88),Y Apple: 100A STA $0400,Y

Since we haven't yet gone into addressing or instructions much, this is like learning to swim by the throw-them-in-the-water method. See if you can make out some of the meanings of these instructions anyway.

~ ~

~ ~

The Monitor 3

r

T

This subroutine will not work. There are two errors and they are two of the most common bugs in ML programming. Unfortunately, they are not obvious bugs. An obvious bug would be mistyping: LDS when you mean LDA. That sort of bug would be caught by your assembler, and it would print an error message to let you know that no such instruction as LDS exists in 6502 ML.

The bugs in this routine are mistakes in logic. If you disassemble this, it will also look fine to the disassembler, and no error messages will be printed there either. But, it will not work the way you wanted it to. Before reading on, see if you can spot the two errors. Also see if you can figure out how this routine would execute its instructions. Where does the computer go after the first pass through this code? When and how does it finish the job?

Two Common Errors

A very common bug, perhaps the most common ML bug, is caused by accidentally using zero page addressing when you mean to use immediate addressing. We mentioned this before, but it is the cause of so much puzzlement to the beginning ML programmer that we'll go over it several times in this book. Zero page addressing looks very similar to immediate addressing. Zero page means that you are addressing one of the cells in the first 256 addresses. A page of memory is 256 bytes. The lowest page is called zero page and is the RAM cells from number zero through 255. Page one is from 256-511 (this is the location of the "stack" which we'll get to later). Addresses 512-767 are page three and so on up to the top memory, page 255.

Immediate addressing means that the number is right within the ML code, that it's the number which follows (which is the operand or the argument of) an instruction. LDA #13 is immediate. It puts the number 13 into the accumulator. LDA $13 is zero page and puts whatever number is in address 13 into the accumulator. It's easy and very common to mix up these two, so you might look at these instructions first when debugging a faulty program. See that all your zero page addressing is supposed to be zero page and that all your immediate addressing is supposed to be immediate.

In the prompt example above, the LDY #00 is correct — we do want to set the Y register counter to zero to begin printing the message. So we want an immediate, the actual number zero. Take a good look, however, at the instruction at location $1005. Here we are not asking the computer to compare the number in the accumulator to zero. Instead, we are asking the computer to compare it to whatever might be in address zero — with unpredictable results. To fix this bug, the instruction should be changed to the immediate addressing mode with CMP # 0.

The second bug is also a very common one. The subroutine, as written, can never leave itself. It is an endless loop. Loop structures

3 The Monitor

are usually preceded by a short initialization phase. The counters have to be set up before the loop can begin. Just as in BASIC, where FOR 1=1 TO 10 tells the loop to cycle ten times, in ML, we set the Y register to zero to let it act as our counter. It kills two birds with one stone in this subroutine. It is the offset (a pointer to the current position in a list or series) to load from the message in the data table and the offset to print to the screen. Without Y going up one (INY) each time through the loop, we would always print the first letter of the message, and always in the first position on the screen.

What's the problem? It's that JMP instruction at $100E. It sends us back to the LDY # 0 address at 1000. We should be looping back to address 1002. As things stand, the Y register will always be reset to zero, and there will never be any chance to pick up the delimiter and exit the subroutine. An endless cycle of loading the "P" and printing it will occur. Y will never get beyond zero because each loop jumps back to 1000 and puts a zero back into Y. To see this, here's the same bug in BASIC:

10 T = 5

20 T = T + 1

30 IF T = 10 THEN 50

40 GOTO 10

Tracking Them Down

The monitor will let you discover these and other errors. You can replace an instruction with zero (BRK) and see what happens when you execute the program up to the BRK. Better yet, you can single step through the program and see that, for example, you are not really computing CMP #00 where you thought you were. It would also be easy to see that the Y register is being reset to zero each time through the loop. You are expecting to use it as a counter and it's not cooperating, it's not counting up each time through the loop. These and other errors are, if not obvious, at least discoverable from the monitor.

Also, the disassembler function of the monitor will permit you to study the program and look, deliberately, for correct use of #00 and $00. Since that mix-up between immediate and zero page addressing is so common an error, always check for it first.

Programming Tools

The single most significant quality of monitors which contributes to easing the ML programmer's job is that monitors, like BASIC, are interactive. This means that you can make changes and test them right away, right then. In BASIC, you can find an error in line 120, make the correction, and RUN a test immediately.

It's not always that easy to locate and fix bugs in ML: there are few, if any, error messages, so finding the location of a bug can be

34

-v

The Monitor 3

difficult. But a monitor does allow interactivity: you make changes and test them on the spot. This is one of the drawbacks of complex assemblers, especially those which have several steps between the writing of the source code and the final assembly of executable object code (ML which can be executed).

These assemblers often require several steps between writing an ML program and being able to test it. There are linkers, relocatable loaders, double-pass assembly, etc. All of these functions make it easier to rearrange ML subroutines, put them anywhere in memory without modification, etc. They make ML more modular (composed of small, self-sufficient modules or subroutines), but they also make it less interactive. You cannot easily make a change and see the effects at once.

However, using a mini-assembler or the Simple Assembler, you are right near the monitor level and fixes can easily and quickly be tested. In other words, the simpler assemblers sometimes gain in efficiency what they lose in flexibility. The simpler assemblers support a style of programming which involves less pre-planning, less forethought, less abstract analysis. If something goes awry, you can just try something else until it all works.

Plan Ahead Or Plunge In?

Some find such trial and error programming uncomfortable, even disgraceful. The more complicated assemblers discourage interactivity and expect careful preliminary planning, flowcharts, even writing out the program ahead of time on paper and debugging it there. In one sense, these large assemblers are a holdover from the early years of computing when computer time was extremely expensive. There was a clear advantage to coming to the terminal as prepared as possible. Interactivity was costly. But, like the increasingly outdated advice urging programmers to worry about saving computer memory space, it seems that strategies designed to conserve computer time are anachronistic. You can spend all the time you want on your personal computer.

Complex assemblers tend to downgrade the importance of a monitor, to reduce its function in the assembly process. Some programmers who've worked on IBM computers for 20 years do not use the word monitor in the sense we are using it. To them, monitors are CRT screens. The deluxe assembler on the SuperPet, for example, does have a monitor, but it has no single-step function and has no provision for SAVEing an ML program to disk or tape from the monitor.

Whether or not you are satisfied with the interactive style of simple, mini-assemblers and their greater reliance on the monitor mode and on trial and error programming is your decision. If you want to graduate to the more complicated assemblers, to move closer

3 The Monitor

to high-level languages with labels and relocatable code, fine. The Atari assembler is fairly high-level already, but it does contain a full-featured monitor, the "debugger," as well. The choice is ultimately a matter of personal style.

Some programmers are uncomfortable unless they have a fairly complete plan before they even get to the computer keyboard. Others are quickly bored by elaborate flowcharting, "dry computing" on paper, and can't wait to get on the computer and see-what-happens-if. Perhaps a good analogy can be found in the various ways that people make telephone calls. When long-distance calls were extremely expensive, many people made lists of what they wanted to say and carefully planned the call before dialing. They would also watch the clock during the call. (Some still do this today.) As the costs of phoning came down, most people found that spontaneous conversation was more satisfying. It's up to you. ^^

Computer time, though, is now extremely cheap. If your computer uses 100 watts and your electric company charges five cents per KWH, leaving the computer on continuously costs about 12 cents a day. —.

Addressing

The 6502 processor is an electronic brain. It performs a variety of manipulations with numbers to allow us to write words, draw pictures, control outside machines such as tape recorders, calculate, and do many other things. Its manipulations were designed to be logical and fast. The computer has been designed to permit everything to be accomplished accurately and efficiently.

If you could peer down into the CPU (Central Processing Unit), the heart of the computer, you would see numbers being delivered and received from memory locations all over the computer. Sometimes the numbers arrive and are sent out, unchanged, to some other address. Other times they are compared, added, or otherwise modified, before being sent back to RAM or to a peripheral.

Writing an ML program can be compared to planning the activities of this message center. It can be illustrated by thinking of computer memory as a city of bytes and the CPU as the main post office. (See Figure 4-1.) The CPU does its job using several tools: three registers, a program counter, a stack pointer, and seven little one-bit flags contained in a byte called the Status Register. We will only concern ourselves with the "C" (carry) flag and the "Z" (it equals zero) flags. The rest of them are far less frequently needed for ML programming so we'll only describe them briefly. (See Figure 4-1.)

Most monitors, after you BRK (like BASIC'S STOP) out of a program, will display the present status of these tools. It looks something like this:

Program 4-1. Current Status Of The Registers.

PC IRQ SR AC XR YR SP 0005 E455 30 00 5E 04 F8

The PC is the Program Counter and it is two bytes long so it can refer to a location anywhere in memory. The IRQ is also two bytes and points to a ROM ML routine which handles interrupts, special-priority actions. A beginning ML programmer will not be working with interrupts and need not worry about the IRQ. You can also more or less let the computer handle the SP on the end. It's the stack

4 Addressing

Addressing 4

pointer. The SP keeps track of numbers, usually return-from-subroutine addresses which are kept together in a list called the stack. The computer will automatically handle the stack pointer for us. It will also deal with IRQ and the program counter. For example, each ML instruction we give it could be one, two, or three bytes long. TYA has no argument and is the instruction to transfer a number from the Y register to the accumulator. Since it has no argument, the PC can locate the next instruction to be carried out by raising itself by one. If the PC held $4000, it would hold $4001 after execution of a TYA. LDA #$01 is a two-byte instruction. It takes up two bytes in memory so the next instruction to be executed after LDA #$01 will be two bytes beyond it. In this case, the PC will raise itself from $4000 to $4002. But we can just let it work merrily away without worrying about it.

The Accumulator: The Busiest Register

The SR, AC, XR, and YR, however, are our business. They are all eight bits (one byte) in size. They are not located in memory proper. You can't PEEK them since they have no address like the rest of memory. They are zones of the CPU. The AC, or A register, but most often called the accumulator, is the busiest place in the computer. The great bulk of the mail comes to rest here, if only briefly, before being sent to another destination.

Any logical transformations (EOR, AND) or arithmetic operations leave their results in the accumulator. Most of the bytes streaming through the computer come through the accumulator. You can compare one byte against another using the accumulator. And nearly everything that happens which involves the accumulator will have an effect on the status register (SR, the flags).

The X and Y registers are similar to each other in that one of their main purposes is to assist the accumulator. They are used as addressing indexes. There are addressing modes that we'll get to in a minute which add an index value to another number. For example, LDA $4000, X will load into A the number found in address $4005, if the X register is currently holding a five. The address is the number plus the index value. If X has a six, then we load from $4006. Why not just LDA $4006? It is far easier to raise or lower an index inside a loop structure than it would be to write in each specific address literally.

A second major use of X and Y is in counting and looping. We'll go into this more in the chapter on the instruction set.

We'll also have some things to learn later about the SR, the Status Register which holds some flags showing current conditions. The SR can tell a program or the CPU if there has been a zero, a carry, or a negative number as the result of some operation, among other things. Knowing about carry and zero flags is especially significant in ML.

For now, the task at hand is to explore the various "classes" of mail delivery, the 6502 addressing modes.

-

-

-

4 Addressing

Aside from comparing things and so forth, the computer must have a logical way to pick up and send information. Rather like a postal service in a dream — everything should be picked up and delivered rapidly, and nothing should be lost, damaged, or delivered to the wrong address. —

The 6502 accomplishes its important function of getting and sending bytes (GET and PRINT would be examples of this same thing in BASIC) by using several "addressing modes." There are 13 different ways that a byte might be "mailed" either to or from the central processor.

When programming, in addition to picking an instruction (of the 56 available to you) to accomplish the job you are working on, you must also make one other decision. You must decide how you want to address the instruction — how, in other words, you want the mail sent or delivered. There is some room for maneuvering. You will probably not care if you accidentally choose a slower delivery method than you could have. Nevertheless, it is necessary to know what choices you have: most addressing modes are designed to aid a common programming activity.

Absolute And Zero

Let's picture a postman's dream city, a city so well planned from a

postal-delivery point of view that no byte is ever lost, damaged, or

sent to the wrong address. It's the City of Bytes we first toured in «^

Chapter 2. It has 65536 houses all lined up on one side of a street (a

long street). Each house is clearly labeled with its number, starting

with house zero and ending with house number 65535. When you

want to get a byte from, or send a byte to, a house (each house holds

one byte) — you must "address" the package. (See Figure 4-2.)

Here's an example of one mode of addressing. It's quite popular and could be thought of as "First Class." Called absolute addressing, it can send a number to, or receive one from, any house in the city. It's what we normally think of first when the idea of "addressing" something comes up. You just put the number on the package and -—

send it off. No indexing or special instructions. If it says 2500, then it means house 2500. —

1000 STA $2500

or 1000 LDA $2500

These two, STore A and LoaD A, STA and LDA, are the instructions which get a byte from, or send it to, the accumulator. The address, though, is found in the numbers following the instruction. The items following an instruction are called the instruction's argument. You could have written the address several ways. Writing it —.

as $2500 tells your assembler to get it from, or send it directly to, hex $2500. This kind of addressing uses just a simple $ and a four-digit .«.

Addressing 4

4 Addressing

number. You can send the byte sitting in the accumulator to anywhere in RAM memory by this method. Remember that the byte value, although sent to memory, also remains in the accumulator. It's more a copying than a literal sending.

To save time, if you are sending a byte down to address 0 through 255 (called the "zero page"), you can leave off the first two numbers: 1000 STA $07. This is only for the first 256 addresses, but they get more than their share of mail. Your machine's BASIC and operating system (OS) use much of zero page for their own •-»

temporary flags and other things. Zero page is a busy place, and there is not much room down there for you to store your own ML pointers or flags (not to mention whole routines).

Heavy Traffic In Zero Page

This second way to address, using only two hex digits, any hex

number between $00 and $FF or a decimal number between 0 and

255, is called, naturally enough, zero page addressing. It's pretty fast

mail service: the deliverer has to decide among only 256 instead of

65536 houses, and the computer is specially wired to service these

special addresses. Think of them as being close to the post office.

Things get in and out fast at zero page. This is why your BASIC and

operating system tend to use it so often. -^

These two addressing modes — absolute and zero page — are very common ones. In your programming, you will probably not use zero page as much as you might like. You will notice, on a map of your computer's flags and temporary storage areas, that zero page is heavily trafficked. You might cause a problem storing things in zero page in places used by the OS (operating system) or BASIC. Several maps of both zero page and BASIC in ROM can be found in Appendix B. a

You can find safe areas to store your own programs' pointers and flags in zero page. A buffer (temporary holding area) for the ~.

cassette drive or for BASIC'S floating point numbers might be used only during cassette loads and saves or during BASIC RUNs to calculate numbers. So, if your flags and pointers were stored in these addresses, things would be fine unless you involved cassette operations. In any case, zero page is a popular, busy neighborhood. Don't put any ML programs in there. Your main use of zero page is for the very efficient "indirect Y" addressing we'll get to in a minute. But you've always got to check your computer's memory map for zero page to make sure that you aren't using bytes which the computer itself uses.

By the way, don't locate your ML programs in page one (256-511 decimal) either. That's for the "stack," about which more later. We'll identify where you can safely store your ML programs in the various computers. It's always OK to use RAM as long as you keep BASIC

Addressing 4

programs from putting their variables on top of ML, and keep ML from writing over your BASIC assembler program (such as the Simple Assembler).

Immediate

Another very common addressing mode is called immediate addressing — it deals directly with a number. Instead of sending out for a number, we can just shove it immediately into the accumulator by putting it right in the place where other addressing modes have an address. Let's illustrate this:

1000 LDA $2500 (Absolute mode)

1000 LDA #$09 (Immediate mode)

The first example will load the accumulator with whatever number it finds at address $2500. In the second example, we simply wanted to put a 9 into the accumulator. We know that we want the number 9. So, instead of sending off for the 9, we just type a 9 in where we would normally type a memory address. And we tack on a # symbol to show that the 9 is the number we're after. Without the #, the computer will load the accumulator with whatever it finds at address number 9 (LDA $09). That would be zero page addressing, instead of immediate addressing.

In any case, immediate addressing is very frequently used, since you often know already what number you are after and do not need to send for it at all. So, you just put it right in with a #. This is similar to BASIC where you define a variable (10 VARIABLE =9). In this case, we have a variable being given a known value. LDA #9 is the same idea. In other words, immediate addressing is used when you know what number you want to deal with; you're not sending off for it. It's put right into the ML code as a number, not as an address.

To illustrate immediate and absolute addressing modes working together, let's imagine that we want to copy a 15 into address $4000. (See Program 4-2.)

Implied

Here's an easy one. You don't use any address or argument with this one.

This is among the more obvious modes. It's called implied, since the mnemonic itself implies what is being sent where: TXA means transfer X register's contents to the Accumulator. Implied addressing means that you do not put an address after the instruction (mnemonic) the way you would with most other forms of addressing.

It's like a self-addressed, stamped envelope. TYA and others are similar short-haul moves from one register to another. Included in this implied group are the SEC, CLC, SED, CLD instructions as well. They merely clear or set the flags in the status register, letting you

4 Addressing

~

c

<

w

c

1

3

c

IN

E

w> o

c

a.

ooooooooooo

iHtNmi'invoi--coCTiOrH

OOOOOOOOOtHtH

ooooooooooo

Addressing 4

and the computer keep track of whether an action resulted in a zero, if a "carry" has occurred during addition or subtraction, etc.

Also "implied" are such instructions as RTS (ReTurn from Subroutine), BRK (BReaK), PLP, PHP, PLA, PHA (which "push" or "pull" the processor status register or accumulator onto or off the stack). Such actions, and increasing by one (incrementing) the X or Y register's number (INX, INY) or decreasing it (DEX, DEY), are also called "implied." What all of these implied addresses have in common is the fact that you do not need to actually give any address. By comparison, an LDA $2500 mode (the absolute mode) must have that $2500 address to know where to pick up the package. TXA already says, in the instruction itself, that the address is the X register and that the destination will be the accumulator. Likewise, you do not put an address after RTS since the computer always memorizes its jump-off address when it does a JSR (Jump to Subroutine). NOP (No OPeration) is, of course, implied mode too.

Relative

One particular addressing mode, the relative mode, used to be a real headache for programmers. Not so long ago, in the days when ML programming was done "by hand," this was a frequent source of errors. Hand computing — entering each byte by flipping eight switches up or down and then pressing an ENTER key — meant that the programmer had to write his program out on paper, translate the mnemonics into their number equivalents, and then "key" the whole thing into the machine. It was a big advance when computers would accept hexadecimal numbers which permitted entering OF instead of eight switches: 00001111. This reduced errors and fatigue.

An even greater advance was when the machines began having enough free memory to allow an assembler program to be in the computer while the ML program was being written. An assembler not only takes care of translating LDA $2500 into its three (eight-switch binary) numbers: 10101101 00000000 00100101, but it also does relative addressing. So, for the same reason that you can program in ML without knowing how to deal with binary numbers — you can also forget about relative addressing. The assembler will do it for you.

Relative addressing is used with eight instructions only: BVS, BVC, BCS, BCC, BEQ, BMI, BNE, BPL. They are all "branching" instructions. Branch on: overflow flag set (or cleared), carry flag set (or cleared), equal, minus, not-equal, or plus. Branch if Not-Equal, like the rest of this group, will jump up to 128 addresses forward or backward from where it is or 127 addresses backward (if the result of the most recent activity is "not equal"). Note that these jumps can be a distance of only 128, or 127 back, and they can go in either direction.

4 Addressing

You specify where the jump should go by giving an address within these boundaries. Here's an example:

1000 LDX #$00

1002 INX

1003 BNE 1002 1005 BRK

(The X register will count up by ones until it hits 255 decimal and then it resets itself to zero.)

This is what you type in to create a ML FOR-NEXT loop. You are branching, relative to address 1003, which means that the assembler will calculate what address to place into the computer that will get you to 1002. You might wonder what's wrong with the computer just accepting the number 1002 as the address to which you want to branch. Absolute addressing does give the computer the actual address, but the branching instructions all need addresses which are "offsets" of the starting address. The assembler puts the following into the computer:

1000 A2 00

1002 E8

1003 DO FD 1005 00

The odd thing about this piece of code is that "FD" at 1004. How does FD tell the computer to Branch back to 1002? (Remember that X will increment up to 255, then reset to zero on the final increment.) $FD means 253 decimal. Now it begins to be clear why relative addressing is so messy. If you are curious, numbers larger than 127, when found as arguments of relative addressing instructions, tell the computer to go back down to lower addresses. What's worse, the larger the number, the less far down it goes. It counts the address 1005 as zero and counts backwards thus:

1005 = 0

1004 = 255 1003 = 254 1002 = 253

Not a very pretty counting method! Luckily, all that we fortunate assembler users need do is to give the address (as if it were an absolute address), and the assembler will do the hard part. This strange counting method is the way that the computer can handle negative numbers. The reason it can only count to 128 is that the leftmost bit is no longer used as a 128th's column. Instead, this bit is on or off to signify a positive or negative number.

Addressing 4

When you are using one of the branch instructions, you sometimes branch forward. Let's say that you want to have a different kind of FOR-NEXT loop:

1000 LDX #0

1002 INX

1003 BEQ 100A 1005 JMP 1002

1008 BRK

1009 BRK 100A BRK

When jumping forward, you often do not yet know the precise address you want to branch to. In the example above, we really wanted to go to 1008 when the loop was finished (when X was equal to zero), but we just entered an approximate address (100A) and made a note of the place where this guess appeared (1004). Then, using the POKE function on the assembler, we can POKE the correct offset when we know what it should be. Forward counting is easy. When we finally saw that we wanted to go to 1008, we would POKE 1004, 3. (The assembler would have written a five because that's the correct offset to branch to 100A, our original guess.)

Remember that the zero address for these relative branches is the address immediately following the branch instructions. For example, a jump to 1008 is three because you count: 1005 a zero, 1006 = 1,1007 = 2, 1008 = 3. All this confusion disappears after writing a few programs and practicing with estimated branch addresses. Luckily, the assembler does all the backwards branches. That's lucky because they are much harder to calculate.

Unknown Forward Branches

Also, the Simple Assembler will do one forward ("not-yet-known") branch calculation for you. If you look at the BASIC program listing of the Simple Assembler, you will see that the pseudo-ops (fake operations) are located from line 241 up. You could add additional forward-resolving pseudo-ops if you just give them new names like Fl resolved later by Rl. Alternatively, you can type a guess in for the forward branches, as we just did in the example above. Then, when you find out the exact address, simply exit from the assembler, give 1004 as your starting address for assembly, and write in BEQ 1008 and let the assembler calculate for you. Either way, you will soon get the hang of forward branching.

We'll get into pseudo-ops later. Essentially, they are instructions to the assembler (such as "please show me the decimal equivalent of the following hex number"), but which are not intended to be thought of as mnemonics which get translated into ML object code. Pseudo-ops are "false" operations, not part of the 6502 instruction set.

4 Addressing

They are requests to the assembler program to perform some extra service for the programmer.

Absolute.X And Absolute.Y

Another important addressing mode provides you with an easy way to manipulate lists or tables. This method looks like absolute addressing, but it attaches an X or a Y to the address. The X or Y stands for the X or Y registers, which are being used in this technique as offsets. That is, if the X register contains the number 3 and you type: LDA 1000, X, you will LoaD the Accumulator with the value (the number) which is in memory cell 1003. The register value is added to the absolute address.

Another method called Zero Page,X works the same way: LDA 05,X. This means that you can easily transfer or search through messages, lists, or tables. Error messages can be sent to the screen using such a method. Assume that the words SYNTAX ERROR are held in some part of memory because you sometimes need to send them to the screen from your program. You might have a whole table of such messages. But we'll say that the words SYNTAX ERROR are stored at address 3000. Assuming that your screen memory address is 32768 (8000 hex), here's how you would send the message:

1000 LDX #$00 (set the counter register to zero)

1002 LDA $3000,X (get a letter at 3000 + X)

1005 BEQ $100E (if the character is a zero, we've

reached the end of message,

so we end the routine) 1007 STA $8000,X (store a letter on the screen)

100A INX (increment the counter so the next

letter in the message, as well as the

next screen position, are pointed

to) 100B JMP $1002 (jump to the load instruction to

fetch the next character) 100E BRK (task completed, message —

transferred)

This sort of indexed looping is an extremely common ML programming device. It can be used to create delays (FOR T = 1 TO 5000: NEXT T), to transfer any kind of memory to another place, to check the conditions of memory (to see, for example, if a particular word appears somewhere on the screen), and to perform many other applications. It is a fundamental, all-purpose machine language technique. <^

Here's a fast way to fill your screen or any other area of memory. This example uses the Commodore 64 Screen RAM starting address. Just substitute your computer's screen-start address. This is a full

~

Addressing 4

source code for the demonstration screen-fill we tried in Chapter 1. See if you can follow how this indexed addressing works. What bytes are filled in, and when? At ML speeds, it isn't necessary to fill them in order — nobody would see an irregular filling pattern because it all happens too fast for the eye to see it, like magic. (See Program 4-3.)

Compare this to Program 1-2 to see the effects of using a different screen starting address and how source code is an expansion of a disassembly.

Indirect Y

This one is a real workhorse; you'll use it often. Several of the examples in this book refer to it and explain it in context. It isn't so much an address in itself as it is a method of creating an address. It looks like this:

$4000 STA ($80),Y

Seems innocent enough. But watch out for the parentheses. They mean that $80 is not the real address we are trying to store A into. Instead, addresses $80 and $81 are holding the address we are really sending our byte in A to. We are not dealing directly with $0080 here; hence the name for this addressing mode: indirect Y.

If $80,81 have these numbers in them:

$0080 01 $0081 20

and Y is holding a five, then the byte in A will end up in address $2006! How did we get $2006?

First, we've got to mentally switch the numbers in $80,81. The 6502 requires that such "address pointers" be held in backwards order. So visualize $80,81 as forming $2001, a pointer. Then add the value in Y, which is five, and you get $2006.

This is a valuable tool and you should familiarize yourself with it. It lets you have easy access to many memory locations very quickly by just changing the Y register or the pointer. To go up a page, add one to the number in $0081. To go down four pages, subtract four from it. Combine this with the indexing that Y is doing for you and you've got great efficiency. The pointers for this addressing mode must be stored in zero page locations.

When an address is put into a pointer, you can see that it was split in half. The address $2001 was split in the example above. It's a two-byte number and ML terminology distinguishes between the bytes by saying that one is the LSB (least significant byte) and the other is the MSB (most significant byte). The $01 is the least significant. To grasp what is meant by "significant," imagine chopping a decimal number such as 5015 in half. Since the left half, 50, stands for fifty 100's and the right half stands for 15 ones,

4 Addressing

o

2 O

o

w w

o

ra w

N

Eh W O CO 2

l-H

a h

<C >H >H » >H

• % % * %

DS o o o o

o <; o o o o di

o K'finwh o

co- U o o o o O

=H= =8= CO- CO- CO-CO J

tH«c<c<Ci=(;<;>H[x]co2

DQEhEhEhEh22EhW

oooooooooooooo

HNrOrfinU)l^cOWOr-l(NM^ OOOOOOOOOrHrHiHMiH OOOOOOOOOOOOOO

^ in vo r^ o o o o

O r-H O O O O iH

O •*!< O O O O pn

M OCTimCTlOMTlCOOO

b I I I I I I I I I

C ON<fh<QOHn

do ■^Tj'^'Tj'xjt^t'inLnLn

g UUUUUOUUU

Addressing 4

obviously the leftmost half, the 100's, is more significant. Likewise, the left half of a two-byte hex number like $2001 is the most significant byte. The $20 stands for 32 times 256 (in decimal terms). It's easy to multiply double-byte numbers by decimal 256 by just adding one to the MSB. This would be a quick way of moving through the "pages" in memory.

The other thing to remember about MSB,LSB is that they are reversed when broken up and used as an address pointer: LSB,MSB.

Indirect X

Not often used, this mode makes it possible to set up a group of pointers (a table) in page zero. It's like Indirect Y except the X register value is not added to the address pointer to form the ultimate address desired. Rather, it points to which of the pointers to use. Nothing is added to the address found in the pointer. It looks like this:

$5000 STA ($90, X)

To see it in action, let's assume that part of zero page has been set up to point to various parts of memory. A table of pointers, not just one:

Pointer #1

(it points to $0400)

Pointer #2

($7005)

Pointer #3

(pointing to $80EA)

If X holds a two when we STA $(90,X), then the byte in A will be sent to $7005. If X holds a four, the byte will go to $80EA.

All in all, this has relatively little merit. It would be useful in rare situations, but at least it's there if you should find you need it.

Accumulator Mode

ASL, LSR, ROL, and ROR shift or manipulate the bits in the byte in the accumulator. We'll touch on them in the chapter on the instruction set. They don't have much to do with addressing, but they are always listed as a separate addressing mode.

Zero Page,Y

This can only be used with LDX and STX. Otherwise it operates just like Zero Page, X discussed above.

There you have them, thirteen addressing modes to choose from. The six you should focus on and practice are: Immediate, Absolute (plus Absolute, Y and ,X), Zero Page, and Indirect Y. The rest are either automatic (implied) or not really worth bothering with until you have full command of the six common and useful ones.

Now that we've surveyed the ways you can move numbers around, it's time to see how to do arithmetic in ML.

51

f

Arithmetic

There'll be many things you'll want to do in ML, but complicated math is not one of them. Mathematics beyond simple addition and subtraction (and a very easy form of elementary division and multiplication) will not be covered in this book. For most games and other ML for personal computing, you will rarely need to program with any complex math. In this chapter we will cover what you are likely to want to know. BASIC is well-suited to mathematical programming and is far easier to program for such tasks.

Before we look at ML arithmetic, it is worth reviewing an important concept: how the computer tells the difference between addresses, numbers as such, and instructions. It is valuable to be able to visualize what the computer is going to do as it comes upon each byte in your ML routine.

Even when the computer is working with words, letters of the alphabet, graphics symbols and the like — it is still working with numbers. A computer works only with numbers. The ASCII code is a convention by which the computer understands that when the context is alphabetic, the number 65 means the letter A. At first this is confusing. How does it know when 65 is A and when it is just 65? The third possibility is that the 65 could represent the 65th cell in the computer's memory.

It is important to remember that, like us, the computer means different things at different times when it uses a symbol (like 65). We can mean a street address by it, a temperature, the cost of a milk shake, or even a secret code. We could agree that whenever we used the symbol "65" we were ready to leave a party. The point is that symbols aren't anything in themselves. They stand for other things, and what they stand for must be agreed upon in advance. There must be rules. A code is an agreement in advance that one thing symbolizes another.

The Computer's Rules

Inside your machine, at the most basic level, there is a stream of input. The stream flows continually past a "gate" like a river through a canal. For 99 percent of the time, this input is zeros. (BASICs differ; some see continuous 255's, but the idea is the same.) You turn it on and the computer sits there. What's it doing? It might be updating a clock, if you have one, and it's holding things coherent on the TV

5 Arithmetic

screen — but it mainly waits in an endless loop for you to press a key on your keyboard to let it know what it's supposed to do. There is a memory cell inside (this, too, varies in its location) which the computer constantly checks. On some computers, this cell always has a 255 in it unless a key is pressed. If you press the RETURN key, a 13 will replace the 255. At last, after centuries (the computer's sense of time differs from ours) here is something to work with! Something has come up to the gate at long last.

You notice the effect at once — everything on the screen moves up one line because 13 (in the ASCII code) stands for carriage return. How did it know that you were not intending to type the number 13 when it saw 13 in the keyboard sampling cell? Simple. The number 13, and any other keyboard input, is always read as an ASCII number.

In ASCII, the digits from 0 through 9 are the only number symbols. There is no single symbol for 13. So, when you type in a 1 followed immediately by a 3, the computer's input-from-fhe-keyboard routine scans the line on the screen and notices that you have not pressed the "instant action" keys (the STOP, BREAK, ESC, TAB, cursor-control keys, etc.). Rather, you typed 1 and 3 and the keyboard sampling cell (the "which key pressed" address in zero page) received the ASCII value for one and then for three. ASCII digits are easy to remember in hex: zero is 30, 1 is 31, and up to 39 for nine. In decimal, they are 48 through 57.

The computer decides the "meaning" of the numbers which flow into and through it by the numbers' context. If it is in "alphabetic" mode, the computer will see the number 65 as "a"; or if it has just received an "a," it might see a subsequent number 65 as an address to store the "a". It all depends on the events that surround a given number. We can illustrate this with a simple example:

2000 LDA #65 A9 (169) 41 (65)

2000 STA $65 85 (133) 41 (65)

This short ML program (the numbers in parentheses are the decimal values) shows how the computer can "expect" different meanings from the number 65 (or 41 hex). When it receives an instruction to perform an action, it is then prepared to act upon a number. The instruction comes first and, since it is the first thing the computer sees when it starts a job, it knows that the A9 (169) is not a number. It has to be one of the ML instructions from its set of instructions (see Appendix A).

Instructions And Their Arguments

The computer would no more think of this first 169 as the number 169 than you would seal an envelope before the letter was inside. If you are sending out a pile of Christmas cards, you perform instruction-argument just the way the computer does: you (1) fill the envelope

54

~

Arithmetic 5

(instruction) (2) with a card (argument or operand). All actions do something to something. A computer's action is called an instruction (or, in its numeric form inside the computer's memory it's called an opcode for operation code). The target of the action is called the instruction's argument (operand). In our program above, the computer must LoaD Accumulator with 65. The # symbol means "immediate"; the target is right there in the next memory cell following the mnemonic LDA, so it isn't supposed to be fetched from a distant memory cell.

Then the action is complete, and the next number (the 133 which means STore Accumulator in zero page, the first 256 cells) is seen as the start of another complete action. The action of storing always signals that the number following the store instruction must be an address of a cell in memory to store to.

Think of the computer as completing each action and then looking for another instruction. Recall from the last chapter that the target can be "implied" in the sense that INX simply increases the X register by one. That "one" is "implied" by the instruction itself, so there is no target argument in these cases. The next cell in this case must also contain an instruction for a new instruction-argument cycle.

Some instructions call for a single-byte argument. LDA #65 is of this type. You cannot LoaD Accumulator with anything greater than 255. The accumulator is only one byte large, so anything that can be loaded into it can also be only a single byte large. Recall that $FF (255 decimal) is the largest number that can be represented by a single byte. STA $65 also has a one byte argument because the target address for the STore Accumulator is, in this case, in zero page. Storing to zero page or loading from it will need only a one byte argument — the address. Zero page addressing is a special case, but an assembler program will take care of it for you. It will pick the correct opcode for this addressing mode when you type LDA $65. LDA $0065 would create ML code that performs the same operation though it would use three bytes instead of two to do it.

The program counter is like a finger that keeps track of where the computer is located in its trip up a series of ML instructions. Each instruction takes up one, two, or three bytes, depending on what type of addressing is going on.

Context Defines Meaning

TXA uses only one byte so the program counter (PC) moves ahead one byte and stops and waits until the value in the X register is moved over to the accumulator. Then the computer asks the PC, "Where are we?" and the PC is pointing to the address of the next instruction. It never points to an argument. It skips over them because it knows how many bytes each addressing mode uses up in a program.

Say that the next addresses contain an LDA $15. This is two bytes long (zero page addressing). The PC is raised by two. The

5 Arithmetic

longest possible instruction would be using three bytes, such as LDA $5000 (absolute addressing). Here the argument takes up two bytes. Add that to the one byte used by any instruction and you have a total of three bytes for the PC to count off. Zero page LDA is represented by the number A5 and Absolute LDA is AD. Since the opcodes are different, even though the mnemonics are identical, the computer can know how many bytes the instruction will use up.

Having reviewed the way that your computer makes contextual sense out of the mass of seemingly similar numbers of which an ML program is composed, we can move on to see how elementary arithmetic is performed in ML.

Addition

Arithmetic is performed in the accumulator. The accumulator holds the first number, the target address holds the second number (but is not affected by the activities), and the result is left in the accumulator. So:

LDA #$40 (remember, the # means immediate, the $ means

hex) ADC #$01

will result in the number 41 being left in the accumulator. We could then STA that number wherever we wanted. Simple enough. The ADC means ADd with Carry. If this addition problem resulted in a number higher than 255 (if we added, say, 250 + 6), then there would have to be a way to show that the number left behind in the accumulator was not the correct result. What's left behind is the carry. What would happen after adding 250 + 6 is that the accumulator would contain a 1. To show that the answer is really 256 (and not 1), the "carry flag" in the status register flips up. So, if that flag is up, we know that the real answer is 255 plus the 1 left in the accumulator.

To make sure that things never get confused, always put in a CLC (CLear Carry) before any addition problems. Then the flag will go down before any addition and, if it is up afterward, we'll know that we need to add 256 to whatever is in the accumulator. We'll know that the accumulator holds the carry, not the total result.

One other point about the status register: there is another flag, the "decimal" flag. If you ever set this flag up (SED), all addition and subtraction is performed in a decimal mode in which the carry flag is set when addition exceeds 99. In this book, we are not going into the decimal mode at all, so it's a good precaution to put a CLear Decimal mode (CLD) instruction as the first instruction of any ML program you write. After you type CLD, the flag will be put down and the assembler will move on to ask for your next instruction, but all the arithmetic from then on will be as we are describing it.

Arithmetic 5

Adding Numbers Larger Than 255

We have already discussed the idea of setting aside some memory cells as a table for data. All we do is make a note to ourselves that, say, $80 and $81 are declared a zone for our personal use as a storage area. Using a familiar example, let's think of this zone as the address that holds the address of a ball-like character for a game. As long as the addresses are not in ROM, or used by our program elsewhere, or used by the computer (see your computer's memory map), it's fine to declare any area a data zone. It is a good idea (especially with longer programs) to make notes on a piece of paper to show where you intend to have your subroutines, your main loop, your initialization, and all the miscellaneous data — names, messages for the screen, input from the keyboard, etc. This is one of those things that BASIC does for you automatically, but which you must do for yourself in ML.

When BASIC creates a string variable, it sets aside an area to store variables. This is what DIM does. In ML, you set aside your own areas by simply finding a safe and unused memory space and then not writing a part of your program into it. Part of your data zone can be special registers you declare to hold the results of addition or subtraction. You might make a note to yourself that $80 and $81 will hold the current address of the bouncing ball in your game. Since the ball is constantly in motion, this register will be changing all the time, depending on whether the ball hit a wall, a paddle, etc. Notice that you need two bytes for this register. That is because one byte could hold only a number from 0 to 255. Two bytes together, though, can hold a number up to 65535.

In fact, a two-byte register can address any cell in most microcomputers because most of us have machines with a total of 65536 memory cells (from zero to 65535). So if your ball is located (on your screen) at $8000 and you must move it down one, just change the ball-address register you have set up. If your screen has 40 columns, you would want to add 40 to this register.

The ball address register now looks like this: $0080 00 80 (remember that the higher, most significant byte, comes after the LSB, the least significant byte in the 6502's way of looking at pointers). We want it to be: $0080 28 80. (The 28 is hex for 40.) In other words, we're going to move the ball down one line on a 40-column screen.

Remember the "indirect Y" addressing mode described in the previous chapter? It lets us use an address in zero page as a pointer to another address in memory. The number in the Y register is added to whatever address sits in 80,81, so we don't STA to $80 or $81, but rather to the address that they contain. STA ($80),Y or, using the simplified punctuation rules of the Simple Assembler: STA (80)Y.

5 Arithmetic

Moving A Ball Down

How to add $28 to the ball address register? First of all, CLC, clear the carry to be sure that flag is down. To simplify our addition, we can set aside another special register which serves only to hold the $28 as a double-byte number all through the game: $4009 28 00. This is the size of one screen line in our 40-column computer and it won't change. Since it moves the ball down one screen line, it can be used equally well for a subtraction that would move the ball up one screen line as well. Now to add them together:

1000 CLC (1000 is our "add 40 to ball address"

subroutine)

1001 LDA $80 (we fetch the LSB of ball address) 1003 ADC $4009 (LSB of our permanent screen line size) 1006 STA $80 (put the new result into the ball address) 1008 LDA $81 (get the MSB of ball address)

100A ADC $400A (add with cam/ to the MSB of screen value) 100D STA $81 (update the ball address MSB)

That's it. Any carry will automatically set the carry flag up during the ADC action on the LSB and will be added into the result when we ADC to the MSB. It's all quite similar to the way that we add ordinary decimal numbers, putting a carry onto the next column when we get more than a 10 in the first column. And this carrying is why we always CLC (clear the carry flag, putting it down) just before additions. If the carry is set, we could get the wrong answer if our problem did not result in a carry. Did the addition above cause a carry?

Note that we need not check for any carries during the MSB + MSB addition. Any carries resulting in a screen address greater than $FFFF (65535) would be impossible on our machines. The 6502 is permitted to address $FFFF tops, under normal conditions.

Subtraction

As you might expect, subtracting single-byte numbers is a snap:

LDA #$41 SBC #$01

results in a $40 being left in the accumulator. \s before, though, it is good to make it a habit to deal with the carry llag before each calculation. When subtracting, however, you set the carry flag: SEC. Why is unimportant. Just always SEC before any subtractions, and your answers will be correct. Here's double subtracting that will move the ball up the screen one line instead of down one line:

$1020 SEC ($1020 is our '' take 40 from ball address"

subroutine) 1021 LDA $80 (get the LSB of ball address)

58

~

~

~

Arithmetic 5

1023 SBC $4009 (LSB of our permanent screen line value)

1026 STA $80 (put the new result into the ball address)

1028 LDA $81 (get the MSB of ball address)

102A SBC $400A (subtract the MSB of screen value)

102D STA $81 (update the ball address MSB)

Multiplication And Division

Multiplying could be done by repeated adding. To multiply 5 x4, you could just add 4 + 4 + 4 + 4 + 4. One way would be to set up two registers like the ones we used above, both containing 04, and then loop through the addition process five times. For practical purposes, though, multiplying and dividing are much more easily accomplished in BASIC. They simply are often not worth the trouble of setting up in ML, especially if you will need results involving decimal points (floating point arithmetic). Perhaps surprisingly, for the games and personal computing tasks where creating ML routines is useful, there is little use either for negative numbers or arithmetic beyond simple addition and subtraction.

If you find that you need complicated mathematical structures, create the program in BASIC, adding ML where super speeds are necessary or desirable. Such hybrid programs are efficient and, in their way, elegant. One final note: an easy way to divide the number in the accumulator by two is to LSR it. Try it. Similarly, you can multiply by two with ASL. We'll define LSR and ASL in the next chapter.

Double Comparison

One rather tricky technique is used fairly often in ML and should be learned. It is tricky because there are two branch instructions which seem to be worth using in this context, but they are best avoided. If you are trying to keep track of the location of a ball on the screen, it will have a two-byte address. If you need to compare those two bytes against another two-byte address, you need a "double compare" subroutine. You might have to see if the ball is out of bounds or if there has been a collision with some other item flying around on screen. Double compare is also valuable in other kinds of ML programming.

The problem is the BPL (Branch on PLus) and BMI (Branch on Minus) instructions. Don't use them for comparisons. In any comparisons, single- or double-byte, use BEQ to test if two numbers are equal; BNE for not equal; BCS for equal or higher; and BCC for lower. You can remember BCS because its "S" is higher and BCC because its "C" is lower in the alphabet. To see how to perform a double-compare, here's one easy way to do it. (See Program 5-1.)

5 Arithmetic

Arithmetic 5

This is a full-dress, luxurious assembler at work. With such assemblers you can use line numbers and labels, add numbers to labels (see TESTED +1 in line 150), add comments, and all the rest. To try this out, type in the hex bytes on the left, starting at address $1010, which make up the program itself. Then fill bytes $1000-100f with zeros — that's your storage area for the numbers you are comparing as well as a simulated "landing place" where your computer will branch, demonstrating that the comparison worked correctly.

Now try putting different numbers into the two-byte zones called TESTED and SECOND. TESTED, at $1000, is the first, the tested, number. It's being tested against the second number, called SECOND. As you can see, you've got to keep it straight in your mind which number is the primary number. There has to be a way to tag them so that it means something when you say that one is larger (or smaller) than the other.

When you've set up the numbers in their registers ($1000 to $1003), you can run this routine by starting at $1010. All that will happen is that you will land on a BRK instruction. Where you land tells you the result of the comparison. If the numbers are equal, you land at $1005. If the TESTED number is less than the SECOND number, you'll end up at $1004. If all you needed to find out was whether they were unequal, you could use BNE. Or you could leave out branches that you weren't interested in. Play around with this routine until you've understood the ideas involved.

In a real program, of course, you would be branching to the addresses of subroutines which do something if the numbers are equal or greater or whatever. This example sends the computer to $1004, $1005, or $1006 just to let you see the effects of the double-compare subroutine. Above all, remember that comparing in ML is done with BCS and BCC (fKrt BPL or BMT).

~

The Instruction Set

There are 56 instructions (commands) available in 6502 machine language. Most versions of BASIC have about 50 commands. Some BASIC instructions are rarely used by the majority of programmers: USR, END, SGN, TAN, etc. Some, such as END and LET, contribute nothing to a program and seem to have remained in the language for nostalgic reasons. Others, like TAN, have uses that are highly specialized. There are surplus commands in computer languages just as there are surplus words in English. People don't often say culpability. They usually say guilt. The message gets across without using the entire dictionary. The simple, common words can do the job.

Machine language is the same as any other language in this respect. There are around 20 heavily used instructions. The 36 remaining ones are far less often used. Load the disassembler program in Appendix D and enter the starting address of your computer's BASIC in ROM. You can then read the machine language routines which comprise it. You will quickly discover that the accumulator is heavily trafficked (LDA and STA appear frequently), but you will have to hunt to find an ROR, SED, CLV, RTI, or BVC.

ML, like BASIC, offers you many ways to accomplish a given job. Some programming solutions, of course, are better than others, but the main thing is to get the job done. An influence still lingers from the early days of computing when memory space was rare and expensive. This influence — that you should try to write programs using up as little memory as possible — is usually safely ignored. Efficient memory use will often be low on your list of objectives. It could hardly matter if you used up 25 instead of 15 bytes to print a message to your screen when your computer has space for programs which exceeds 30,000 bytes.

Rather than memorize each instruction individually, we will concentrate on the workhorses. Bizarre or arcane instructions will get only passing mention. Unless you are planning to work with ML for interfacing or complex mathematics and such, you will be able to write excellent machine language programs for nearly any application with the instructions we'll focus on here.

For each instruction group, we will describe three things before getting down to the details about programming with them. 1. What

6 The Instruction Set

the instructions accomplish. 2. The addressing modes you can use with them. 3. What they do, if anything, to the flags in the Status Register. All of this information is also found in Appendix A.

The Six Instruction Groups

The best way to approach the "instruction set" might be to break it down into the following six categories which group the instructions according to their functions: 1. The Transporters 2. The Arithmetic Group 3. The Decision-makers 4. The Loop Group 5. The Subroutine and Jump Group and 6. The Debuggers. We will deal with each group in order, pointing out similarities to BASIC and describing the major uses for each.

As always, the best way to learn is by doing. Move bytes around. Use each instruction, typing a BRK as the final instruction to see the effects. If you LDA #65, look in the A register to see what happened. Then STA $12 and check to see what was copied into address $12. If you send the byte in the accumulator (STA), what's left behind in the accumulator? Is it better to think of bytes as being copied rather than being sent?

Play with each instruction to get a feel for it. Discover the effects, qualities, and limitations of these ML commands.

I. The Transporters: LDA, LDX, LDY STA, STX, STY TAX, TAY TXA, TYA

These instructions move a byte from one place in memory to another. To be more precise, they copy what is in a source location into a target location. The source location still contains the byte, but after a "transporter" instruction, a copy of the byte is also in the —^

target. This does replace whatever was in the target.

All of them affect the N and Z flags, except STA, STX, and STY which do nothing to any flag.

There are a variety of addressing modes available to different instructions in this group. Check the chart in Appendix A for specifics. —

Remember that the computer does things one at a time. Unlike the human brain which can carry out up to 1000 different instructions simultaneously (walk, talk, and smile, all at once) — the computer goes from one tiny job to the next. It works through a series of

The Instruction Set 6

instructions, raising the program counter (PC) each time it handles an instruction.

If you do a TYA, the PC goes up by one to the next address and the computer looks at that next instruction. STA $80 is a two-byte long instruction, it's zero page addressing, so the PC = PC + 2. STA $8500 is a three-byte long absolute addressing mode and PC = PC + 3.

Recall that there's nothing larger than a three-byte increment of the PC. However, in each case, the PC is cranked up the right amount to make it point to the address for the next instruction. Things would get quickly out of control if the PC pointed to some argument, thinking it was an instruction. It would be incorrect (and soon disastrous) if the PC landed on the $15 in LDA $15.

If you type SYS 1024 (or USR or CALL), the program counter is loaded with $0400 and the computer "transfers control" to the ML instructions which are (we hope!) waiting there. It will then look at the byte in $0400, expecting it to be an ML instruction. It will do that job and then look for the next instruction. Since it does this very fast, it can seem to be keeping score, bouncing the ball, moving the paddle, and everything else — simultaneously. It's not, though. It's flashing from one task to another and doing it so fast that it creates the illusion of simultaneity much the way that 24 still pictures per second look like motion in movies.

The Programmer's Time Warp

Movies are, of course, lots of still pictures flipping by in rapid succession. Computer programs are composed of lots of individual instructions performed in rapid succession.

Grasping this sequential, step-by-step activity makes our programming job easier: we can think of large programs as single steps, coordinated into meaningful, harmonious actions. Now the computer will put a blank over the ball at its current address, then add 40 to the ball's address, then print a ball at the new address. The main single-step action is moving information, as single-byte numbers, from here to there, in memory. We are always creating, updating, modifying, moving and destroying single-byte variables. The moving is generally done from one double-byte address to another. But it all looks smooth to the player during a game.

Programming in ML can pull you into an eerie time warp.You might spend several hours constructing a program which executes in seconds. You are putting together instructions which will later be read and acted upon by coordinated electrons, moving at electron speeds. It's as if you spent an afternoon slowly and carefully drawing up pathways and patterns which would later be a single bolt of lightning.

6 The Instruction Set

Registers

In ML there are three primary places where variables rest briefly on their way to memory cells: the X, the Y, and the A registers. And the A register (the accumulator) is the most frequently used. X and Y are used for looping and indexing. Each of these registers can grab a byte from anywhere in memory or can load the byte right after its own opcode (immediate addressing):

LDX $8000 (puts the number at hex address 8000 into X,

without destroying it at $8000) LDX #65 (puts the number 65 into X) LDA and LDY work the same.

Be sure you understand what is happening here. LDX $1500 does not copy the "byte in the X register into address $1500." It's just the opposite. The number (or "value" as it's sometimes called) in $1500 is copied into the X register.

To copy a byte from X, Y, or A, use STX, STY, or STA. For these "store-bytes" instructions, however, there is no immediate addressing mode. No STA #15. It would make no sense to have STA #15. That would be disruptive, for it would modify the ML program itself. It would put the number 15 into the next cell beyond the STA instruction within the ML program itself.

Another type of transporter moves bytes between registers — TAY, TAX, TYA, TXA. See the effect of writing the following. Look at the registers after executing this:

1000 LDA #65 TAY TAX

^

~

The number 65 is placed into the accumulator, then transferred to the Y register, then sent from the accumulator to X. All the while, however, the A register (accumulator) is not being emptied. Sending bytes is not a "transfer" in the usual sense of the term "sending." It's more as if a Xerox copy were made of the number and then the copy is sent. The original stays behind after the copy is sent.

LDA #15 followed by TAY would leave the 15 in the —

accumulator, sending a copy of 15 into the Y register.

Notice that you cannot directly move a byte from the X to the Y _

register, or vice versa. There is no TXY or TYX.

Flags Up And Down

Another effect of moving bytes around is that it sometimes throws a flag up or down in the Status Register. LDA (or LDX or LDY) will affect the N and Z, negative and zero, flags.

We will ignore the N flag. It changes when you use "signed numbers," a special technique to allow for negative numbers. For our purposes, the N flag will fly up and down all the time and we won't

The Instruction Set 6

care. If you're curious, signed numbers are manipulated by allowing the seven bits on the right to hold the number and the leftmost bit stands for positive or negative. We normally use a byte to hold values from 0 through 255. If we were working with "signed" numbers, anything higher than 127 would be considered a negative number since the leftmost bit would be "on" — and an LDA #255 would be thought of as -1. This is another example of how the same things (the number 255 in this case) could signify several different things, depending on the context in which it is being interpreted.

The Z flag, on the other hand, is quite important. It shows whether or not some action during a program run resulted in a zero. The branching instructions and looping depend on this flag, and we'll deal with the important zero-result effects below with the BNE, INX, etc., instructions.

No flags are affected by the STA, STX, or STY instructions.

The Stack Can Take Care Of Itself

There are some instructions which move bytes to and from the stack. These are for advanced ML programmers. PHA and PLA copy a byte from A to the stack, and vice versa. PHP and PLP move the status register to and from the stack. TSX and TXS move the stack pointer to or from the X register. Forget them. Unless you know precisely what you are doing, you can cause havoc with your program by fooling with the stack. The main job for the stack is to keep the return addresses pushed into it when you JSR (Jump To Subroutine). Then, when you come back from a subroutine (RTS), the computer pulls the addresses off the stack to find out where to go back to.

The one major exception to this warning about fiddling with the stack is Atari's USR instruction. It is a worthwhile technique to master. Atari owners can move between BASIC and ML programs fairly easily, passing numbers to ML via the stack. The parameters (the passed numbers) must be pulled off the stack when the ML program first takes control of the computer.

For most ML programming, on the other hand, avoid stack manipulation until you are an advanced programmer. If you manipulate the stack without great care, you'll give an RTS the wrong address and the computer will travel far, far beyond your control. If you are lucky, it sometimes lands on a BRK instruction and you fall into the monitor mode. The odds are that you would get lucky roughly once every 256 times. Don't count on it. Since BRK is rare in your BASIC ROM, the chances are pretty low. If your monitor has a FILL instruction which lets you put a single number into large amounts of RAM memory, you might want to fill the RAM with "snow." FILL 1000 8000 00 would put zeros into every address from 1000 to 8000. This greatly improves the odds that a crash will hit a BRK.

6 The Instruction Set

As an aside, there is another use for a blanket of "zero page snow." Many Atari programs rely on the fact that the computer leaves page six ($0600-06FF) pretty much alone. The PET doesn't make much use of the second cassette buffer. So, you can safely put an ML subroutine in these places to, for example, add a routine which customizes an ML word processor. Does your Atari's ML word-processing program use any memory space in page six? Probably. What locations does it use? Fill page six with 00's, put the word-processor through its paces, then look at the tracks, the non-zeros, in the snow.

2. The Arithmetic Group: ADC, SBC, SEC, CLC

Here are the commands which add, subtract, and set or clear the carry flag. ADC and SBC affect the N, Z, C, and V (overflow) flags. CLC and SEC, needless to say, affect the C flag and their only addressing mode is Implied.

ADC and SBC can be used in eight addressing modes: Immediate, Absolute, Zero Page, (Indirect,X), (Indirect),Y, Zero Page,X, and Absolute,X and Y.

Arithmetic was covered in the previous chapter. To review, before any addition, the carry flag must be cleared with CLC. Before any subtraction, it must be set with SEC. The decimal mode should be cleared at the start of any program (the initialization): CLD. You can multiply by two with ASL and divide by two with LSR. Note that you can divide by four with LSR LSR or by eight with LSR LSR LSR. You could multiply a number by eight with ASL ASL ASL. What would this do to a number: ASL ASL ASL ASL? To multiply by numbers which aren't powers of two, use addition plus multiplication. To multiply by ten, for example: copy the original number temporarily to a vacant area of memory. Then ASL ASL ASL to multiply it by eight. Then multiply the stored original by two with a single ASL. Then add them together.

If you're wondering about the V flag, it is rarely used for anything. You can forget about the branch which depends on it, BVC, too. Only five instructions affect it and it relates to "twos complement" arithmetic which we have not touched on in this book. Like decimal mode or negative numbers, you will be able to construct your ML programs very effectively if you remain in complete ignorance of this mode. We have largely avoided discussion of most of the flags in the status register: N, V, B, D, and I. This avoidance has also removed several branch instructions from our consideration: BMI, BPL, BVC, and BVS. These flags and instructions are not

68

~

~

~ ~

The Instruction Set 6

usually found in standard ML programs and their use is confined to specialized mathematical or interfacing applications. They will not be of use or interest to the majority of ML programmers.

The two flags of interest to most ML programmers are the Carry flag and the Zero flag. That is why, in the following section, we will examine only the four branch instructions which test the C and Z flags. They are likely to be the only branching instructions that you'll ever find occasion to use.

3. The Decision-Makers: CMP, BNE, BEQ, BCC, BCS

The four "branchers" here — they all begin with a "B" — have only one addressing mode. In fact, it's an interesting mode unique to the "B" instructions and created especially for them: relative addressing. They do not address a memory location as an absolute thing; rather, they address a location which is a certain distance from their position in the ML code. Put another way, the argument of the "B" instructions is an offset which is relative to their position. You never have to worry about relocating "B" instructions to another part of memory. You can copy them and they will work just as well in the new location. That's because their argument just says "add five to the present address" or "subtract twenty-seven," or whatever argument you give them. But they can't branch further back than 127 or further forward than 128 bytes.

None of the brancher instructions have any effect whatsoever on any flags; instead, they are the instructions which look at the flags. They are the only instructions that base their activity on the condition of the status register and its flags. They are why the flags exist at all.

CMP is an exception. Many times it is the instruction that comes just before the branchers and sets flags for them to look at and make decisions about. Lots of instructions — LDA is one — will set or "clear" (put down) flags — but sometimes you need to use CMP to find out what's going on with the flags. CMP affects the N, Z, and C flags. CMP has many addressing modes available to it: Immediate, Absolute, Zero Page, (Indirect,X), (Indirect), Y, ZeroPage,X, and Absolute,X and Y.

The Foundations Of Computer Power

This decision-maker group and the following group (loops) are the basis of our computers' enormous strength. The decision-makers allow the computer to decide among two or more possible courses of action. This decision is based on comparisons, //the ball hits a wall, then reverse its direction. In BASIC, we use IF-THEN and ON-GOTO

6 The Instruction Set

structures to make decisions and to make appropriate responses to conditions as they arise during a program run.

Recall that most micros use memory mapped video, which means that you can treat the screen like an area of RAM memory. You can PEEK and POKE into it and create animation, text, or other visual events. In ML, you PEEK by LDA $VIDEO MEMORY and examine what you've PEEKed with CMP. You POKE via STA $VIDEO MEMORY.

CMP does comparisons. This tests the value at an address against what is in the accumulator. Less common are CPX and CPY. Assume that we have just added 40 to a register we set aside to hold the current address-location of a ball on our screen during a game. Before the ball can be POKEd into that address, we'd better make sure that something else (a wall, a paddle, etc.) is not sitting there. Otherwise the ball would pass right through walls.

Since we just increased the location register (this register, we said, was to be at $80,81), we can use it to find out if there is blank space (32) or something else (like a wall). Recall that the very useful "indirect Y" addressing mode allows us to use an address in zero page as a pointer to another address in memory. The number in the Y register is added to whatever address sits in 80,81; so we don't LDA from 80 or 81, but rather from the address that they contain, plus Y's value.

To see what's in our potential ball location, we can do the following:

LDY #0 (we want to fetch from the ball address itself, so we don't want to add anything to it. Y is set to zero.)

LDA (80), Y (fetch whatever is sitting where we plan to next

send the ball. To review Indirect, Y addressing once more: say that the address we are fetching from here is $1077. Address $80 would hold the LSB ($77) and address $81 would hold the MSB ($10). Notice that the argument of an Indirect, Y instruction only mentions the lower address of the two-byte pointer, the $80. The computer knows that it has to combine $80 and $81 to get the full address — and does this automatically.)

At this point in your game, there might be a 32 (ASCII for the space or blank character) or some other number which we would know indicated a wall, another player, a paddle, etc. Now that this questionable number sits in the accumulator, we will CMP it against a space. We could compare it with the number which means wall or the other possibilities — it doesn't matter. The main thing is to compare it:

The Instruction Set 6

2000 CMP #32 (is it a space?)

2002 BNE 200A (Branch if Not Equal [if not 32] to address 200A, which contains the first of a series of comparisons to see if it's a wall, a paddle, etc. On the other hand, if the comparison worked, if it was a 32 (so we didn't Branch Not Equal), then the next thing that happens is the instruction in address 2004. We "fall through" the BNE to an instruction which jumps to the subroutine (JSR), which moves the ball into this space and then returns to address 2007, which jumps over the series of comparisons for wall, paddle, etc.)

(the ball printing subroutine)

(jump over the rest of the comparisons)

(is it our paddle symbol?)

(if not, continue to next comparison)

(do the paddle-handling subroutine and . . .)

(jump over the rest, as before in 2007)

(is it a wall. . . and so forth with as many

comparisons as needed)

This structure is to ML what ON-GOTO or ON-GOSUB is to BASIC. It allows you to take multiple actions based on a single LDA. Doing the CMP only once would be comparable to BASIC'S IF-THEN.

2004 JSR 3000

2007 JMP 2020

200A CMP #128

200C BNE 2014

200E JSR 3050

2011 JMP 2020

2014 CMP #144

Other Branching Instructions

In addition to the BNE we just looked at, there are BCC, BCS, BEQ, BMI, BPL, BVC, and BVS. Learn BCC, BCS, BEQ, and BNE and you can safely ignore the others.

All of them are branching, IF-THEN, instructions. They work in the same way that BNE does. You write BEQ followed by the address you want to go to. If the result of the comparison is "yes, equal-to-zero is true," then the ML program will jump to the address which is the argument of the BEQ. "True" here means that something EQuals zero. One example that would send up the Z flag (thereby triggering the BEQ) is: LDA #00. The action of loading a zero into A sets the Z flag up.

You are allowed to "branch" either forward or backward from the address that holds the "B—" instruction. However, you cannot branch any further than 128 bytes in either direction. If you want to go further, you must JMP (JuMP) or JSR (Jump to SubRoutine). For all practical purposes, you will usually be branching to instructions located within 30 bytes of your "B" instruction in either direction. You will be taking care of most things right near where a CoMPare, or other flag-setting event, takes place.

6 The Instruction Set

If you need to use an elaborate subroutine, simply JSR to it at the target address of your branch:

2000 LDA 65

2002 CMP 85 (is what was in address 65 equal to what was in

address 85?) 2004 BNE 2009 (if Not Equal, branch over the next three bytes

which perform some elaborate job) 2006 JSR 4000 (at 4000 sits an elaborate subroutine to take care

of cases where addresses 65 and 85 turn out to

be equal) 2009 (continue with the program here)

If you are branching backwards, you've written that part of your program, so you know the address to type in after a BNE or one of the other branches. But, if you are branching forward, to an address in part of the program not yet written — how do you know what to give as the address to branch to? In complicated two-pass assemblers, you can just use a word like "BRANCHTARGET", and the assembler will "pass" twice through your program when it assembles it. The first "pass" simply notes that your BNE is supposed to branch to "BRANCHTARGET," but it doesn't yet know where that is.

When it finally finds the actual address of "BRANCHTARGET," it makes a note of the correct address in a special label table. Then, it makes a second "pass" through the program and fills in (as the next byte after your BNE or whatever) the correct address of ■-».

"BRANCHTARGET". All of this is automatic, and the labels make the program you write (called the source code) look almost like English. In fact, complicated assemblers can contain so many special features that they can get close to the higher-level languages, such as BASIC: ^^

(These initial definitions of labels TESTBYTE = 80

are sometimes called '' equates.") NEWBYTE = 99 —-

2004 LDA TESTBYTE

2006 CMP NEWBYTE —

2008 BNE BRANCHTARGET

200A JR SPECIALSUBROUTINE BRANCHTARGET 200D . . . etc.

Instead of using lots of numbers (as we do when using the Simple Assembler) for the target/argument of each instruction, these assemblers allow you to define ("equate") the meanings of words like "TESTBYTE" and from then on you can use the word instead of the number. And they do somewhat simplify the problem of forward branching since you just give (as above) address 200D a name, "BRANCHTARGET," and the word at address 2009 is later replaced with 200D when the assembler does its passes.

This is how the example above looks as the source code listing from a two-pass, deluxe assembler:

The Instruction Set 6

oooooooooooooooo

OOOOOOOOOi— I i—I i— Ir-Hr—li— I .—I

oooooooooooooooo

O M< o

INO O

o cx> ro o o

CO <T\ O r-H O

£

a.

6 The Instruction Set

Actually, we should note in passing that a 200D will not be the number which finally appears at address 2009 to replace "BRANCHTARGET". To save space, all branches are indicated as an "offset" from the address of the branch. The number which will finally replace "BRANCHTARGET" at 2009 above will be three. This is similar to the way that the value of the Y register is added to an address in zero page during indirect Y addressing (also called "indirect indexed"). The number given as an argument of a branch instruction is added to the address of the next instruction. So, 200A + 3 = 200D. Our Simple Assembler will take care of all this for you. All you need do is give it the 200D and it will compute and put the 3 in place for you.

Forward Branch Solutions

There is one responsibility that you do have, though. When you are writing 2008 BNE 200D, how do you know to write in 200D? You can't yet know to exactly which address up ahead you want to branch. There are two ways to deal with this. Perhaps easiest is to just put in BNE 2008 (have it branch to itself). This will result in a FE being temporarily left as the target of your BNE. Then, you can make a note on paper to later change the byte at 2009 to point to the correct address, 200D. You've got to remember to "resolve" that FE to POKE in the number to the target address, or you will leave a little bomb in your program — an endless loop. The Simple Assembler has a POKE function. When you type POKE, you will be asked for the address and value you want POKEd. So, by the time you have finished coding 200D, you could just type POKE and then POKE 2009,3.

The other, even simpler, way to deal with forward branch addresses will come after you are familiar with which instructions use one, two, or three bytes. This BNE-JSR-TARGET construction is common and will always be six away from the present address, an offset of 6. If the branch instruction is at 2008, you just count off three: 200A, 200B, 200C and write BNE 200D. Other, more complex branches such as ON-GOTO constructions will also become easy to count off when you're familiar with the instruction byte-lengths. In any case, it's simple enough to make a note of any unsolved branches and correct them before running the program.

Alternatively, you can use a single "unresolved" forward branch in the Simple Assembler; see its instructions. You just type BNE FORWARD.

Recall our previous warning about staying away from the infamous BPL and BMI instructions? BPL (Branch on PLus) and BMI (Branch on Minus) sound good, but should be avoided. To test for less-than or more-than situations, use BCC and BCS respectively. (Recall that BCC is alphabetically less-than BCS — an easy way to

The Instruction Set 6

remember which to use.) The reasons for this are exotic. We don't need to go into them. Just be warned that BPL and BMI, which sound so logical and useful, are not. They can fail you and neither one lives up to its name. Stick with the always trustworthy BCC, BCS.

Also remember that BNE and the other three main "B" group branching instructions often don't need to have a CMP come in front of them to set a flag they can test. Many actions of many opcodes will automatically set flags during their operations. For example, LDA $80 will affect the Z flag so you can tell if the number in address $80 was or wasn't zero by that flag. LDA $80 followed by BNE would branch away if there were anything besides a zero in address $80. If in doubt, check the chart of instructions in Appendix A to see which flags are set by which instructions. You'll soon get to know the common ones. If you are really in doubt, go ahead and use CMP.

4. The Loop Group: DEY, DEX, INY, INX, INC, DEC

INY and INX raise the Y and X register values by one each time they are used. If Y is a 17 and you INY, Y becomes an 18. Likewise, DEY and DEX decrease the value in these registers by one. There is no such increment or decrement instruction for the accumulator.

Similarly, INC and DEC will raise or lower a memory address by one. You can give arguments to them in four addressing modes: Absolute, Zero Page, Zero Page,X and Absolute,X. These instructions affect the N and Z flags.

The Loop Group are usually used to set up FOR-NEXT structures. The X register is used most often as a counter to allow a certain number of events to take place. In the structure FOR 1 = 1 TO 10: NEXT I, the value of the variable J goes up by one each time the loop cycles around. The same effect is created by:

2000 LDX #10

2002 DEX ("DEcrement" or "DEcrease X" by 1)

2003 BNE 2002 (Branch if Not Equal [to zero] back up to

address 2002)

Notice that DEX is tested by BNE (which sees if the Z flag, the zero flag, is up). DEX sets the Z flag up when X finally gets down to zero after ten cycles of this loop. (The only other flag affected by this loop group is the N [negative] flag for signed arithmetic.)

Why didn't we use INX, INcrease X by 1? This would parallel exactly the FOR I = 1 TO 10, but it would be clumsy since our starting count which is #10 above would have to be #245. This is because X will not become a zero going up until it hits 255. So, for clarity and

6 The Instruction Set

simplicity, it is customary to set the count of X and then DEX it downward to zero. The following program will accomplish the same thing as the one above, and allow us to INX, but it too is somewhat clumsy:

2000 LDX #0

2002 INX

2003 CPX #10 2005 BNE 2002

Here we had to use zero to start the loop because, right off the bat, the number in X is INXed to one by the instruction at 2002. In any case, it is a good idea to just memorize the simple loop structure in the first example. It is easy and obvious and works very well.

Big Loops

How would you create a loop which has to be larger than 256 cycles? When we examined the technique for adding large numbers, we simply used two-byte units instead of single-byte units to hold our information. Likewise, to do large loops, you can count down in two bytes, rather than one. In fact, this is quite similar to the idea of "nested" loops (loops within loops) in BASIC.

2000 LDX #10 (start of 1st loop)

2002 LDY #0 (start of 2nd loop)

2004 DEY

2005 BNE 2004 (if Y isn't yet zero, loop back to DEcrease Y

again — this is the inner loop)

2007 DEX (reduce the outer loop by one)

2008 BNE 2002 (if X isn't yet zero, go through the entire DEY

loop again) 200A (continue with the rest of the program . . .)

One thing to watch out for: be sure that a loop BNE's back up to one address after the start of its loop. The start of the loop sets a number into a register and, if you keep looping up to it, you'll always be putting the same number into it. The DEcrement (decrease by one) instruction would then never bring it down to zero to end the looping. You'll have created an endless loop.

The example above could be used for a "timing loop" similarly to the way that BASIC creates delays with: FOR T = 1 TO 2000: NEXT T. Also, sometimes you do want to create an endless loop (the BEGIN . . . UNTIL in "structured programming"). A popular "endless" loop structure in BASIC waits until the user hits any key: 10 GET K$: IFK$=""THEN10.

10 IF PEEK (764) = 255 THEN 10 is the way to accomplish this on the Atari; it will cycle endlessly unless a key is pressed. The simplest way to accomplish this in ML is to look on the map of your computer

The Instruction Set 6

to find which byte holds the "last key pressed" number. On Upgrade and 4.0 CBM/PET, it's address 151. On Atari, it's 764. On Apple II, it's -16384. On VIC and Commodore 64, it's 203 with a 64 in that location if no key is pressed. In any event, when a key is pressed, it deposits its special numerical value into this cell. If no key is pressed, some standard value stays there all the time. We'll use the CBM as our model here. If no key is pressed, location 151 will hold a 255:

2000 LDA 151 2002 CMP #255 2004 BEQ 2000

If the CMP is EQual, this means that the LDA pulled a 255 out of address 151 and, thus, no key is pressed. So, we keep looping until the value of address 151 is something other than 255. This setup is like GET in BASIC because not only does it wait until a key is pressed, but it also leaves the value of the key in the accumulator when it's finished.

Recall that a CMP performs a subtraction. It subtracts the number in its argument from whatever number sits in the accumulator at the time. LDA #12 CMP $15 would subtract a 5 from 12 if 5 is the number "held" in address 15. This is how it can leave flags set for testing by BEQ or BNE. The key difference between this "subtraction" and SBC is that neither the accumulator nor the argument is affected at all by it. They stay what they were. The result of the subtraction is "thrown away," and all that happens is that the status flags go up or down in response to the result. If the CMP subtraction causes an answer of zero, the Z flag flips up. If the answer is not zero, the Z flag flips down. Then, BNE or BEQ can do their job — checking flags.

Dealing With Strings

You've probably been wondering how ML handles strings. It's pretty straightforward. There are essentially two ways: known-length and zero-delimit. If you know how many characters there are in a message, you can store this number at the very start of the text: "5ERROR." (The number 5 will fit into one byte, at the start of the text of the message.) If this little message is stored in your "message zone" — some arbitrary area of free memory you've set aside to hold all of your messages — you would make a note of the particular address of the "ERROR" message. Say it's stored at 4070. To print it out, you have to know where you "are" on your screen (cursor position). Usually, the cursor address is held in two bytes in zero page so you can use Indirect,Y addressing.

Alternatively, you could simply set up your own zero-page pointers to the screen. For Apple II and Commodore 64, the screen memory starts at 1024; for CBM/PET it's 32768. In any case, you'll be able to set up a "cursor management" system for yourself. To

6 The Instruction Set

simplify, we'll send our message to the beginning of the Apple's screen:

2000 LDX 4070 (remember, we put the length of the message as the first byte of the message, so we load our counter with the length)

2003 LDY #0 (Y will be our message offset)

2005 LDA 4071,Y (gets the character at the address plus Y. Y is zero the first time through the loop, so the "e" from here lands in the accumulator. It also stays in 4071. It's just being copied into the accumulator.)

2008 STA 1024, Y (we can make Y do double duty as the offset for both the stored message and the screen-printout. Y is still zero the first time through this loop, so the "e" goes to 1024.)

2011 INY (prepare to add one to the message-storage

location and to the screen-print location)

2012 DEX (lower the counter by one)

2013 BNE 2005 (if X isn't used up yet, go back and get-and-

print the next character, the "r")

If The Length Is Not Known

The alternative to knowing the length of a string is to put a special character (usually zero) at the end of each message to show its limit. This is called a delimiter. Note that Atari users cannot make zero the delimiter because zero is used to represent the space character. A zero works well for other computers because, in ASCII, the value 0 has no character or function (such as carriage return) coded to it. Consequently, any time the computer loads a zero into the accumulator (which will flip up the Z flag), it will then know that it is at the end of your message. At 4070, we might have a couple of error messages: "Ball out of rangeOTime nearly up!0". (These are numeric, not ASCII, zeros. ASCII zero has a value of 48.)

To print the time warning message to the top of the CBM/PET screen (this is in decimal):

2000 LDY #0

2002 LDA 4088,Y (get the "T")

2005 BEQ 2005 (the LDA just above will flip the zero flag up if it loads a zero, so we forward branch out of our message-printing loop. "BEQ 2005" is a dummy target, used until we know the actual target and can POKE it into 2006.)

2007 STA 32768,Y (we're using the Y as a double-duty offset again)

The Instruction Set 6

2010 INY

2011 JMP 2002 (in this loop, we always jump back. Our exit

from the loop is not here, at the end. Rather, it is the Branch if EQual which is within the loop.) 2014 (continue with another part of the program)

By the way, you should notice that the Simple Assembler will reject the commas in this example and, if you've forgotten to set line 10 to accept decimal, it will not accept the single zero in LDY #0. Also, if you get unpredictable results, maybe decimal 2000 is not a safe address to store your ML. You might need to use some other practice area.

Now that we know the address which follows the loop (2014), we can POKE that address into the "false forward branch" we left in address 2006. What number do we POKE into 2006? Just subtract 2007 from 2014, which is seven. Using the Simple Assembler, type POKE and you can take care of this while you remember it. The assembler will perform the POKE and then return to wait for your next instruction.

Both of these ways of handling messages are effective, but you must make a list on paper of the starting addresses of each message. In ML, you have the responsibility for some of the tasks that BASIC (at an expense of speed) does for you. Also, no message can be larger than 255 using the methods above because the offset and counter registers count only that high before starting over at zero again. Printing two strings back-to-back gives a longer, but still under 255 byte, message:

2000 LDY #0

2002 LDX #2 (in this example, we use X as a counter which

represents the number of messages we are

printing) 2004 LDA 4000,Y (get the "B" from "Ball out of .. .") 2007 BEQ 2016 (go to reduce [and check] the value of X) 2009 STA 32768,Y (we're using the Y as a double-duty offset

again)

2012 INY

2013 JMP 2004

2016 INY (we need to raise Y since we skipped that step

when we branched out of the loop)

2017 DEX (at the end of the first message, X will be

a " 1"; at the end of the second message, it will be zero)

2018 BNE 2004 (if X isn't down to zero yet, re-enter the loop to

print out the second message)

6 The Instruction Set

To fill your screen with instructions instantly (say at the start of a game), you can use the following mass-move. We'll assume that the instructions go from 5000 to 5400 in memory and you want to transfer them to the PET screen (at $8000). If your computer's screen RAM moves around (adding memory to VIC will move the screen RAM address), you will need to know and substitute the correct address for your computer in these examples which print to the screen. This is in hex:

2000 LDY #0

2002 LDA 5000,Y

2005 STA 8000, Y

2008 LDA 5100,Y

200B STA 8100,Y

200E LDA 5200,Y

2011 STA 8200,Y

2014 LDA 5300,Y

2017 STA 8300,Y

201A INY

201B BNE 2002 (if Y hasn't counted up to zero — which comes just above 255 — go back and load-store the next character in each quarter of the large message )

This technique is fast and easy any time you want to mass-move one area of memory to another. It makes a copy and does not disturb the original memory. To mass-clear a memory zone (to clear the screen, for example), you can use a similar loop, but instead of loading the accumulator each time with a different character, you load it at the start with the character your computer uses to blank the screen. (Commodore including VIC and Apple = decimal 32; Atari = 0):

Of course, you could simply JSR to the routine which already exists in your BASIC to clear the screen. In Chapter 7 we will explore the techniques of using parts of BASIC as examples to learn from and also as a collection of ready-made ML subroutines. Now, though, we can look at how subroutines are handled in ML.

The Instruction Set 6

5. The Subroutine and Jump Group: JMPJSR, RTS

JMP has only one useful addressing mode: Absolute. You give it a firm, two-byte argument and it goes there. The argument is put into the Program Counter and control of the computer is transferred to this new address where an instruction there is acted upon. (There is a second addressing mode, JMP Indirect, which, you will recall, has a bug and is best left unused.)

JSR can only use Absolute addressing.

RTS's addressing mode is Implied. The address is on the stack, put there during the JSR.

None of these instructions has any effect on the flags.

JSR (Jump to SubRoutine) is the same as GOSUB in BASIC, but instead of giving a line number, you give an address in memory where the subroutine sits. RTS (ReTurn from Subroutine) is the same as RETURN in BASIC, but instead of returning to the next BASIC command, you return to the address following the JSR instruction (it's a three-byte-long ML instruction containing JSR and the two-byte target address). JMP (JuMP) is GOTO. Again, you JMP to an address, not a line number. As in BASIC, there is no RETURN from a JMP.

Some Further Cautions About The Stack

The stack is like a pile of coins. The last one you put on top of the pile is the first one pulled off later. The main reason that the 6502 sets aside an entire page of memory especially for the stack is that it has to know where to go back to after GOSUBs and JSRs.

A JSR instruction pushes the correct return address onto the "stack" and, later, the next RTS "pulls" the top two numbers off the stack to use as its argument (target address) for the return. Some programmers, as we noted before, like to play with the stack and use it as a temporary register to PHA (PusH Accumulator onto the stack). This sort of thing is best avoided until you are an advanced ML programmer. Stack manipulations often result in a very confusing program. Handling the stack is one of the few things that the computer does for you in ML. Let it.

The main function of the stack (as far as we're concerned) is to hold return addresses. It's done automatically for us by "pushes" with the JSR and, later, "pulls" (sometimes called pops) with the RTS. If we don't bother the stack, it will serve us well. There are thousands upon thousands of cells where you could temporarily leave the accumulator — or any other value — without fouling up the orderly arrangement of your return addresses.

Subroutines are extremely important in ML programming. ML programs are designed around them, as we'll see. There are times

6 The Instruction Set

when you'll be several subroutines deep (one will call another which calls another); this is not as confusing as it sounds. Your main Player-input routine might call a print-message subroutine which itself calls a wait-until-key-is-pressed subroutine. If any of these routines PHA (PusH the Accumulator onto the stack), they then disturb the addresses on the stack. If the extra number on top of the stack isn't PLA-ed off (PulL Accumulator), the next RTS will pull off the number that was PHA'ed and half of the correct address. It will then merrily return to what it thinks is the correct address: it might land somewhere in the RAM, it might go to an address at the outer reaches of your operating system — but it certainly won't go where it should.

Some programmers like to change a GOSUB into a GOTO (in the middle of the action of a program) by PLA PLA. Pulling the two top stack values off has the effect of eliminating the most recent RTS address. It does leave a clean stack, but why bother to JSR at all if you later want to change it to a GOTO? Why not use IMP in the first place?

There are cases, too, when the stack has been used to hold the current condition of the flags (the Status Register byte). This is pushed/pulled from the stack with PHP (PusH Processor status) and PLP (PulL Processor status). If you should need to "remember" the condition of the status flags, why not just PHP PLA STA $NN? ("NN" means the address is your choice.) Set aside a byte somewhere that can hold the flags (they are always changing inside the Status Register) for later and keep the stack clean. Leave stack acrobatics to FORTH programmers. The stack, except for advanced ML, should be inviolate.

FORTH, an interesting language, requires frequent stack manipulations. But in the FORTH environment, the reasons for this and its protocol make excellent sense. In ML, though, stack manipulations are a sticky business.

Saving The Current Environment

There is one exception to our leave-the-stack-alone rule. Sometimes (especially when you are "borrowing" a routine from BASIC) you will want to take up with your own program from where it left off. That is, you might not want to write a "clear the screen" subroutine because you find the address of such a routine on your map of BASIC. However, you don't know what sorts of things BASIC will do in the meantime to your registers or your flags, etc. In other words, you just want to clear the screen without disturbing the flow of your program by unpredictable effects on your X, Y, A, and status registers. In such a case, you can use the following "Save the state of things" routine:

2000 PHP (push the status register onto the stack)

2001 PHA

82

The Instruction Set 6

2002 TXA

2003 PHA

2004 TYA

2005 PHA

2006 JSR (to the clear-the-screen routine in BASIC. The RTS

will remove the return address [2009], and you'll have a mirror image of the things you had pushed onto the stack. They are pulled out in reverse order, as you can see below. This is because the first pull from the stack will get the most recently pushed number. If you make a little stack of coins, the/;'rsf one you pull off will be the last one you put onto the stack.)

2009 PLA (now we reverse the order to get them back)

2010 TAY

2011 PLA

2012 TAX

2013 PLA (this one stays in A)

2014 PLP (the status register)

Saving the current state of things before visiting an uncharted, unpredictable subroutine is probably the only valid excuse for playing with the stack as a beginner in ML. The routine above is constructed to leave the stack intact. Everything that was pushed on has been pulled back off.

The Significance Of Subroutines

Maybe the best way to approach ML program writing — especially a large program — is to think of it as a collection of subroutines. Each of these subroutines should be small. It should be listed on a piece of paper followed by a note on what it needs as input and what it gives back as parameters. "Parameter passing" simply means that a subroutine needs to know things from the main program (parameters) which are handed to it (passed) in some way.

The current position of the ball on the screen is a parameter which has its own "register" (we set aside a register for it at the start when we were assigning memory space on paper). So, the "send the ball down one space" subroutine is a double-adder which adds 40 or whatever to the "current position register." This value always sits in the register to be used any time any subroutine needs this information. The "send the ball down one" subroutine sends the current-position parameter by passing it to the current-position register.

This is one way that parameters are passed. Another illustration might be when you are telling a delay loop how long to delay. Ideally, your delay subroutine will be multi-purpose. That is, it can delay for

6 The Instruction Set

anywhere from V2 second to 60 seconds or something. This means that the subroutine itself isn't locked into a particular length of delay. The main program will "pass" the amount of delay to the subroutine.

3000 LDY #0

3002 INY -

3003 BNE 3002

3005 DEX —-

3006 BNE 3000

3008 RTS ~

Notice that X never is initialized (set up) here with any particular value. This is because the value of X is passed to this subroutine from the main program. If you want a short delay, you would:

2000 LDX #5 (decimal) 2002 JSR 3000

And for a delay which is twice as long as that:

2000 LDX #10 —

2002 JSR 3000

In some ways, the less a subroutine does, the better. If it's not entirely self-sufficient, and the shorter and simpler it is, the more versatile it will be. For example, our delay above could function to time responses, to hold sounds for specific durations, etc. When you make notes, write something like this: 3000 DELAY LOOP (Expects duration in X. Returns 0 in X.). The longest duration would be LDX #0. This is because the first thing that happens to X in the delay subroutine is DEX. If you DEX a zero, you get 255. If you need longer delays than the maximum value of X, simply: *"-

3000 LDX #0

3002 JSR 3000

3005 JSR 3000 (notice that we don't need to set X to zero this

second time. It returns from the subroutine

with a zeroed X.)

—

You could even make a loop of the JSR's above for extremely long delays. The point to notice here is that it helps to document each subroutine in your library: what parameters it expects, what registers, flags, etc., it changes, and what it leaves behind as a result. This documentation — a single sheet of paper will do — helps you remember each routine's address and lets you know what effects and preconditions are involved.

JMP

Like BASIC'S GOTO, JMP is easy to understand. It goes to an address: JMP 5000 leaps from wherever it is to start carrying out the

The Instruction Set 6

instructions which start at 5000. It doesn't affect any flags. It doesn't do anything to the stack. It's clean and simple. Yet some advocates of "structured programming" suggest avoiding JMP (and GOTO in BASIC). Their reasoning is that JMP is a shortcut and a poor programming habit.

For one thing, they argue, using GOTO makes programs confusing. If you drew lines to show a program's "flow" (the order in which instructions are carried out), a program with lots of GOTO's would look like boiled spaghetti. Many programmers feel, however, that JMP has its uses. Clearly, you should not overdo it and lean heavily on JMP. In fact, you might see if there isn't a better way to accomplish something if you find yourself using it all the time and your programs are becoming impossibly awkward. But JMP is convenient, often necessary in ML.

A 6502 Bug

On the other hand, there is another, rather peculiar JMP form which is hardly ever used in ML: JMP (5000). This is an indirect jump which works like the indirect addressing we've seen before. Remember that in Indirect,Y addressing (LDA (81),Y), the number in Y is added to the address found in 81 and 82. This address is the real place we are LDAing from, sometimes called the effective address. If 81 holds a 00, 82 holds a 40, and Y holds a 2, the address we LDA from is going to be 4002. Similarly (but without adding Y), the effective address formed by the two bytes at the address inside the parentheses becomes the place we JMP to in JMP (5000).

There are no necessary uses for this instruction. Best avoid it the same way you avoid playing around with the stack until you're an ML expert. If you find it in your computer's BASIC ROM code, it will probably be involved in an "indirect jump table," a series of registers which are dynamic. That is, they can be changed as the program progresses. Such a technique is very close to a self-altering program and would have few uses for the beginner in ML programming. Above all, there is a bug in the 6502 itself which causes indirect JMP to malfunction under certain circumstances. Put JMP ($NNNN) into the same category as BPL and BMI. Avoid all three.

If you decide you must use indirect JMP, be sure to avoid the edge of pages: JMP ($NNFF). The "NN" means "any number." Whenever the low byte is right on the edge, if $FF is ready to reset to 00, this instruction will correctly use the low byte (LSB) found in address $NNFF, but it will not pick up the high byte (MSB) from $NNFF plus one, as it should. It gets the MSB from NN00!

Here's how the error would look if you had set up a pointer to address $5043 at location $40FF:

$40FF 43 $4100 50

6 The Instruction Set

Your intention would be to JMP to $5403 by bouncing off this pointer. You would write JMP ($40FF) and expect that the next instruction the computer would follow would be whatever is written at $5043. Unfortunately, you would land at $0043 instead (if address $4000 held a zero). It would get its MSB from $4000.

6. Debuggers: BRKandNOP

BRK and NOP have no argument and are therefore members of that class of instructions which use only the Implied addressing mode. They also affect no flags in any way with which we would be concerned. BRK does affect the I and B flags, but since it is a rare situation which would require testing those flags, we can ignore this flag activity altogether.

After you've assembled your program and it doesn't work as expected (few do), you start debugging. Some studies have shown that debugging takes up more than fifty percent of programming time. Such surveys might be somewhat misleading, however, because "making improvements and adding options" frequently takes place after the program is allegedly finished, and would be thereby categorized as part of the debugging process.

In ML, debugging is facilitated by setting breakpoints with BRK and then seeing what's happening in the registers or memory. If you insert a BRK, it has the effect of halting the program and sending you into your monitor where you can examine, say, the Y register to see if it contains what you would expect it to at this point in the program. It's similar to BASIC'S STOP instruction:

2000 LDA #15

2002 TAY

2003 BRK

If you run the above, it will carry out the instructions until it gets to BRK when it will put the program counter plus two on the stack, put the status register on the stack, and load the program counter with whatever is in addresses $FFFE, $FFFF. These are the two highest addresses in your computer and they contain the vector (a pointer) for an interrupt request (IRQ).

These addresses will point to a general interrupt handler and, if your computer has a monitor, its address might normally be found here. Remember, though, that when you get ready to CONT, the address on the top of the stack will be the BRK address plus two. Check the program counter (it will appear when your monitor displays the registers) to see if you need to modify it to point to the

The Instruction Set 6

next instruction instead of pointing, as it might be, to an argument. Some monitors adjust the program counter when they are BRKed to so that you can type g (go) in the same way that you would type CONT in BASIC. See the instructions for your particular monitor.

Debugging Methods

In effect, you debug whenever your program runs merrily along and then does something unexpected. It might crash and lock you out. You look for a likely place where you think it is failing and just insert a BRK right over some other instruction. Remember that in the monitor mode you can display a hex dump and type over the hex numbers on screen, hitting RETURN to change them. In the example above, imagine that we put the BRK over an STY 8000. Make a note of the hex number of the instruction you covered over with the BRK so you can restore it later. After checking the registers and memory, you might find something wrong. Then you can fix the error.

If nothing seems wrong at this point, restore the original STY over the BRK, and insert a BRK in somewhere further on. By this process, you can isolate the cause of an oddity in your program. Setting breakpoints (like putting STOP into BASIC programs) is an effective way to run part of a program and then examine the variables.

If your monitor or assembler allows single-stepping, this can be an excellent way to debug, too. Your computer performs each instruction in your program one step at a time. This is like having BRK between each instruction in the program. You can control the speed of the stepping from the keyboard. Single-stepping automates breakpoint checking. It is the equivalent of the TRACE command sometimes used to debug BASIC programs.

Like BRK ($00), the hex number of NOP ($EA) is worth memorizing. If you're working within your monitor, it will want you to work in hex numbers. These two are particularly worth knowing. NOP means No OPeration. The computer slides over NOP's without taking any action other than increasing the program counter. There are two ways in which NOP can be effectively used.

First, it can be an eraser. If you suspect that STY 8000 is causing all the trouble, try running your program with everything else the same, but with STY 8000 erased. Simply put three EA's over the instruction and argument. (Make a note, though, of what was under the EA's so you can restore it.) Then, the program will run without this instruction and you can watch the effects.

Second, it is sometimes useful to use EA to temporarily hold open some space. If you don't know something (an address, a graphics value) during assembly, EA can mark that this space needs to be filled in later before the program is run. As an instruction, it will

6 The Instruction Set

let the program slide by. But, remember, as an address or a number, EA will be thought of as 234. In any case, EA could become your "fill this in" alert within programs in the way that we use self-branching (leaving a zero after a BNE or other branch instruction) to show that we need to put in a forward branch's address.

When the time comes for you to "tidy up" your program, use your monitor's "find" command, if it has one. This is a search routine: you tell it where to start and end and what to look for, and it prints out the addresses of any matches it finds. It's a useful utility; if your monitor does not have a search function, you might consider writing one as your first large ML project. You can use some of the ideas in Chapter 8 as a starting point.

Less Common Instructions

The following instructions are not often necessary for beginning applications, but we can briefly touch on their main uses. There are several "logical" instructions which can manipulate or test individual bits within each byte. This is most often necessary when interfacing. If you need to test what's coming in from a disk drive, or translate on a bit-by-bit level for I/O (input/output), you might work with the "logical" group.

In general, this is handled for you by your machine's operating system and is well beyond beginning ML programming. I/O is perhaps the most difficult, or at least the most complicated, aspect of ML programming. When putting things on the screen, programming is fairly straightforward, but handling the data stream into and out of a disk is pretty involved. Timing must be precise, and the preconditions which need to be established are complex.

For example, if you need to "mask" a byte by changing some of its bits to zero, you can use the AND instruction. After an AND, both numbers must have contained a 1 in any particular bit position for it to result in a 1 in the answer. This lets you set up a mask: 00001111 will zero any bits within the left four positions. So, 00001111 AND 11001100 result in 00001100. The unmasked bits remained —

unchanged, but the four high bits were all masked and zeroed. The ORA instruction is the same, except it lets you mask to set bits (make ^

them a 1). 11110000 ORA 11001100 results in 11111100. The accumulator will hold the results of these instructions. _

EOR (Exclusive OR) permits you to "toggle" bits. If a bit is one it will go to zero. If it's zero, it will flip to one. EOR is sometimes useful in games. If you are heading in one direction and you want to go back when bouncing a ball off a wall, you could "toggle." Let's say that you use a register to show direction: when the ball's going up, the byte contains the number 1 (00000001), but down is zero (00000000). To toggle this least significant bit, you would EOR with 00000001. This would flip 1 to zero and zero to 1. This action results in the

~

The Instruction Set 6

complement of a number. 11111111 EOR 11001100 results in 00110011. To know the effects of these logical operators, we can look them up in "truth tables" which give the results of all possible combinations of zeros and ones:

AND OR EOR

0AND0=0 0OR0=0 0EOR0=0

0AND1=0 0ORl=l 0EORl=l

1AND0=0 lOR0=l 1EORO=1

1AND1=1 10R1=1 1EOR1=0

BIT Tests

Another instruction, BIT, also tests (it does an AND), but, like CMP, it does not affect the number in the accumulator — it merely sets flags in the status register. The N flag is set (has a 1) if bit seven has a 1 (and vice versa). The V flag responds similarly to the value in the sixth bit. The Z flag shows if the AND resulted in zero or not. Instructions, like BIT, which do not affect the numbers being tested are called nondestructive.

We discussed LSR and ASL in the chapter on arithmetic: they can conveniently divide and multiply by two. ROL and ROR rotate the bits left or right in a byte but, unlike with the Logical Shift Right or Arithmetic Shift Left, no bits are dropped during the shift. ROL will leave the 7th (most significant) bit in the carry flag, leave the carry flag in the 0th (least significant bit), and move every other bit one space to the left:

ROL 11001100 (with the carry flag set) results in

10011001 (carry is still set, it got the leftmost 1)

If you disassemble your computer's BASIC, you may well look in vain for an example of ROL, but it and ROR are available in the 6502 instruction set if you should ever find a use for them. Should you go into advanced ML arithmetic, they can be used for multiplication and division routines.

Three other instructions remain: SEI (SEt Interrupt), RTI (ReTurn from Interrupt), and CLI (CLear Interrupt). These operations are, also, beyond the scope of a book on beginning ML programming, but we'll briefly note their effects. Your computer gets busy as soon as the power goes on. Things are always happening: timing registers are being updated; the keyboard, the video, and the peripheral connectors are being refreshed or examined for signals. To "interrupt" all this activity, you can SEI, perform some task, and then CLI to let things pick up where they left off.

SEI sets the interrupt flag. Following this, all maskable interruptions (things which can be blocked from interrupting when the interrupt status flag is up) are no longer possible. There are also

6 The Instruction Set

non-maskable interrupts which, as you might guess, will jump in anytime, ignoring the status register.

The RTI instruction (ReTurn from Interrupt) restores the program counter and status register (takes them from the stack), but the X, Y, etc., registers might have been changed during the interrupt. Recall that our discussion of the BRK involved the above actions. The key difference is that BRK stores the program counter plus two on the stack and sets the B flag on the status register. CLI puts the interrupt flag down and lets all interrupts take place.

If these last instructions are confusing to you, it doesn't matter. They are essentially hardware and interface related. You can do nearly everything you will want to do in ML without them. How often have you used WAIT in BASIC?

Borrowing From BASIC

BASIC is a collection of ML subroutines. It is a large web of hundreds of short, ML programs. Why not use some of them by JSRing to them? At times, this is in fact the best solution to a problem.

How would this differ from BASIC itself? Doesn't BASIC just create a series of JSR's when it RUNs? Wouldn't using BASIC'S ML routines in this way be just as slow as BASIC?

In practice, you will not be borrowing from BASIC all that much. One reason is that such JSRing makes your program far less portable, less easily RUN on other computers or other models of your computer. When you JSR to an address within your ROM set to save yourself the trouble of re-inventing the wheel, you are, unfortunately, making your program applicable only to machines which are the same model as yours. The subroutine to allocate space for a string in memory is found at $D3D2 in the earliest PET model. A later version of PET BASIC (Upgrade) used $D3CE and the current models use $C61D. With Atari, Texas Instruments, Sinclair and other computers as exceptions, Microsoft BASIC is nearly universally used in personal computers. But each computer's version of Microsoft differs in both the order and the addresses of key subroutines.

Kernals And Jump Tables

To help overcome this lack of portability, some computer manufacturers set aside a group of frequently used subroutines and create a Jump Table, or kernal, for them. The idea is that future, upgraded BASIC versions will still retain this table. It would look something like this:

FFCF 4C 15 F2 (INPUT one byte)

FFD2 4C 66 F2 (OUTPUT one byte)

FFD5 4C 01 F4 (LOAD something)

FFD8 4C DD F6 (SAVE something)

This example is part of the Commodore kernal.

There is a trick to the way this sort of table works. Notice that each member of the table begins with 4C. That's the JMP instruction and, if you land on it, the computer bounces right off to the address which follows. $FFD2 is a famous one in Commodore computers. If you load the accumulator with a number (LDA #65) and then JSR FFD2, a character will be printed on the screen. The screen location is

7 Borrowing From BASIC

incremented each time you use it, so it works semi-automatically. In

other words, it also keeps track of the current "cursor position" for

you. -^

This same "output" routine will work for a printer or a disk or a tape — anything that the computer sees as an output device. However, unless you open a file to one of the other devices (it's simplest to do this from BASIC in the normal way and then SYS, USR, or CALL to an ML subroutine), the computer defaults to (assumes) the screen as the output device, and FFD2 prints there.

What's curious about such a table is that you JSR to FFD2 as you would to any other subroutine. But where's the subroutine? It's not —.

at FFD5. That's a different JMP to the LOAD code. A naked JMP (there is no RTS here in this jump table) acts like a rebound: you hit one of these JMP's in the table and just bounce off it to the true subroutine. ^

The real subroutine (at $F266 in one BASIC version's $FFD2's JMP) will perform what you expect. Why not just JSR to F266 directly? Because, on other models of Commodore computers — Original BASIC, for example — the output subroutine is not located at F266. It's somewhere else. But a JSR to FFD2 will rebound you to the right address in any Commodore BASIC. All Commodore machines have the correct JMP for their particular BASIC set up at FFD2. This means that you can JSR to FFD2 on any Commodore computer and get predictable results, an output of a byte.

So, if you look into your BASIC code and find a series of JMP's (4C xx xx 4C xx xx), it's a jump table. Using it should help make your programs compatible with later versions of BASIC which might be released. Though this is the purpose of such tables, there are never any guarantees that the manufacturer will consistently observe them. And, of course, the program which depends on them will certainly not work on any other computer brand.

What's Fastest?

Why, though, is a JSR into BASIC code faster than a BASIC program? When a BASIC program RUNs, it is JSRing around inside itself. The answer is that a program written entirely in ML, aside from the fact that it borrows only sparingly from BASIC prewritten routines, differs from BASIC in an important way. A finished ML program is like compiled code; that is, it is ready to execute without any overhead. In BASIC each command or instruction must be interpreted as it RUNs. This is why BASIC is called an "interpreter." Each instruction must be looked up in a table to find its address in ROM. This takes time. Your ML code will contain the addresses for its JSR's. When ML runs, the instructions don't need the same degree of interpretation by the computer.

There are special programs called compilers which take a BASIC

~

Borrowing From BASIC 7

program and transform ("compile") it into ML-like code which can then be executed like ML, without having to interpret each command. The JSR's are within the compiled program, just as in ML. Ordinarily, compiled programs will RUN perhaps 20 to 40 times faster than the BASIC program they grew out of. (Generally, there is a price to pay in that the compiled version is almost always larger than its BASIC equivalent.)

Compilers are interesting; they act almost like automatic ML writers. You write it in BASIC, and they translate it into an ML-like program. Even greater improvements in speed can be achieved if a program uses no floating point (decimal points) in the arithmetic. Also, there are "optimized" compilers which take longer during the translation phase to compile the finished program, but which try to create the fastest, most efficient program design possible. A good compiler can translate an 8K BASIC program in two or three minutes.

GET And PRINT

Two of the most common activities in a computer program are getting characters from the keyboard and printing them to the screen. To illustrate how to use BASIC from within an ML program, we'll show how both of these tasks can be accomplished from within ML.

For the Atari, SF6E2 works like BASIC'S GET#. If you JSR $F6E2, the computer will wait until a key is pressed on the keyboard. Then, when one is pressed, the numerical code for that key is put into the accumulator, and control is returned to your ML program. To try this, type:

2000 JSR $F6E2 2003 BRK

Then run this program and hit a key on the keyboard. Notice that the code number for that letter appears in the accumulator.

Another location within Atari's BASIC ROM will print a character (whatever's in the accumulator) to the next available position on the screen. This is like PUT#6. Try combining the above GET# with this:

2000 JSR $F6E2 (get the character) 2003 JSR $F6A4 (print to the screen) 2006 BRK

Using $F6A4 changes the numbers in the X and Y registers (explained below).

For the Apple, there are BASIC routines to accomplish these same jobs. Apple Microsoft BASIC'S GET waits for user input. (Commodore's GET doesn't wait for input.)

2000 JSR $FD0C (GET a byte from the keyboard) 2003 RTS (the character is in the accumulator)

7 Borrowing From BASIC

~

This address, $FD0C, will wait until the user types in a character. It will position a flashing cursor at the correct position. However, it will not print an "echo," an image of the character on the screen.

To print to the screen:

2000 LDA #65 (put "a" into the accumulator)

2002 JSR $FBFD (print it)

For Commodore computers (VIC, 64, and PET/CBM) which also use Microsoft BASIC, the two subroutines are similar:

2000 JSR $FFE4 (GET whatever key is being pressed)

2003 BEQ 2000 (if no key is pressed, a zero is in the

accumulator, so you BEQ back and try for a character again) 2005 RTS (the character's value is in the accumulator)

The $FFE4 is another one of those "kernal" jump table locations common to all Commodore machines. It performs a GET. -^

An ML routine within your BASIC which keeps track of the current cursor position and will print things to the screen is often needed in ML programming.

The VIC, 64, and PET/CBM use the routine called by $FFD2. -..

Apple uses $FDED. Atari uses $F6A4.

You can safely use the Y register to print out a series of letters (Y ^

used as an index) in any BASIC except Atari's. You could print out a whole word or block of text or graphics stored at $1000 in the following way. (See Program 7-1.)

Atari's BASIC alters the X and Y registers when it executes its "print it" subroutine so you need to keep count some other way. Whenever you borrow from BASIC, be alert to the possibility that the A, X, or Y registers, as well as the flags in the status register, might well be changed by the time control is returned to your ML program. Here's one way to print out messages on the Atari. (See Program 7-2.)

If you look at Appendix B you will see that there are hundreds of freeze-dried ML modules sitting in BASIC. (The maps included in this book are for VIC, PET, Atari, and Commodore 64. Appendix B contains information on how to obtain additional maps for Apple and Atari.)

It can be intimidating at first, but disassembling some of these routines is a good way to discover new techniques and to see how professional ML programs are constructed. Study of your computer's BASIC is worth the effort, and it's something you can do for yourself. From time to time, books are published which go into great detail about each BASIC routine. They, too, are often worth studying.

94

~

~

~

~

Borrowing From BASIC 7

C3 z

03

Eh M

M Q O

U X Eh H X

a h

Z Eh

w u «

o w

Eh Eh

Q

O

171 H

K

Eh

H

as o

Eh

W

o o o

06

Cd

Q

cc

63

a

LTl C3 LTI CO

•to- -

Z O t—1

CO

os

>

Id

a

Ol CN

Ou CO-

oa 03 o a CQ

H 06

o

Q

o

£ £ O U

03

W U

Eh Z

Z M

CD 03

O Eh

o w

O O O O O

i-l cm ro «4< in

o o o o o

o o o o o

o

z

o J

CO 06

<

U

CO

CS z

M

03 Eh CO

H

OS

o

Q

o £ £

o u

ta

■z

o

CO

D

J a,

CO

Eh

a

z

w

Cd Eh

o z

o ooooooooooooo

vd r^cocriOi-HCNco^mior-oocT,

O OOOt—It—li— I 1—1 I—I r— I.—If— I r— Ir— I

o ooooooooooooo

00

o

o ■<* m

LD •"# "tf

in cm o

in in in

ro in in cm

in ^ in m

I I I i

O ro >x> <T\

O C3 O CD

o o o o

O &J CN fci

O O O O

CN

Q

m in o pu

o

o

CJ o

O CM O II CN

II

a, o o

O CXi O CO O O O

I I I I I I I

<; o Cu cm ro in r*

O O O i—I .—I i—I rH

O O O O O O O

CM (N (N CN (N CM (N

CO CO

<c

a

z

[d

H

J H

J

w

m <

z

M

06

CO

03

o <;

o o

o o

II CM II

33

Eh Eh

a 03

Z <C

W Eh

J CO

in cm

in a

0-i Cu

II

03 Eh

W l-H

Eh Eh

Z Z

D l-H

O 03

7 Borrowing From BASIC

~

z o

CO

Ed Q

IX)

a

w u

<c a,

O 06 H

IS!

Ed

a z

as os ><

Eh Eh

z z

n n

o o

o o

os

Eh W

O H Eh

Z Eh Z

M Z CD

OS M O

OS O

OS Ed Eh Z ED O O

=8= * 3£ {£ Ol * =»*; .„ J

QEhQQCOZQSZEhW

ooooooooooooooo

OOOOHrlrHHrHHHHi-lrlN OOOOOOOOOOOOOOO

O [xj

oininofinffiino OLnino<;tnoLnEu

(Mnooiowuiinoo <oo<ffl(NHi<OQw

I l I I I I I I I I

<;uwopncm<uw

OOOrHrHfHrHr-HrHrH OOOOOOOOOO

co co

<

a,

a

z

Ed

CO

o o o

ee

Eh Eh

O OS

Z <

H Eh

iJ CO

Ed J

HH fcl

J W

Q

in «* u-i <;

O VD

OS

w

Eh

Z

O OS O 0j

Eh

Z

-

-

~ -"

Building A Program

Using what we've learned so far, and adding a couple of new techniques, let's build a useful program. This example will demonstrate many of the techniques we've discussed and will also show some of the thought processes involved in writing ML.

Among the computer's more impressive talents is searching. It can run through a mass of information and find something very quickly. We can write an ML routine which looks through any area of memory to find matches with anything else. If your BASIC doesn't have a FIND command or its equivalent, this could come in handy. Based on an idea by Michael Erperstorfer published in COMPUTE! Magazine, this ML program will report the line numbers of all the matches it finds.

Safe Havens

Before we go through some typical ML program-building methods, let's clear up the "where do I put it?" question. ML can't just be dropped anywhere in memory. When the Simple Assembler asks "Starting Address?", you can't give it any address you want to. RAM is used in many ways. There is always the possibility that a BASIC program might be residing in part of it (if you are combining ML with a BASIC program). Or BASIC might use part of RAM to store arrays or variables. During execution, these variables might write (POKE) into the area that you placed your ML program, destroying it. Also, the operating system, the disk operating system, cassette/disk loads, printers — they all use parts of RAM for their activities. There are other things going on in the computer beside your hard-won ML program.

Obviously, you can't put your ML into ROM addresses. That's impossible. Nothing can be POKEd into those addresses. The 64 is an exception to this. You can POKE into ROM areas because a RAM exists beneath the ROM. Refer to the Programmer's Reference Guide or see Jim Butterfield's article on 64 architecture (COMPUTE! Magazine, January 1983) for details.

Where to put ML? There are some fairly safe areas.

If you are using Applesoft in ROM, 768 to 1023 ($0300 to $03FF) is safe. Atari's page six, 1536 to 1791 ($0600 to $06FF) is good. The 64 and VIC's cassette buffer at 828 to 1019 ($033C to $03FB) are good if you are not LOADing or SAVEing from tape.

97

8 Building A Program

The PET/CBM makes provision for a second cassette unit. In theory, it would be attached to the computer to allow you to update files or make copies of programs from Cassette #1 to Cassette #2. In practice, no one has mentioned finding a use for a second cassette drive. It is just as easy to use a single cassette for anything that a second cassette could do. As a result, the buffer (temporary holding area) for bytes streaming in from the second cassette unit is very safe indeed. No bytes ever flow in from the phantom unit so it is a perfect place to put ML. —.

The "storage problem" can be solved by knowing the free zones, or creating space by changing the computer's understanding -•.

of the start or end of BASIC programs. When BASIC is running, it will set up arrays and strings in RAM memory. Knowing where a —

BASIC program ends is not enough. It will use additional RAM. Sometimes it puts the strings just after the program itself. Sometimes it builds them down from the "top of memory," the highest RAM address. Where are you going to hide your ML routine if you want to use it along with a BASIC program? How are you going to keep BASIC from overwriting the ML code?

Misleading The Computer

If the ML is a short program you can stash it into the safe areas listed above. Because these safe areas are only a couple of hundred bytes long, and because so many ML routines want to use that area, it can become crowded. Worse yet, we've been putting the word "safe" in quotes because it just isn't all that reliable. Apple uses the "safe" place for high-res work, for example. The alternative is to deceive the computer into thinking that its RAM is smaller than it really is. This is — -

the real solution.

Your ML will be truly safe if your computer doesn't even suspect >_

the existence of set-aside RAM. It will leave the safe area alone because you've told it that it has less RAM than it really does. Nothing can overwrite your ML program after you misdirect your computer's operating system about the size of its RAM memory. There are two bytes in zero page which tell the computer the highest RAM address. You just change those bytes to point to a lower address.

These crucial bytes are 55 and 56 ($37,38) in the 64 and VIC. They are 52,53 ($34,35) in PET/CBM Upgrade and 4.0 BASIC. In the PET with Original ROM BASIC, they are 134,135 ($86,87). The Apple uses 115,116 ($73,74), and you lower the Top-of-BASIC pointer just as you do in Commodore machines.

The Atari does something similar, but with the bottom of RAM. It is easier with the Atari to store ML just below BASIC than above it. Bump up the "lomem" pointer to make some space for your ML. It's convenient to start ML programs which are too long to fit into page

Building A Program 8

six ($0600-06FF) at $1F00 and then put this address into lomem. The LSB and MSB are reversed, of course, as the 6502 requires its pointers to be like this:

$02E7 00 $02E8 IF

$02E7,8 is Atari's low memory pointer. You should set up this pointer (LDA $00, STA $02E7, LDA #$1F, STA $02E8) as part of your ML program. Following that pointer setup, JMP $A000 which initializes BASIC. If you are not combining ML with a BASIC program, these preliminary steps are not necessary.

Safe Atari zero page locations include $00-04, $CB-D0, $D4-D9 (if floating point numbers are not being used); $0400 (the printer and cassette buffer), $0500-057F (free), $0580-05FF (if floating point and the Editor are not being used), $0600-06FF (free) are also safe. No other RAM from $0700 (Disk Operating System) to $9FFF or $BFFF is protected from BASIC.

To repeat: address pointers such as these are stored in LSB, MSB order. That is, the more significant byte comes second (this is the reverse of normal, but the 6502 requires it of address pointers). For example, $8000, divided between two bytes in a pointer, would look like this:

0073 00

0074 80

As we mentioned earlier, this odd reversal is a peculiarity of the 6502 that you just have to get used to. Anyway, you can lower the computer's opinion of the top-of-RAM-memory, thereby making a safe place for your ML, by changing the MSB. If you need one page (256 bytes): POKE 116, PEEK (116)-1 (Apple). For four pages (1024 bytes) on the Upgrade and 4.0 PETs: POKE 53, PEEK (53) -4. Then your BA or start of assembling could begin at (Top-of-RAM-255 or Top-of-RAM-1023, respectively. You don't have to worry much about the LSB here. It's usually zero. If not, take that into account when planning where to begin storage of your object code.

Building The Code

Now we return to the subject at hand — building an ML program. Some people find it easiest to mentally break a task down into several smaller problems and then weave them into a complete program. That's how we'll look at our search program. (See Program 8-1.)

For this exercise, we can follow the PET/CBM 4.0 BASIC version to see how it is constructed. All the versions (except Atari's) are essentially the same, as we will see in a minute. The only differences are in the locations in zero page where addresses are temporarily stored, the "start-of-BASIC RAM" address, the routines to print a

8 Building A Program

c o

'35

u

>

U

< ca

u

OinvDr^OOOOOOOOOOOiHOOOOOOiHCNrO^

HrliHrHrlMr'i'ii'inior^Or-iNNrii'UHoi^ajCDCorooj

OOOOOOOOOOOiHMiHi-lhHhHhHrHh-lr-liHrHrHrH OOOOOOOOOOOOOOOOOOOOOOOOO

I

ft.

-

~

~ -

-

-

Building A Program 8

oooooooiHcviro-^in<>or-co<T\oooooooooooo o

morHNn-juii/iuii/ii/iminini/ii/nDt^oomoiHMroH/uno^ oo

H(NM(NN(NOIINNN(NN(M(NnlN(NN(NNcncimmnfOrnn ro

oooooooooooooooooooooooooooo o

O < U) <C rH

o m o mo

O rH O CO i—I O O

4! m a O X Q vo

Oi— itncoi— imcoiHincoi— tin in <:xcoCJXcorjXcorjXcn <

I I I I I I I

<; U X o rH n in

ud u3 »-o r~ r r r-

m n m ro ro ro ro

o o o o o C3 <z>

I l l I I I I I I I I l I

\OCO<UQ(uHN1' lOM^ x

r-~r-r^r^r^r~-cococococooo co

rororororororororocoroco ro

oooooooooooo o

8 Building A Program

oooooorHCNro^HHLni^or^-ootTtOrHoooooorHCNro-^Lnvjor-

O 1 OrHrNrOv}'i 3''^'-HH->3 , ^l'-«l'^f^fH, ir)irilX>t^-OOCT>OrHrHi—IrHrHrHiHrH

n^ii'ff^iiffi"J , ^ , ^'^ l "3 ,, i"5 , '*'*'*'*^'ioioioinuiioininin oooooooooooooooooooooooooooooo

■* ft CPO (0

O DO 00 O CO

00 <Tl U0 LiO CTl LT)

rH l£> 00 <d CD 00

I I I I I I

0 63 O (N M< ^D

CC CO <7\ <T\<T\ a\

m ro ro m ro ro

o o o o o o

I I I I I I I

CD rtl O H H ci Tf

0> <Ti <Ti CTi < r< r<

00 ro ro ro ro ro ro

o o o o o o o

Building A Program 8

CDCTiOOOOOOOOOh-ICMM>JUHOMDOOOOOHN(<1'JU1

i—ii— irNn^incor^oocnooooooooorHCNn^inLnininLnin

LninLnininLnini/iLnLncDC.OCDCDCDCDCDCDCDCDCDCDCDCJ3CD'^>COC£)kO

ooooooooooooooooooooooooooooo

o

o o

O

O

o

cd r~ <: lti <; in o o CQ fa <n CJ

o <: Q CQ <

CQ CQ CQ CQ cd

(NcnooDOr-HOCJo raSCdOcQfaQfa-^CN

in in in in u

< CO f£ 00 ■*

I I I I I I I I I r-cn<;cqCdocN-^r~-si;<;<:<c<:cqcqcqcq

rororornrorororom ooooooooo

i i i l l

<; CJ Cd O CN

CQ CQ CQ CJ CJ

co m co m n

o o o o o

8 Building A Program

~ ^

VOt^CO O.HCNOOO.HCNO

intnin ko«)«)Mxioioi(jio

<D t^O t£> VOVDt*OV£>V£>^jO*£>t£>r—

ooo ooooooooo

Building A Program 8

character and to print a line number, and the RAM where it's safe to store the ML program itself. In other words, change the defined variables between lines 20 and 100 in Program 8-1 and you can use the program on another computer.

We will build our ML program in pieces and then tie them all together at the end. The first phase, as always, is the initialization. We set up the variables and fill in the pointers. Lines 20 and 30 define two, two-byte zero page pointers. L1L is going to point at the address of the BASIC line we are currently searching through. L2L points to the starting address of the line following it.

Microsoft BASIC stores four important bytes just prior to the start of the code in a BASIC line. Take a look at Figure 8-1. The first two bytes contain the address of the next line in the BASIC program. The second two bytes hold the line number. The end of a BASIC line is signaled by a zero. Zero does not stand for anything in the ASCII code or for any BASIC command. If there are three zeros in a row, this means that we have located the "top," the end of the BASIC program. (The structure of Atari BASIC is significantly different. See Figure 8-2.)

But back to our examination of the ML program. In line 40 is a definition of the zero page location which holds a two-byte number that Microsoft BASIC looks at when it is going to print a line number on the screen. We will want to store line numbers in this location as we come upon them during the execution of our ML search program. Each line number will temporarily sit waiting in case a match is found. If a match is found, the program will JSR to the BASIC ROM routine we're calling "PLINE," as defined in line 70. It will need the "current line number" to print to the screen.

Line 50 establishes that BASIC RAM starts at $0400 and line 60 gives the address of the "print the character in the accumulator" ROM routine. Line 100 says to put the object code into the PET's (all BASIC versions) second cassette buffer, a traditional "safe" RAM area to store short ML programs. These safe areas are not used by BASIC, the operating system (OS), or, generally, by monitors or assemblers. If you are working with an assembler or monitor, however, and keep finding that your object code has been messed up — suspect that your ML creating program (the monitor or assembler) is using part of your ''safe" place. They consider it safe too. If this should happen, you'll have to find a better location.

Refer to Program 8-1 to follow the logic of constructing our Microsoft search program. The search is initiated by typing in line zero followed by the item we want to locate. It might be that we are interested in removing all REM statements from a program to shorten it. We would type 0:REM and hit RETURN to enter this into the BASIC program. Then we would start the search by a SYS to the

8 Building A Program

eo

3 W

Building A Program 8

u

if) 1

8 Building A Program

~ *"

~

~ •

Building A Program 8

starting address of the ML program. In the PET 4.0 version of Program 8-1, it would be SYS 864 (hex $0360).

By entering the "sample" string or command into the BASIC program as line zero, we solve two problems. First, if it is a string, it will be stored as the ASCII code for that string, just as BASIC stores strings. If it is a keyword like REM, it will be translated into the "tokenized," one-byte representation of the keyword, just as BASIC stores keywords. The second problem this solves is that our sample is located in a known area of RAM. By looking at Figure 8-1, you can tell that the sample's starting address is always the start of BASIC plus six. In Program 8-1 that means 0406 (see line 550).

Set Up The Pointers

We will have to get the address of the next line in the BASIC program we are searching. And then we need to store it while we look through the current line. The way that BASIC lines are arranged, we come upon the link to the next line's address and the line number before we see any BASIC code itself. Therefore, the first order of business is to put the address of the next line into L1L. Lines 150 through 180 take the link found in start-of-BASIC RAM (plus one) and move it to the storage pointer "L1L."

Next, lines 190 to 250 check to see if we have reached the end of the BASIC program. It would be the end if we had found two zeros in a row as the pointer to the next line's address. If it is the end, the RTS sends us back to BASIC mode.

The subroutine in lines 260 through 440 saves the pointer to the following line's address and also the current line number. Note the double-byte addition in lines 390-440. Recall that we CLC before any addition. If adding four to the LSB (line 400) results in a carry, we want to be sure that the MSB goes up by one during the add-with-carry in line 430. It might seem to make no sense to add a zero in that line. What's the point? The addition is with carry; in other words, if the carry flag has been set up by the addition of four to the LSB in line 400, then the MSB will go up by one. The carry will make this happen.

First Characters

It's better to just compare the first character in a word against each byte in the searched memory than to try to compare the entire sample word. If you are looking for MEM, you don't want to stop at each byte in memory and see if M-E-M starts there. Just look for M's. When you come upon a M, then go through the full string comparison. If line 490 finds a first-character match, it transfers the program to "SAME" (line 520) which will do the entire comparison. On the other hand, if the routine starting at line 451 comes upon a zero (line 470), it knows that the BASIC line has ended (they all end with zero). It then goes down to "STOPLINE" (line 610) which puts the "next line" address

8 Building A Program

~

pointer into the "current line" pointer and the whole process of reading a new BASIC line begins anew.

If, however, a perfect match was found (line 560 found a zero at the end of the 0:REM line, showing that we had come to the end of the sample string) — we go to "PERFECT" and it makes a JSR to print out the line number (line 660). That subroutine bounces back (RTS) to "STOPLINE" which replaces the "current line" (L1L) pointer with the "next line" pointer (L2L). Then we JMP back to "READLINE" which, once again, pays very close attention to zeros to see if the whole BASIC program has ended with double zeros. We have returned to the start of the main loop of this ML program.

This sounds more complicated than it is. If you've followed this so far, you can see that there is enormous flexibility in constructing ML programs. If you want to put the "STOPLINE" segment earlier than the "SAME" subroutine — go ahead. It is quite common to see a structure like this:

INITIALIZATION

LDA #15 STA $83 MAIN LOOP START JSR 1

JSR 2

JSR 3 BEQ START (until some index runs out) RTS (to BASIC)

SUBROUTINES 1

2 (each ends with RTS back to the MAIN LOOP)

3

DATA ~

Table 1

Table 2 ~

Table 3

The Atari FIND Utility

The second source listing, Program 8-2, adds a FIND command to Atari BASIC. You access it with the USR command. It is written to assemble in page six (1536 or $0600) and is an example of a full-blown assembly. You'll need the assembler/editor cartridge to type it in.

After you've entered it, enter "ASM" to assemble it into memory. After it is finished, use the SAVE command to store the object (executable ML) code on tape or disk. Use: ^

SAVE#C: > 0600,067E for tape

SAVE#D:FIND.OBJ<0600 067E for disk —

no

Building A Program 8

You can then put the BASIC cartridge in and enter the machine language with the BASIC loader program, or with the L command of DOS.

Using FIND from BASIC is simple. Say you want to search a master string, A$ for the substring "hello". If B$ contains "hello", the USR call would look like:

POS = USR (1536,ADR(A$),LEN(A$),ADR(B$),LEN(B$) )

POS will contain the position of the match. It will be a memory location within the ADRress of A$. To get the character position within A$, just use POS-ADR(A$) +1. If the substring (B$) is not found, POS will be zero.

It's easy to add commands like this to Atari BASIC. Also see "Getting The Most Out Of USR" in the November 1982 issue of COMPUTE! Magazine (p. 100).

64, Apple, & VIC Versions

Versions of the search routine for the Commodore 64 and VIC-20 and the Apple II are provided as BASIC loader programs. Remember from Chapter 2 that a loader is a BASIC program which POKEs a machine language program (stored in DATA statements) into memory. Once you have entered and run the BASIC programs, you can examine the ML programs using a disassembler. (See Appendix D.)

These versions are similar to the PET Version outlined in Program 8-1. The characters to be searched for are typed in line 0. To start the search in the 64 version (Program 8-3), type SYS 40800. Use CALL 768 to activate the Apple version (Program 8-4). The VIC version (Program 8-5) is activated with SYS 828.

As your skills improve, you will likely begin to appreciate, and finally embrace, the extraordinary freedom that ML confers on the programmer. Learning it can seem fraught with obscurity and rules. It can even look menacing. But there are flights you will soon be taking through your computer. Work at it. Try things. Learn how to find your errors. It's not circular — there will be considerable advances in your understanding. One day, you might be able to sit down and say that you can combine BASIC with ML and do pretty much anything you want to do with your machine.

8 Building A Program

CO

E

H

0 i_ Q.

112

CSiSQSSS&SSSSSQStStSlSIStStSlCStSISS

SH(sro<ti/nfl>cOffiSHCNro<jirnfl>comSHMd

HHHHHHHHHHMMOICNCNtNfNfNNINCinoOtO SSSSSSSSSSSSSSSSSSSSSSSS

Building A Program 8

SS>lS!a?3IStSi<SSSllS3K)tStSIS*S!SSS3<S&&S>tSS3tSS3

^finvi)t^coffi!SH(sm^ , if)ii)r-coo3SHCNnri'invo[^coff»

co en

P P

53 S3 <S S3

ts

ta

S3

u m

u u

00 CO IT) 00 m CO

VO VD CO VO CO VD

S3 H CN ^ IT) r-

Q Q ts & S S

<D <vD v£> kD VD vD

IS IS S S5 IS

8 Building A Program

cscatscsscsjtacsscscsicstatsicsicscacscacsisicscacstsi carHcNro^invDr^co^carHcNco^Ln^r^oocTicSrHCNrO't ^c£ivDvX)'X>vovDco^cor^r^[^[^r^[^r^r^r~-r--oooocococo !ststacstscsssts)c3tatac3ts3tacsicscatacacsiscacac3

W O ta Cn rH

U U Q U Q

incomcoincoinco com

CJDvDOOvOCO'X'CO^O *X> CO

co<(UQHisHm ^ in

SSiSCiSHHH rH i-l

<X>V£)<,Qv£)l£v£llX>VO VOVD

stassssQca tacsj

Building A Program 8

ISSSSSSQISiSISBSSSSSSSSSSSSCSISiS Ln^t^CX>CTitS'HCNm^invOr^CX>CTiCSrHCNm^Ln;Or^C0<TitS

C0CX)COCO00O>(?CTi(7\cna\CJM^(?^QS!aSlSSc2SCSSH SSQBSCSlQSC3SSSSSC5HHHrHHHHHHHH

8 Building A Program

■^J* ro rH \Q

QQC3Q

00 03 < 00 "* >X> CO <

Tf ^J* ^ "r^ 1

Cfl Cfl CC C0

ISISISQ

Building A Program 8

sissiscacasEiisstsssssQssscasssiscas t-~cocyicsirHCNrn^Ln^t^m^cS)iHCNro^l , ijnvot~~oO(TicsirHCN

rlrlrlrlHHHrHHHHHrlrlHrlHrlrlHHrlrlHHH

cq r- o co

U Q U Q

id in inin

<! oo rij en

OHSCM

■^t rf ld in

CO kD ^O vfl

S SIS IS

c£>r-l>J3fav£>m CNC0 nt^CTi

QQrHUQP SQ QQW

^^QrHlXI^COCaii) <tf r-l Kl

<CUfamw<;uQw co q fa

'tcflcoriluwsHn int^-cri

iDininininirniHino vovdvd

^cOcOcOcOcDiflcflcO VOvDvO

cscaQcacsscasQcs! is ts ta

8 Building A Program

.0 <C

+j

fd

o

<u 0

U H (0 0) H in

CW

C fa ■H S

tf

<D <4-l

(0 0

P ■«

o

H to

*C

CD

o t-t

9

33 U

E-< P Eh to ctf OS

o 33

33

23

w a; q

J h <c

Q

a

D

O

to fa E-i to fa fa

H in r- en ^ s> U Q Q n ^ r--

S SSvfl »£) «3 SSSSSS II II II

fa

o

O X

J y

12 X K Pi

p w w u w

ZJQQPi^

J 2! H H fa O

to fa fa fa CO U

o o

C 5)

■H fa

03 fa

e

Q

a D

o fa EH o

S3

0

c u

u

(D Pi

u o U PQ

cr; fa

c

■H

(n

CO

0)

M

■a

13

<u

c

4-1 <D

Pi

53

i +

Q Q K> Q IS

05 <d fa < tS fa

fa to fa to * fa

<; u a q

to fa

D

a w

Q

a

p

o fa

SQissisiasisisisisisisssstSIS f Y i' 5 *ir)vor-~co<nSiHCNn's) , Lnv£ir^aDa>s

Q U Q U S Q cOLnir>inincr<Lnis

r-HsdvOCOrfl^COvO

carHninr^cTiCQQ w SSSSiSSSS s

Q .-) *£> S3 r- <tf UDQSNin C2 5} Q VD yQ ^O Q tS> S S) S S II II II

Dj CN

o p

fa K

Q Q S Q

<C 55 « Z

to fa fa W to fa

u

<

fa X

U Q Q Q CM n Q S S S KD KO

SiSSSSiS

II II II II

to fa to

w fa

Eh Eh

CO fa m (3D i-l Q m UUQDNNifl CS S S O vD VQ i^>

s s s s is ta cs 11 11 11 11

Building A Program 8

Program 8-3. 64 Search BASIC Loader.

799 X=PEEK(55):POKE55,X-l:REM PROTECT ML

800 FOR ADRES=40800TO40913:READ DATTA: POKE ADRES,DATTA:NEXT ADRES

900 PRINT"SYS40800 TO ACTIVATE"

4096 DATA 162, 0, 173, 1, 8, 133

4102 DATA 165, 173, 2, 8, 133, 166

4108 DATA 160, 0, 177, 165, 208, 6

4114 DATA 200, 177, 165, 208, 1, 96

4120 DATA 160, 0, 177, 165, 141, 167

4126 DATA 0, 200, 177, 165, 141, 168

4132 DATA 0, 200, 177, 165, 133, 57

4138 DATA 200, 177, 165, 133, 58, 165

4144 DATA 165, 24, 105, 4, 133, 165

4150 DATA 165, 166, 105, 0, 133, 166

4156 DATA 160, 0, 177, 165, 240, 28

4162 DATA 205, 6, 8, 240, 4, 200

4168 DATA 76, 158, 159, 162, 0, 232

4174 DATA 200, 189, 6, 8, 240, 7

4180 DATA 209, 165, 240, 245, 76, 158

4186 DATA 159, 32, 201, 159, 165, 167

4192 DATA 133, 165, 165, 168, 133, 166

4198 DATA 76, 108, 159, 32, 201, 189

4204 DATA 16.9, 32, 32, 210, 255, 96 READY.

Program 8-4. Apple Version.

700 FOR AD=768TO900: READ DA:POKE A

D,DA:NEXT AD

768 DATA169,76,141,245,3,169

774 DATA16,141,246,3,169,3

780 DATA141,247,3,96,162,0

786 DATA173,1,8,133,1,173

792 DATA2,8,133,2,160,0

798 DATA177,1,208,6,200,177

804 DATA1,208,1,96,160,0

810 DATA177,1,133,3,200,177

816 DATA1,133,4,200,177,1

822 DATA133,117,200,177,1,133

8 Building A Program

828 DATA118,165,1,24,105,4

834 DATA133,1,165,2,105,0

840 DATA133,2,160,0,177,1

846 DATA240,28,205,6,8,240

852 DATA4,200,76,76,3,162

858 DATA0,232,200,189,6,8

864 DATA240,7,209,1,240,245

870 DATA76,76,3,76,119,3

876 DATA165,3,133,1,165,4

882 DATA133,2,76,28,3,169

888 DATA163,32,237,253,32,32

894 DATA237,169,160,32,237,253

900 DATA76,108,3

Program 8-5. VIC-20 Search BASIC Loader.

800 FOR ADRES=828T0941:READ DATTA:POKE ADR

ES,DATTA:NEXT ADRES 810 PRINT"SYS 828 TO ACTIVATE" 828 DATA 162, 0, 173, 1, 16, 133 834 DATA 187, 173, 2, 16, 133, 188 840 DATA 160, 0, 177, 187, 208, 6 846 DATA 200, 177, 187, 208, 1, 96 852 DATA 160, 0, 177, 187, 141, 190 858 DATA 0, 200, 177, 187, 141, 191 864 DATA 0, 200, 177, 187, 133, 57 870 DATA 200, 177, 187, 133, 58, 165 876 DATA 187, 24, 105, 4, 133, 187 882 DATA 165, 188, 105, 0, 133, 188 888 DATA 160, 0, 177, 187, 240, 28 894 DATA 205, 6, 16, 240, 4, 200 900 DATA 76, 122, 3, 162, 0, 232 906 DATA 200, 189, 6, 16, 240, 7 912 DATA 209, 187, 240, 245, 76, 122 918 DATA 3, 32, 165, 3, 165, 190 924 DATA 133, 187, 165, 191, 133, 188 930 DATA 76, 72, 3, 32, 194, 221 936 DATA 169, 32, 32, 210, 255, 96

ML Equivalents Of BASIC Commands

What follows is a small dictionary, arranged alphabetically, of the major BASIC commands. If you need to accomplish something in ML — TAB for example — look it up in this chapter to see one way of doing it in ML. Often, because ML is so much freer than BASIC, there will be several ways to go about a given task. Of these choices, one might work faster, one might take up less memory, and one might be easier to program and understand. When faced with this choice, I have selected example routines for this chapter which are easier to program and understand. At ML speeds, and with increasingly inexpensive RAM memory available, it will be rare that you will need to opt for velocity or memory efficiency.

CLR

In BASIC, this clears all variables. Its primary effect is to reset pointers. It is a somewhat abbreviated form of NEW since it does not "blank out" your program, as NEW does.

We might think of CLR, in ML, as the initialization routine which erases (zeros) the memory locations you've set aside to hold your ML flags, pointers, counters, etc. Before your program RUNs, you may want to be sure that some of these "variables" are set to zero. If they are in different places in memory, you will need to zero them individually:

2000 LDA # 0

2002 STA 1990 (put zero into one of the "variables") 2005 STA 1994 (continue putting zero into each byte which needs to be initialized)

On the other hand, maybe you've got your tables, flags, etc., all lined up together somewhere in a data table at the start or end of your ML program. It's a good idea. If your table is in one chunk of RAM, say from 1985 to 1999, then you can use a loop to zero them out:

2000 LDA # 0

2002 LDY # 15 (Y will be the counter. There are 15 bytes to zero out in this example.)

CONT

2004 STA 1985, Y (the lowest of the 15 bytes) _

2007 DEY

2008 BNE 2004 (let Y count down to zero, BNEing until Y is zero,

then the Branch if Not Equal will let the program fall through to the next instruction at 2010)

CONT

This word allows your program to pick up where it left off after a STOP command (or after hitting the system break key). You might want to look at the discussion of STOP, below. In ML, you can't usually get a running program to stop with the BREAK (or STOP) key. If you like, you could write a subroutine which checks to see if a particular key is being held down on the keyboard and, if it is, BRK:

3000 LDA 96 (or whatever your map says is the "key currently

depressed" location for your machine) —

3002 CMP # 13 (this is likely to be the RETURN key on your

machine, but you'll want CMP here to the value that appears in the "currently pressed" byte for the key you select as your STOP key. It could be any key. If you want to use "A" for your "stop" key, try CMP #65.)

3004 BNE 3007 (if it's not your target key, jump to RTS)

3006 BRK (if it is the target, BRK)

3007 RTS (back to the routine which called this subroutine)

The 6502 places the Program Counter (plus two) on the stack after a BRK.

A close analogy to BASIC is the placement of BRK within ML .».

code for a STOP and then typing .G or GO or RUN — whatever your monitor recognizes as the signal to start execution of an ML program - to CONT.

DATA

In BASIC, DATA announces that the items following the word DATA are to be considered pieces of information (as opposed to being thought of as parts of the program). That is, the program will probably use this data, but the data are not BASIC commands. In ML, such a zone of "non-program" is called a table. It is unique only in that the program counter never starts trying to run through a table to carry out instructions. Program control is never transferred to a table since there are no meaningful instructions inside a table. Likewise, BASIC slides right over its DATA lines.

To keep things simple, tables of data are usually stored together either below the program or above it in memory. (See Figure 9-1.)

DIM

From within the program, tables can be used to print messages to the screen, update or examine flags, etc. If you disassemble your BASIC in ROM, you'll find the words STOP, RUN, LIST, and so forth, gathered together in a table. You can suspect a data table when your disassembler starts giving lots of error messages. It cannot find groups of meaningful opcodes within tables.

Figure 9-1. Typical ML program organization with data tables — one at top or bottom of program.

Bottom of Memory Start Of ML Program

Subroutines

DIM

With its automatic string handling, array management, and error messages, BASIC makes life easy for the programmer. The price you pay for this "hand-holding" is that a program is slow when it's RUN. In ML, the DIMensioning of space in memory for variables is not explicitly handled by the computer. You must make a note that you are setting aside memory from 6000 to 6500, or whatever, to hold variables. It helps to make a simple map of this "dimensioned" memory so you know where permanent strings, constants, variable strings, and variables, flags, etc., are within the dimensioned zone.

A particular chunk of memory (where, and how much, is up to you) is set aside, that's all. You don't write any instructions in ML to set aside the memory; you just jot it down so you won't later use the reserved space for some other purpose. Managing memory is left up to you. It's not difficult, but it is your responsibility.

END

END -

There are several ways to make a graceful exit from ML programs. You can look for the "warm start" address on your particular computer (in the map of its BASIC locations) and JMP to that address. Or you can go to the "cold start" address. This results in the computer resetting itself as if you had turned the power off and then back on again.

If you went into the ML from BASIC (with a USR or SYS), you can return to BASIC with an RTS. Recall that every JSR matches up with its own RTS. Every time you use a JSR, it shoves its "return here" address onto the top of the stack. If the computer finds another JSR (before any RTS's), it will shove another return address on top of the first one. So, after two JRS's, the stack contains two return addresses. When the first RTS is encountered, the top return address is lifted from the stack and put into the program counter so that the program returns control to the current instruction following the most recent JSR. ^

When the next RTS is encountered, it pulls its appropriate return (waiting for it on the stack) and so on. The effect of a SYS or USR —

from BASIC is like a JSR from within ML. The return address to the correct spot within BASIC is put on the stack. In this way, if you are _

within ML and there is an RTS (without any preceding JSR), what's on the stack had better be a return-to-BASIC address left there by SYS or USR when you first went into ML.

Another way to END is to put a BRK in your ML code. This drops you into the machine's monitor. Normally, you put BRKs in during program development and debugging. When the program is finished, though, you would not want to make this ungraceful exit any more than you would want to end a BASIC program with STOP.

In fact, many ML programs, if they stand alone and are not part of a larger BASIC program, never END at all! They are an endless loop. The main loop just keeps cycling over and over. A game will not end until you turn off the power. After each game, you see the score and are asked to press a key when you are ready for the next game. Arcade games which cost a quarter will ask for another quarter, but they don't end. They go into "attract mode." The game graphics are left running on screen to interest new customers.

An ML word processor will cycle through its main loop, waiting for keys to be pressed, words to be written, format or disk instructions to be given. Here, too, it is common to find that the word processor takes over the machine, and you cannot stop it without turning the computer off. Among other things, such an endless loop protects software from being easily pirated. Since it takes control of the machine, how is someone going to save it or examine it once it's

FOR-NEXT

in RAM? Some such programs are "auto-booting" in that they cannot be loaded without starting themselves running.

BASIC, itself a massive ML program, also loops endlessly until you power down. When a program is RUNning, all sorts of things are happening. BASIC is an interpreter, which means that it must look up each word (like INT) it comes across during a RUN (interpreting it, or translating its meanings into machine-understandable JSRs). Then BASIC executes the correct sequence of ML actions from its collection of routines.

In contrast to BASIC RUNs, BASIC spends 99 percent of its time waiting for you to program with it. This waiting for you to press keys is its "endless" loop, a tight, small loop indeed. It would look like our "which key is pressed?" routine.

2000 LDA 96 (or wherever your machine's map shows that the "which key down" value is stored)

2002 CMP #255 (or whatever value is normally left in this address by default when no key is being pressed)

2004 BEQ 2000 (if it says "no key down," cycle back and wait for one)

FOR-NEXT

Everyone has used "delay loops" in BASIC (FOR T= 1 TO 1000: NEXT T). These are small loops, sometimes called do-nothing loops because nothing happens between the FOR and the NEXT except the passage of time. When you need to let the user read something on the screen, it's sometimes easier just to use a delay loop than to say "When finished reading, press any key."

In any case, you'll need to use delay loops in ML just to slow ML itself down. In a game, the ball can fly across the screen. It can get so fast, in fact, that you can't see it. It just "appears" when it bounces off a wall. And, of course, you'll need to use loops in many other situations. Loops of all kinds are fundamental programming techniques.

In ML, you don't have that convenient little counter ("T" in the BASIC FOR/NEXT example above) which decides when to stop the loop. When Tbecomes 1000, go to the instructions beyond the word NEXT. Again, you must set up and check your counter variable by yourself.

If the loop is going to be smaller than 255 cycles, you can use the X register as the counter (Y is saved for the very useful indirect indexed addressing discussed in Chapter 4: LDA (96), Y). So, using X, you can count to 200 by:

2000 LDX #200 (or $C8 hex)

2002 DEX

2003 BNE 2002

FOR-NEXT-STEP

For loops involving counters larger than 255, you'll need to use two bytes to count down, one going from 255 to zero and then clicking (like a gear) the other (more significant) byte. To count to 512:

2000 LDA # 2

2002 STA 0 (put the 2 into address zero, our MSB, Most

Significant Byte, counter) 2004 LDX #0 (set X to zero so that its first DEX will make it 255.

Further DEX's will count down again to zero,

when it will click the MSB down from 2 to 1 and

then finally 0)

2006 DEX

2007 BNE 2006

2009 DEC 0 (click the number in address zero down 1)

2011 BNE 2006

Here we used the X register as the LSB (least significant byte) and address zero as the MSB. We could use addresses zero and one to hold the MSB/LSB if we wanted. This is commonly useful because then address zero (or some available, two-byte space in zero page) can be used for LDA (0), Y. You would print a message to the screen using the combination of a zero page counter and LDA (zero page address),Y.

FOR-NEXT-STEP

Here you would just increase your counter (usually X or Y) more than once. To create FOR I = 100 TO 1 STEP-2 you could use:

2000 LDX # 100

2002 DEX

2003 DEX

2004 BCC 2002

For larger numbers you create a counter which uses two bytes working together to keep count of the events. Following our example above for FOR-NEXT, we could translate FOR I = 512 TO 0 STEP -2:

2000 LDA # 2

2002 STA 0 (this counts the MSB)

2004 LDX # 0 (X counts the LSB)

2006 DEX

2007 DEX (here we click X down a second time, for -2)

2008 BNE 2006

2010 DEC 0

2012 BNE 2006

GET

To count up, use the CoMPare instruction. FOR 1 = 1 TO 50 STEP 3:

2000 LDX # 0

2002 INX

2003 INX

2004 INX

2005 CPX # 50

2007 BNE 2002

For larger STEP sizes, you can use a nested loop within the larger one. This would avoid a whole slew of INX's. To write the ML equivalent of FOR I = 1 TO 50 STEP 10:

2000 LDX #0

2002 LDY #0

2004 INX

2005 INY

2006 CPY #10

2008 BNE 2004 2010 CPX #50 2012 BNE 2002

GET

Each computer model has its own "which key is being pressed?" address, where it holds the value of a character typed in from the keyboard. To GET, you create a very small loop which just keeps testing the first address in the buffer. For Atari (in decimal):

2000 LDA 764 ("which key pressed" decimal address. In advanced assemblers, you could freely mix decimal with hex, but not in the Simple Assembler.)

2003 CMP #255 (when an FF value is in this address, it means

that no key is pressed) 2005 BEQ 2000 (keep going back and looking until there is some key pressed)

For PET (Upgrade and 4.0) (in decimal)

2000 LDA 151 ("which key pressed" decimal address) 2003 CMP #255 ^ 2005 BEQ 2000

For PET (Original):

2000 LDA 515 ("which key pressed" decimal address) 2003 CMP #255 2005 BEQ 2000

GOSUB

For Apple II (hex):

2000 LDA C000 ("which key pressed" — note: this is in hex)

2003 BPL 2000 —'

2005 STA C010 (clears the keyboard)

2008 AND #7F (to give you the correct character value)

For VIC and 64 (decimal):

2000 LDA 197

2003 CMP #255 —

2008 BEQ 2000

The Commodore computers have a GET routine similar to the one illustrated by these examples, which is built in at $FFE4 which can be used for all ROM versions (all models of CBM) because it is a fixed JMP table which does not change address when new BASIC versions are introduced. See your BASIC'S map for Print a Byte to the Screen, GET a Byte, and other routines in the Commodore Jump Tables. They start at $FFBD. —

The examples above do not conform to PET BASIC'S GET. In this version of BASIC, the computer does not "wait" for a character. .«.

If no key is being held down during a GET, the computer moves on and no GET takes place. In our ML GETs above, we loop until some character is actually pressed.

For most programming purposes, though, you want to wait until a key has actually been pressed. If your program is supposed to fly around doing things until a key is pressed, you might use the above routines without the loop structure. Just use a CMP to test for the particular key that would stop the routine and branch the program somewhere else when a particular key is pressed. How you utilize and construct a GET-type command in ML is up to you. You can, with ML's flexibility, make special adjustments to use the best kind of GET for each different application.

GOSUB

This is nearly identical to BASIC in ML. Use JSR $NNNN and you will go to a subroutine at address NNNN instead of a line number, as in BASIC. ("NNNN" just means you can put any hex number in there you want to.) Some assemblers allow you to give "labels," names to JSR to instead of addresses. The Simple Assembler does not allow labels. You are responsible (as with DATA tables, variables, etc.) for keeping a list on paper of your subroutine addresses and the parameters involved.

Parameters are the number or numbers handed to a subroutine to give it information it needs. Quite often, BASIC subroutines work with the variables already established within the BASIC program. In ML, though, managing variables is up to you. Subroutines are useful

GOTO

because they can perform tasks repeatedly without needing to be programmed into the body of the program each time the task is to be carried out. Beyond this, they can be generalized so that a single subroutine can act in a variety of ways, depending upon the variable (the parameter) which is passed to it.

A delay loop to slow up a program could be general in the sense that the amount of delay is handed to the subroutine each time. The delay can, in this way, be of differing durations, depending on what it gets as a parameter from the main routine. Let's say that we've decided to use address zero to pass parameters to subroutines. We could pass a delay of "five" cycles of the loop by:

2000 LDA # 5 The Main Program 2002 STA 0

2004 JSR 5000

The Subroutine 5000 DEC 0

5002 BEQ 5012 (if address zero has

counted all the way down from five to zero, RTS back to the Main Program)

5004 LDY # 0

5006 DEY

5007 BNE 5006 5009 JMP 5000 5012 RTS

A delay which lasted twice as long as the above would merely require a single change: 2000 LDA # 10.

GOTO

In ML, it's JMP. JMP is like JSR, except the address you leap away from is not saved anywhere. You jump, but cannot use an RTS to find your way back. A conditional branch would be CMP #0 BEQ 5000. The condition of equality is tested by BEQ, Branch if EQual. BNE tests a condition of inequality, Branch if Not Equal. Likewise, BCC (Branch if Carry is Clear) and the rest of these branches are testing conditions within the program.

GOTO and JMP do not depend on any conditions within the program, so they are unconditional. The question arises, when you use a GOTO: Why did you write a part of your program that you must always (unconditionally) jump over? GOTO and JMP are sometimes used to patch up a program, but, used without restraint, they can make your program hard to understand later. Nevertheless, JMP can many times be the best solution to a programming problem. In fact, it is hard to imagine ML programming without it.

GOTO

One additional note about JMP: it makes a program non-relocatable. If you later need to move your whole ML program to a different part of memory, all the JMP's (and JSR's) need to be checked to see if they are pointing to addresses which are no longer correct (JMP or JSR into your BASIC ROM's will still be the same, but not those which are targeted to addresses within the ML program). This can be an important consideration if you are going to use an ML subroutine in other programs where the locations might well differ. Fully relocatable ML routines can be convenient if you like to program by drawing from a personal collection of solved problems.

2000 JMP 2005

2003 LDY #3 2005 LDA #5

If you moved this little program up to 5000, everything would survive intact and work correctly except the JMP 2005 at address 2000. It would still say to jump to 2005, but it should say to jump to 5005, after the move. You have to go through with a disassembly and check for all these incorrect JMP's. To make your programs more "relocatable," you can use a special trick with unconditional branching which will move without needing to be fixed:

2000 LDY #0

2002 BEQ 2005 (since we just loaded Y with a zero, this Branch-if-EQual-to-zero instruction will always be true and will always cause a pseudo-JMP)

2004 NOP

2005 LDA #5

This works because we set the Z flag. Then, when BEQ tests the zero flag, it will pass the test, it will find that flag "up" and will branch. If you load X, Y, or A with a zero, the zero flag goes up.

Various monitors and assemblers include a "move it" routine, which will take an ML program and relocate it somewhere else in memory for you. On the Apple, you can go into the monitor and type *5000<2000.2006M (although you will have to give the monitor these numbers in hex). The first number is the target address. The second and third are the start and end of the program you want to move.

On CBM computers, the built-in monitor (the VIC-20 and the Original 2001 ROM set do not have a built-in monitor) does not have a Move it command. However, it is easy to add a "monitor extension" program to the built-in monitor. Superman and Micromon are such extensions. The format for Moveit in Commodore machines is .T 2000 2006 5000 (start and end of the program to be moved, followed by the target address). Again, these numbers must be in hex. The T stands for transfer.

The Atari Assembler Editor Cartridge follows a convention similar to Apple's: M 5000 < 2000,2006.

IF-THEN

This familiar and primary computing structure is accomplished in ML with the combination of CMP-BNE or any other conditional branch: BEQ, BCC, etc. Sometimes, the IF half isn't even necessary. Here's how it would look:

2000 LDA 57 (what's in address 57?)

2002 CMP #15 (is it 15?)

2004 BEQ 2013 (IF it is, branch up to 2013)

2006 LDA #10 (or ELSE, put a 10 into address 57)

2008 STA 57

2010 JMP 2017 (and jump over the THEN part)

2013 LDA #20 (THEN, put a 20 into address 57)

2015 STA 57

2017 (continue with the program . . .)

Often, though, your flags are already set by an action, making the CMP unnecessary. For example, if you want to branch to 2013 if the number in address 57 is zero, just LDA 57 BEQ 2013. This is because the act of loading the accumulator will affect the status register flags. You don't need to CMP #0 because the zero flag will be set if a zero was just loaded into the accumulator. It won't hurt anything to use a CMP, but you'll find many cases in ML programming where you can shorten and simplify your coding. As you gain experience, you will see these patterns and learn how and what affects the status register flags.

INPUT

This is a series of GETs, echoed to the screen as they are typed in, which end when the typist hits the RETURN key. The reason for the echo (the symbol for each key typed is reproduced on the screen) is that few people enjoy typing without seeing what they've typed. This also allows for error correction using cursor control keys or DELETE and INSERT keys. To handle all of these actions, an INPUT routine must be fairly complicated. We don't want, for example, the DELETE to become a character within the string. We want it to immediately act on the string being entered during the INPUT, to erase a mistake.

Our INPUT routine must be smart enough to know what to add to the string and what keys are intended only to modify it. Here is the basis for constructing your own ML INPUT. It simply receives a character from the keyboard, stores it in the screen RAM cells, and ends when the RETURN key is pressed. This version is for Upgrade and 4.0 CBM/PETs and we'll write it as a subroutine. That simply means that when the 13 (ASCII for carriage return) is encountered,

131

LET

we'll perform an RTS back to a point just following the main program address which JSRed to our INPUT routine:

5000 LDY #0 (Y will act here as an offset for storing the

characters to the screen as they come in)

5002 LDA 158 (this is the "number of keys in the keyboard buffer' location. If it's zero, nothing has been typed yet)

5004 BNE 5002 (so we go back to 5002)

5006 LDA 623 (get the character from the keyboard buffer)

5009 CMP #13 (is it a carriage return?)

5011 BNE 5014 (if not, continue)

5013 RTS (otherwise return to the main program)

5014 STA 32768, Y (echo it to the screen) 5017INY

5018 LDA #0

5020 STA 158 (reset the "number of keys" counter to zero)

5022 JMP 5002 (continue looking for the next key)

This INPUT could be made much larger and more complex. As it stands, it will contain the string on the screen only. To save the string, you would need to read it from screen RAM and store it elsewhere where it will not be erased. Or, you could have it echo to the screen, but (also using Y as the offset) store it into some safe location where you are keeping string variables. The routine above does not make provisions for DELETE or INSERT either. The great freedom you have with ML is that you can redefine anything you want. You can softkey: define a key's meaning via software; have any key perform any task. You might use the $ key to DELETE.

Along with this freedom goes the responsibility for organizing, writing, and debugging these routines.

LET

Although this word is still available on most BASICs, it is a holdover from the early days of computing. It is supposed to remind you that a statement like LET NAME = NAME + 4 is an assignment of a value to a variable, not an algebraic equation. The two numbers on either side of the "equals" sign, in BASIC, are not intended to be equal in the algebraic sense. Most people write NAME = NAME+ 4 without using LET. However, the function of LET applies to ML as well as to BASIC: we must assign values to variables.

In the Atari, VIC, and Apple, for example, where the address of the screen RAM can change depending on how much memory is in the computer, etc. — there has to be a place where we find out the starting address of screen RAM. Likewise, a program will sometimes require that you assign meanings to string variables, counters, and the like. This can be part of the initialization process, the tasks performed

LET

before the real program, your main routine, gets started. Or it can happen during the execution of the main loop. In either case, there has to be an ML way to establish, to assign, variables. This also means that you must have zones of memory set aside to hold these variables.

For strings, you can think of LET as the establishment of a location in memory. In our INPUT example above, we might have included an instruction which would have sent the characters from the keyboard to a table of strings as well as echoing them to the screen. If so, there would have to be a way of managing these strings. For a discussion on the two most common ways of dealing with strings in ML, see Chapter 6 under the subhead "Dealing With Strings."

In general, you will probably find that you program in ML using somewhat fewer variables than in BASIC. There are three reasons for this:

1. You will probably not write many programs in ML such as data bases where you manipulate hundreds of names, addresses, etc. It might be somewhat inefficient to create an entire data base management program, an inventory program for example, in ML. Keeping track of the variables would be a nightmare. An important benefit of ML is its speed of execution, but a drawback is that it slows programming down. So, for an inventory program, you could write the bulk of the program in BASIC and simply attach ML routines for sorting and searching tasks within the program.

2. Also, the variables in ML are often handled within a series of instructions (not held elsewhere as BASIC variables are). FOR 1 = 1 TO 10: NEXT I becomes LDY #1, INY, CPY #10, BNE. Here, the BASIC variable is counted for you and stored outside the body of the program. The ML "variable," though, is counted by the program itself. ML has no interpreter which handles such things. If you want a loop, you must construct all of its components yourself.

3. In BASIC, it is tempting to assign values to variables at the start of the program and then to refer to them later by their variable names, as in: 10 BALL = 79. Then, any time you want to PRINT the BALL to the screen, you could say, PRINT CHR$(BALL). Alternatively, you might define it this way in BASIC: 10BALL$ = "0". In either case, your program will later refer to the word BALL. In this example we are assuming that the number 79 will place a ball character on your screen.

In ML we are not free to use variable names except when using a complicated, advanced assembler. With the Simple Assembler, you will find it easier just to LDA #79, STA (screen position) each time. Some people like to put the 79 into their zone of variables (that arbitrary area of memory set up at the start of a program to hold tables, counters, and important addresses). They can pull it out of that zone whenever it's needed. That is somewhat cumbersome,

though, and slower. You would LDA 1015, STA (screen position), assuming you had put a 79 into this "ball" address earlier.

Obviously a value like BALL will remain the same throughout a program. A ball will look like a ball in your game, whatever else happens. So, it's not a true variable, it does not vary. It is constant. A true variable must be located in your "zone of variables," your variable table. It cannot be part of the body of your program itself (as in: LDA #79) because it will change. You don't know when writing your program what the variable will be. So you can't use immediate mode addressing because it might not be a #79. You have to LDA 1015 (or whatever) fiom within your table of variables.

Elsewhere in the program you have one or more STA 1015's or INC 1015's or some other manipulation of this address which keeps updating this variable. In effect, ML makes you responsible for setting aside areas which are safe tc hold variables. What's more, you have to remember the addresses, and update the variables in those addresses whenever necessary. This is why it is so useful to keep a piece of paper next to you when you are writing ML. The paper lists the start and end addresses of the zone of variables, the table. You also write down the specific address of each variable as you write your program.

LIST

This is done via a disassembler. It will not have line numbers (though, again, advanced assembler-disassembler packages do have line numbers). Instead, you will see the address of each instruction in memory. You can look over your work and debug it by working with the disassembler, setting BRKs into problem areas, etc. See Appendix D.

LOAD

The method of saving and loading an ML program varies from computer to computer. Normally, you have several options which can include loading: from within the monitor, from BASIC, or even from an assembler. When you finish working on a program, or a piece of a program, on the Simple Assmbler you will be given the starting and ending addresses of your work. Using these, you can save to tape or disk in the manner appropriate to your computer. To LOAD, the simplest way is just to LOAD as if you were bringing in a BASIC program. Unfortunately, this only works on Commodore machines. You'll get your ML program, not a BASIC program, so it won't start at the normal starting address for BASIC unless you wrote and saved it at that address. You should type NEW after loading it, however, to reset some pointers in the computer. That will not NEW out the ML program.

134

NEW

To save from within the monitor on Commodore machines:

.S "PROGRAM NAME",01,NNNN,NNNN* (for tape) .L "PROGRAM NAME",01 (for tape) .S "0:PROGRAM NAME",08,NNNN,NNNN* (for disk) .L "0:PROGRAM NAME",08 (for disk)

*You should add one to the hex number for the end of your program or the SAVE will clip off the last byte. If your program exists in RAM from $0300 to $0350, you save it like this: .S "PROGRAM NAME",01,0300,0351.

On the Apple, you must BLOAD from disk. On the Atari, if you have DOS you can use the "L" command from the DOS menu to LOAD in an ML program. If you don't, you need to use a short BASIC program that grabs in the bytes via a series of GETs:

10OPEN#l,4,0,"C:"

20 GET#1,NN:GET#1,NN: REM DISCARD THE HEADER

30 GET#l,LO:GET#l,HI: REM START ADDRESS

40 START = LO + 256*HI

50 GET#l,LO:GET#l,HI: REM ENDING ADDRESS

60FIN = LO + 256*HI

70 TRAP 100

80 FORI = START TO FIN: GET#1,A: POKEI,A:NEXTI

90 GOTO 30

100 END

Note: This will not work correctly if the START and FIN addresses overlap this BASIC program in memory. It would then load in on top of itself.

NEW

In Microsoft BASIC, this has the effect of resetting some pointers which make the machine think you are going to start over again. The next program line you type in will be put at the "start-of-a-BASIC-program" area of memory. Some computers, the Atari for example, even wash memory by filling it with zeros. There is no special command in ML for NEWing an area of memory, though some monitors have a "fill memory" option which will fill a block of memory as big as you want with whatever value you choose.

The reason that NEW is not found in ML is that you do not always write your programs in the same area of memory (as you do in BASIC), building up from some predictable address. You might have a subroutine floating up in high memory, another way down low, your table of variables just above the second subroutine, and your main program in the middle. Or you might not. We've been using

ON GOSUB

2000 as our starting address for many of the examples in this book and 5000 for subroutines, but this is entirely arbitrary.

To "NEW" in ML, just start assembling over the old program. _,

Alternatively, you could just turn the power off and then back on again. This would, however, have the disadvantage of wiping out ^.

your assembler along with your program.

ON GOSUB

In BASIC, you are expecting to test values from among a group of

numbers: 1,2,3,4,5 .... The value of X must fall within this narrow

range: ON X GOSUB 100, 200, 300 .. . (X must be 1 or 2 or 3 here). In

other words, you could not conveniently test for widely separated

values of X (18, 55, 220). Some languages feature an improved form of

ON GOSUB where you can test for any values. If your computer

were testing the temperature of your bathwater: -^

CASE

80 OF GOSUB HOT ENDOF

100 OF GOSUB VERYHOT ENDOF

120 OF GOSUB INTOLERABLE ENDOF ENDCASE

ML permits you the greater freedom of the CASE structure. Using CMP, you can perform a multiple branch test:

2000 LDA150 (get a value, perhaps input from the keyboard)

2002 CMP #80 ~

2004 BNE 2009

2006 JSR 5000 (where you would print "hot," following your

example of CASE) 2009 CMP # 100 _

2011 BNE 2016

2013 JSR 5020 (print "very hot") 2016 CMP # 120 2018 BNE 2023 2020 JSR 5030 (print "intolerable")

Since you are JSRing and then will be RTSing back to within the multiple branch test above, you will have to be sure that the subroutines up at 5000 do not change the value of the accumulator. If —

the accumulator started out with a value of 80 and, somehow, the subroutine at 5000 left a 100 in the accumulator, you would print "hot" and then also print "very hot." One way around this would be to put a zero into the accumulator before returning from each of the subroutines (LDA #0). This assumes that none of your tests, none of your cases, responds to a zero.

PRINT

ON GOTO

This is more common in ML than the ON GOSUB structure above. It eliminates the need to worry about what is in the accumulator when you return from the subroutines. Instead of RTSing back, you jump back, following all the branch tests.

2000 LDA 150 2002 CMP # 80 2004 BNE 2009

2006 JMP 5000 (print "hot")

2009 CMP # 100 2011 BNE 2016

2013 JMP 5020 (print "very hot") 2016 CMP # 120

2018 BNE 2023

2020 JMP 5030 (print "intolerable")

2023 (all the subroutines JMP 2023 when they finish)

Instead of RTS, each of the subroutines will JMP back to 2023, which lets the program continue without accidentally "triggering" one of the other tests with something left in the accumulator during the execution of one of the subroutines.

PRINT

You could print out a message in the following way:

2000 LDY #0

2002 LDA #72 (use whatever your computer's screen POKE

value is for the letter "H") 2004 STA 32900,Y (an address on the screen)

2007 INY

2008 LDA #69 (the letter "E")

2010 STA 32900,Y

2013 INY

2014 LDA #76 (the letter "L") 2016 STA 32900,Y

2019 INY

2020 LDA #76 (the letter "L") 2022 STA 32900, Y

2025 INY

2026 LDA #79 (the letter "O") 2028 STA 32900, Y

But this is clearly a cumbersome, memory-eating way to go about it. In fact, it would be absurd to print out a long message this way. The most common ML method involves putting message strings into a data table and ending each message with a zero. Zero is never a

PRINT

printing character in computers (excepting Atari which cannot use the technique described here). To print the ASCII number zero, you use 48: LDA #48, STA 32900. So, zero itself can be used as a delimiter to let the printing routine know that you've finished the message. In a data table, we first put in the message "hello". Recall that you should substitute your own computer's screen POKE code:

1000 72 H

1001 69 E

1002 76 L

1003 76 L

1004 79 O

1005 0 (the delimiter, see Chapter 6)

1006 72 H

1007 73 I (another message)

1008 0 (another delimiter)

Such a message table can be as long as you need; it holds all your messages and they can be used again and again:

2000 LDY #0

2002 LDA 1000,Y

2005 BEQ 2012 (if the zero flag is set, it must mean that we've

reached the delimiter, so we branch out of this

printing routine) 2005 STA 39000,Y (put it on the screen)

2008 INY

2009 JMP 2002 (go back and get the next letter in the message) 2012 (continue with the program.)

Had we wanted to print "HI," the only change necessary would have been to put 1006 into the LDA at address 2003. To change the location on the screen that the message starts printing, we could just put some other address into 2006. The message table, then, is just a mass of words, separated by zeros, in RAM memory.

The easiest way to print to the screen, especially if your program will be doing a lot of printing, is to create a subroutine and use some bytes in zero page (addresses 0 to 255) to hold the address of the message and the screen location you want to send it to. This is one reason why hex numbers can be useful. To put an address into zero page, you will need to put it into two bytes. It's too big to fit into one byte. With two bytes together forming an address, the 6502 can address any location from $0000 to the top $FFFF. So, if the message is at decimal location 1000 like "HELLO" above, you should turn 1000 into a hex number. It's $03E8.

Then you split the hex number in two. The left two digits, $03, are the MSB (the most significant byte) and the right digits, $E8, make

PRINT

up the LSB (least significant byte). If you are going to put this target address into zero page at 56 (decimal):

2000 LDA #232 (LSB, in decimal)

2002 STA 56

2004 LDA #3 (MSB)

2006 STA 57

2008 JSR 5000 (printout subroutine)

5000 LDY #0

5002 LDA (56),Y

5004 BEQ 5013 (if zero, return from subroutine)

5006 STA 32900,Y (to screen)

5009 INY

5010 JMP 5002 5013 RTS

One drawback to the subroutine is that it will always print any messages to the same place on the screen. That 32900 (or whatever you use there) is frozen into your subroutine. Solution? Use another zero page pair of bytes to hold the screen address. Then, your calling routine sets up the message address, as above, but also sets up the screen address.

The Atari contains the address of the first byte of the screen addresses in zero page for you at decimal 88 and 89. You don't need to set up a screen address byte pair on the Atari. We are using the Apple II's low resolution screen for the examples in this book, so you will want to put 0 and 4 into the LSB and MSB respectively. The PET's screen is always located in a particular place, unlike the Atari, Apple, VIC, and 64 screen RAM locations which can move, so you can put a $00 and an $80 into LSB and MSB for PET. The following is in decimal:

2000 LDA #232 (LSB)

2002 STA 56 (set up message address)

2004 LDA #3 (MSB)

2006 STA 57

2008 LDA #0 (LSB for PET and Apple)

2010 STA 58 (we'll just use the next two bytes in zero page

above our message address for the screen address)

2012 LDA # 4 (this is for Apple II; use 128 ($80) for PET) 2014 STA 59 2016 JSR 5000

5000 LDY #0

5002 LDA (56),Y

5004 BEQ 5013 (if zero, return from subroutine)

READ

5006 STA (58),Y (to screen)

5009 INY

5010 JMP 5002 5013 RTS

For Atari: 5006 STA (88), Y. You have less flexibility because you will always be printing your messages to the first line on screen, using address 88 as your screen storage target. To be able to put the message anywhere on screen, Atari users will have to use some other zero page for the screen address, as we did for Apple II and PET above. Atari users would have to keep track of the "cursor position" for themselves in that case.

READ

There is no reason for a reading of data in ML. Variables are not placed into ML "DATA statements." They are entered into a table when you are programming. The purpose of READ, in BASIC, is to assign variable names to raw data or to take a group of data and move it somewhere, or to manipulate it into an array of variables. These things are handled by you, not by the computer, in ML programming.

If you need to access a piece of information, you set up the addresses of the datum and the target address to which you are moving it. See the "PRINT" routines above. As always, in ML you are expected to keep track of the locations of your variables. You keep a map of data locations, vectors, tables, and subroutine locations. A pad of paper is always next to you as you program in ML. It seems as if you would need many notes. In practice, an average program of say 1000 bytes could be mapped out and commented on, using only one sheet.

REM

You do this on a pad of paper, too. If you want to comment or make notes about your program — and it can be a necessary, valuable explanation of what's going on — you can disassemble some ML code like a BASIC LISTing. If you have a printer, you can make notes on the printed disassembly. If you don't have a printer, make notes on your pad to explain the purpose of each subroutine, the parameters it expects to get, and the results or changes it causes when it operates. Complex, large assemblers often permit comments within the source code. As you program with them, you can include REMarks ,«.

by typing a semicolon, or parentheses, or some other signal to the assembler to ignore the REMarks when it is assembling your program. In these assemblers, you are working much closer to the way you work in BASIC. Your remarks remain part of the source program and can be listed out and studied.

RUN

RETURN

RTS works the same way that RETURN does in BASIC: it takes you back to just after the JSR (GOSUB) that sent control of the program away from the main program and into a subroutine. JSR pushes, onto the stack, the address which immediately follows the JSR itself. That address then sits on the stack, waiting until the next RTS is encountered. When an RTS occurs, the address is pulled from the stack and placed into the program counter. This has the effect of transferring program control back to the instruction just after the JSR.

RUN

There are several ways to start an ML program. If you are taking off into ML from BASIC, you just use SYS or USR or CALL. They act just like JSR and will return control to BASIC, just like RETURN would, when there is an unmatched RTS in the ML program. By unmatched we mean the first RTS which is not part of a JSR/RTS pair. USR and SYS and CALL can be used either in immediate mode (directly from the keyboard) or from within a BASIC program as one of the BASIC commands.

USR is just like SYS and CALL except that you can "send" values from BASIC to ML by attaching them to the USR ( ) within the parentheses. In Microsoft BASIC (Apple, PET/CBM, etc.), you must set up the location of your target ML program in special USR addresses, before exiting BASIC via USR. For example, to "gosub" to an ML routine located at $0360 (hex), you want to put a $60 (hex) into address 1 and an 03 into address 2. The 03 is obvious, just POKE 2,3. Atari goes from BASIC to ML via USR. The USR's argument may place several parameters on the stack along with the "count," the number of parameters which were passed.

The hex 60 means that you would multiply 16 x 6, since the second column in hex is the "16's" column. So you would POKE 1, 96. Recall that we always set up ML addresses to be used by "indirect indexed addressing" (LDA (00), Y) by putting the LSB (least significant byte) first. To set up 0360, then, you first separate the hex number into its two bytes, 03 60. Then you translate them into decimal since we're in BASIC when we use USR: 3 96. Then you switch them so that they conform to the correct order for ML: LSB/MSB 96 3. Finally, you POKE them into memory locations 1 and 2.

If this seems rather complex, it is. In practice, Microsoft BASIC users rarely use USR. The number which is "passed" to ML from within the parentheses is put into the floating point accumulator. Following this you must JSR to FPINT, a BASIC ROM routine which converts a floating point value into an integer that you could work

RUN

with in ML. As we mentioned, working with floating point arithmetic in ML is an arcane art. For most applications which must pass information from BASIC to ML, it is far easier to use ordinary "integer" numbers and just POKE them into some predetermined ML variable zone that you've set aside and noted on your workpad. Then just SYS to your ML routine, which will look into the set-aside, POKEd area when it needs the values from BASIC.

In Atari BASIC, USR works in a more simplified and more convenient way. For one thing, the target ML address is contained within the argument of the USR command: USR (address). This makes it nearly the exact parallel of BASIC'S GOSUB. What's more, USR passes values from BASIC by putting them on the stack as a two-byte hex number. USR (address,X) does three things. 1. It sends program control to the ML routine which starts at "address." 2. It pushes the number X onto the stack where it can be pulled out with PLA's. 3. Finally, it pushes the total number of passed values onto the stack. In this case, one value, X, was passed to ML. All of these actions are useful and make the Atari version of USR a more sensible way of GOSUBing from BASIC to ML.

If you are not going between BASIC and ML, you can start (RUN) your ML program from within your "monitor." The PET/CBM and the Apple have built-in monitor programs in their ROM chips. On the Atari, a monitor is available as part of a cartridge. On the "Original" PET/CBM (sometimes called BASIC 2.0), there is no built-in monitor. A cassette with a program called TIM (terminal interface monitor) can be LOADed, though, and used in the same way that the built-in versions are on later models. Neither the VIC nor the 64 has a built-in monitor.

To enter "monitor mode" (as opposed to the normal BASIC mode), you can type SYS 1024 or SYS 4 on the PET/CBM. These locations always contain a zero and, by "landing" on a zero in ML, you cause a BRK to take place. This displays the registers of your 6502 and prints a dot on the screen while waiting for your instructions to the monitor. To enter the monitor on Apple II, type CALL -151 and you will see an asterisk (instead of PET's period) as your prompt. From within Atari's Assembler Cartridge, you would type BUG to enter the equivalent of the Apple and PET monitor. The Atari will print the word DEBUG and then the cursor will wait for your next instruction.

To RUN an ML program, all five computers use the abbreviation G to indicate "goto and run" the hex address which follows the G. Unfortunately, the format of the ML RUN (G), as always, differs between machines. To run a program which starts at address $2000:

Apple II, you type: 2000G (8192 in decimal)

PET, VIC,64, you type: G 2000 Atari, you type: G 2000

STOP

One other difference: the Apple II expects to encounter an unmatched RTS to end the run and return control to the monitor. Put another way, it will think that your ML program is a subroutine and 2000G causes it to JSR to the subroutine at address (in hex) 2000. The Commodores and the Atari both look for a BRK instruction (00) to throw them back into monitor mode.

SAVE

When you SAVE a BASIC program, the computer handles it automatically. The starting address and the ending address of your program are calculated for you. In ML, you must know the start and end yourself and let the computer know. From the Apple II monitor, you type the starting and ending address of what you want saved, and then "W" for write:

2000.2010W (This is only for cassette and these commands are in hex. These addresses are 8192.8208, in decimal.)

From BASIC to disk use:

BSAVEName,A,L (A = address, L = length)

On the VIC, 64, and PET, the format for SAVE is similar, but includes a filename:

.S "PROGRAM NAME",01,2000,2010 (the 01 is the "device number" of the tape piayer)

To save to disk, you must change the device number to 08 and start the filename with the number of the drive you are SAVEing to:

.S "0:NAME",08,2000,2010

(Always add one to the "finish" address; the example above saves from 2000 to 200F.)

With the Atari Assembler Cartridge, you:

SAVE#C:NAME< 2000,2010 (do this from the EDIT, not DEBUG, mode). The NAME is not required with cassette.

To write Atari source code to cassette, type: SAVE#C. For disk, type SAVE#D:FILENAME.EXT or use DOS.

STOP

BRK (or an RTS with no preceding JSR, on the Apple) throws you back into the monitor mode after running an ML program. This is most often used for debugging programs because you can set "breakpoints" in the same way that you would use STOP to examine variables when debugging a BASIC program.

ASC

String Handling ^

ASC

In BASIC, this will give you the number of the ASCII code which

stands for the character you are testing. ?ASC(" A") will result in a 65

being displayed. There is never any need for this in ML. If you are

manipulating the character A in ML, you are using ASCII already. In ,^

other words, the letter A is 65 in ML programming. If your computer

stores letters and other symbols in nonstandard ways (such as .^

Commodore character codes for lowercase, and Atari's ATASCII),

you will need to write a special program to be able to translate to

standard ASCII if you are using a modem or some other peripheral

which uses ASCII. See your computer's manual, the Atari BASIC

Reference Manual for example, for information on your computer's

internal character code.

CHR$

This is most useful in BASIC to let you use characters which cannot be represented within normal strings, will not show up on your screen, or cannot be typed from the keyboard. For example, if you have a printer attached to your computer, you could "send" CHR$(13) to it, and it would perform a carriage return. (The correct numbers which accomplish various things sometimes differ, though decimal 13 — an ASCII code standard — is nearly universally recognized as carriage return.) Or, you could send the combination CHR$(27)CHR$(8) and the printer would backspace. ~

Again, there is no real use for CHR$ within ML. If you want to specify a carriage return, just LDA #13. In ML, you are not limited to the character values which can appear on screen or within strings. Any value can be dealt with directly.

The following string manipulation instructions are found in Microsoft BASIC:

LEFTS

As usual in ML, you are in charge of manipulating data. Here's one way to extract a five-character-long "substring" from out of the left side of a string as in the BASIC statement: LEFT$ (X$,5)

2000 LDY #5

2002 LDX #0 (use X as the offset for buffer storage)

2004 LDA 1000,Y (the location of X$)

2007 STA 4000,X (the "buffer," or temporary storage area for the substring)

2010 INX

2011 DEY

2012 BNE 2004

~

RIGHTS

LEN

In some cases, you will already know the length of a string in ML. One of the ways to store and manipulate strings is to know beforehand the length and address of a string. Then you could use the subroutine given for LEFTS above. More commonly, though, you will store your strings with delimiters (zeros, except in Atari) at the end of each string. To find out the length of a certain string:

2000 LDY #0

2002 LDA 1000, Y (the address of the string you are testing)

2003 BEQ 2009 (remember, if you LDA a zero, the zero flag is set.

So you don't really need to use a CMP #0 here to test whether you've loaded the zero delimiter)

2005 INY

2006 BNE 2002 (we are not using a JMP here because we assume

that all your strings are less than 256 characters long.)

2008 BRK (if we still haven't found a zero after 256 INY's, we

avoid an endless loop by just BRKing out of the subroutine)

2009 DEY (the LENgth of the string is now in the Y register)

We had to DEY at the end because the final INY picked up the zero delimiter. So, the true count of the LENgth of the string is one less than Y shows, and we must DEY one time to make this adjustment.

MID$

To extract a substring which starts at the fourth character from within the string and is five characters long (as in MID$(X$,4,5) ):

2000 LDY #5 (the size of the substring we're after)

2002 LDX #0 (X is the offset for storage of the substring)

2004 LDA 1003,Y (to start at the fourth character from within the

X$ located at 1000, simply add three to that address. Instead of starting our LDA,Y at 1000, skip to 1003. This is because the first character is not in position one. Rather, it is at the zeroth position, at 1000.)

2007 STA 4000,X (the temporary buffer to hold the substring)

2010 INX

2011 DEY

2012 BNE 2004

RIGHTS

This, too, is complicated because normally we do not know the LENgth of a given string. To find RIGHT$(X$,5) if X$ starts at 1000,

RIGHTS

we should find the LEN first and then move the substring to our holding zone (buffer) at 4000:

2000 LDY #0 a

2002 LDX #0

2004 LDA 1000, Y _>

2007 BEQ 2013 (the delimiting zero is found, so we know LEN)

2009INY ^

2010 JMP 2004

2013 TYA (put LEN into A to subtract substring size from it) —

2014 SEC (always set carry before subtraction)

2015 SBC #5 (subtract the size of the substring you want to _

extract)

2017 TAY (put the offset back into Y, now adjusted to point to

five characters from the end of X$)

2018 LDA 1000,Y

2021 BEQ 2030 (we found the delimiter, so end) 2023 STA 4000,X

2026 INX

2027 DEY

2028 BNE 2018 2030 RTS

The above does not apply to Atari since it cannot use zero as a delimiter.

SPC

-*

This formatting instruction is similar to TAB. The difference is that SPC(10) moves you ten spaces to the right from wherever the cursor is on screen at the time. TAB(10) moves ten spaces from the left-hand side of the screen. In other words, TAB always counts over from the first column on any line; SPC counts from the cursor's current position.

In ML, you would just add the amount you want to SPC over. If you were printing to the screen and wanted ten spaces between A and B so it looked like this (A B), you could write:

2000 LDA #65 (A)

2002 STA 32768 (screen RAM address)

2005 LDA #66 (B)

2007 STA 32778 (you've added ten to the target address)

Alternatively, you could add ten to the Y offset: —-

2000 LDY #0

2002 LDA #65 "~

2004 STA 32768,Y

2007 LDY #10 (add ten to Y) —

146

~ ~

TAB

2009 LDA #66 2011 STA 32768,Y

If you are printing out many columns of numbers and need a subroutine to correctly space your printout, you might want to use a subroutine which will add ten to the Y offset each time you call the subroutine:

5000 TYA

5001 CLC

5002 ADC #10

5004 TAY

5005 RTS

This subroutine directly adds ten to the Y register whenever you JSR 5000. To really do this job, however, you should use a two-byte register to keep track of the cursor.

TAB

Quite similar to SPC, except that you don't add the offset from the cursor position (whatever location you most recently printed). Rather, TAB(X) moves ten over from the left side of the screen, or, if you are using a printer, from the left margin on the piece of paper. There is no particular reason to use TAB in ML. You have much more direct control in ML over where characters are printed out.

Appendix A

ASL

ASL

Shift Left One Bit

Status Flags

N

C

D V

Addressing Mode

Mnemonics

Opcode

Size In Bytes

Accumulator Zero Page Zero Page, X Absolute Absolute, X

ASL A ASL Arg ASL Arg, X ASL Arg ASL Arg, X

OA 06 16 0E IE

BCS

Branch On Carry Set

Status Flags

N

C I D V

Addressing Mode

Mnemonics

Opcode

Size In Bytes

Relative

BCS Arg

BO

~

*

BNE

BPL

CLV

CMP

DEY

EOR

JSR

LDA

">

-

NOP

160

ROL

ROR

SED

SEI

TSX

TXA

-

—

Appendix B

These maps, primarily the work of Jim Butterfield, all originally appeared in COMPUTE! Magazine. (See the copyright page for references.)

Map I. PET Original And Upgrade BASIC.

Appendix B

-

Appendix B

Map 2. Upgrade PET/CBM Map.

USR Jump instruction

Search character

Scan-between-quotes flag

BASIC input buffer pointer;#subscripts

Default DIM flag

Type: FF = string, 00 = numeric

Type: 80 = integer, 00 = floating point

DATA scan flag; LIST quote flag;

memory flag

Subscript flag; FNx flag

0 = input; 64 = get; 152 = read

ATN sign flag; comparison evaluation flag

input flag; suppress output if negative

current I/O device for prompt-suppress

BASIC integer address (for SYS, GOTO, etc.

Temporary string descriptor stack pointer

Last temporary string vector

Stack of descriptors for temporary strings

Pointer for number transfer

Misc. number pointer

Product staging area for multiplication

Pointer: Start-of-BASIC memory

Pointer: End-of-BASIC, Start-of-Variables

Pointer: End-of-Variables, Start-of-Arrays

Pointer: End-of-Arrays

Pointer: Bottom-of-strings (moving down)

Utility string pointer

Pointer: Limit of BASIC Memory

Current BASIC line number

Previous BASIC line number

Pointer to BASIC statement (for CONT)

Line number, current DATA line

Pointer to current DATA item

Input vector

Appendix B

Current variable name

Current variable address

Variable pointer for FOR/NEXT

Y save register; new-operator save

Comparison symbol accumulator

Misc. numeric work area

Work area; garbage yardstick

Jump vector for functions

Misc. numeric storage area

Misc. numeric storage area

Accumulator^ :E,M,M,M,M,S

Series evaluation constant pointer

Accumulator hi-order propagation word

Accumulator #2

Sign comparison, primary vs. secondary

low-order rounding byte for Ace #1

Cassette buffer length/Series pointer

Subrtn: Get BASIC Char; 77,78 = pointer

RND storage and work area

Jiffy clock for TI and TI$

Hardware interrupt vector

Break interrupt vector

NMI interrupt vector

Status word ST

Which key depressed: 255 = no key

Shift key: 1 if depressed

Correction clock

Keyswitch PIA: STOP and RVS flags

Timing constant buffer

Load = 0, Verify = 1

#characters in keyboard buffer

Screen reverse flag

IEEE-488 mode

End-of-line-for-input pointer

Cursor log (row, column)

PBD image for tape I/O

Key image

0 = flashing cursor, else no cursor

Countdown for cursor timing

Character under cursor

Cursor blink flag

EOT bit received

Input from screen/input from keyboard

X save flag

How many open files

Appendix B

Input device, normally 0

Output CMD device, normally 3

Tape character parity

Byte received flag

Tape buffer character

Pointer in file name transfer

Serial bit count

Cycle counter

Countdown for tape write

Tape buffer #1 count

Tape buffer #2 count

Write leader count; Read pass 1/pass 2

Write new byte; Read error flag

Write start bit; Read bit seq error

Pass 1 error log pointer

Pass 2 error correction pointer

0 = Scan; 1-15 = Count; $40 = Load; $80 = End

Checksum

Pointer to screen line

Position of cursor on above line

Utility pointer: tape buffer, scrolling

Tape end address/end of current program

Tape timing constants

00 = direct cursor, else programmed cursor

Timer 1 enabled for tape read; 00 = disabled

EOT signal received from tape

Read character error

# characters in file name Current logical file number

Current secondary addrs, or R/W command

Current device number

Line length (40 or 80) for screen

Start of tape buffer, address

Line where cursor lives

Last key input; buffer checksum; bit buffer

File name pointer

Number of keyboard INSERTS outstanding

Write shift word/Receive input character

# blocks remaining to write/read Serial word buffer

Screen line table: hi order address & line wrap

Cassette #1 status switch

Cassette #2 status switch

Tape start address

Binary to ASCII conversion area

Appendix B

0100-013E 256-318 Tape read error log for correction

0100-01FF 256-511 Processor stack area

0200-0250 512-592 BASIC input buffer

0251-025A 593-602 Logical file number table

025B-0264 603-612 Device number table

0265-026E 613-622 Secondary address, or R/VV cmd, table

026F-0278 623-632 Keyboard input buffer

027A-0339 634-825 Tape #1 buffer

033A-03F9 826-1017 Tape #2 buffer

03FA-03FB 1018-1019 Vector for Machine Language Monitor

0400-7FFF 1024-32767 Available RAM including expansion

8000-8FFF 32768-36863 Video RAM

9000-BFFF 36864-49151 Available ROM expansion area

C000-E0F8 49152-57592 Microsoft BASIC interpreter

E0F9-E7FF 57593-59391 Keyboard, Screen, Interrupt programs

E810-E813 59408-59411 PIA1 - Keyboard I/O

E820-E823 59424-59427 P1A2 - IEEE-488 I/O

E840-E84F 59456-59471 VIA - I/O and Timers

F000-FFFF 61440-65535 Reset, tape, diagnostic monitor

Map 3. PET/CBM 4.0 BASIC. Zero Page.

Appendix B

Current DATA line number

Current DATA address

Input vector

Current variable name

Current variable address

Variable pointer for FOR/NEXT

Y-save; op-save; Basic pointer save

Comparison symbol accumulator

Misc work area, pointers, etc

Jump vector for functions

Misc numeric work area

Accum//1: Exponent

Accum#1: Mantissa

Accum#1: Sign

Series evaluation constant pointer

Accum#1 hi-order (overflow)

Accum#2: Exponent, etc.

Sign comparison, Acc#1 vs #2

Accum#1 lo-order (rounding)

Cassette buff len/Series pointer

CHRGET subroutine; get Basic char

Basic pointer (within subrtn)

Random number seed.

Jiffy clock for TI and TI$

Hardware interrupt vector

BRK interrupt vector

NMI interrupt vector

Status word ST

Which key down; 255=no key

Shift key: 1 if depressed

Correction clock

Keyswitch PIA: STOP and RVS flags

Timing constant for tape

LoadrO, Verify=1

Number of characters in keybd buffer

Screen reverse flag

IEEE output; 255=character pending

End-of-line-for-input pointer

Cursor log (row, column)

IEEE output buffer

Key image

0=flash cursor

Cursor timing countdown

Character under cursor

Cursor in blink phase

EOT received from tape

Input from screen/from keyboard

X save

How many open files

Input device, normally 0

Output CMD device, normally 3

Tape character parity

Byte received flag

Logical Address temporary save

Tape buffer character; MLM command

File name pointer; MLM flag, counter

Serial bit count

Cycle counter

Appendix B

^

OOBA

00BB-

OOBD

OOBE

OOBF

OOCO-

00C2

OOC3

00C4-

00C6

00C7-

00C9-

OOCB-

OOCD

OOCE

OOCF

OODO

OOD1

00D2

00D3

00D4

00D5

00D6

00D8

OOD9

OODA.

OODC

OODD

OODE

OODF

OOEO

OOEO'

0OE2

OOE3

00E4

00E5

0OE6

00E7

00E8

OOE9

OOEB.

OOF9'

OOFB.

OOFD

0100

0100

0100

0200

0251

025B

0265

026F

027A

033A

033A

033B

033C

033D

OOBC

00C1

00C5

00C8 OOCA OOCC

'-11

-00D7

OODB

•00F8 •00E1

■OOEA

■ OOEC

• OOFA OOFC

• OOFE

■ 010A

■ 01 3E •01FF

■ 0250 •025A ■0264 ■026E •0278 ■0339 ■03F9

■193

200 202 20 4

1

187

189

190

191

192

194

195

196-197

198

199-

201-

203-

205

206

207

208

209

210

211

212

213

214-

216

217

218-

220

221

222

223

224-

224-

226

227

228

229

230

231

232

233-

235-

249-

251-

253-

256-

256-

256-

512-

593-

603-

613-

623-

634-

826-

826

827

828

829

215

219

248 225

234 236 250 252 254 266 318 511 592 602 612 622 632 825 1017

Tape Tape Writ Writ Writ Erro 0 = Sc Writ Poin Posi Util Tape Tape Ordi Tape EOT Read it ch Curr Curr Curr Righ Poin Line Last File Numb Writ Tape Seri 40-80-80-80-80-80-80 80-80 80-80 Cass MLM MLM, STR$ Tape Proc MLM File File File Keyb Tape Tape DOS DOS DOS DOS

writer buffer e leader e new by e start r log po an/1-15= e leader ter to s tion of ity poin end add timing rect cur read ti received charact aracters ent file ent file ent file t-hand w ter: Sta where c key/che name po er of IN e shift blocks al word column column column column column column column column column column column ette sta pointer/ DOS poi work ar read er essor st work are logical device seconda oard inp #1 input #2 input characte drive 1 drive 2 length/w

countdow pointers

count; te; read bit; rea inters, Count/$4 length; creen li cursor o ter: tap rs/End o constant sor, els mer 1 en from ta er error in file logical seconda device indow or rt of ta ursor li cksum/mi inter SERTs ou word/rea remainin buffer Screen 1 Top, bot Left win Limit of Key repe Repeat c New key Chime ti HOME cou Input ve Output v tus, #1 Tape sta nter, mi ea, MLM ror log ack

a; Input address number t ry adds ut buffe buffer buffer r pointe flag flag rite fla

#1 and #2 ad pass1/2 rror flag bit seq error ss1/2

Load/$80=End ead checksum

above line

scroll current program

programmed led

name

address ry addrs number

line margin pe buffer ves sc.

tstanding

d character in

g to write/read

ine wrap table torn of window dow margin

keybd buffer at flag ountdown marker me nt

ctor ector and #2 rt address sc . work

buffer table

able

table

r

Appendix B

033E 830 DOS syntax flags

033F-0340 831-832 DOS disk ID

0341 833 DOS command string count

0342-0352 834-850 DOS file name buffer

0353-0^80 851-896 DOS command string buffer

03EE-03F7 1006-1015 (80-column) Tab stop table

03FA-03FB 1018-1019 Monitor extension vector

03FC 1020 IEEE timeout defeat

0400-7FFF 1024-32767 Available RAM including expansion

8000-83FF 32768-33791 (40-column) Video RAM

8000-87FF 32768-34815 (80-column) Video RAM

9000-AFFF 36864-45055 Available ROM expansion area

B0OO-DFFF 45056-57343 Basic, DOS, Machine Lang Monitor

E000-E7FF 57344-59391 Screen, Keyboard, Interrupt programs

E810-E813 59408-59411 PIA 1 - Keyboard I/O

E820-E823 59424-59427 PIA 2 - IEEE-488 1/0

E840-E84F 59456-59471 VIA - I/O and timers

E880-E881 59520-59521 (80-column) CRT Controller

F000-FFFF 61440-65535 Reset, 1/0 handlers, Tape routines

Map 4. PET/CBM 4.0 BASIC ROM Routines.

Description B000-B065 Action addresses for primary keywords B066-B093 Action addresses for functions B094-B0B1 Hierarchy and action addresses for operators B0B2-B20C Table of Basic keywords B20D-B321 Basic messages, mostly error messages B322-B34F Search the stack for FOR or GOSUB activity B350-B392 Open up space in memory B393-B39F Test: stack too deep? B3AO-B3CC Check available memory B3CD Send canned error message, then:

B3FF-B41E Warm start; wait for Basic command B41F-BMB5 Handle new Basic line input B4B6-B4E1 Rebuild chaining of Basic lines B4E2-B4FA Receive line from keyboard B4FB-B5A2 Crunch keywords into Basic tokens B5A3-B5D1 Search Basic for given line number B5D2 Perform NEW, and;

B5EC-B621 Perform CLR

B622-B62F Reset Basic execution to start B630-B6DD Perform LIST B6DE-B784 Perform FOR B785-B7B6 Execute Basic statement B7B7-B7C5 Perform RESTORE B7C6-B7ED Perform STOP or END B7EE-B807 Perform C0NT B808-B812 Perform RUN B813-B82F Perform GOSUB B830-B85C Perform GOTO B85D Perform RETURN, then:

Appendix B

B883-B890 Perform DATA: skip statement

B891 Scan for next Basic statement

8894-8882 Scan for next Basic line

B8B3 Perform IF, and perhaps:

B8C6-B8D5 Perform REM: skip line

B8D6-B8F5 Perform ON

B8F6-B92F Accept fixed-point number

B930-BA87 Perform LET

BA88-BA8D Perform PRINT//

BA8E-BAA1 Perform CMD

BAA2-BB1C Perform PRINT

BB1D-BB39 Print string from memory

BB3A-BB4B Print single format character

BB4C-BB79 Handle bad input data

BB7A-BBA3 Perform GET

BBA4-BBBD Perform INPUT*

BBBE-BBF4 Perform INPUT

BBF5-BC01 Prompt and receive input

BC02-BCF6 Perform READ

BCF7-BD18 Canned Input error messages

BD19-BD71 Perform NEXT

BD72-BD97 Check type mismatch

BD98 Evaluate expression

BEE9 Evaluate expression within parentheses

BEEF Check parenthesis, comma

BF00-BF0B Syntax error exit

BF8C-C046 Variable name setup

C047-C085 Set up function references

C086-C0B5 Perform OR, AND

C0B6-C11D Perform comparisons

C11E-C12A Perform DIM

C12B-C1BF Search for variable

C1C0-C2C7 Create new variable

C2C8-C2D8 Setup array pointer

C2D9-C2DC 32768 in floating binary

C2DD-C2FB Evaluate integer expression

C2FC-Ci(A7 Find or make array

C4A8 Perform FRE, and:

C4BC-C4C8 Convert fixed-to-floating

Ci|C9-C4CE Perform POS

C4CF-C4DB Check not Direct

C4DC-C509 Perform DEF

C50A-C51C Check FNx syntax

C51D-C58D Evaluate FNx

C58E-C59D Perform STR$

C59E-C5AF Do string vector

C5B0-C61C Scan, set up string

C61D-C669 Allocate space for string

C66A-C74E Garbage collection

C74F-C78B Concatenate

C78C-C7B4 Store string

C7B5-C810 Discard unwanted string

Appendix B

C811-C821 Clean descriptor stack

C822-C835 Perform CHR$

C836-C861 Perform LEFT$

C862-C86C Perform RIGHT!

C86D-C896 Perform MID$

C897-C8B1 Pull string data

C8B2-C8B7 Perform LEN

C8B8-C8C0 Switch string to numeric

C8C1-C8DO Perform ASC

C8D1-C8E2 Get byte parameter

C8E3-C920 Perform VAL

C921-C92C Get two parameters for POKE or WAIT

C92D-C942 Convert floating-to-fixed

C943-C959 Perform PEEK

C95A-C962 Perform POKE

C963-C97E Perform WAIT

C97F-C985 Add 0.5

C986 Perform subtraction

C998-CA7C Perform addition

CA7D-CAB3 Complement accural 1

CAB4-CAB8 Overflow exit

CAB9-CAF1 Multiply-a-byte

CAF2-CB1F Constants

CB20 Perform LOG

CB5E-CBC1 Perform multiplication

CBC2-CBEC Unpack memory into accum#2

CBED-CC09 Test & adjust accumulators

CC0A-CC17 Handle overflow and underflow

CC18-CC2E Multiply by 10

CC2F-CC33 10 in floating binary

CC34 Divide by 10

CC3D Perform divide-by

CC45-CCD7 Perform divide-into

CCD8-CCFC Unpack memory into accum#1

CCFD-CD31 Pack accum/M into memory

CD32-CD41 Move accum//2 to (M

CD42-CD50 Move accum/M to it2

CD51-CD60 Round accum/M

CD61-CD6E Get accum/M sign

CD6F-CD8D Perform SGN

CD8E-CD90 Perform ABS

CD91-CDD0 Compare accura/M to memory

CDD1-CE01 Floating-to-fixed

CE02-CE28 Perform INT

CE29-CEB3 Convert string to floating-point

CEB1I-CEE8 Get new ASCII digit

CEE9-CEF8 Constants

CF78 Print IN, then:

CF7F-CF92 Print Basic line //

CF93-D0C6 Convert floating-point to ASCII

D0C7-D107 Constants

D108 Perform SQR

Appendix B

D112 Perform power function —-

D14B-D155 Perform negation

D156-D183 Constants

D184-D1D6 Perform EXP

D1D7-D220 Series evaluation

D221-D228 RND constants <^

D229-D281 Perform RND

D282 Perform COS A

D289-D2D1 Perform SIN

D2D2-D2FD Perform TAN

D2FE-D32B Constants

D32C-D35B Perform ATN

D35C-D398 Constants —*

D399-D3B5 CHRGET sub for zero page

D3B6-DU71 Basic cold start ^

D472-D716 Machine Language Monitor

D717-D7AB MLM subroutines

D7AC-D802 Perform RECORD

D803-D837 Disk parameter checks

D838-D872 Dummy disk control messages -—-

D873-D919 Perform CATALOG or DIRECTORY

D91A-D92E Output ^

D92F-D941 Find spare secondary address

D942-D976 Perform DOPEN

D977-D990 Perform APPEND

D991-D9D1 Get disk status

D9D2-DA06 Perform HEADER —

DA07-DA30 Perform DCLOSE

DA31-DA64 Set up disk record

DA65-DA7D Perform COLLECT

DA7E-DAA6 Perform BACKUP

DAA7-DAC6 Perform COPY

DAC7-DAD3 Perform CONCAT

DAD4-DB0C Insert command string values -—.

DB0D-DB39 Perform DSAVE

DB3A-DB65 Perform DLOAD

DB66-DB98 Perform SCRATCH

DB99-DB9D Check Direct command

DB9E-DBD6 Query ARE YOU SURE? —"•

DBD7-DBE0 Print BAD DISK

DBE1-DBF9 Clear DS$ and ST —

DBFA-DC67 Assemble disk command string

DC68-DE29 Parse Basic DOS command

DE2C-DE48 Get Device number

DE49-DE86 Get file name

DE87-DE9C Get small variable parameter —*

** Entry points only for E000-E7FF **

E000 Register/screen initialization —.

E0A7 Input from keyboard

E116 Input from screen

E202 Output character

E442 Main Interrupt entry

Appendix B

E455 Interrupt: clock, cursor, keyboard

E600 Exit from Interrupt

#* **

F000-F0D1 File messages

F0D2 Send 'Talk'

F0D5 Send 'Listen'

F0D7 Send IEEE command character

F109-F142 Send byte to IEEE

F143-F1 50 Send byte and clear ATN

F151-F16B Option: timeout or wait

F16C-F16F DEVICE NOT PRESENT

F170-F184 Timeout on read, clear control lines

F185-F192 Send canned file message

F193-F19D Send byte, clear control lines

F19E-F1AD Send normal (deferred) IEEE char

F1AE-F1BF Drop IEEE device

F1C0-F204 Input byte from IEEE

F205-F211 GET a byte

F215-F265 INPUT a byte

F266-F2A1 Output a byte

F2A2 Abort files

F2A6-F2C0 Restore default I/O devices

F2C1-F2DC Find/setup file data

F2DD-F334 Perform CLOSE

F335-F342 Test STOP key

F343-F348 Action STOP key

F3U9-F350 Send message if Direct mode

F351-F355 Test if Direct mode

F356-F400 Program load subroutine

F401-F448 Perform LOAD

F449-F46C Print SEARCHING

F46D-F47C Print LOADING or VERIFYING

F47D-F4A4 Get Load/Save parameters

F4A5-F4D2 Send name to IEEE

F4D3-F4F5 Find specific tape header

F4F6-F50C Perform VERIFY

F50D-F55F Get Open/Close parameters

F560-F5E4 Perform OPEN

F5E5-F618 Find any tape header

F619-F67A Write tape header

F67B-F694 Get start/end addrs from header

F695-F6AA Set buffer address

F6AB-F6C2 Set buffer start & end addrs

F6C3-F6CB Perform SYS

F6CC-F6DC Set tape write start & end

F6DD-F767 Perform SAVE

F768-F7AE Update clock

F7AF-F7FD Connect input device

F7FE-F84A Connect output device

F84B-F856 Bump tape buffer pointer

F857-F879 Wait for PLAY

Appendix B

F87A-F88B Test cassette switch

F88C-F899 Wait for RECORD

F89A Initiate tape read

F8CB Initiate tape write

F8E0-F92A Common tape I/O

F92B-F934 Test I/O complete

F935-F944 Test STOP key

F945-F975 Tape bit timing adjust

F976-FA9B Read tape bits

FA9C-FBBA Read tape characters

FBBB-FBC3 Reset tape read address

FBC4-FBC8 Flag error into ST

FBC9-FBD7 Reset counters for new byte

FBD8-FBF3 Write a bit to tape

FBF4-FC85 Tape write

FC86-FCBF Write tape leader

FCCO-FCDA Terminate tape; restore interrupt

FCDB-FCEA Set interrupt vector

FCEB-FCF8 Turn off tape motor

FCF9-FD0A Checksum calculation

FD0B-FD15 Advance load/save pointer

FD16-FD4B Power-on Reset

FD4C-FD5C Table of interrupt vectors

** Jump table: **

FF93-FF9E CON CAT,DOPEN,DCLOSE,RECORD

FF9F-FFAA HEADER,COLLECT,BACKUP,COPY

FFAB-FFB6 APPEND,DSAVE,DLOAD,CATALOG

FFB7-FFBC RENAME,SCRATCH

FFBD Get disk status

FFCO OPEN

FFC3 CLOSE

FFC6 Set input device

FFC9 Set output device

FFCC Restore default I/O devices

FFCF INPUT a byte

FFD2 Output a byte

FFD5 LOAD

FFD8 SAVE

FFDB VERIFY

FFDE SYS

FFE1 Test stop key

FFE4 GET byte

FFE7 Abort all files

FFEA Update clock

FFFA-FFFF Hard vectors: NMI, Reset, INT

Appendix B

>

id

Z

QDIi l b,CO^HOHflhHClcOO«<UHft. l JWDIl,a 1

Z

0 ec

v

<

00

XI ■"■

era «— =r cv-i t^- c\j ^r

rfEc\j^3-i43 c\j c\j m m =r =r

jU -rH I I 1 I I I I I I

ojj oo(^l4^t^ooa^O'^c\lcv^r4-LJ^^'^^-cx)a^oc\J^oLo^rco^n

(fl CD r-r-r-^T-^-v-r-^-T-ajOJCMCMMm^-

a- q o

!_

CU

^cvj^rvo i_n X3 t— lo <t; o

Uooo t— <— C\J CM C\J CM

ooo oooooo

ooo oooooo

III I I I I I I

xorOLnt--oocn<muQtLiii, o<-(\imj-iO t^oojioffl

S CDOOOOOOOOOOOOt— t— t— t-i— t— r-t-OJC\J(M

HOOOOOOOOOOOOOOOOOOOOOOO

OOOOOOOOOOOOOOOOOOOOOOO

Appendix B

X3CJDOC\lrJ-vDX)OC\l^-vOX)OC\J-=rMD PO^DO O r-

=r=rLTiLTiuriLnLrivo»x)>x>>x>vDt~-t--c^t-- coco a^ t— t—

i i i i i i i i i l i i i l i i ill i i

Ln^cTiT-roLnt—crvT-ooL^^-criT— oomt-cxD^rt^-c—cx)C\jm^rLn

^^^i^inLnLnLnvO'£iio«Dvoisst^i>-iscococri(jioooo

uoc\i3-voco<a;uuor\ja-ioco<i|o c\j on m no po oo ro ro m ^ =r^r^r-i-=i-^r oooooooooooooooo oooooooooooooooo i i I I i I I I I i i i i i I I

QtiH'— rOinf-CTiffl Q tL, r- 0OLTic--CriCQ

c\Jc\jn^rn(v^rv~iooonrncY-)-j-^--4= j-^r-3-oooooooooooooooo oooooooooooooooo

182

(V-| vo O

i_n in >£> o o o o o I

o o

o

o

I

^3- i^ '— c\j »x> c--oo cr>

LnLPivO^OMDvO^OvO

oooooooo oooooooo

^ ~ ^ -*

Appendix B

S CO bO

L ID bO CD

CD X CD i-H •—I I—

P O iH Cm O CD P

CM -HOC i P <n 3

=fc ^ O -H P C/3 CD >, C 3 TJ O

boa.ni > x >H O X) <U O

CO G CO .a PC OU rH O P bo

> -h n cq 3 cd o oco i- o <o p

tdcdw -a a "O-p E p h d h h

<— C -H P CCO CDO >, I- 3 X i- Cw Xl

% D L C C CO P i-CD i—I O >> aO L *«%

OO<Db0-H l-t ,-H C P P \ O H <U

Oi-OT X Q-l- 0) CO COCD *H 3b0O O 4J

«al w \ • - .p OO <h£ CDS-l-bOOO UJ -H

ccd-h h^ ai o-a h t. (u ra ra h to \ U

- i- CD C 3 CO t— TJ CD -H O O Q.i-1 O O P 3 E CD 1—I rl — P II T3 > CmCtH <m II i. 1—1 J C

O-O 4-5 CDE-h"C>,"CDtH > i- 2o S3 CD

Wt. t H3t.3M<;ratH+JSH<D(DC ►IDIUllD'-^IO CU

■H O <h O (B H H-P'HDLO>ll)(D'D+)llJKt,l, O CO

I- l3i--PCD'OQ-,C0l_Q,CDCDC0a,O O > \ CD CD .id -P-P

ai Ofl^ c > l cn)p t Hi<nO'HQra-rio opc -

Q. .H 3-H O X O > 3 CD >2 S- (UOO'-N 0>H3 C

E CD CO O T> 3 O O OX)HLh>.CDC_)C0o-69 1—I X) O 3

O *- P O.CD P O O CO £ 111 || B M O OO

0=fePE-i CDCO-HbOO,— 1.— IWPCO .p O S- P CO CO iH "O

SCDWOC032CIICOCO CO E P 3 | O CD CO >, CO CD P

C 3 CO O -H P CO -H T3 -H t-I CD tH 3Q.CD<1>CDCL 1 Pl,<i-it-Ii— IC

b0ocOKWQcD>>EC0t.l-Q.b03Q,PQ.Ps^ <h I. O 3

■HOCDXCDSP(D-HOCDCDC0OOC3C0>>THa,Q.-HCD>,O W<iUUBKW^HJCOMHKIHOhcqQhh*iWOU

^•co mm cm

1— en cm -=r vo

1 1 1 1 1

c\JmLnc\jai^-invoi>-coaior-r\im^rLrnD noo o^o^-^" m ^^^cMrn^^^za-^^LHLnLTiLnLriLriLriLnLnLrivovo'Xi'X)

CM -a! 03 U_, CM

[-—OO t—<X> <%

OOOO O

OOOO O

I I I I I

u,Or-m<i;fflO'-(\im^irnoNoocn<i;cQUQU[iHom4-i^

voc—t—C—t-— cXDCTvO^O^C^^c^O^C^CTlCJ^a^a^CJ^O^a^Cr^C^^<I; , =I; <: I!• : I! OO0OOO0OOOOOOOOOOOOOOOOOOO

00000000000000000000000000

Appendix B

c

o

X> -P P •H -rH -H X) XI £> CEP •H -H CO

>1

P

•rH i»

CO faO CX C

CO

w ca

ex

i- "O

-p \

C 4-> ■H C O 3 CX O

o

CD S-.

<m -a

<« rH

3 X> -P

i-,

CD ^2

CX P

ffl a ai.

E-< H H S

T3

\ -a <d PC P v.

>-.P £1 -H

co 3 i-i to o

<D ^i S~ ■POO

>,0) w

\ o -

■a s-

CT3<h

uj cc 3

••**%. X)

•a x ,j -p a>

•-MD,

p c ro

c CD P>

O rH C T3 i-

ca ra a>

O (UP

WH C

•rH CX i- O

H 13 a.

S-.

CD c M

Ch

3 CO X Ct-l P

oca;

CO CX

■a p co c W-P

UJ c

CO o o

•a ■a

ra

5-O

-a

P (D C CO

o

o p -p

3 X

0J P

>>

X CD -P B

3 CO

O C

i- -a co os

o t-

X -p \

bOP C S-. •H CO

S P

•H CO

0) P

C) l) L

cx ap ca co c

H E-i CU

.-X TJ

0) -P

rH X

X CU

CO C

C

0) CM

m

i- CM CD w S cc

•H \

P E-i O

cxw

E-h II D. r- E-l

U CD CD

OiHH SU -H -H

CL)

W

CO

CD

-a

ra

C rH

CO CD S- P

ca o

co >, o X3 •H C M O O 0) rH CO

X

o

ro

co -a x: co o

CD

p -p -p

c c c

CD (D 0)

S i- S-.

i. i. I-

3 3 3

K

o c_> o

-p 3 CD CX S C CO -H C

TJ CD CC rH \ •H -Q

O O ^ P

■P S-. c« CD -H

p x

C CO •H O i-

cu 3e

o •p

hO t-C CD ■H<« C tn •H 3 CO X

e

CD X3

I- i-

O

co 3

J*

O rH

O CO

rH -H

XI L.

CD

J*

o o

r-H

s~

CD P CO

c -a

•h -a

co

ex CO

CO

p

ro P co

0)

exo co \

<D

Cm

Cw D X

-a x

CD

C •H

CO

ro X o

~ ^

on lo t— en r- t>- f- >-

i i i i

c\j ^r vo co o

CO CO

^3- VO

C\]CY^=rLTlvOt^C^O'— C\JCY1LTlt~-CXD

vosooc^or- (\i^-koooo >- cvjoo^rLrivot^c^O'— c\jcyilti>-co vovovovo t-— t— t— t^-t— t— co oo oo co oo co oo xico cT>criCTso^criCricri

o o

o o

I I

vO t~-CO Cf\<l CQ U DJ

<i;<t:«aj<i;<«a; CI ;«=c; oooooooo oooooooo

CQ 0Q CQ O O

O O O 3 O

O O O O O

II I II

O OJ4- lilvO t^CO CTi^DPOUtOr- rOLTivD CQCQCQCQCQCQCQCQCQCQCQCQCQCJOOOO OOOOOOOOOOOOOOOOOO

oooooooooooooooooo

Appendix B

CMC\1CMCMCMCM(M(MCM(MCMC\JCMCM(MCMCM<MCM(MC\JCMCM

o

o o

ooooooou

OOOOOOOO

oooooooo

Appendix B

CD G •H

M

<D 4-> C

4->—,

c w

0) «

§ Eh

far-, Eh -— O Q Z S3 Eh S O D

WUUK

CD rH CD O

c to c a-—

■Hll^Bl-rinr-, EH

rH C 3 C H&tf4J WKiJW •H Qj CD fa J X M O <0 H

SHCJi U2U(1) Jfci+JOi

CD -H O -H ■—"—' 4-> ■—"—■ 01 ■—' ■— •<-"— ■ —

c a -p oi

■ho flEBfteeiue e e e g

CDf0>jCP4S-<M3V4M4->U U J-l J-l M

H£-HU OO 0030.^0000

ci ODCi-iuouwajwaiMt-iuJH

tC&tfUfafafacQfafaWfacQfafafafa ~-

c

■H

a

rH

o c

CD

rH

X!

<0

EH

ft-

E

3

l"3

IT) CN

LO

in I

CO nH

"*

IT)

m

fa

fa

fa

I

<:

CO

fa fa

01 rH

CD

c c (0

,£ U

<D C

c

XI O

-P 3

Dj4J C 3 M O

-P 4-> CD 0)

0 M

c

C 4->

(0 rH

.c 3

U (0

4-) 01 3 "O

at

01 fa

a) s

0)

O 4->

tQM«H(l|

4->

oi En

CD H

Eh O

I I I I I I I

VO (Ti CJ fa CN rH «*

U CJ U O Q W W fa fa fa fa fa fa fa fa fa fa fa fa fa fa

OmOCTiCOCN CD 01 O CN *0 T3 U<WHr>H

oiro^r—i-H^moocyiTtf cd rH cn cn n in r— •»i , ir)ir)LnvDvovovDVor~-r~cooooocooooo ooooooouuoouooooo

01

u o

4J U 0) >

01

u

o

4J U 0) > 01 01

COO

O 4-1 4->

O 4->

•H

rH 4-> O O

U CO

O U

0) CD

> >

01 CO

01 CD O

CPU

01 PS

o oi O cu ai fa > e

01 M

CD CD 01 O

tJiC03 IH (0 (0 O

01 01 (D J*

4->

13 U O

4-1 C U 01

CO oo-o

O U-H4J J-l 4-> (0 O O M >

OlOlCOOfOEHS CD CD «$ CO E 4-> *

o o

a >i c cp >i m

' & CD

O <D 3

E >H 4J

rH 01 U <D

o o c

>-l 01 «3

CD 01

E

CD O

a a-

CD 01 >i

fa" CD

>iO c

O fa 4->

3

o

u

CD

CD O O H > CD CD D

HM-HOO-CCO

U 0}

rO >< PQ M

4-> U

C - O CD • "4H

.* Q >i

fO < T3

CD W (0

U ctj CD

« ^howwuswauu wpQ fa

o UNo cd cDcoin (Ocoj3cotnr-cTi-<*o ooinoocncricN^coiUHOMM vo r- oo ooooorHrororonn'*'*^!'^!''*'* OOOOOUOOUOOOOO ooo

-~

-

-

~

Appendix B

i-Hr~44CO'*OOr--CDCriCDi-HXlrO r O'^ , LnCNrHinOO CJTDrH CD CO CO lM4-l4HOrHCN fO CD d) H CO CO H H Ol <UX) r OTl«'tf^ , r--0 > >CT>fdX] m ni niiuiuajujujujnnnr-i^. —i •—I «—I ■— irMCMcnrororomm

munrN to ui lUrtiuuurI i— iui iu j-< v-> ■vr t t i^- >ji wi «j j-i CD (D4-4H-|M-(444-l4HOOC3iHr-Hr-Hi-HrHi-HCNCNnrOnrOrOrO

o o o u o o o oT3 r o r o r o r o r o r o r o , u r o r o r o r o r o , o r O'u

mocNoovooo^ajajainococD CD^a r oj3ir)4-ic^io O cdoo 000

00 rdt344oCNrO'*VO fdCOOO <GrHCl*3 , f~faX!4-l04-lrHr--Cft CtJ

oooooooocTicricricriCTicTi nj cd rexxxxxxx cj o'O'D'O cd OOOOOOOOOOOOOOOOOOOCJUOCJOOO

Appendix B

CD -M >1 — Si —, S 4-1

Eh >h O a

C_) iH O (J rH i—i

<C # V-i On <N CN 4-4 Cn HH >i

.—i—, # O CU rH 4J =«= =*t= i-l o G Q=«=UiHCN

W Eh g EH .—, rd N^-,EH-HCJ\(UnH-H MUO** >—i

« H O PQQiH CJ >-4 XI .< rH > 4Jo> Hi g U U S

0< UDP >iOD I &i* O >i«IHHlH 0 10 <H e>

04 [2 4-ICQ<4J-XllJSfd CJ \X1 O P glH fe* CO

^-.^ | __ c |2 ^^ , o rtj 5 H >i— O U —

-U 0> O >i >i4J &4 O >i4-4 Xl 4-> o o o < c

ggmOggg,JrHggrH <-H -H g 4J 4-> 4-> 1x4 Cn g

u u •<duua>&4&Mi-iCi4>i4-ii4-iCi)c:ai»-i>i -hu

OOOWOOHK-HOO>KU(tkl<Hfl l OOUrlNH 1 OKIO 4-14-1 4->4-l4-)Cl|W4->4-l4-l4-l0 3 0>4-l tIIhO#*#C 4-| >-4l-l r OXl>-lV-ig>rHl-4UrHg-r-l'OrHO>»-lgC_)OU34->»-l

cua) r aa(ua)003cucu3cu r acDiH-H<i}cu<<<oa)<u

PM 04 < CO PL, pn c_> - aftftS2<DS + QftSli ( Ii i Cmi;Uft

^■Oaion i0> 0> ro rd Xl cti Ur-^ojo^ 0) cn cn r- O o Xl XI cri cscN^LninvD^r-oo cu cn in oo Xl ^ cjihihh cd owohnpi oooooooooooocnaicncTirtJnjnjnJfdronjtaxiXJXJXi ooou i u r o r u r u r o r c5'o r o r o r a r o r o r a'o r o r o r D'n r O'o r o r o r o r o r a r c5

ufcfiiUoiaoouuinouPiOiPitiiBiMiiPiHPiu&iPi

rH'*tninr-«*vci r o^ r ucdnxiOoor~rH u cn XI X) "O XI t^'O 0)4-ivDt^oo4HCNX<oror~-'d r ooio<Nrnvor~oooo<T»(dcU4-io ^^

'D'O'D'OTI'D'U'O'O'O'O'O'O'O'D'O'D'D'O'D'O'O'O'DtJ'O

188

-

Appendix B

U3rHOOrH.Q.OOr-'tfC^4-I.Q.r~VOOCr>CM UOLD COCO O UH rH f^ .Q

rH^o«^)J2or-cjo co cm ** in »£> r~ ca id j3J2oooH'jincoco J3 CU01CUOICDO>0>0)0>OIO>OI010>OIO>0>OIOIOI010>0>0>0>0>

§

B

o

-p -p

■H

-—.rH r—, u Cn CO # Eh ccj -H CQ CJ TD S3 UH T3 << <DH —• Cn X <—• O •H 4J g OJiH g OP

V-i >-i I >-4 tX> W

o td -p o c re p

UH ft ca UH -H re

vj e o v-i v-i p o

0) O rH QJ -P 0) rH ft CJ ft ft CO CJ ft

H O

to p C ca P to CO

rO

E •H

O

0> HH Q Eh

PS

ao

CO ft

o ta

p

Cfl

C

o o

>

hH 01

EH P

< —. ca

O CI, 3

W X rH

SB cj ca — — >

01

rH CM

e e

V-I V-I

O O

UH UH

V-l V-l

QJ Oi

ft ft

E E

V-l V-l

O O

UH UH

U V-l

QJ OJ

0) 4-J

ca " 3 p

rH S3

ca «

Oi

E

o

VJ

0)

ft ft CO CO ft

o-

P ■—!

c co

•H X O CO

ft—

ca E 0) v-i P o CQ UH u

fv. ft

>

to

\

fO O

r-,ft —.

WHO > « <

< w o co > J

E E E

V-l V-l V-l

O O O

4H MH MH

V-l U V-l

0) 01 cu

ft ft ft

r-. W

S3 CO

W O

ft J

O CJ

E E

V-l v-i

O O

UH UH

l-l V-l

CU 01

ft ft

CO V-J

cu p

0)

V-l

(0

ft ca E

E O

o

V-l

O

3 CO

UH

Oi "O UH

o o o> 01

ft CJ CJ

ooXl-Q CJro CUTSvo nlHXI'*'OOiO , *^>nCNinxi'S'Hroil ldlocti OUHrr OrHnr~~r-~XJ oi^ina^UHcMinvjD^.Q O'Ooo

O O O O O'O'OUHUHUHUHUHUHOOOOrHrHrHrHrHrHrHCMCN

TD'O'O'O'O'D'oti'D'D'a'O'a a) <d cu <u oi cu ai <u cuoicucuoi

Appendix B

^WlUUUUIHjHU'nUVIUV uiti u/r Ivuaivuu/VUrHrHU)

uwd)wowstou>«nototQWtHWUtawtomctiuuQ 0) o vo airHoXi iB rrj'D'jr- o cxi r- o in 0)'*vo'<a , <TiTi< r avD

H O <* in (N m ITHO (d dlMHrHrHrH-tfXlO O O 0) IHOHOOCOffl

xiooo r a f o r a r OTD r o r ciaioiajoioio)a)oia'a)i4Hi4H4Hi)-iiw aiajatodjatdtataiaidtdtatataidtdiaiaiaidtataiaidtdi

muHUHooLn (om'OCNroooco njcNrHcriLn at^o at dtTirH id cm m-i u o^rxi o dtrHCN"^* utd dt<4HrHCNCNr^ atinvot^oo f3 (0X1X1 in mvovovow3r~-r~r-~oocx)<xioo(Tt<ricTi<Ticri (d rd (d fd (d (d (d (d at ataiaiaiaiatataiaidtdtdtatataiaiataidtdtaiaidiaidi

'

*•

Appendix B

B 014J Dl (1)<1)H C 3d 3-H H-H(OC <0 4-1 4-|

CD

m cd

in to o c

H

■P 4-1

-p

0)

(D

TO

cu o

CD

p

ID < r-1 CO O -P

-p m to w c

CD O <D

CN ro

CN

to

p e>

ens O H

p a:

fttj

U fc en ,«: « Q w

TO <

CO W ft O CQ

O J-

c

CD

M >H

PK

M

>

u

s

M

Q

<: o

P

Cn O -

as

u o

rH

u

M -P -P

CD >

> <

(0 co

CO -

ft e -p

3 CD CQ O

0)

ft

o

-P CD CO

e c o

P CO CD CD TO

Cn 03

<o cd

-p -p

co <;

CD TO

-H -H

fe En

CO P [fl CD <D

rG TO V-l (0 TO

CD CD TO

ftrC CO

CO

4-) CD P

ft CD

>i CO 4-1

C -P m 3

CD XI -P

■H 4-J

)-i CD

& O

(0

-P P (0 4-)

CO CO

1-1

l-l CD

CD -P

4-1 C

4-1 -H

3 O

XI ft

•P TO

CD C

CQ CD

CD

TO

(0 p CD CD

,G -P

C

O -H

■H O

4-J ft

•H

O CD CD ft ft (0 CO -P

TO ft

c e

•H 3 Cli CQ

(0^-l>4-^l^-l^o^3l^)r~cN^^c^lCOLnco■^ , OI^oCD^^-|r-TO'^ , r- (0

Tj'OTO<D4-ic3<TiOTj<^i , Lnc£>r~cNfn>>ovDr~-i~-nJCDTi<Ln vo co

C<inMMM'*'t^ , l/HOCDCOCDM^I^>I^M^>0000 0000

IW4-I4-I4-IU-I4-IM-I4-J4-I^M-IM-|4-|I4-|4-J4-J4-I4-|4-|IJ-JU-|IJ-|IJ-J M-l IP

ro CDV£>r~-vo.QTOcNinooeri oTO^o4-io'tf«cNLr>Ln mo rOot^-cn co cDp-HCNroLncyi co co coxixi cd i—i >* vo t~- <D 4-j o o in r~ cxi u o

4-I4-IOOOOOOOOOOOi— li— IHr IrHiHCNCNCNCNCNCNrO CD CD4-I4-I4-|4-J4-|IP4-I4-I4-I4-|4-I4-J4-J4-I4-I4-I4-J4-|4-|IP4-J4-I4-I4-I

Appendix B

0>

X! 0> CO

(0 H u

4-> >(X! O

l-i 01 4-1

Cn-P -U U

■H

g

0) Qt >

e

3

M (0

CM O 3:

CN -H g M

CO <ti 3 CO

ps s h> m

t3rH(TiCr»or~-V£)4-lnCNrHCT|CNI OJ (O U (N IS (0

oo4-iu-i^'LnLn«x)V£>r~oo(Ti rd r o r Uir>Lnr~-oo | 4-i r a r o r o <DCL>a)a)cuci)<ij<ija)CDCL) < 4-iM-)i4-iM-iii-i

1|-|IWIWI)_|IHU-414-|IWI4-|IH4-I14-( 1 WI)-1U-|1W4-|M-I4-I

'*X!l > -OrO'JXJ r D CD T3 CN -Q (OvoXiLriM-IVDOOr-lXlCNy-lCN CXi CO X) O CD 04 rr LT) 00 cOTDTD OJOOOl UlHOHHCMClLfl

oooocooooooocncria^ foxiX!X3 o o u u o r a r a r a r a r o r a

U^lW4^4-(lMU-|44i|-|lW^y^4-|lW4^M-J l 4-l<4-)04l4-|l4-4 l WlW l W l W

Appendix B

Map 6. Commodore 64 Memory Map.

VI V2 V3

D400 D407 D40E D401 D408 D40F

D402 D409 D410 D403 D40A D411

D404 D40B D412 D405 D40C D413

D406 D40D D414

SID

(6581)

Commodore 64

Voices (Write Only)

VI V2 V3

54272 54279 54286

54273 54280 54287

54274 54281 54288

54275 54282 54289

54276 54283 54290

54277 54284 54291

54278 54285 54292

54293 54294

54295

54296

Filter & Volume (Write Only)

Appendix B

D419 D41A D41B D41C

Sense

54297 54298 54299 54300

(Read Only) Special voice features (TEST, RING MOD, SYNC) are omitted from the above diagram.

' ~

$DD00

$DD01

$DD02 SDD03

$DD04 $DD05

$DD06 SDD07

CIA 2 (NMI)

(6526)

Commodore 64

Serial Clock Serial Clock ATN RS-232 In In Out Out Out Out

DSR

IN

CTS In

DCD* In

RI*

DTR Out

RTS* Out

RS-232 In

Parallel User Port

IN Out Out

Out $3F

Out Out

Out

$06 For RS-232

Timer A

Timer B

PRA 56576

PRB 56577

DDRA 56578 DDRB 56579

TAL 56580 TAH 56581

TBL 56582 TBH 56583

ICR 56589 CRA 56590 CRB 56591

'Connected but not used by system.

Appendix B

$0000 $0001

DDR 0 PR 1

$DC00

$DC01

$DC02 $DC03

$DC04 SDC05 $DC06 $DC07

CIA 1 (IRQ) Paddle SEL'

(6526)

Commodore 64

Joystick 0

R L D U PRA 56320

$DC0D $DC0E

SDC0F

Keyboard Row Select (Inverted)

Joystick 1

Keyboard Column Read

$FF-All Output

$00 - All Input

Timer A

Timer B

PRB 56321

DDRA 56322

DDRB 56323

TAL 56324

TAH 56325

TBL 56326

TBH 56327

Tape Input

J_

Timer Interr.

B , A

One Out

Shot Mode

J I L

Time

Timer

PB6

Out A Start

One Out Time Tinier

Shot Mode PB7 B Start Out

J I I I

ICR 56333 CRA 56334

CRB 56335

Appendix B

~

~

ra

E "« U3

•H I I

UOrHriir)r^co<riOrHCNiro'q'Lnu>r^oocn

0) r—ti—\^-it—ir-ir-{r-i^f—^^-{

Q

rH *t m r- (N vj. cn cn n r»i <r ^r I I I I I I

o n nifif co ro

cm cm cm cn m n «3"

Appendix B

rxcxcxix3rxua.ixuuMUu>>HUs:ns<<<:cjj<

CDCOOCN^'COCOON'Cf^COOCNI^ n CD U3 O

"j'jiflini/iiniricDiDcDcDcDi^r^or- cococti rn i I I I I I I I I I I I I I I I ill i

i/ir^^rHrninr^o^iHroLnr~crirHroi/ir^ro^r^r^cocNc^*r M , 'T^LnLOunLnLnu3cDcDv43cDr^r^r^r^r^oocr)a , icrvOOO

baocM^cr3co<cjuocNi-rrcDcoi<u mcoo LD cMnmmnnmnnT^^iT'i'T ltilocd ud oooooooooooooooo ooo o oooooooooooooooo ooo o I I I I I I I I I I I I I I I I III I QCi ) Hroinr^cAmQfj4rHroLrir-criD4DU'rfr>H(Ncor>co NCNrorororornmr r ir r iv^ , v'T'ci''j , 'j , 'Tincnu)U3vDcoco ooooooooooooooooooooooooo ooooooooooooooooooooooooo

Appendix B

i i i I i 7

LnrHCNrntncNCT^^i^ix>r^rocnorHrNir^^Lncor^cocriorn

w

CO

o

o

I

CTl Cu CD CO

o o o o

CN <

r~- en

o o

CQ Cu

o o

I <

CN <

o o

OrHCNm-q«Lncor^oocr\<cQuoufc l o ^^r~oocri^mCTiaio\oso>CTichO\mavaimcri(st:

ooooooooooooooooooooo ooooooooooooooooooooo

m

< o

o

Appendix B

I I I I I I

'3'iny2MX)criOH(N'q'y3aDOH(Nm'3 , i/HDr>o\oHrMm UD^u^a>^^r>-[^i^r^r^r>-oooDcx)coooc»cx>oooocncncricri

Appendix B

^ c <u e 4-1 a

°°>. .* XI u en >« en m

ul U > "^ **-' E(U (U W D —I Q, V-

**-• ■*-".> nr>ninr-<, n\ —i «■ n m _ .

Vj H-l a O

OO0)OCCQ)^l(l)X)'O^H u

.2 O m c ~ c 4-> U «TJ U-. ^ H (U .H £ TO 10 0) §

.5 -a <n £ m ** ^ 00lCCO ■* «" n c h

q.w'S.qjo^ uu^cuqjuu wuaco-uu u

3 £ S <D n wccr-jww u <u 3 u « u .5 a c 3 a, c > c S ^ "2 d ^ c •MS^o^Haiooao

m5m5Sr DE EOOUO >,«C.-HCL>4J

<u to I u ^ jc 4J y-iuou-utDcz £ ^ ^ £T

£ J ,£^?^-e , °oroo.U4J4->ua>:s a; <D o c^ m 4J

5"? " .^ c £ ii 3 £ 3 c o o ii 3 o ro uoojcnwr-i

<£> CM

O^ O

CN ^

N^VOOO O ID

^r ^ ^r ^r in kd

tN CN IN fSJ CM CN

-HrH-l-ICNCNcNCMCMCNCNCNCNrNCN CNCNOJCNCNCNOsJCNi^cN!

I

o o o o o o o o o o o

-

XS CO)

u .he

<0 ME

O (0 —

•

O O § Cutn Cn Cm Cn S

o o o

OOOOOOOOOOOOOOO OOOOOOOOOO OOOOOOOOOOOOOOO OOOOOOOOOO

Appendix B

i/iiOrHOH(Nm'j , '5''3 1 ^'^'v , ^ , Lniriiriini/iiriinir)incD

CNCNcncDCDCDCDCDCD COCDCOVOCDCOCOCDCDCDCOCDCOCOCO

WfciOOCNCJCDOCN-C O

T0[i4CncDCDr--C0CX)CO OS

rH^HCNCNCNCNCNCNCN CN

Qoooooooo o

I I I I I I I I I I oooo^r^Qr^.-Hr^cncDr^cocri<moQW[ij.— icnco^t

oooincocor>(X>oo cxiaDcx)a3c»cx)ooooooooooCTicTicTicTi

i-lrHCNCNCNCNCNrNCN CNCNCNCNCNCNCNCNCNCNCNCNCNCNCN

OOOOOOOOO OOOOOOOOOOOOOOO

Appendix B

In

0)

M 3 4J

cn ro C 4J •h in

e

■H CN

-p co 4J I

CO

cc

CD

O O

O

a

CD

>

o

4-J

CQ

T3

a) cd a. u in QJ

W CM

+j n cm •rt cm en

XI I CM CO CO

o

0-,-rH

e

3 C O. ro C >h

•rH 4->

CM CM co ro

CM CM CO CO

cc cc

o

4J

c

O O \ U

M

4-)

una <u a, a

4-1 (T3 t-l 1h 0)

cn 4J

-u a, c c

ro

cn

O r-i

rH 4-1

1-1

0) 4J C

•rH

o

a

C 4->

4-1 U 3 D CXX3

CD

>

ro

I/] CM

CM CO

(NO< CO CC n CChO

o

u cn

4J O

C J

o

U 4J

a

< 3

qj qj

E 4J

>H C

< < M t-H

u u

T3 CD

i—I

X) ro vj

C CD CD .*

< ro 6

ki cd 3

E O

•rH Vj

c

rH CD

CD

< In M U U CO

c

c

c

•rH .* rH

c

•■H W

rH C

CD

4J J*

rH U

ro

<D 4J Cn W

ro

cn

o u

u

e in in ro

cd ro cn

E S x;

k-i u u

CL, lH CO 3 -rH

co in ro u u

4-1

c

CD

E

QJ

QJ >

ro cn

HH u J) QJ CD cn

QJ

u cq O a. co cj

> > >

ro ro ro in in in

in Cn cn cn 3 QJ QJ QJ 4J

vh u u ro

I I I 4J

< x >h in

co co co co

rH >H >1 >H

co co co co

CM C£>

I

CM

*x> I

l£> CTi

Hn^jin>(]oaiOrHm , q , incDi^'TCD cx)ix>vr)ix)ix)ix)cx)r~r^ r --r>[^-r>-r^ou3

CDVDUD^V£)ii)>X)tX)VOVD^£)iX)'X)lX)r--r^

m m m r- <r>

r- I-- r~ r> r--

i— t— i— r>- c—

I t i I i

OCNV^OOOHNn

r--i>r~-r^[-~ooooao(x>

vo < o

cr> cr> <

CM CM CM

o o o

I I l

mMOOMSUQWh CTiCTiCTiCTiO^CTiChCTiCTi

rH cm co *q<

< < < <

[i] h m in > o\ to

Dl, o o o o o o

cm co co m m ro ro

o o o o o o o

I l l l l l I

OOCN^COCO<UQU[ii CJOOOOOOOOOO

CMCMCMCMCMCNCMCMCMCMCMCNCMCNCNCOCOCOrOrOCOCOCOCOCO

ooooooooooooooooooooooooo

Appendix B

QQ W^kjInfclnl^CnCnCnCnCnCnCnCnCn

U

o xi

u

>

s 3

3 >-■

•n t-i

0)

C 4-1

o c

o

4J

u

0) 4->

> u

qj ■u > a.

3 4->

i-i a,

u

c

QJ TO

PC h< 3 cn

o

4J

u

QJ >

1-j

o

4-1

U 4J

o J-J u

QJ

>

0) o

4-1

c

m CO. 2

3

3 a,

CU 4-1 C 3 •H 6

I I

O 4-1 4-1

J u gi u w en

o

4-1

u aj >

a,

2

O

o

QJ

>

4-1

o

4->

u (1) >

a, o

E-<

en I

o

4-> U

0J

>

u

o

4-1

u >

a. 4J

4-1 l/l

3 CD

O E-<

0) U-l

XI 1*1 "=3" LTl

TO QJ u

TO

I

cn

3

H

o o

>. E CC U 0) O E

w CO 4-1 W

£ i-i

3

0J

4J CD 0) 0)

4-1 4-1 4-1 4-1

c

<H •!-! -i-t QJ

V-J U U QJ

a, cu a. vj

m wtn w u

2 «

^£ u 1-1

•H 01 l/l 4J TO

u -'^ "— cn cq <

m en h n in I s - 01

00 00 CT\ (T\ &\ CJ\ Ci

r*» r r- r— r-~ r~ r»

1 1 1 1 1 1 1

«T 00 O CN ^T IX) CO

00 00 CTi G\ CTi <Ti CT>

f* r- r- r— r» r- r~

cn cn r-

r-tmmr-criiHrnLnr~cTi>-i^roocN'3'

OOOOO-HrHiHi-HcHOCTNLnOO OOOOOOOOOOOOOOCOOOOOi-HCOCTifHCN

I I I I I I I I I I I I I I I

OCN^TlXiOOOCNTVDOOOOCNllXlO^r OOOOO^-lr-Hi— I H H (Nrn OIHJ N

cooooocooooooooooooooooooocno

cn cn

LT) LT) CTi <Ti

o o I I

00 00

o r~

CN CN

m

m o

I

o

. CN CN CM CN CN CM CN

cn Lnr^cnmQ[nHmui>(jicaQ[nHm(riww

in n m fe r- m

m m ro ro n m

00000

I I I I I

o cn u o o

00 mncimncicimmnncimnfonnnfi o 0000000000000000000

o o l l

i£> 00

OOOO I I I I

OOOO

I I I I

O o

I I < u

. CN CN CN CN CN CN CN

mnnnnnmrnnncnmn 0000000000000

<UHotNTCDra

o

1

W

CN

o o

Cn Cn

Cn Cn

Cn Cn

cn cn

I I

o o

o o

00 o

o 00

Appendix B

~

204

-"

Appendix B

LU Z2

•X.

Q

o E

Z "C

<

•-3 CD Q Q <

■ - I— i Q_ 0Q cr

a-

Z

CD CD CD CD CD CD CD CD CD CD CD CD

CO O) < U LU

CD CD CD CD CD

CD CD CD CD CD

CD CD C CD CD

CD CD T-t C\J

CD CD CD CD CD CD CD CD

Appendix B

II II II II

n 11 11 11 11

I— r*g ^^oco^^^^M^J^M^J^-gh>J^^J^Mr^J^M^MO

oi— coo- cowo<qo5:<jijiji^mq:z

SZLuE OCQI-HCai-lSOl— <<t— h- —l—IXXQ-Q

I—I Q. JLJ OOXODZQtJCOCDCDO_Q_COCO<<COl— I

1— CO CO h H^OcCOUOOOCJUC^CJC_jC_JC_)tJC_)tJ

Q_Q.CL.Q -N I-l^f-Hf-Hh-il— IHHHHf-HHHI— Ik-HH-)( _ < |_-t(—It-t

-

C_J •- CO CJ CO CO CO

(J a UJ U. C3CDCDCDC_D--(CMCO^LOCOr^C0OTcCLt-C_)UJU_

r-i t-t r-t r-t CMt-<COCMCMCMCMCMCMCMCMCMCMCMCMCMCMCMCM

OOOO oooooooooooaooooooo

OOOO OOOOOOOOOOOOOOOOOOO

O H N n 't

ro n n co co

CD CD CD CD CD

CD CD CD CD CD

Appendix B

c/J cc o —1 o CJ

UJ

I— >-

5-

Q UJ

CO

CO

OCDOOCDCDCDCD OOCDOCDOCDCD

O UJ U- CD *-• C\J

CD CD O CD CD CD

CD CD CD CD CD O

&> t/> V> fa»> t-% CO-

Q _l O

>

CD CD CD

N CO W W

t-i >- >- _i=:si— s:

uZCCCHLuQOZLO

UJLUliJU-Uf-^U

u-ct.ciL.zjLuis:_u:o

DDUQlDKXUZ

cc: CJ lu O •—<

Q. Q. U-CZ H I- >OlU uH

d. h lu a. o cr

CO U_ UU u_ CO CJ

mmnnroencon

DOOOOOOO

aooooooo

Q HI U-O

m ro ro **

CD CD CD CD

CD CD CD CD

>* ■* ^r

CD CD CD CD CD CD

CD CD CD CD CD CD

Appendix B

~ ~

o

crl CC LU

3 O O 2-

o

CO

o3 B

LiJ _I Q_ *— O- ^ < CJ

3

GO CD

C

CO o ►—I «*-

UJ

^ o o o

O I—

o

3 O

< UJ

1

WrO'finNCO'tCDQUJOHCO'ttDCOctlllUUJQ.CQU

OOOOOOOODOOOOOOOOOOOUJOO OOOOOOOOOOOOOOOOOOOOhOO

^:

o

Q

ii II M n

it ii n H

szcocoxcj3-Jcr: trLcuuorctJU

<<3J2>DQQ

s:s:ooh-i<— i_i—i

—JCCCCCJCjOOOOO

OODOOOOOOOOOOOOOOOOO

oooooooooooooooooooo

Appendix B

o U

LJJ >

cd ooooooooooooooooooooaoooooooo

II II II II I)

O^Mn^-LOHQTHtMncC'tLnuuij cc>>>>>idid<<u_E:u.u_h-l— f— J— t-iESESE-J-JEESIi-H^^LJtOGOC-J

Shhhi-Kcccai-hi-h-i-i-zJuy

iO>>QOOCCQCjC_jCjQcO IU>>UUUWUUOOWOOLO

I— h- 1—1 Q CD

>>>uu

CD OC\J'*lDCO<UlUO(\J'J-CDCr)<ULUOW't(Dff)<[I3ULUU.OHN

CD OOOOOOOOHrlrlHHHrlH(M(MNNNMC\JlM(NJ(\innn

CM C^JCMCMCMCMCMCMCMCMCMCMCMOJCMCMCMCMCMCMCMCMCMCMCMCMCMCMCMCM

CD OOOOOOOOOOOOOOOOOOOOOOOOOOOOO

Appendix B

> -i

<

_i >

<

o o

l-H t—

O uj

X >

~

*t in

cm cm o o

CMCMCMCMCNJCMCMCM OOOOOOOO

CO CI < 03

r- r-. r— r-

CM CM CM CM

o o o o

ty^ e^ </> fc*>

ii it

X >

LU UJ

Q. O.

II II II II

r i i i i l i r

oooooooo oooooooo <:<;<i:<i:«c<:<<

■c1.cli1d.cllccl

't in u_

OT-icMm^mtor^

CMCMcMCMCMrMCMCM OOCDOOOOCD

CO CO <C CO

IN r^ in r-«

CM CM CM CM

o o o o

Appendix B

CD

CD

E O

>>

CD 3

Q. CD CO i—

E en

i— L SJ fl

t_ c_ X :> -o ro *r- -r-OOCDCCi— i— >CC

i— "O O 03 <«-

CD CT3 c/; c*_

+J 4J i

CO O Q. CJ

X><XO~OCJCT>r—

CDCDCDOr— c/>OC

O LJ —I >—< u_

OOOOOOOO GO GO GO GO vy v) t^J 1 CO

O O O CD faO t/» GO feO

OrHCO^(DC\JC\l(OrN

oirocDcr)0)<cQcDco

CMNNCMCMNCMCMN

ooooooooo GOGOfcOGOGOfcOfcOfcObo

<

ii ii ii ii ii n

o

II It

II

II II

en en en h- I— (— a. a. a.

cr or a: a: cc I— h- I— l— I— D_ CL. D_ Q_ Q_

dc or a: cc i— J— i— i—

—i x o o CD a_ <£ o

OuJCO_IJ<J_i

oqs:ou_s:u.u,

I— Zl— I— f_>cJ3>_I

xt-ixxooos)— t

oooooooo

■^r lo to r^

CO CO CO CO

N M N N

CD CD CD CD

cD*-Hn-3-tDc\]c\icor"-

G10)CJ1010)<CQCOCQ CMCMCMCMNCMCMCMCM OOOOOOOOO

Appendix B

u u u-

o: d: tE cc

OOOOO-rHCMrOtJ-

o o o o cr IX o o o o o o

o o o

cm CO O O O O

Q. Q. UUUU

o*-iojr">*3"mcOr^.co

OOOOOOOOO CMCMCMCMCMCMCMcMCM

ooooooooo w w w w L-) t/i w w y)

M WWW

w w w w w

ti ii ii ii

CC U_ (— CJ X O CO CO CD

cccciiticcrccccccccri _j_i_i_jooooo oooo—i— 1—i_j_j ooooooooo

■Q.Q,Q_C1.0000CJ

E Z 2 Q -.-O^OOU.

0Q UJ Li-CCl CQ CO CM CM CM

CD-r-lCMCO^LOCOr^aO

UUUUU'JUUU CMCMCMCMCMCMCMCMCM

ooooooooo

CD CD CD CD

CD «-* CD «3- O

CM CM CM CM CM CD O CD CD CD

Appendix B

CD

• - CO O a; cr r—

CO

o

2: cz

C_J •!-

> en

CO re

> O-

1- (*- en t-

d) l-H

O to X3 (0 H- (*-

a: ji c

CD CD

=d ca o q

D O if- T3

2 Q C_) CO 33

CQ U UJ U.

C\J C\J CXI CXI CD O O CD

t/» fee £*<» e^

OOOOOOOOOOOO OOOOOOOOOOOO

cncomcocoronen

OOOODOOO

f- o

> l-H

CD CO

II II II II

II II II II

< O O CO

C_) O CO O ►-" O O i~<

M|-ZI-_II_l_IXi-'CM >MS:<LLLi.EI I— XX CQUIZOI ID 3 1-1 >- >- 3 3

UDDU(/)iocci-mLa<<

■OOQOOOQOOOOO

O O S < —I !E

CD t-i S O I— < <C

CJ £ Q O CO CQ CD

o uu u uu u

OOH(\jfO'tiDlOCOCn<CQ OOOOOOOOOOOO

cooooooooooo

rocomronroron

00000000

Appendix B

<

IB •

CT -I-

E w

«-l + + o

> U- M

N CO 0)< CDU

^ ^J- ^ «^ ■* -rt

en en <ry en n en

CD c? o CD o cz>

to- t^> to to ia e^

E o

CO U_

cr: en

ID < QCDtOQQcOCn

LU«t IN CO (D Q N CX)

CO oi cn < < ef DD Q

QO QQOQOO

CO tO iO iO to fcO fcO £0

i J i h cvj cr zd

\— _J _l X X Q_ CD

D_ an an < < co ^

uuuuuu cr

i-h .—i i-, t-| ih M • - Q_

CO D J > »-H =3 O ID *-H Q. '/3 < 2 Q

~

r^ co cn < co u *f **■ *f v ^* «*

Appendix B

<J3

CC +

CL. or. in

Q_ >• CU

—I - _J CD CD

u_ x u_ cc ce:

V V V V V

CD «H *-( *-^ CC

CC K [E >- I-

13

O

o D

LU

O

o • o

»—I o t— o

o

Q © U ^ tO ID O

tO Q Ol < < m 't

Q O Q D O Q Q

O O O Q Q Q O

(ft WW t/> W w te

II II II II

CD CD CD

cc cc cr

n m t-i

r- -* CO

O O LU

CD CO CO

fcO tO tO

II II II tl

u_ II

o. e a d: a uj >

O H ri O O > U

a o Q I— «— o >■ _j _i _i co co __: —i

Ll_Li_LL.U_LL.u_0_

Q_ Q_ O O X X O O

CO <£ CC O !— O C_J < CO

CD tH X

-ret h

U. U_ C_J

O CO U i^ CD (O O

a3 en cn < < m ■*

a o a a q a a

o o o a q a a

o U Q h

C_J C_) C_J Q

Q t__ LU LU

a o o q

rj- CD CM

O LU U_

CD CD CD

CD CD CD

Appendix B

LUUJUJlLlUJULJLiJ

CCEcecCKCirirc:

as uj

CD =3

0£ r-. r^ r*- r^- r-. r».

OOOOOOOZDO

lu or: t—

S (J3 <t

t—( LU O

CO II

to

cr to cc

LU LU

CO CO

3 co r)

2!

o < o

a: _j

LU O

u_ a.

3:oi-iovin^tLntDr-O-i—J—l—l—J—I E 1

f— lOOOOOOOO t— QQQQQOOQ

AAAAAAAA ^ 1 I I I I I I I 3£ 1 I I 1 I f I 1

< o — icxin^LOccr^ —it— I— I— 1— 1— K-l— t— CDOOOOOOOO >Q.Q_Q_D_CLD-Q_Q_

O U_ CO U_

OHC\|n<Jifl(ON + + + + + + + +

Q til hi n| i;i u,J ' ■ I UJ i > '

ooooooooo

tOQ_CL.Q_Q_Q_CLa-Q.

u c_d or

U_ _l h-

^ U_ Q_

ac Q i—

^ <n —i

H C U.

=3 >-

n co 't m to rv

-> ~

ooHcsjn«tmiDN ooooooooo

Appendix B

+ + + + + + + >->■>■ >- > >- >-

LU LU LU UJ LU LU LU

^ ^ ^ ;*: cfl^:^^

oooo^ooo

□_Q_D_D_ C a Q. Q.

o t-H cvi <r? ^J-

LU LU LU LU LU

it: ^ ■*£ ^ ^

o o o o o

a. o_ a. a, q_

—1 CC < O

< v; cr a.

<-Ht-<ojc>jnn , ^r , ^rh-LULOO--j2:_i

LLUu.uu-uu-uu5:ijJe;oLUh

QQQOQQQQOt-Hceh— or: O O

m n o h

□_ o_ □_ a.

<t CO W W CO w w

►H o o o o o o

h- D_ Q. Q_ D. Q_ CL

-U I I I I I I

CO CD < CD

CD CD CD) CD

CSJ CNJ CVJ CSJ

Q Q O Q

h ro n 't m co

co cn < to q u Ll

O O O OOOOOOOOOOOOOOO

CMCMCSJ C\JNC\JCMCSICMt>OCVlCMt\JCSJC\Jt\iCNJOJ

OOO QOQQOOOQQOOOQQQ

O O r< CM CO «t LO

o o o o o o o

CD CD CD CD CD CD CD

q o o a a a o

Appendix B

§

<

(a.

CO

o o

A

O CO

o

A

-"

OHNCO-tmiONCO CDr^COO)'»-it-iTHf-'r-iTH>r-1«-l^-H

+ + + + + + + + + + + + +

vHiMNMMNCMCMiNrvjrsinfr) own

+ + + + + + + + + + + + + + + +

J— I— I— 1— J— )— I— h-t— h- h- h- h-

f— I— I— 1— I— h- h- h- 1— I— h- \— h-

UUUUUCJUUUUUUU

II [I II II II II II II II II

n to o h w n o h cm n o s:s:a.a.a.Q.£:DL.Q_Q_a_s:2:

t/)COUJIjJUJUJLlJLl_Ll_U_Ll_Li_D_

OONNNNN<<<<<J

a. a_ hh t— it—it— it— i a: cc k c cc O

>- _1 DC _l

a.aao.aiiQ.cooJuuc/1

_J_J_l_J_J_J_J_ll— I U < I- s OOOOOOOOcCQcCH-tO <_)(_300C_JOOOCL>tI3XLJ

(DMDO)<l2UDlJJLLOHW CDCDCDOCDCDC3CDCDCDt-(<-Ht— I CDCDCDCDCDCDCDCDCDCDCDCDCD

CO ^ lO (D

COO)<ffl(JOUJU.

oaooooooooooo

QCZiQQQQQQQOQQQ

CD CD CD CD CD CD

Appendix B

□_ Q_ O. Q. O.

o o o o o

o o o o

<

CO

v

LU

on

< en

o h cm n ^ ifl 'O !— co o> o cd «h cnj m 't io r^ cd

tm(OMDO)rHHrlrlr l rl' H ' H,H ' HN + + + + + + + +

+ + + + + + -t- + + -i- + -i.+ + + + + OO O o o OOOO

<r<<t<3:<i:<:<:«a;<<<t:'^: e: ^ <rc t < ^* : ^ © m hh i—i t— i (—r •—^ *—* #—»

H M HI J—» I— ti—ii—(i— i »—i i— (i—(i— i H-II"< H-ll-HKH ""3" I— I— I— I— I— I— h- h-

i— i— j— i— j— j— i— i — i— i— i— j— i— i — i — i — J— o=z s: z s zs^s

oc_J<— jt_)0(_J(_JOc_)c_>c w jc_j<-Jt_><->c_JCJ v> <c <a; «C <£ <c<3;«t«a;

ii ii ir ii ii ii ii ii ii ii ii « ii " » " » » » » " » I" » ti

_l I— _l 3T _J_JUJLiJ

o h r\J ro c_jt— o I— t— OOtOCO

u_u_u i i i j ] i j fC^c_Dc_sc^ i-ho en co co en en -=c <

HD.O.Q.HQ.O.CLQ.IlC. HHHH —I I— *£ *C ►—• M U U CQ CQ

T-it\jfoo'-ic\jnorHCNjnQ:^>2: D:: < zs :r: _i _j cooo^x:

CuCLQ-^S^Sa-Q-Q-O-^— I I I CL "< O C_> Q CD. IE > Q. CJ

'tm(DNcoco<couCuJii0rl,,Jr ^^ oo r-t co ro ■*}■ m r-» en

OOOOOOOOOOOO h *' h ' hh ' h O CD O O CD O O CD O

ooooooooooaoooooo ^j*^j" *3" ^ ^t -3~ *3" •rj rj-

QDOQQOQQQQQOQQQQQ QQ Q Q O QOQQ

Appendix B

>

m >

o <

JJJ

a

m

< 3

-I <

m _J

> m

>

03

T-t CM CO *t O +J "O

r3 03

■r [_

t_ TD 03

03 CJ V-

E E <►-

E 0!

I— M □

T-t cnj n "J- in co r~

+-> > > u

II II II II

2 D X > > O Z S CO CJ LU LU

3 > D_ D_

II II II II II

> > > > >

rZ or ^ o f— 1—

tO 1— LU CO Z LU

J~! H Lt>H ^

^ Q (J U K <

K lu c/) y: a. u

CD CQ en o C_ CO CO

^ —I =&-.

X >- <. I— i— !— 1—

II II II II II

n ■* m e; ll cc a: cc e lu

^

■ CO to CO CO CO CO CO

< m UQ uj u_ u_

00000 I

-3- ^r «=f -<3- -^ >

Q Q Q Q Q ,

00000 en cj> o «h oj n ^ lo lo

"^ ^ ^J" ^ ^ rf -cr

rH C\J n vt if) (D 1^

0000000

CD CO CD CD O CD CD

CD CD CD CD O CD CD

Appendix B

For Further Reference

Apple: What's Where In The Apple, William Luebbert, Micro Ink, Inc., 34 Chelmsford St., Chelmsford, MA 01824, 1981.

Atari: Mapping The Atari, Ian Chadwick, COMPUTE! Books, P.O. Box 5406, Greensboro, NC 27403, 1983. (This covers the operating system and provides lengthy cross-referenced explanations of Atari's memory addresses.)

Atari: The Atari BASIC Sourcebook, Bill Wilkinson, COMPUTE! Books, P.O. Box 5406, Greensboro, NC 27403, 1983. (Complete commented source code of Atari BASIC, with explanatory text.)

-

Appendix C

Simple Assembler

Notes On Assembling

This program is written in BASIC because there is no reason not to. Since the program runs quickly enough and there is some complicated arithmetic involved, BASIC is the language of choice. There are assemblers in ML which make two "passes" through the source code and do need the extra speed. But this is a simple, "one-pass" assembler. The virtue of simplicity is that you can easily and quickly make small ML routines, test them, and debug them. An added bonus is that modifying the Simple Assembler is easy in BASIC. We'll see how you can customize it in a minute.

The assembler accepts your opcodes and their arguments, translates them into the correct numeric values, and POKEs them into RAM memory. You have a choice between using hex or decimal during your ML programming on the Simple Assembler (SA). If you remove line 10, the SA will accept only decimal numbers as arguments, will print all addresses in decimal, and will display the object code (the numbers it is POKEing) in decimal. Leaving line 10 in the program will result in the SA accepting, addressing, and displaying only hexadecimal numbers.

The circumflex in lines 4010 and 5030 — the character following the number 16 — means "to the power of" and generally appears on computer keyboards as an arrow pointing up. Since this is not a complicated assembler, a decision had to be made concerning whether or not to include two of the conventions which have been traditional in ML programming. They were left out because it saves programming time to avoid them and they are unnecessary.

The first one is the dollar sign ($). When an assembler can accept either hex or decimal simultaneously it must have a way to tell, if you type in "10", whether you mean decimal 10 or hex 10 (decimal 16). The convention requires that you write decimal ten as "10" and hex as "$10."However, this can quickly become a burden. In the SA, you let it know which kinds of numbers you are using by setting H in line ten. After that, just type in the numbers. No $ is used. The second convention that is not included in the SA is the use of the comma. Again, there is no particular reason to use commas, but it has been the tradition to include them for certain addressing modes. They, too, can become burdensome when you are programming. Also, each line

Appendix C

of your ML program is brought into the computer via the INPUT statement in line 240. Microsoft BASIC'S INPUT statement dislikes seeing commas. So, it is expedient in several ways to drop the comma convention. There is just no reason to use them.

One additional note. The SA does not accept the indirect jump: JMP ($0FFF). You could add it if you wish, but because of a bug in the 6502, it is far safer to avoid it.

Here is a list of the traditional conventions used in most assemblers compared to the simplified conventions of the SA. Notice that each addressing mode has its own appearance, its own punctuation. This is how an assembler knows which addressing mode you mean to use.

Spaces are important.

Addressing Mode Conventions Immediate Absolute Zero Page

Simple Assembler

LDA #15 LDA 1500 LDA 15

Accumulator Zero Page, X Zero Page, Y Absolute, X Absolute, Y Indexed Indirect Indirect Indexed

ASL

LDA 15X LDX 15Y

LDA 1500X LDA 1500Y LDA(15X) LDA (15)Y

Traditional

LDA #$15 LDA $1500 LDA $15 (sometimes LDA *$15) ASL A LDA $15, X LDX $15, Y LDA $1500,X LDA $1500, Y LDA ($15,X) LDA($15),Y

~ ~

Customizing The Simple Assembler

An assembler is only supposed to get your typed opcodes and their arguments, translate them into the right numbers, and put them in memory for you. Nevertheless, the assembler is there for your benefit and it is a computer program. It can be taught to do whatever else would assist you in your ML programming. This is where "pseudo-ops" come in. They are not part of the 6502 ML instruction set. They are false opcodes. When you enter one of these, the assembler doesn't put it into 6502 and POKE it. It can't. It does something for you like figure out the hex equivalent of a decimal number or whatever.

The SA has four built-in pseudo-ops and you can add others. Following the input of the opcode (line 240) there is a short quiz. The first question the computer asks itself is: "did they type the word 'FORWARD'?" If so, it means that you are planning to branch forward, but you don't yet know how far. It will make a mental note of this and later, when you type in another pseudo-op, "RESOLVE,"

Appendix C

it will go back and put in the correct address for the branch. Also, you can hand-POKE in anv number in any address by typing the pseudo-op "POKE". And, when you are finished with a program, type "END" and the assembler will quit, reporting the starting and ending addresses of your program in decimal.

A full-featured assembler can include dozens of pseudo-ops. Let's briefly examine several popular ones to see if there are some that you might want to add to the SA. Then we'll add a hex/decimal pseudo-op to the SA to show how it's done.

BA — Begin Assembly. The SA asks you directly for the starting address (variable SA$). BA signifies the location in RAM memory where you want the object code to start. Example: BA $0400

BY — Bytes. This is for the creation of data tables. The BY is followed by numbers or text characters which are POKEd into memory at the current address. You put these BYtes at the start or end of a program (it could result in havoc if it were in the middle of a program; they would likely be meaningless as instructions). Example: BY 46 46 48 42 12 11 or BY "THIS IS A MESSAGE"

DE — Define a label. Labels require a two-pass assembler that goes through the source code first to create a table of labels which would look something like this:

Then, the second time through your source code, the assembler would replace all the labels with their correct values. This is called "resolving" the labels. DE is usually part of the initialization process. A number of the example programs in this book start off with a series of DE pseudo-ops, telling the assembler the meaning of various important labels that will be used later in the source code instead of literal numbers. Example: START DE 1500 or LETTER.A DE 65.

EN — The end of the source program. Stop assembling at this point. The SA uses END.

MC — Move code. This interesting pseudo-op takes care of a problem that sometimes comes up when you want your object code to be ultimately used in an address that is now being used by the assembler itself or cannot be directly POKEd at this time with the object code. For instance, if your computer's RAM memory starts at address 2048 like the Commodore 64, and you want to put your final ML object code there, what do you do? If the SA was told to start assembly there, it would begin to nibble away at itself. It's in RAM starting at 2048.

Appendix C

To allow you to store object code elsewhere, but have it assembled appropriately for final use in 2048, you could instruct the assembler:

MC 25000 (temporarily store it here)

BA 2048 (but make internal JMPs, JSRs, and table references correct for this starting address)

You can add your own pseudo-ops to the SA following line 240. Many times when you are working along in hex you will want to know the decimal equivalent of a number and vice versa. It's nice to be able to just ask for the translation right during assembling. The answer is printed on the screen and you continue on with your programming. The assembler will do nothing to the ML during all this; it's just giving you an answer.

If you are working in the hex mode and want a decimal number, just type DECIMAL and the computer will accept a hex number from you and give back its decimal equivalent. Conversely, type HEX and give a decimal number for that translation.

To include this pseudo-op in the SA, add the following lines:

Program C-l. Adding The Conversion Pseudo-op.

245 IFMN$="HEX"THENGOTO7000

246 IFMN$ = "DECIMAL"THENGOTO7200 —-

7000 PRINT"ENTER DECIMAL NUMBER";:INPUTDE:IFD

E>255THENSZ=3:GOTO7020 ~

7010 SZ=1 7020 GOSUB4000:PRINT" = $ —

"H$:GOTO230 7200 PRINT"ENTER HEX NUMBER";:INPUTH? "

7 210 SX=LEN(H$):BK$="000":H$=LEFT$(BK$,4-SX)+

H$ ~

7220 GOSUB5000:PRINT" = "

DE:GOTO230 —'

The Simple Assembler has a few error messages that it will print when it can't make sense out of something. The primary responsibility for finding errors, however, is yours. You can create and save ML routines and then look at them with the Disassembler to see if they look like they should. SA takes up about 4.5K so it will not run on an unexpanded VIC. A 3K RAM expansion will provide 2000 bytes for storage of your ML routines.

Appendix C

Program C-2. Simple Assembler (VIC, PET, Apple, 64 Version).

10 H=1:REM IF H = 0 THEN ASSEMBLY IS IN DEC

IMAL 5 0 HE$="012 3456789ABCDEF":SZ=1:ZO$="000" 100 PRINT" SIMPLE ASSEMBLER CONVENTIONS

II

110 DIMM$(56),TY(56),OP(56)

120 F0RI=1T056:READM$(I)

122 ROP$=MID$(M$(I),4,1):TY(I)=VAL(ROP$)

124 OP$=RIGHT$(M$(i),3):OP(I)=VAL(OP$)

126 M$(l)=LEFT$(M$(I),3)

140 NEXTI: PRINT

150 PRINT"IMMEDIATE LDA #15

155 PRINT"ABSOLUTE LDA 1500

160 PRINT"ZERO PAGE LDA 15

165 PRINT"ACCUMULATOR ASL

170 PRINT"INDIRECT X LDA (15X)

175 PRINT"INDIRECT Y LDA (15)Y

177 PRINT"ZERO PAGE X LDA 15X

179 PRINT"ZERO PAGE Y LDX 15Y

180 PRINT"ABSOLUTE X LDA 1500X 185 PRINT"ABSOLUTE Y LDA 1500Y

189 PRINT:PRINT" ENTER ALL NUMBERS IN ";

190 IFH=1 THENPRINT"HEX":GOTO200 195 PRINT"DECIMAL"

200 PRINT:PRINT"PLEASE INPUT STARTING ADDRES

S FOR ML PROGRAM" : INPUT SA$ 210 IFH=1THENH$=SA$:GOSUB5000:SA=DE:GOTO220 215 SA=VAL(SA$) 220 TA=SA:PRINT"{CLEAR}":REM CLEAR THE SCREE

N 230 IFH=1THENDE=SA:SZ=3:GOSUB4000:PRINTH$;:G

OTO240 2 35 PRINTSA" ";

240 INPUTMN?:PRINT"{UP}"SPC(20);:REM GO UP O NE LINE AND OVER 20 SPACES

241 REM ADD NEW PSEUDO-OPS HERE

242 IFRIGHT$(MN$,7)="FORWARD"THENFB=SA

243 IFRIGHT$(MN$,7)="RESOLVE"THENFR=SA-FB:PO KEFB+l,FR-2:PRINT" OK":GOTO230

244 IFRIGHT$(MN$,4)="POKE"THENPRINT"ADDR,NUM BER(DEC)";:INPUTADR,NUM:POKEADR,NUM :GOTO2 30

Appendix C

250 IFMN$="END"THENPRINT:PRINT" PROGRAM

IS FROM"TA"TO"SA:END 260 L=LEN(MN$):L$=LEFT$(MN$, 3) 270 F0RI=1T056:IFL$=M$(l)THEN300

2 80 NEXTI 290 GOTO850

300 REM PRIMARY OPCODE CATEGORIES

301 TY=TY(l) :OP=OP(l)

305 IFFB=SATHENTN=0:GOTO2010 310 IFTY=0THENGOTO1000

3 20 IFTY=3THENTY=1:IFL=3THENOP=OP+8:GOTO1000 330 R$=RIGHT$(MN$,L-4):IFH=1THENGOSUB6000

3 40 LR$=LEFT$(R$,l):LL=LEN(R$):IFLR$="#"THEN

480 350 IFLR$="("THEN520 360 IFTY=8THEN600 370 IFTY=3THENOP=OP+8:GOTO1000 380 IFRIGHT$(R$,l)="X"ORRIGHT$(R$,l)="Y"THEN

630 390 IFLEFT$(L$,1)="J"THEN820 400 TN=VAL(R$):IFTN>255THEN430 410 IFTY=10RTY=30RTY=40RTY=5THENOP=OP+4 420 GOTO2000 430 H%=TN/256:L%=TN-256*H%:IFTY=20RTY=7THENO

P=OP+8:GOTO470 440 IFTY=10RTY=30RTY=40RTY=5THENOP=OP+12:GOT

0470

4 50 IFTY=6ORTY=9THEN470 460 GOTO850

470 GOTO3000

480 TN=VAL(RIGHT?(R$,LL-1))

490 IFTY=1THEN'OP=OP+8:GOTO2000

500 IFTY=4ORTY=5THENGOTO2000

510 GOTO850

520 IFRIGHT$(R$,2)=")Y"THEN540

5 30 IFRIGHT$(R$,2)="X)"THEN570 540 TN=VAL(MID$(R$,2,LL-3))

5 50 IFTY=1THENOP=OP+16:GOTO2000 560 GOTO850

570 TN=VAL(MID$(R$,2.LL-3)) 580 IFTY=1THENGOTO2000 590 GOTO850

600 TN=VAL(R$):TN=TN-SA-2:IFTN<-1280RTN>127T HENPRINT"T00 FAR ";:GOTO850

Appendix C

610 IFTN<0THENTN=TN+256

620 GOTO2000

630 IFRIGHT$(R$,2)=")Y"THEN540

640 IFRIGHT$(R$,l)="X"THEN720

650 REM *ZERO Y

660 TN=VAL(LEFT$(R$,LL-1)):IFTN>255THEN680

670 IFTY=2ORTY=5THEN730

675 IFTY=1THEN760

6 80 GOSUB770:IFTY=lTHENOP=OP+24:GOTO710 690 IFTY=5THENOP=OP+28:GOTO710

7 00 GOTO8 50 710 GOTO3000

7 20 TN=VAL(LEFT$(R$,LL-l)):IFTN>25 5THENGOSUB 770:GOTO780

7 30 IFTY=2THENOP=OP+16:GOTO760

740 IFTY=1ORTY=3ORTY=5THENOP=OP+20:GOTO760

750 GOTO850

760 GOTO2000

770 H%=TN/256:L%=TN-256*H%:RETURN

780 IFTY=2THENOP=OP+24:GOTO810

790 IFTY=1ORTY=3ORTY=5THENOP=OP+28:GOTO810

800 GOTO850

810 GOTO3000

8 20 TN=VAL(R$) 830 GOSUB770 840 GOTO710

850 PRINT"{REV} ERROR ":GOTO230 1000 REM 1 BYTE INSTRUCTIONS 1010 P0KESA,0P:SA=SA+1:IFH=1THEN 1030 1020 PRINTOP:GOTO230

1030 DE = OP:GOSUB4000:PRINTH$:GOTO230 2000 REM 2 BYTE INSTRUCTIONS

2005 IFTN>256THENPRINT" INCORRECT ARGUMENT. ( #5 IN HEX IS #05)":GOTO230

2 010 POKESA,OP:POKESA+l,TN:SA=SA+2:IFH=lTHEN2

030 2020 PRINTOP;TN:GOTO230 2030 DE = OP:GOSUB4000:PRINTH$" "; 2040 DE = TN:GOSUB4000:PRINTH$:GOTO230 3000 REM 3 BYTE INSTRUCTIONS

3 010 POKESA,OP:POKESA+l,L%:POKESA+2,H%:SA=SA+

3:IFH=1THEN3030 3020 PRINTOP;L%;H%:GOTO230 3030 DE = OP:GOSUB4000:PRINTH$" ";

Appendix C

3040 DE = L%:GOSUB4000:PRINTH$" ";

3050 DE = H%:GOSUB4000:PRINTH$:GOTO230

4000 REM DECIMAL TO HEX (DE TO H$)

4010 H$="":FORM=SZTO0STEP-1:N%=DE/(16*M):DE=D

E-N°s*16"M:H$=H$+MID$(HE$,N% + 1,1) 4020 NEXT:SZ=1:RETURN

5000 REM HEX TO DECIMAL (H$ TO DE) 5010 D=0:Q=3:F0RM=1T04:FORW=0TO15:IFMID$(H$,M

,1)=MID$(HE$,W+1,1)THEN5030

5 020 NEXTW

5030 D1=W*(16*(Q)):D=D+D1:Q=Q-1:NEXTM:DE=INT(

D):RETURN 6000 REM ACCEPT HEX OPCODE INPUT AND TRANSLAT

E IT TO DECIMAL 6010 IFLEFT$(R$,l)="#"THENH$="00"+RIGHT$(R$,2

):GOSUB5000:R$="#"+STR$(DE):RETURN

6 020 LS=LEN(R$):AZ$=LEFT$(R$,l):ZA$=MID$(R$,L

S,1):IFAZ$ <>"("THEN6050 6 030 IFZA$="Y"THENH$="00"+MID$(R$,2,2):GOSUB5

000:R$="("+STR$(DE)+")Y":RETURN 6040 IFZA$=")"THENH$="00"+MID$(R$,2,2):GOSUB5

000:R$="("+STR$(DE)+"X)":RETURN 6050 IFZA$="X"ORZA$="Y"THEN6070 6 060 H$=LEFT$(ZO$,4-LS)+R$:GOSUB5000:R$=STR$(

DE):RETURN 6070 IFLS=5THENH$=LEFT$(R$,4):GOTO6090 6080 H$="00"+LEFT$(R$,2) 6090 GOSUB5000:R$=STR$(DE)+ZA$:RETURN 20000 DATAADC1097,AND103 3,ASL3002,BCC8144 /

BCS8176,BEQ8 240,BIT7036,BMI8048 20010 DATABNE8208,BPL8016,BRK0000,BVC8080,BVS8

112,CLC00 24,CLD0216,CLI0088 2 0020 DATACLV0184,CMP1193,CPX42 24,CPY419 2,DEC2

198,DEX020 2,DEY0136,EOR106 5 2 0030 DATAINC2 2 30,INX02 3 2,INY0200,JMP607 6,JSR9

0 3 2,LDA1161,LDX5162,LDY5160 2 0040 DATALSR3066,NOP0234,ORA1001,PHA007 2,PHP0

0 08,PLA0104,PLP0040,ROL3034 2 0050 DATAROR3098,RTI0064,RTS0096,SBC12 2 5,SEC0

0 56,SED0248,SEI0120,STA1129 20060 DATASTX2134,STY2132,TAX0170,TAY0168,TSX0

186,TXA0138,TXS0154,TYA015 2

Appendix C

Program C-3. Simple Assembler: Atari Version.

10 HX=lsREM IF HX = 0 THEN ASSEMBLY I

S IN DECIMAL

20 DIM HE$ < 16) , Z 0$ < 3) . R* < 10) , MN$ (12)

. Z A * ( 1 ) . A Z $ < 1 ) , L *• < 3 ) . S A * ( 4 ) , H * ( 4 }

.LR$(1) 3 0 OPEN #1.12,0,"E:" 5 0 HE$="0123456799ABCDEF":SZ=1;ZO$="

000 "

1 0 0 PRINT " C 3 gBB3@a ? aBBEOE

■C3 gEana^i>sagBBmi g — hh ■ i '* i=ni±i*n±i

B" ;

110 DIM M $ (5 6 * 3) , T Y (5 6) . 0P (56) 12 0 FOR 1 = 1 TO 5 6:R EA D MN$:M$(1*3-2, I * 3 ) = M N * ( 1 , 3 )

12 2 TY(I>=VAL(MN*(4«4) ) :OP « I > = VAL (MN

$ (5) )

13 0 NEXT I

14 0 PRINT :?

150 PRINT "ImmediateC5 SPACES]LDA #1

c 11

155 PRINT "Absolute^ SPACES1LDA 150

0"

16 0 PRINT "Zero p a q e i 5 S P A C E S 3 L D A 15

It

165 PRINT "Accumulator^ SPACES]ASL" 170 PRINT "Indirect XC4 SPACES]LDA (

1 5 X ) " 175 PRINT "Indirect YC4 SPACES]LDA (

1 5 ) Y " 177 PRINT "Zero page X i 3 SPACES]LDA

1 5 X "

179 PRINT "Zero page YC3 SPACES]-LDX 15Y "

180 PRINT "Absolute XC4 SPACES]LDA 1 5 0 0 X "

185 PRINT "Absolute Yt4 SPACES]LDA 1

500 Y " 189 PRINT : PRINT "{4 SPACES]Enter al

1 numbers in ": 19 0 IF HX=1 THEN PRINT "in^;^"' :

Appendix C

195

197

2 0 0

210

215 217

220

Z J> (i!

240

24 1 242

243

244

*T> cr ,"-:(

;05

:i0

PRINT

? :? "Addresses:UsG 15 3 6-1791 < *

0600-*06FF)":? :?

PRINT " £2 DEL LINETPlease enter

starting":? "address for ML prog

ram";: INPUT SA$:IF SA$="" THEN ?

" £2 UP3 " ; : GOTO 200 IF HX=1 THEN H$=SA$:GDSUB 5000:3 A=DE:GOTO 217 SA=VAL (SA$)

IF SA<256 OR SA>=409&0 THEN ? " <4 UP}IMot ZPAGE or ROM!'*;? :GOTO

2 00 TA=SA: PRINT "{CLEAR}":GOTO 230 ? :? " CBELL3MHH5E 1S135333 I" : ? :IF

H X =1 THEM ? " < e. g. #5 should be

#05)":? IF H X =1 THEN DE=SA:SZ=3:G0SUB 40 00: PRINT H*:": ";:60T0 240 PRINT SA:": ";

TRAP 225:INPUT #1:MN$:? "(UP;";: POKE 35,20:IF MN$=" N THEN ? " {DEL LINEI";:GOTO 230 REN ADD NEW PSEUDO-OPS HERE IF LEN(MN$>-6 THEN IF MN*<LEN<MN *)-6)="FORWARD" THEN FB = SA IF MN$="RESOLVE" THEN FR=SA-FB:P OKE FB+1 , FR-2: PRINT " OK": GOTO 2 3 0

IF MN$="POKE" THEN PRINT "ADDR«N UMBER<DEC;":: INPUT ADDR,NUM:POKE

ADDR,NUM:GOTO 230 IF MN*= ,i END" THEN 8000 L=LEN(MN$) :L$=MN$!1,3! FOR 1 = 1 TO 56: IF L* = M$ ( I *3-2, I *3 > THEN 300 N EXT I GOTO 850

REM PRIMARY OPCODE CATEGORIES T Y = T Y ( I ) : O P = O P < I ) IF FB=SA THEN TN=0:BOTO 2010 IF TY=0 THEN GOTO 10 0 0

Appendix C

3 2 0 IF T Y = 3

N

1 : IF L'

THEN OP

40

4 40

=0p+8:GOTO 100 0

R$ = MN*(5) : IF IIX = 1 THEN GOSUB 600

0

LR*=R$U. U:LL=LEN(R$): IF LR$="#

" THEN 4 30

IF L R* =" < " THEN 52 0

IF T Y = 3 T H E N 6 0 0

IF T Y = 3 THEN OP = OP + 8:GOTO 1000

IF R$(LL)="X" OR R*<LL)="Y" THEN

6 30 IF L$(1,1)="J" THEN 320 T N = VA L < R*) : IF T N>2 5 5 THEN 4 30 IF T Y =1 OR T Y = 3 OR T Y = 4 OR T Y = 5 THEN O P = O P + 4 GOTO 2000

H = I N T ( T N / 2 5 6 >: L = < T N - 2 5 6 * m:IF TY = 2 OR TY = 7 THEN 0P=0P+3:G0T0 470 IF T Y =1 OR T Y = 3 OR T Y = 4 OR T Y = 5 THEN 0 P = O P + 1 2 : G O T O 47 0 IF TY-6 OR TY=9 THEN 470 GOTO 350 GOTO 3000 T N = V A L < R * i 2 ) )

IF T Y = 1 THEN 0 P = D P •♦

a » qpTn

DTi

3 a i

H

P M C2 r,

IF T Y = 4 OR T Y = f

GOTO 8 5 0

IF R$(LL-i)=")Y" THEN 54 0

IF R * ( L L - 1 ) =" X ) " THEN 570

TO 2 0 0 0

TN =

iL (R* « 2. LL-1 ) )

6 1 0 6 20 6 3 0 640

IF TY=i THEN OP=OP+16:GOTO 2000 GOTO 350

T N = yA L <R * (2,LL-1 ) ) IF T Y =1 THEN GOTO 2000 GOTO 8 50

TN=VAL(P$! :TN=TN-3A-2; IF TN--123 0 R T N > 12 7 THE N P R I N T " fMSBWKSSS " ; :GOTO 850

IF T N <0 THEN T N =T M + 25 6 GOTO 2000

IF R * i LL-1) = " ) Y" THE N 5 4 0 IF R$iLL'l)="X" THEN 72 0

Appendix C

6 5 0 REM * 2 E R 0 Y

6 6 0 TN = VAL < R* ( 1 , LL-1 ) ) : IF TN

N 6 S 0 67 0 IF TY = 2 OR TY-5 THEN 73 0 675 IF TY=i THEN 760

HE

i THEN QP=0P+24: 3F=0P+28:G0T0 7 10

68 0 GOSUB 77 0: IF T

GOTO 7 10

69 0 IF T Y = 5 THEN 7 0 0 GOTO S 50

7 10 GOTO 3000

720 TN = VAL <R* (1 , LL-1 M : IF TN:>255 THE

N GOSUB 770: GOTO 700 730 IF TY=2 THEN OP = OP+16:GOTO 760 7 4 0 IF T Y = 1 OR TY = 3 OR T Y = 5 THEN D P =

OF+20:GOTO 760 750 GOTO 350

7 6 0 GOTO 2000

770 H=INT (TN/256) : L = TN-25 6 *H: RETURN 730 IF TY=2 THEN OP = OP+24:GOTO 810 790 IF TY=1 OR TY=3 OR TY=5 THEN 0P =

O P+ 28: G O T O 810 800 GOTO 850

8 10 GOTO 3000 820 TN=VAL(R$} 330 GOSUB 770 8 4 0 GOTO 710

850 PRINT " cBELLJiaanaai" : GOTO

50

1 0 0 0 REM 1 BYTE INSTRUCTIONS

1010 POKE SA,OP:SA=SA+1:IF HX=1 THEN

1 0 3 0 10 2 0 PRINT O P: G O T O 230 1030 DE=OP:GOSUB 40 00: PRINT H$:GOTO

2 30 2000 REM 2 BYTE INSTRUCTIONS 2005 IF TN>256 THEN ? :? "Error--" ; T

N; " >256 (* 100)": GOTO 230 20 10 POKE SA, OP: POKE SA+1 . TN : SA = SA+2

: I F H X =1 THEN 2030 2020 PRINT OP;" ";TN:GOTO 230 2030 DE=OP:GOSUB 4000: PRINT H*;" "; 2040 DE=TN:GOSUB 4000: PRINT H$:GOTO

230

~ ~

Appendix C

EM 3 BYTE INSTRUCTIONS GKE SA.OP:POKE SA+1,L:P0KE SA+ ,H:SA=SA+3:IF HX=1 THEN 3030 RINT OP;" ";L;" ";H:GOTO 230 E = OP:GOSLIB 4000: PR I NT H$;" "; E=L:GOSUB 4000: PRINT H$;" "; E=H:GOSUB 4000: PRINT H$:GOTO 2

0

EM DECIMAL TO HEX (DE TO H $ )

* ="":A=INT(DE/256):IF A>0 THEN

AH=INT\A/I6):AL=A-AH*16:H*=HE$

AH+UAH+i) :H$(2>=HE*(AL+l,AL+i

=DE-A*2 5 6:AH=INT(A/16):AL=A-AH

i6:H$(LEN(H$)+i)=HE$;AH+l,AH+l

:H* (LEN (H*> +1)=HE$ (AL+1 , AL+1 ) :

Z=l:RETURN

EM HEX TO DECIMAL ( H $ TO DE)

=0:0=3:FOR M=1 TO 4:W=ASC(H$(M

> -4 8: IF W >9 THEN W = W-7

= D*16 + W: NEXT Ms DE=J NT CD) : RETUR

EM ACCEPT HEX OPCODE INPUT AND

TRANSLATE IT TO DECIMAL

F R*<i,1)="#" THEN H*="00":HS<

> =R$ < 2) : GOSUB 5000:R*="# " : R* ( 2 = STR* i DE) :RETURN

S = LEN (R* > : AZ$ = R$ (1,1): ZA$ = R$ (L

) : IF AZ*<>"(" THEN 6050

F Z A * = " Y " THEN H % ="0 0":H * (3) = R

(2,4) : GOSUB 5000:R*=" (":R*(2) =

TR*(DE) s R* CLEN<R*)+1)=">Y":RET

RN

F ZA$ :

it \ II

HEN H % = " 0 0 " : H * ( 3 ) = R

(2,4) : GOSUB 5000: R$=" ( " :R* (2> = TR*(DE) :R$(LEN(R$)+1)="X) ":RET RN

F ZA*="X" OR ZA$="Y" THEN 6070 $="":IF L S < 4 THEN H$=ZO$(l,4-L )

$ (LEN < H* > +1 ) =R*: GOSUB 5000: R* = TR$(DE):RETURN

-

Appendix C

6070 IF LS=5 THEN H$=R$(1,4):GOTO 60

9 0 6080 H$="00":H$(3)= R*(1,2) 6090 GOGUB 5000:Rt=STR$(DE):R$(LEN(R

$)+l)=ZA$:RETURN

8000 PRINT : PR I NT "*STARTS ";TA;:5Z=

3:DE=TA:GOSUB 4000: PRINT " ($";

H*;">"

80 10 PRINT " ENDSC3 SPACES)";SA;:DE=

SA:SZ=3:GOSUB 4000: PRINT " (*";

H$;")":END ~

20000 DATA ADC 1097, AND 1033. ASL3002, B

CC8144,BCSBi76,BEQ8240,BIT7036

,BMI8048 200 10 DATA BNE8208,BPL80 16,BRK0000,B

VC3030,BVS8112,CLC0024,CLD02 16

,CLI0088 20020 DATA CLV0184, CMP 1 193, CPX4224, C

PY4192,DEC2198,DEX0202.DEY0 136

, E O R 1 0 6 5 _

20030 DATA INC2230,INX0232,INY0200,J

MP6076,JSR9032, LDA1 16 1,LDX5 162 ^

, L D Y 5 1 6 0 20040 DATA LSR3066.NOP0234, ORA1001 ,P

HA0072,PHP0008,PLA0104,PLP0040

,ROL3034 20050 DATA ROR3098,RT10064,RTS0096,S

BC1225,SEC0056,SED0248,SEI0120

,STA1129 20060 DATA STX2134,STY2132,TAX0170,T

AY0 168,TSX0186, TXA 0138,TXS0 154

, T Y A 0 1 5 2

-

Appendix D

Note: The ^ means "to the power of" as in 2 A 2 = 4. Program D-l. Disassembler (VIC, PET, Apple, 64 Version).

1 HE$="0123456789ABCDEF"

2 L $=»

II

4 J$=" >"

13 PRINT" DISASSEMBLER

14 PRINT

16 DIMM$(15,15)

17 FORI=0TO15:FORB=0TO14:READM$(l,B):NEXTB:

NEXT I

2 5 REM START MAIN LOOP

30 PRINT"STARTING ADDRESS (DECIMAL)";:INPUT

SA:TA=SA

31 PRINT"START ADDRESS HEX ";:DE=SA:ZX=3:G

OSUB1200:PRINTH$"

3 5 IFSA<0THENEND 41 I=SA

45 REM PRINT ADDRESS

46 PRINTI" "; 50 X=PEEK(I)

55 GOSUB5000

56 IFL%=150RM$(H%,L%)="0"THENPRINT" ?

X:CK=0:LN=LN+1:GOTO70 58 PRINTM$(H%,L%); 60 GOSUB6000:IFEQTHENEQ=0 70 1=1+1

72 IFLN=20THENLN=0:GOTO2000 80 GOT045

600 IFCK=12THEN603 6 01 B=PEEK(l+l):IFB>12 7THENB=((NOTB)AND2 5 5)+

1:B=-B

602 BAD=I+2+B:PRINT" "BAD:1=1+1:RETUR N

603 IFH%>8THEN800

604 IFH%=2THENJ=1:GOTO850

605 IFH%=6THENPRINT:PRINTL$:EQ=1:RETURN

Appendix D

606 IFH%=6THENRETURN

607 PRINT

608 RETURN

610 IFCK=12THEN615

611 PRINT" ("PEEK(I+1)"),Y"

612 1=1+1:RETURN

615 PRINT" ("PEEK(l+l)",X)"

616 1=1+1:RETURN

630 IFCK=12THEN635

631 PRINT" "PEEK(I+1)",X" 6 32 1=1+1:RETURN

635 PRINT" "PEEK(I+1)

636 1=1+1:RETURN

640 IFCK=12THEN645

641 PRINT" "PEEK(I+1)",X"

642 1=1+1:RETURN

645 PRINT" "PEEK(I+1)

646 1=1+1:RETURN

660 IFCK=12THEN645

661 IFH%=90RH%=11THENPRINT" "PEEK(l+l)",Y"

662 IFH%=7ORH%=15ORH%=5ORH%=3THEN640

663 IFH%=13THEN631

664 PRINT:GOT0642 680 PRINT: RETURN

690 IFCK=12THEN800

691 I$="Y":GOTO850 720 IFCK=12THEN725 722 I$="X":GOTO850

725 IFH%=6THENPRINT" (IND. ";:I=I+1

726 IFH%=2THEN850

727 IFH%=4THENPRINTJ$;:GOTO850

728 IFH%=8ORH%=10ORH%=12ORH%=14THEN850

729 GOTO610

730 IFCK=12THEN850

731 I$="X":GOTO850

740 IFCK=12THEN850

741 IFH%=11THENI$="Y":GOTO850

742 I$="X":GOTO850

800 PRINT" #"PEEK(I+1)

801 1=1+1:RETURN

850 N=PEEK(l+l)+PEEK(l+2)*256 860 IFI$=""THEN900 870 IFI$="X"THENPRINT" "N",X" 880 IFI$="Y"THENPRINT" "N",Y"

Appendix D

890 1$="":1=1+2:RETURN

900 PRINT" "N:1=1+2

906 RETURN

1000 DATABRK,ORA,0,0,0,ORA,ASL,0,PHP,ORA,ASL,

0,0,ORA,ASL,BPL,ORA,0,0,0,ORA,ASL 1010 DATA0,CLC,ORA,0,0,0,ORA,ASL,JSR,AND,0,0,

BIT,AND,ROL,0,PLP,AND,ROL,0,BIT 1020 DATAAND,ROL,BMI,AND,0,0,0,AND,ROL,0,SEC,

AND,0,0,0,AND,ROL,RTI,EOR,0,0,0 1030 DATAEOR,LSR,0,PHA,EOR,LSR,0,JMP,EOR,LSR,

BVC,EOR,0,0,0,EOR,LSR,0,CLI,EOR,0 1040 DATA0,0,EOR,LSR,RTS,ADC,0,0,0,ADC,ROR,0,

PLA,ADC 1045 DATAROR,0,JMP,ADC,ROR,BVS,ADC,0,0,0 1050 DATAADC,ROR,0,SEI,ADC,0,0,0,ADC,ROR,0,ST

A 105 5 DATA0,0,STY,STA,STX,0,DEY,0,TXA,0,STY,ST

A 1060 DATASTX,BCC,STA,0,0,STY,STA,STX,0,TYA,ST

A,TXS,0,0,STA,0,LDY,LDA,LDX,0 1070 DATALDY,LDA,LDX,0,TAY,LDA,TAX,0,LDY,LDA,

LDX,BCS,LDA,0,0,LDY,LDA,LDX,0 1080 DATACLV,LDA,TSX,0 1090 DATALDY,LDA,LDX,CPY,CMP,0,0,CPY,CMP,DEC,

0,INY,CMP,DEX,0,CPY,CMP,DEC 109 5 DATABNE,CMP,0,0,0,CMP,DEC,0,CLD,CMP,0,0,

0,CMP,DEC,CPX,SBC,0,0,CPX,SBC,INC

1098 DATA0,INX,SBC,NOP,0,CPX,SBC,INC,BEQ,SBC, 0,0,0,SBC,INC,0,SED,SBC,0,0,0,SBC

1099 DATAINC

1200 REM MAKE DECIMAL INTO HEX

1201 H$="":FORM=ZXTO0STEP-1:N%=DE/(16"M):DE=D E-N%*16~M:H$=H$+MID$(HE$,N%+1,1)

1202 NEXT:RETURN

2000 PRINT"TYPE C TO CONTINUE FROM" I

2001 GETK$:IFK$=""THEN2001

2002 IFK$="C"THENSA=I:TA=SA:GOT035

2003 INPUTSA:TA=SA:G0T035

5000 REM ANALYZE H & L OF OPCODE

5010 H%=X/16:L%=X-H%*16

5020 -.RETURN

6000 REM FIND ADDRESS TYPE & GOSUB

6020 CK=H%/2:IFCK=INT(CK)THENCK=12

6025 L%=L%+1

Appendix D

6030 ONL%GOSUB600,610,800,6050,640,640,660,60

50,680,690,680,6050,720,7 30,740 6 040 CK=0 6045 LN=LN+1 6050 RETURN

Program D-2. Atari Disassembler.

1 0 0

1 0 5

1 10

120 125 130 140 ISO 160 170

ISO

190

2 0 0

210 220 2 30

240 242

243

244 246

250 2 60

11.0:? " M3Z&B "Loading ope

DIS ttSS EMBLER

REM

GRAPHICS 0:P0SITI0N

■ > a sga = < =i mm j t = ? =

odes. . . "

DIM OPCODE* (256*10) , LN <255) .

5) ,T*(10) ,D* (5)

FOR 1=0 TO 255

READ T*,NB

LN(I)= LEN(T*)

OPCODE*(1*10+1,I*10+LN(I))=T*

NB(I)= NB

NEXT I

GRAPHICS

O: POSITION 11,0:

Hiao

DIS R55 EMBLER

TRAP g Ad TRAP IF A OP = P T* = 0 PRIN POKE ON N NB = 2 T = T PRIN ; ADD

190 dres

4 O 0 DDR< EEK ( PCOD T AD

85, B + 2 : T = P -256 T T; R + 2 +

5 (

0 0

0 O

ADD

E* (

DR;

15

SOT

EEK

"{UP3CDEL LINE] Starti n Decimal)";:INPUT ADDR:

R ADDR>65535 THEN 190 R):NB=NB(OP) OP*10+1,OP*lO+LN(OP)) :POKE 85, 10:PR I NT OP;:

O 242,244,250,260,270 (ADDR+1):IF T>128 THEN

UniHPlewente.

PRIN POKE PRIN = STR O 30

T T* 85, T PE * (PE 0

:POKE 85,20:PRINT T*; " T : G O T O 3 O 0

NB=l:GOTO 300 ;" ";ADDR+2+T:GOTO 300 20: PRINT T$:GOTO 300 EK (ADDR+1 );:POKE 85,20:D* EK(ADDR+1)):GOSUB 4O0:G0T

Appendix D

2 70 280

3 0 0

PRINT PEEK(ADDR+1>;:POKE 85,15:PR

INT PEEK(ADDR+2);:POKE 85,20

D* = STR$( (PEEK (ADDR+1 )+256*PEEK (AD

DR+2))):GOSUB 400

ADDR=ADDR+NB: IP ADDR<C> THEN ADDR =

3iO IF ADDR>65535 THEN ADDR=T

320 IF PEEK(53279)=7 THEN 210

330 OOTO 190

400 ? T$(l,4+(LN(OP)>4));D$;T$(4+2*(L

N(OP)>5)):RETURN 500 DATA BRK,1,0RA (X),2,?,O,?,0,?,O,

ORA ,2,ASL ,2,?,O,PHP,1,ORA # ,

2,?,0,CL ORA X,3

51 O DATA ASL A, 1 , ?,0,?, O,ORA ,3,ASL

, 3 , 2 , O , BPL , - 1 , OR A ( ) Y , 2 , 2 , O , ? , O

520 DATA 7,0,ORA X,2,ASL X

C,l,ORA Y,3,7,O,7,O,7,O. 530 DATA ASL ,2,7,0,JSR ,3,AND (X),2

,7,0,^,0,BIT ,2,AND ,2,ROL ,2,^,0

540 DATA PLP,1,AND # ,2,ROL A,1,?,O,B

IT ,3,AND ,3,ROL ,3,7,O,BMI,-1,AN

D ()Y,2 " 550 DATA 7,O,7,O,7,O,AND X,2,R0L X,

2,7,O,SEC, 1 , AND Y , 3,CLI , 1 , 7,O 560 DATA 7,0,AND X,3,ROL X,3,7,0,RT

I , 1 ,EOR (X) ,2, 7,O, 7,O, 7, O,EOR ,2 570 DATA LSR ,2,7,O,PHA,1,EOR # ,2,L

SR ,3,7,O,JMP ,3,EOR ,3,LSR ,

3 "? O 580 DATA BVC,-1,E0R ()Y,2,7,O,7,O,7,O

,E0R X,2,LSR X,2,7,O,CLI,1,EOR Y, 2 590 DATA 7,O,7,O,7,O,EOR X,3,LSR X,

3,7,O,RTS,1,ADC (X),2,7,0,7,0 600 DATA 7,0,ADC ,2,ROR ,2,7,0,PLA,

1 ,ADC # ,2,ROR A, 1,7,0,JMP ( ) , 108

,ADC ,3 61O DATA ROR ,3,7,O,BVS,-1,ADC ()Y,2

,7,O,7,O,7,O,ADC X,2,ROR X,2,7,

O

Appendix D

620 DATA SE1,1,ADC Y,3,7,0,7,0,7,O,A

DC X,3,R0R X,3,7,O,?,O,STA <X>,

^-630 DATA 7,O,7,0,STY ,2,STA ,2,STX

,2,?,O,DE Y, 1 , ? , 0 , T X A, i, ? , 0 640 DATA STY , 3,STA ,3,STX ,3,7,0,

BCC,-1,STA ()Y,2,?,O,?,0,STY X,2

,STA X,2 650 DATA STX Y,2,?,O,TYA,1,STA Y,3,

TXS, 1 ,7,O, ?,O,STA X, 3, ?,O, ? , O 660 DATA LDY # ,2,LDA (X),2,LDX # ,2,

?,0,LDY ,2,LDA ,2,LDX ,2,?,0,T

AY,1,LDA « ,2 670 DATA TAX,1,7,O,LDY ,3,LDA ,3,LD

X ,3,7,O,BCS,-1,LDA ()Y,2,?,0,7,

0 680 DATA LDY X,2,LDA X,2,LDX Y,2,7

,O,CLV, 1 ,LDA Y,3,TSX, 1 ,7,O,LDY

X,3,LDA X,3 690 DATA LDX Y,3,?,0,CPY # ,2,CMP (X

),2,7,O,7,O,CPY ,2,CMP ,2,DEC

,2,7,0 700 DATA INY,1,CMP # ,2,DEX,1,7,O,CPY ,3,CMP ,3,DEC ,3,7,O,BNE,-1,C

MP (> Y,2 71O DATA 7,O,7,O,7,O,CMP X,2,DEC X,

2,7,O,CLD,1,CMP Y,3,7,0,7,0 720 DATA 7,0,CMP X,3,DEC X,3,7,0,CP

X # ,2,SBC (X) ,2,7,O,7,O,CP X ,2,

SBC , 2 730 DATA INC ,2,7,O,INX,1,SBC # ,2,N

0P,1,7,0,CPX ,3,SBC ,3,INC ,3,

7,0

740 DATA BEQ,-1,SBC (Y),2,7,O,7,O,7,O , SBC X,2,INC X,2.7,O,SED, 1 ,SBC Y, 3

750 DATA 7,O,7,O,7,O,SBC X,3,INC X, 3,7, O

Appendix E

Number Tables

This lookup table should make it convenient when you need to translate hex, binary, or decimal numbers. The first column lists the decimal numbers between 1 and 255. The second column is the hexadecimal equivalent. The third column is the decimal equivalent of a hex most significant byte or "MSB." The fourth column is the binary.

If you need to find out the decimal equivalent of the hex number $FD15, look up $FD in the MSB column and you'll see that it's 64768. Then look up the $15 in the LSB column (it's 21 decimal) and add 21 + 64768 to get the answer: 64789.

Going the other way, from decimal to hex, you could translate 64780 into hex by looking in the MSB column for the closest number (it must be smaller, however). In this case, the closest smaller number is 64768 so jot down $FD as the hex MSB. Then subtract 64768 from 64780 to get the LSB: 12. Look up 12 in the decimal column (it is $0C hex) and put the $FD MSB together with the $0C LSB for your answer: $FD0C.

With a little practice, you can use this chart for fairly quick conversions between the number systems. Most of your translations will only involve going from hex to decimal or vice versa with the LSB of hex numbers, the first 255 numbers, which require no addition or subtraction. Just look them up in the table.

Appendix E

11

12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49

OB OC OD OE OF 10 11 12 13 14 15 16 17 18 19 1A IB 1C ID IE IF 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31

2816

3072

3328

3584

3840

4096

4352

4608

4864

5120

5376

5632

5888

6144

6400

6656

6912

7168

7424

7680

7936

8192

8448

8704

8960

9216

9472

9728

9984

10240

10496

10752

11008

11264

11520

11776

12032

12288

12544

00001011 00001100 00001101 00001110 00001111 00010000 00010001 00010010 0001 v 0011 00010100 00010101 00010110 00010111 00011000 00011001 00011010 00011011 00011100 00011101 00011110 00011111 00100000 00100001 00100010 00100011 00100100 00100101 00100110 00100111 00101000 00101001 00101010 00101011 00101100 00101101 00101110 00101111 00110000 00110001

Appendix E

50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87

32 33 34

35 36 37 38 39 3A 3B 3C 3D 3E 3F 40 41 42 43 44 45 46 47 48 49 4A 4B AC 4D 4E 4F 50 51 52 53 54 55 56 57 58

12800 13056 13312 13568 13824 14080 14336 14592 14848 15104 15360 15616 15872 16128 16384 16640 16896 17152 17408 17664 17920 18176 18432 18688 18944 19200 19456 19712 19968 20224 20480 20736 20992 21248 21504 21760 22016 22272 22528

00110010 00110011 00110100 00110101 00110110 00110111 00111000 00111001 00111010 00111011 00111100 00111101 00111110 00111111 01000000 01000001 01000010 01000011 01000100 01000101 01000110 01000111 01001000 01001001 01001010 01001011 01001100 01001101 01001110 01001111 01010000 01010001 01010010 01010011 01010100 01010101 01010110 01010111 01011000

Appendix E

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111 112 113 114 115 116

117 118 119 120 121 122 123 124 125 126 127

59 5A 5B 5C 5D 5E 5F 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E

6F 70 71 72 73 74

75 76 77 78 79 7A 7B 7C 7D 7E 7F

22784 23040 23296 23552 23808 24064 24320 24576 24832 25088 25344 25600 25856 26112 26368 26624 26880 27136 27392 27648 27904 28160

28416 28672 28928 29184 29440 29696 29952 30208 30464 30720 30976 31232 31488 31744 32000 32256 32512

01011001 01011010 01011011 01011100 01011101 01011110 01011111 01100000 01100001 01100010 01100011 01100100 01100101 01100110 01100111 01101000 01101001 01101010 01101011 01101100 01101101 01101110

01101111 01110000 01110001 01110010 01110011 01110100 01110101 01110110 01110111 01111000 01111001 01111010 01111011 01111100 01111101 01111110 01111111

-

Appendix E

128 129 130

131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166

80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F A0 Al A2 A3 A4 A5 A6

32768 33024 33280 33536 33792 34048 34304 34560 34816 35072 35328 35584 35840 36096 36352 36608 36864 37120 37376 37632 37888 38144 38400 38656 38912 39168 39424 39680 39936 40192 40448 40704 40960 41216 41472 41728 41984 42240 42496

10000000 10000001 10000010 10000011 10000100 10000101 10000110 10000111 10001000 10001001 10001010 10001011 10001100 10001101 10001110 10001111 10010000 10010001 10010010 10010011 10010100 10010101 10010110 10010111 10011000 10011001 10011010 10011011 10011100 10011101 10011110 10011111 10100000 10100001 10100010 10100011 10100100 10100101 10100110

Appendix E

167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 18J5 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204

A7 A8 A9 AA AB AC AD AE AF BO Bl B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF CO CI C2 C3 C4 C5 C6 C7 C8 C9 CA CB

cc

42752 43008 43264 43520 43776 44032 44288 44544 44800 45056 45312 45568 45824 46080 46336 46592 46848 47104 47360 47616 47872 48128 48384 48640 48896 49152 49408 49664 49920 50176 50432 50688 50944 51200 51456 51712 51968 52224

10100111 10101000 10101001 10101010 10101011 10101100 10101101 10101110 10101111 10110000 10110001 10110010 10110011 10110100 10110101 10110110 10110111 10111000 10111001 10111010 10111011 10111100 10111101 10111110 10111111 11000000 11000001 11000010 11000011 11000100 11000101 11000110 11000111 11001000 11001001 11001010 11001011 11001100

~

Appendix E

205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242

CD CE CF DO Dl D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF EO El E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF FO Fl F2

52480 52736 52992 53248 53504 53760 54016 54272 54528 54784 55040 55296 55552 55808 56064 56320 56576 56832 57088 57344 57600 57856 58112 58368 58624 58880 59136 59392 59648 59904 60160 60416 60672 60928 61184 61440 61696 61952

11001101 11001110 11001111 11010000 11010001 11010010 11010011 11010100 11010101 11010110 11010111 11011000 11011001 11011010 11011011 11011100 11011101 11011110 11011111 11100000 11100001 11100010 11100011 11100100 11100101 11100110 11100111 11101000 11101001 11101010 11101011 11101100 11101101 11101110 11101111 11110000 11110001 11110010

Appendix E

243 244 245 246 247 248 249 250 251 252 253 254 255

F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF

62208 62464 62720 62976 63232 63488 63744 64000 64256 64512 64768 65024 65280

11110011 11110100 11110101 11110110 11110111 11111000 11111001 11111010 11111011 11111100 11111101 11111110

11111111

The following program will print copies of this number table. You might need to make some adjustments to the printout conventions of your computer's BASIC and your printer itself. This program is for Microsoft BASIC and will not work on the Atari.

~

Program E-l. Microsoft Table Printer.

10 OPEN4,4:REM OPEN CHANNEL TO PRINTER

100 HE$="0123456789ABCDEF"

110 F0RX=1T0255

120 B=2:C=1

122 IFX<10THENPRINT#4," ";:GOTO130

124 IFX<100THENPRI!SJT#4, " ";

130 PRINT#4,X;" ";:DE=X:GOSUB240

135 REM CREATE BINARY

140 IFXAND1THENK$(C)="1":GOTO160

150 K$(C)="0"

160 C=C+1:IFBANDXTHENK$(C)="1":GOTO180

170 K$(C)="0"

180 B=B*2:IFO8THEN200

190 GOTO160

200 FORI=8T01STEP-l:PRINT#4,K$(I);:NEXTI

Appendix E

220 PRINT#4:NEXTX

2 30 END:REM TRANSFORM TO HEX

240 H$="":FORM=1TO0STEP-1:N%=DE/(16"M):DE=DE

-N%*16"M 250 H$=H$+MID$(HE$,N%+l,l):NEXT 260 PRINT#4,H$" ";:DE=X*256 262 IFDE<1000THENPRINT#4," ";:GOTO270 264 IFDE<10000THENPRINT#4," "; 270 PRINT#4,DE" ";:RETURN

-

Appendix F

SUPERMONForPET

Tlic folloiving monitor extensions are the work of several programmers ami were previously published in COMPUTE! Magazine. (See the copyright page for references.)

Here is the legendary Supermon — a version for Upgrade (3.0 or "New ROM") and 4.0 PETs, all keyboards, all memory sizes, 40 or 80 column screens. You need not yet know how to program in machine language (ML) to enter this program — or to use it. In fact, exploring with Supermon, you will find that the mysterious world of your computer's own language becomes gradually understandable. You will find yourself learning ML.

Many ML programmers with PET/CBM machines feel that Supermon is the essential tool for developing programs of short to medium length. All Upgrade and 4.0 machines have a "resident" monitor, a program within the computer's ROM which allows you to type SYS 1024 and see the registers, load and save and run ML programs, or see a memory dump (a list of numbers from the computer's memory cells). But to program or analyze ML easily, disassembler, assembler, hunt, and single-step functions are all practical necessities. Supermon provides these and more.

Even if you've never assembled a single instruction and don't know NOP from ROL, this appendix will lead you step-by-step through the entry and SAVE of Supermon.

How To Enter Supermon

1. Type in the BASIC program (Program 1). It is the same for all versions. Then save it normally by typing SAVE "CONTROL". This program will be used later to automatically find your memory size, transfer Supermon to the top, and report to you the SYS address you use to activate it.

2. Now the hard part: type SYS 1024 which enters you into the machine language monitor. You will see something like the following:

Figure I.

B*

PC IRQ SR AC XR YR SP 0401 E455 32 04 5E 00 EE

Then type: M 0600 0648 and you will see something similar to this (the numbers will be different, but we are going to type over them which, after hitting RETURN on each line, will enter the new numbers into the computer's memory):

Appendix F

Figure 2.

We have divided Supermon into 21 blocks with 80 hexadecimal numbers per block to make typing easier. There is a final, shorter block with 64 numbers. Type right over the numbers on the screen so that line 0600 looks like it does in Program 2. Then hit RETURN and cursor over to the A5 on line 0608. (Set a TAB to this position if your keyboard has a TAB key.) Then type over the numbers in this line and so on. When you have finshed typing your RETURN on line 0648, type in: M 0650 0698 and the next block will appear for you to type over. Continue this way until you finish entering the new version of line 0CC8 at the end. (Hope that no lightning or fuses blow.)

3. If you have Upgrade ROMs, you will need to correct the lines listed in Program 3 at this point. To change line 06D0, simply type M 06D0 06D0 and it will appear so that you can type over it and RETURN as in step 2.

4. Now Supermon is in your memory and you must SAVE it. Hit RETURN so that you are on a new line and tvpe: S"SUPERMON", 01,0600,0CCC (to SAVE to tape) or type: S"0:SUPERMON",08,0600,0CCC (to SAVE to disk drive 0).

5. Finally, you will want to use the Checksum program to see if you made any errors during the marathon. You probably did, but to make it as painless as possible, the Checksum program will flash through your Supermon and let you know which blocks need to be corrected. So, type in Program 4 (or if you have Upgrade ROMs, use the first three lines from Program 5). SAVE Checksum just in case. Then LOAD "SUPERMON" (an ordinary LOAD as with a BASIC program will slide it in starting at address 1536, above the end of Checksum). Then RUN. Incorrect blocks will be announced. When you know where the errors are, type SYS 1024 and then M XXXX XXXX for the starting and ending addresses of the bad block. Check

~ ~

^

Appendix F

the numbers against Program 2 (or Program 3) and in all corrections. If, despite everything, you cannot find an error within a block, make sure that the corresponding number within the DATA statement of the Checksum program is correct. Then SAVE the good version "SUPERMON1" as in step 4.

6. Your reward is near. LOAD "CONTROL" and then LOAD SUPERMON1. Then type RUN and hold your breath. If all goes well, you should see:

Figure 3.

SUPERM0N4!

DISASSEMBLER BY WOZNIAK/BAUM SINGLE STEP BY JIM RUSSO MOST OTHER STUFF ,BY BILL SEILER

TIDIED & WRAPPED BY JIM BUTTERFIELD

LINK TO MONITOR — SYS 31283

SAVE WITH MLM: .S "SUPERMON" ,01,7A33,8000 READY.

And you should be able to use all the commands listed in the Supermon Summary. If some, or all, of the commands fail to function, check the last, short block of code to see if there are any errors.

After Supermon is relocated to the top of your memory, use a ML SAVE to save it in its final form. Instructions are on screen after RUN.

SUPERMON SUMMARY

COMMODORE MONITOR INSTRUCTIONS:

G GO RUN

L LOAD FROM TAPE OR DISK

M MEMORY DISPLAY

R REGISTER DISPLAY

S SAVE TO TAPE OR DISK

X EXIT TO BASIC

Appendix F

SUPERMON ADDITIONAL INSTRUCTIONS:

A SIMPLE ASSEMBLER

D DISASSEMBLER

F FILL MEMORY

H HUNT MEMORY

I SINGLE INSTRUCTION

P PRINTING DISASSEMBLER

T TRANSFER MEMORY

SUPERMON WILL LOAD ITSELF INTO THE

TOP OF MEMORY .. WHEREVER THAT HAPPENS

TO BE ON YOUR MACHINE.

YOU MAY THEN SAVE THE MACHINE CODE FOR FASTER LOADING IN THE FUTURE. BE SURE TO NOTE THE SYS COMMAND WHICH LINKS SUPERMON TO THE COMMODORE MONITOR.

SIMPLE ASSEMBLER .A 2000 LDA #$12 .A 2002 STA $8000,X .A 2005 (RETURN)

IN THE ABOVE EXAMPLE THE USER STARTED ASSEMBLY AT 2000 HEX. THE FIRST INSTRUCTION WAS LOAD A REGISTER WITH IMMEDIATE 12 HEX. IN THE SECOND LINE THE USER DID NOT NEED TO TYPE THE A AND ADDRESS. THE SIMPLE ASSEMBLER PROMPTS WITH THE NEXT ADDRESS. TO EXIT THE ASSEMBLER TYPE A RETURN AFTER THE THE ADDRESS PROMPT. SYNTAX IS THE SAME AS THE DISASSEMBLER OUTPUT.

DISASSEMBLER .D 2000

(SCREEN CLEARS)

., 2000 A9 12 LDA #$12 ., 2002 9D 00 80 STA $8000,X . , 20 0 5 AA TAX

. , 20 06 AA TAX

(FULL PAGE OF INSTRUCTIONS)

DISASSEMBLES 22 INSTRUCTIONS STARTING AT 2000 HEX. THE THREE BYTES FOLLOWING THE ADDRESS MAY BE MODIFIED.

Appendix F

USE THE CRSR KEYS TO MOVE TO AND MODIFY THE BYTES. HIT RETURN AND THE BYTES IN MEMORY WILL BE CHANGED. SUPERMON WILL THEN DISASSEMBLE THAT PAGE AGAIN.

PRINTING DISASSEMBLER .P 2000,2040

2000 A9 12 LDA #$12 2002 9D 00 80 STA $3000,XY. 2 00 5 AA TAX

203F A2 00 LDX #$00

TO ENGAGE PRINTER, SET UP BEFOREHAND:

OPEN 4,4:CMD4 ON 4.0, ACCESS THE MONITOR VIA A CALL SYS 54386 (*NOT* A BREAK) COMMAND

SINGLE STEP . I

ALLOWS A MACHINE LANGUAGE PROGRAM TO BE RUN STEP BY STEP.

CALL REGISTER DISPLAY WITH .R AND SET THE PC ADDRESS TO THE DESIRED FIRST INSTRUCTION FOR SINGLE STEPPING. THE .1 WILL CAUSE A SINGLE STEP TO EXECUTE AND WILL DISASSEMBLE THE NEXT. CONTROLS:

< FOR SINGLE STEP;

RVS FOR SLOW STEP;

SPACE FOR FAST STEPPING;

STOP TO RETURN TO MONITOR. [ON BUSINESS KEYBOARDS—

USE 8,^,6 AND STOP].

FILL MEMORY .F 1000 1100 FF

FILLS THE MEMORY FROM 1000 HEX TO 1100 HEX WITH THE BYTE FF HEX.

GO RUN .G

GO TO THE ADDRESS IN THE PC REGISTER DISPLAY AND BEGIN RUN CODE. ALL THE REGISTERS WILL BE REPLACED WITH THE DISPLAYED VALUES. .G 1000

Appendix F

GO TO ADDRESS 1000 HEX AND BEGIN RUNNING CODE.

HUNT MEMORY •H C000 D000 'READ

HUNT THRU MEMORY FROM C000 HEX TO D000 HEX FOR THE ASCII STRING READ AND PRINT THE ADDRESS WHERE IT IS FOUND. A MAXIMUM OF 32 CHARACTERS MAY BE USED. .H C000 D000 20 D2 FF

HUNT MEMORY FROM C000 HEX TO D000 HEX FOR THE SEQUENCE OF BYTES 20 D2 FF AND PRINT THE ADDRESS. A MAXIMUM OF 32 BYTES MAY BE USED.

LOAD • L

LOAD ANY PROGRAM FROM CASSETTE #1. .L "RAM TEST"

LOAD FROM CASSETTE #1 THE PROGRAM NAMED RAM TEST. ,L "RAM TEST",08

LOAD FROM DISK (DEVICE 8) THE PROGRAM NAMED RAM TEST.

THIS COMMAND LEAVES BASIC POINTERS UNCHANGED.

MEMORY DISPLAY •M 0000 0080

.: 0000 00 01 02 03 04 05 06 07 .: 0008 08 09 0A 0B 0C 0D 0E 0F

DISPLAY MEMORY FROM 0000 HEX TO 0080 HEX. THE BYTES FOLLOWING THE .: CAN BE ALTERED BY TYPING OVER THEM THEN TYPING A RETURN.

REGISTER DISPLAY .R

PC IRQ SR AC XR YR SP 0000 E6 2E 01 02 03 04 05 DISPLAYS THE REGISTER VALUES SAVED WHEN SUPERMON WAS ENTERED. THE VALUES MAY BE CHANGED WITH THE EDIT FOLLOWED BY A RETURN.

~

Appendix F

USE THIS INSTRUCTION TO SET UP THE PC VALUE BEFORE SINGLE STEPPING WITH

. I

SAVE .S "PROGRAM NAME" ,01,0800,0C80

SAVE TO CASSETTE #1 MEMORY FROM 0800 HEX UP TO BUT NOT INCLUDING 0C80 HEX AND NAME IT PROGRAM NAME. .S "0:PROGRAM NAME",08,1200,1F50

SAVE TO DISK DRIVE #0 MEMORY FROM 1200 HEX UP TO BUT NOT INCLUDING 1F50 HEX AND NAME IT PROGRAM NAME.

TRANSFER MEMORY .T 1000 1100 5000

TRANSFER MEMORY IN THE RANGE 1000 HEX TO 1100 HEX AND START STORING IT AT ADDRESS 5000 HEX.

EXIT TO BASIC .X

RETURN TO BASIC READY MODE. THE STACK VALUE SAVED WHEN ENTERED WILL BE RESTORED. CARE SHOULD BE TAKEN THAT THIS VALUE IS THE SAME AS WHEN THE MONITOR WAS ENTERED. A CLR IN BASIC WILL FIX ANY STACK PROBLEMS.

Program I. CONTROL.

100 PRINT"{CLEAR}{02 DOWN}{REV} SUP

ERMON!!" 110 PRINT"{DOWN} DISSASSEMBLER '

{REV}D{OFF} BY WOZNIAK/BAU

M 120 PRINT" SINGLE STEP {REV}l

{OFF} BY JIM RUSSO 130 PRINT"MOST OTHER STUFF {REV},HA

LT{OFF} BY BILL SEILER 150 PRINT"{DOWN}TIDIED & WRAPPED BY JIM BUTTERFIELD"

Appendix F

+PEEK(53) *256:SYS1536

) :N=PEEK (34)

OKE53,N:POKE48,M:POKE

*256

DOWN}LINK TO MONITOR '

T"SAVE WITH MLM:" ;CHR$(34);"SUPERMON"; ,01"; :X=N/4096:G

OSUB250:END

F0RJ=1T04:X%=X:X=(X-X

>9THENX%=X%+7

X%+4 8);:NEXTJ:RETURN

Program 2. SUPERMON 4.0

Appendix F

06A8 AA BD E9 FF

48 BD E8

I6B0 FF 00 48 60 CA 10 EA 4C

I6B8 9A FA 00 A2 02 2C A2 00

I6C0 00 B4 FB D0 08 B4 FC D0

I6C8 02 E6 DE D6 FC D6 FB 60

Appendix F

07 07 07 07 08 08 08 08 08 08

E0 E8 F0 F8 00 08 10 18 20 28

98 D7 C9 F0 D7 CF D7 B4 00 0C

D7 9D 0D 1C 9 0 FF 9 0 20 00 C8

C9 10 F0 8E C6 C9 B6 34 Bl E8

27 02 2 2 00 9D 0D E0 D5 FB E4

D0 E8 E0 00 10 F0 20 A2 DD B4

14 20 20 01 02 09 D0 00 10 D0

20 CF D0 2 0 E8 20 EC 00 02 F3

98 FF Fl 6B 20 63 86 A0 D0 20

08 08 08 08 08 08 08 08 08 08

30 38 40 4 8 50 58 60 68 70 78

17 A6 B0 00 02 02 FF 00 FC D2

D7 DE DD 8D A9 8E A9 20 C6 FF

20 D0 4C 0D 04 0A 16 64 B5 4C

31 92 BA 02 A2 02 85 FC D0 BA

D5 2 0 D4 A5 00 A9 B5 00 F2 D4

20 CA 2 0 FC 00 93 2 0 85 A9 A0

39 FA

81 8D 8D 20 0 6 FB 91 2C

D5 00 FA 0E 09 D2 FC 84 20 20

0!

m

08

80 79 D5 20 17 D7 20 31 D5

88 A2 00 00 Al FB 20 74 FC

90 00 48 20 BB FC 00 68 20

98 D3 FC 00 A2 06 E0 03 D0

A0 13 AC 1C 02 F0 0E A5 FF

A8 C9 E8 Bl FB B0 1C 20 5C

B0 FC 00 88 D0 F2 06 FF 90

B8 0E BD 51 FF 00 20 45 FD

C0 00 BD 57 FF 00 F0 03 20

C8 45 FD 00 CA D0 D4 60 20

08D0 68 FC 00 AA E8 D0 01 C8

08D8 98 20 5C FC 00 8A 86 B4

08E0 20 22 D7 A6 B4 60 AD 1C

08E8 02 38 A4 FC AA 10 01 88

08F0 65 FB 90 01 C8 60 A8 4A

08F8 90 0B 4A B0 17 C9 22 F0

0900 13 29 07 09 80 4A AA BD

1908 00 FF 00 B[

14 4A 4A 4A

Appendix F

Appendix F

~

Appendix F

Program 3. Changes For SUPERMON 3.0.

06D0 20 EB E7 C9 20 F0 F9 60

06E0 79 FA 00 20 BE E7 20 AA

06E8 E7 90 09 60 20 EB E7 20

06F0 A7 E7 B0 DE AE 06 02 9A

06F8 4C F7 E7 20 CD FD CA D0

0738 81 FA 00 20 97 E7 20 92

0748 FA 00 20 CA FA 00

0750 E7 90 15 A6 DE D0

0760 FD 20 A8 FA 00 20

0798 00 20 81 FA 00 20

07A0 20 92 FA 00 20 97

07A8 EB E7 20 B6 E7 90

07C0 D5 FD D0 EE 4C 9A

07C8 4C 56 FD 20 81 FA

07D0 97 E7 20 92 FA 00

07D8 E7 20 EB E7 A2 00

07E0 EB E7 C9 27 D0 14

I7E8 E7 9D ll

I7F8 F0

1800 E7

1808 CF

1810 E7

J818 B4

1C 90 FF 90 20

8E C6 C9 B6 D0

02 E8 20

00 00 01

9D 10 02

0D F0 09

E0 20 D0

FD A2 00

1830 6A E7 20 CD FD 20 1840 B0 DD 4C 56 FD 20

20 97 65 20

D5 FD

97 E7 E7 20 14 85

FA 0 0 00 20 20 97 00 20 20 EB CF FF

20 BE E8 20 20 B6 EC 86 00 A0

D5 FD

81 FA

->

Appendix F

0878 D2 FF 4C 56 FD A0 2C 20

0880 15 FE 20 6A E7 20 CD FD

08E0 20 75 E7 A6 B4 60 AD 1C

0980 4C CD FD 20 81 FA 00 20

0988 97 E7 20 92 FA 00 20 97

0990 E7 A9 04 A2 00 00 8D 09

0998 02 8E 0A 02 20 D0 FD 20

09A8 FB 84 FC 20 01 F3 F0 05

09B0 20 CA FA 00 B0 E9 4C 56

09B8 FD 20 81 FA 00 A9 03 85

09C0 B5 20 EB E7 20 A7 FD D0

09E8 0A 02 20 D0 FD 78 AD FA

Program 4. SUPERMON 4.0 Checksum.

100 REM SUPERMON 4 CHECKSUM

110 DATA7331,12186,10 071,10 387,1082

9,9175,10314,9823,9715,871

4 ,8852 120 DATA88 50,9748,7754,10247,10423,

10948, 10075,6093,5492,7805

: S=1536

Appendix F

130 F0RB=1T021 :READX: FORI=STOS+79:N

= PEEK(I):Y=Y+N 140 NEXTI : I FYOXTHENPRINT" ERROR IN '

BLOCK #"B:GOTO160 150 PRINT"BLOCK #"B" IS CORRECT" 160 S=I:Y=0:NEXTB:PRINT"CHECK THE F

INAL, SHORT BLOCK BY HAND"

Program 5. Changes For SUPERMON 3.0 Checksum.

100 REM SUPERMON 3 CHECKSUM

110 DATA7331,1 2186, 10467,10880, 1112

4,10005, 10906, 10196,9951,8

813 120 DATA8 8 5 2,9 3 29, 10239,8457,10334,

10423,11047,10311,6093,549

2,7805:S=15 36

~

~

Appendix F

PETMICROMON

An Enhanced Machine Language Monitor

Micromon is for Upgrade and 4.0 BASICs, all memory sizes, all keyboards and is in the public domain. If you have enough memory, you can add the additional commands of "Micromon Plus" as well. "Plus" is from $5B00 to $5F48 and you will want to move Micromon from $1000 up to $6000.

There is quite a bit of typing here so there are two checksum programs which will find and flag any errors. See the instructions for typing in Supermon.

Micromon Instructions

SIMPLE ASSEMBLER

.A 2000 LDA*$12 .A 2002 STA $8000, X .A 2005 DEX:GARBAGE

In the above example, the user started assembly at 2000 hex. The first instruction was load a register with immediate 12 hex. In the second line the user did not need to type the A and address. The simple assembler retypes the last entered line and prompts with the next address. To exit the assembler, type a return after the address prompt. Syntax is the same as the Disassembler output. A colon (:) can be used to terminate a line.

BREAK SET

.B 1000 00FF

The example sets a break at 1000 hex on the FF hex occurrence of the instruction at 1000. Break set is used with the QUICK TRACE command. A BREAK SET with count blank stops at the first occurrence of the break address.

COMPARE MEMORY

.C 1000 2000 cooo Compares memory from hex 1000 to hex 2000 to memory beginning at hex C000. Compare will print the locations of the unequal bytes.

DISASSEMBLER

.D 2000 3000

., 2000 A9 12 LDA#$12

., 2002 9D 00 80 STA $8000, X

., 2005 A A TAX

Appendix F

Disassembles from 2000 to 3000. The three bytes following the address may be modified. Use the CRSR KEYS to move to and modify the bytes. Hit return and the bytes in memory will be changed. Micromon will then disassemble that line again.

Disassembly can be done under the control of the cursor. To disassemble one at a time from $1000.

.D 1000

If the cursor is on the last line, one instruction can be disassembled for each pressing of the cursor down key. If it is held down, the key will repeat and continuous disassembly will occur. Disassembly can even be in reverse! If the screen is full of a disassembly listing, place the cursor at the top line of the screen and press the cursor up key.

EXIT MICROMON

.E

Combine the killing of Micromon and exit to BASIC.

FILL MEMORY

.F 1000 1100 FF Fills the memory from 1000 hex to 1100 hex with the bvte FF hex.

GO RUN

.G

Go to the address in the PC Register display and begin run code. All the registers will be replaced with the displayed values. .G 1000

Go to address 1000 hex and begin running code. HUNT MEMORY

.H C000 DOOO'READ

Hunt through memory from C000 hex to D000 hex for the ASCII string "read" and print the address where it is found. Maximum of 32 characters may be used.

.H C000 D000 20 D2 FF

Hunt memory from C000 hex to D000 hex for the sequence of bytes 20 D2FF and print the address. A maximum of 32 bytes maybe used. Hunt can be stopped with the STOP key.

KILL MICROMON

.K

Restore the Break vector and IRQ that was saved before Micromon was called and break into the TIM monitor. A return to Micromon can be done with a Go to the value in the PC register.

LOAD

.L"RAMTEST",08

Appendix F

Load the program named RAM TEST from the disk. Note for cassette users: To load or save to cassette. Kill Micromon with the K command to return to the TIM monitor. Then use the TIM monitor L and S commands to load and save to the cassettes. This has to be done because of the repeat keys of Micromon. BASIC 4.0 users then can return to Micromon with a Go command to the PC value, but BASIC 2.0 users should return to BASIC, then SYS to Micromon because the TIM overwrites the IRQ value for loads and saves with a filename.

MEMORY DISPLAY

.M 0000 0008

.: 0000 30 31 32 33 34 35 36 37 1234567

.: 0008 38 41 42 43 44 45 46 47 89ABCDE

Display memory from 0000 hex to 0008 in hex and ASCII. The bytes following the address may be modified by editing and then typing a RETURN.

Memory display can also be done with the cursor control keys.

NEW LOCATER

.N 1000 17FF 6000 1000 1FFF .N 1FB0 1FFF 6000 1000 1FFF W

The first line fixes all three byte instructions in the range 1000 hex to 1FFF hex by adding 6000 hex offset to the bytes following the instruction. New Locater will not adjust any instruction outside of the 1000 hex to 1FFF hex range. The second line adjusts Word values in the same range as the first line. New Locater stops and disassembles on any bad op code.

CALCULATE BRANCH OFFSET

.O 033A 033A FE Calculate the offset for branch instructions. The first address is the starting address and the second address is the target address. The offset is then displayed.

QUICK TRACE

■Q

.Q 1000

The first example begins trace at the address in the PC of the register display. The second begins at 1000 hex. Each instruction is executed as in the WALK command, but no disassembly is shown. The Break Address is checked for the break on Nth occurrence. The execution may be stopped by pressing the STOP and = (left arrow on business) keys at the same time.

REGISTER DISPLAY

.R

PC IRQ SR AC XR YR SP

.: 0000 E455 01 02 03 04 05

Appendix F

Displays the register values saved when Micromon was entered. The values may be changed with the edit followed by a RETURN.

SAVE

.S "l:PROGRAM NAME",08,0800,OC80

Save to disk drive #1 memory from 0800 hex up to, but not including, 0C80 hex and name it PROGRAM NAME. See note in LOAD command for cassette users.

TRANSFER MEMORY

.T 1000 1100 5000

Transfer memory in the range 1000 hex to 1100 hex and start storing it at address 5000 hex.

WALK CODE

.w Single step starting at address in register PC.

.W 1000 Single step starting at address 1000 hex. Walk will cause a single step to execute and will disassemble the next instruction. Stop key stops walking. The J key finishes a subroutine that is walking, then continues with the walk.

EXIT TO BASIC

.X

Return to BASIC READY mode. The stack value saved when entered will be restored. Care should be taken that this value is the same as when the monitor was entered. A CLR in BASIC will fix any stack problems. Do not X to BASIC then return to Micromon via a SYS to the cold start address. Return via a SYS to BRK (SYS 1024) or SYS to the Warm start of Micromon (Warm start = Cold start+ 3). An X and cold start will write over the TIM break vector that was saved.

CHANGE CHARACTER SETS .z

Change from uppercase/graphics to lower/uppercase mode or vice versa.

HEX CONVERSION

.$4142 16706 A B 0100 0001 0100 0010

A hex number is input and the decimal value, the ASCII for the two bytes, and the binary values are returned. The ASCII control values are returned in reverse.

Hex conversion can also be scrolled with the cursor control keys.

DECIMAL CONVERSION

.#16706 4142 A B 0100 0001 0100 0010

Appendix F

A decimal number is input and the hex value, the ASCII for the two bytes, and the binary values are returned.

BINARY CONVERSION

.%0100000101G00010 4142 16706 A B

A binary number is input and the hex value, the decimal number, and the ASCII values are returned.

ASCII CONVERSION

."A 41 65 0100 0001

An ASCII character is input and the hex value, decimal value, and binary values are returned. Because of the quote, the control characters can be determined also.

ADDITION

.+ 1111 2222 3333

The two hex numbers input are added, and the sum displayed. SUBTRACTION

-3333 1111 2222

The second number is subtracted from the first number and the difference displayed.

CHECKSUM

.& A000 AFFF 67E2 The checksum between the two addresses is calculated and displayed.

MICROMON INSTRUCTIONS:

A SIMPLE ASSEMBLE

B BREAK SET

C COMPARE MEMORY

D DISASSEMBLER

E EXIT MICROMON

F FILL MEMORY

G GO RUN

H HUNT MEMORY

K KILL MICROMON

L LOAD

M MEMORY DISPLAY

N NEW LOCATE R

O CALCULATE BRANCH

Q QUICK TRACE

R REGISTER DISPLAY

S SAVE

T TRANSFER MEMORY

W WALKCODE

X EXIT TO BASIC

Z CHANGE CHARACTER SETS

$ HEX CONVERSION

# DECIMAL CONVERSION

Appendix F

% BINARY CONVERSION " ASCII CONVERSION + ADDITION

SUBTRACTION & CHECKSUM

Micromon also has repeat for all keys.

Micromon is executed by the following: SYS 4096 as listed in Program 2, where it resides in $1000 to $1FFF.

For 8032, make the following changes for Micromon operation. In location the X stands for the start of Micromon. Values in hex.

Micromon Plus Instructions PRINTING DISASSEMBLER

.(Shift) D 1000 1FFF

The same as the Disassembler but no ., printed before each line. Also the ASCII values for the bytes are output at the end of the line.

FORM FEED SET

.1 Sets a form feed for printout. Gives 57 printed lines per page. Works with the Shift D and Shift M commands.

.1 "Heading" Sets form feed with a message to be printed at the top of each page.

.IX

Cancels form feed. PRINT LOAD ADDRESS

J "File name"

Read the load address of the file and print it in hex. Device number 8 is used.

Appendix F

KILL MICROMON ADDITIONS

.(Shift) K Kill Micromon and its additions and BRK to the TIM monitor. This is the same as the unshifted K command except now a G command will reinitialize Micromon and the additions.

LOAD FROM DISK

.(Shift) L "filename" This is the same as the normal load command except that the disk (device #8) is used as the default, not the cassette.

PRINTING MEMORY DUMP

.(Shift) M F000 F100

The same as the normal Memory dump, but does not print the .: and prints out 16 hex bytes and the ASCII for them.

PRINT SWITCHER

.P

If the output is to the CRT then switch the output to the printer (device #4). If the output is not to the CRT then clear the output device and restore the output to the CRT.

.P06 Make device #6 the output device if the current output is to the CRT.

SEND TO PROM PROGRAMMER

.U06 7000 7FFF

This command will send out bytes to a PROM programmer on the IEEE bus. The first byte is the device number and the two addresses are the range of memory to output. A CHR$(2) is sent first to start the programmer. This is followed by the memory bytes as ASCII characters separated by spaces. After all bytes have been sent, a CHR$(3) is sent to stop the programmer. Micromon then does a checksum on the range to compare against the programmer checksum. Although this is for a particular programmer, it could be modified for others.

SPECIFY LOAD ADDRESS

.Y 7000 "Filename"

This command allows a file to be loaded starting at the address you specify and not the load address it would normally load into. The disk (device #8) is used for loading.

TEXT FLIP FOR 8032 & FAT 40's

.(Shift) Z

This is for 8032 and Fat 40's to go from Text to Graphics mode or vice versa.

Appendix F

DOS SUPPORT

.@or .> This reads the error channel from disk device number 8.

.@ disk command or . > disk command This sends the disk command to disk device number 8.

.@$0or.>$0 This reads the directory from disk device number 8. The SPACE BAR will hold the display, any other key will start it again, and the STOP key will return to command mode.

CONTROL CHARACTERS

.(Up arrow)g

This command will print the control character of the ASCII character input.

Examples of controls:

g Ring bell

M n N c \

Q

s

S ii v

V

Tab set and clear Insert line Text mode Graphics mode Cursor down Cursor up Home cursor Clear screen Delete line Erase end Erase begin

MICROMON PLUS INSTRUCTIONS

(Shift) D PRINTING DISASSEMBLER

I HEADING AND FORM FEED CONTROL

J PRINT LOAD ADDRESS

(Shift) K KILL MICROMON ADDITIONS

(Shift) L LOAD FROM DISK

(Shift) M PRINT MEMORY DISPLAY

P PRINTER SWITCHING

U SEND TO PROM PROGRAMMER

Y SPECIFY LOAD ADDRESS

(Shift) Z TEXT/GRAPHICS FLIP

> DOS SUPPORT COMMANDS

@ DOS SUPPORT COMMANDS

(Up arrow) CONTROL CHARACTERS

^

~ ~

Appendix F

Program I. Checksum For Micromon.

10 DATA 15463,14894,14290,11897,12

453,13919, 14116,11715,1257

5 ,14571 20 DATA 13693,11853,12903,14513,12

137,15006,12654,13291,1243

6,13899 30 DATA 15366,9999,11834,13512,128

92,14475,15149,14896,15782

,9511 4 0 DATA 12171,8985 100 Q=4 09 6 110 FOR BL0CK=1T032 120 FOR BYTE=0TO127 130 X=PEEK(Q+BYTE):CK=CK+X 140 NEXT BYTE 150 READ SUM 160 IF SUM <> CK THEN PRINT" E RROR

IN BLOCK #"BLOCK:GOTO170 165 PRINT" BLOCK"

BLOCK" IS CORRECT 170 CK=0:Q=Q+128 180 NEXT BLOCK

Program 2. Micromon.

1278 Al 12 88 D0 Fl 0E 96 02

1280 90 0E BD E9 IE 20 AD 15

1288 BD EF IE F0 03 20 AD 15

1290 CA D0 D2 60 20 B7 12 AA

1298 E8 D0 01 C8 98 20 Al 12

12A0 8A 3E 88 02 20 1A 18 AE

12A8 88 02 60 AD 8B 02 20 B6

12B0 12 85 FB 84 FC 60 38 A4

Appendix F

12B8 FC AA 10 01 88 65 FB 90 12C0 01 C8 60 A8 4A 90 0B 4A 12C8 B0 17 C9 22 F0 13 29 07 12D0 09 80 4A AA BD 98 IE B0 12D8 04 4A 4A 4A 4A 29 0F D0 12E0 04 A0 80 A9 00 AA BD DC 12E8 IE 8D 96 02 29 03 8D 8B 12F0 02 98 29 8F AA 98 A0 03 12F8 E0 8A F0 0B 4A 90 08 4A

1300 4A 09 20 88 D0 FA C8 81

-

1470 3F A0 05 4A 6E A4 02 6E 1478 A3 02 88 D0 F6 F0 E9 A2

Appendix F

1640 AD 9C 02 D0 5D AD 9D

Appendix F

~ -

Appendix F

1890 18 20 99 18 0D 97 02 38 1898 60 C9 3A 08 29 0F 28 90

1980 18 AD 82 02 20 1A 18 20

1988 30 19 20 03 19 4C 93 10

1990 4C 8E 10 20 4C 18 20 FC

1998 18 20 5D 18 8D 82 02 8E

19A0 81 02 20 30 19 8D 89 02

19A8 20 A4 18 20 19 19 D0 F8

19B0 F0 DB 20 60 1C AE 80 02

19B8 9A 6C 94 00 4C 8E 10 A0

Appendix F

19C0 01 84 D4 88 84 Dl 84 96

19C8 84 9D A9 02 85 DB A9 A3

19D0 85 DA 20 06 10 C9 20 F0

19D8 F9 C9 0D F0 1A C9 22 D0

19E0 DB 20 06 10 C9 22 F0 36

19E8 C9 0D F0 0B 91 DA E6 Dl

19F0 C8 C0 10 F0 C7 D0 EA AD

19F8 87 02 C9 4C D0 El AD 00

1A00 C0 C9 40 D0 06 20 22 F3

1A08 4C 12 1A C9 4C D0 AD 20

1A10 56 F3 20 BC 18 A5 96 29

1A18 10 D0 El 4C 93 10 20 06

1A20 10 C9 0D F0 D2 C9 2C D0

1A28 F0 20 6F 18 29 0F F0 C3

1A30 C9 03 F0 FA 85 D4 20 06

1A38 10 C9 0D F0 BA C9 2C D0

1A40 E6 20 F9 17 20 06 10 C9

1A48 2C D0 F4 20 60 18 85 C9

1A50 86 CA 20 06 10 C9 20 F0

1A58 F9 C9 0D D0 EC AD 87 02

1A60 C9 53 D0 F7 AD 00 C0 C9

1A68 40 D0 06 20 A4 F6 4C 93

1A70 10 C9 4C D0 D4 20 E3 F6

1A78 4C 93 10 20 01 .18 20 3B

-^

Appendix F

1D00 El C6 FE D0 DD 8D 87

Appendix F

1D40 4C C2 1C 20 3B 19 20 B3 1D48 1A 4C 39 ID C9 91 D0 F0

Program 3. Checksum For Micromon Plus.

10 DATA 15965,14778,13059,14282,14

416,17693,12979,129 03,1767

6 ,21760 20 DATA 14416,17693,12979,12903 100 Q=23296 110 FOR BL0CK=1T08 120 FOR BYTE=0TO127 130 X = PEEK (Q+BYTE) :CK = CK + X 140 NEXT BYTE 150 READ SUM 160 IF SUM <> CK THEN PRINT" ERROR '

IN BLOCK #"BLOCK:GOTO170 165 PRINT" BLOCK"

. BLOCK" IS CORRECT 170 CK=0:Q=Q+128 180 NEXT BLOCK 190 PRINT"ANY REMAINING PROBLEMS AR

E EITHER WITHIN THE FINAL"

200 PRINT"SHORT BLOCK OR WITHIN DAT A STATEMENTS IN THIS PROGR

AM. "

Appendix F

Program 4. Micromon Plus.

5B80 6C FB 00 CA 10 E5 4C 8E <m

5B88 60 20 39 64 20 13 61 90

5B90 17 20 EF 60 8E 8C 02 20 ~

5B98 52 62 20 AB 5B 20 AB 62

5BA0 20 93 5C 20 AE 68 D0 E4

5BA8 4C 9B 60 A2 IE 20 Fl 60

5BB0 A0 00 Bl FB 20 60 5C CC

5BB8 8B 02 C8 90 F5 60 A5 B0

5BC0 C9 03 D0 19 20 06 60 AA

5BC8 A9 04 E0 0D F0 09 20 6F

5BD0 68 29 IF C9 04 90 AF 20

5BD8 E3 5B 4C 9B 60 20 CC FF

5BE0 4C 93 60 85 B0 85 D4 20

5BE8 09 5C AE 00 C0 E0 40 D0 _

5BF0 0B 20 BA F0 20 2D Fl A5

5BF8 96 D0 E2 60 E0 4C D0 5D ^»,

5C00 20 D5 F0 20 48 Fl 4C F7 5C08 5B A9 00 85 96 8D FC 03 5C10 85 0D 8D E8 02 60 20 39

Appendix F

5D48 D0 F6 20 55 69 A9 00 85

5D50 AF 20 E4 FF F0 C9 D0 05

5D58 20 E4 FF F0 FB C9 20 F0

5D60 F7 C9 03 D0 BA 20 12 5E

5D68 20 55 69 4C 93 60 20 17

5D70 6C C9 22 D0 7B A2 00 20

5D78 06 60 C9 0D F0 0C C9 22

5D80 F0 08 9D A3 02 E8 E0 40

5D88 90 ED 60 86 Dl A9 A3 85

5D90 DA A9 02 85 DB 20 CC FF

5D98 20 F3 5D 4C C9 5D A9 08

5DA0 85 D4 85 B0 AC 00 C0 C0

5DA8 40 D0 0B 20 BA F0 A9 6F

5DB0 20 28 Fl 4C F7 5B C0 4C

5DB8 D0 36 20 D5 F0 A9 6F 20

5DC0 43 Fl 4C F7 5B A9 6F 85

5DC8 D3 A9 08 85 D4 85 AF AC

5DD0 00 C0 C0 40 D0 0B 20 B6

5DD8 F0 A5 D3 20 64 Fl 4C F7

5DE0 5B C0 4C D0 0B 20 D2 F0

5DE8 A5 D3 20 93 Fl 4C F7 5B

5DF0 4C 8E 60 A9 08 85 D4 A9

5DF8 60 85 D3 AD 00 C0 C9 40

5E00 D0 06 20 66 F4 4C F7 5B

5E08 C9 4C D0 E4 20 A5 F4 4C

5E10 F7 5B A9 00 85 AF AD 00

5E18 C0 C9 40 D0 03 4C 8F F3

5E20 C9 4C D0 CC 4C CE F3 A9

5E28 02 2C 4C E8 08 A9 0E 28

5E30 F0 02 09 80 20 09 60 4C

5E38 93 60 20 09 5C 20 6E 5D

5E40 20 8B 5D 20 06 60 8D FB

5E48 00 20 06 60 8D FC 00 20

5E50 12 5E 20 52 69 A9 24 A2

5E58 20 20 29 68 20 13 68 4C

5E60 93 60 20 60 6C 00 6C 3E

5E68 5F A0 08 84 D4 A0 4C 8C

Appendix F

5F00 F3 4C 12 6A AD 00 C0 C9

5F38 82 5E D2 5E 69 5B 00 5B 5F40 31 30 32 31 38 31 AA AA

Appendix F

VIC Micromon

VIC machine language programmers: here's one of the most valuable tools there is for working in machine language. Thirty-four commands are at your disposal including single-step, hex conversion, search, EPROM routines, and a relocator. If you aren't yet working with machine language, the instructions for entering and using this program are easy to follow. As presented, this program takes up 4K of memory from $4000 (16384 decimal) to $4FFF (20479), but there are instructions for locating it elsewhere in RAM memory. To enter Micromon directly, see the Tiny PEEKer/POKEr program with Supermon 64 (in this Appendix). The commands for VIC Micromon are the same as the PET/CBM version except as noted below.

VIC Micromon Instructions

Initialize Memory And Screen Pointers

.1 1000 1E0O IE Define low memory as $1000 and high memory as $1E00 regardless of the memory present. The screen is defined to start at the $1E page of memory. The screen memory should always be on an even page within the range of $1000 to $1E00. Odd page values result in incorrect setup and operation of the VIC display. Although 3K of RAM can be added at $400 to $FFF, this memory is not accessible for use as screen memory.

Memory pages at $000 and $200 are accessible, but are not usable since they are used for BASIC and kernal storage, working buffers, and stack area. If the screen page is within the low to high memory range specified, there can be usage conflict of the screen memory pages. If the "I" command is used and exit is made to BASIC, the NEW command must be invoked in the BASIC environment to clean up the memory pointers used by BASIC.

Jump To Micromon Subroutine

J 2000

The subroutine at $2000 is called while remaining in the VIC Micromon environment. The assembly language subroutine should exit by using a RTS instruction, which causes a return to the command input section of VIC Micromon. The machine image as shown by the Register display command is not used, nor is it disturbed when the subroutine returns to the VIC Micromon.

Load

.L 2000 "TEST FILE" 01 Search for and, if found, load into memory the data file on device #1 named TEST FILE. If the name is not specified, the first file found is

Appendix F

loaded. The data is loaded into memory starting at location $2000. The last address loaded is determined by the length of the binary data file. If the device number is not specified, it defaults to device #1, which is the VIC cassette tape. The original memory addresses and name of the last file read can be inspected by doing a Memory display of the tape buffer which is at $375 for VIC Micromon.

Print Switcher .P

If the output is to the screen, then switch the ouput to the RS-232 channel (device #2). If the output is not to the screen, restore the output to the screen with the RS-232 channel left active until the RS-232 output buffer is drained. Note that opening the RS-232 channel grabs 512 bytes for I/O buffering from the top of memory.

.p oooo

Regardless of the output, clear the RS-232 channel and set output to the screen.

.P CCBB If the output is to the screen, set CC into the RS-232 command register at location $294 and BB into the RS-232 control register at location $293. Output is then switched to the RS-232 channel. This command is invalid if output is not currently to the screen.

Command Register Format

Appendix F

Control Register Format

~

~

Save

.S 2000 3000 "TEST FILE" 01

Save memory from $2000 up to, but not including, $3000 onto device #1, which is the VIC cassette tape. If the device number is not specified, it defaults to device #1. The name TEST FILE is placed in the file header for the file saved.

Verify

.V 2000 "TEST FILE" 01

Search for and verify, if found, the data file on device #1 named "TEST FILE." If the name is not specified, the first file found is verified. The data is verified by reading the file and comparing it to the data in memory starting at location $2000. If not specified, the device defaults to device #1. If there is a mismatch, the message ERROR is output to the screen at the end of the file verification.

Command End Tone

.( Enable the command end tone. A continuous tone will be generated at the end of execution of the next command. The tone can be turned off but still be enabled by just hitting the carriage return. No tone is

Appendix F

generated if there is a syntax error while inputting the next command.

.) Disable the command end tone.

Program EPROM

,ir 2800 2FFF 00 Program the 2716 type EPROM via the EPROM programmer on the VIC User I/O port with data read from memory starting at location $2800 and ending at location $2FFF. The last input parameter specifies in hex the starting 256 byte page offset on the EPROM. If the low order byte of the starting memory address is zero and the offset is zero, then the programming starts with the first byte of the EPROM. For example, to program only the last byte of the 2K EPROM with a data byte from location $2FFF in memory, the command would be:

.t 2FFF 2FFF 07

During programming, a compare of EPROM to memory is done for each data byte just after it is written to the EPROM. Any mismatch due to failure to program the EPROM results in output to the screen of the mismatched memory location. If programming must be terminated early, just hit the STOP key. No other means should be used to abort EPROM programming. A warm restart or power down while programming can damage the EPROM.

Read EPROM

.£ 2000 27FFOO

Load memory starting at location $2000 and ending at location $27FF with data read from the EPROM via the EPROM programmer on the VIC User I/O port. The last input parameter specifies in hex the starting 256 byte page offset on the EPROM. If the low order byte of the starting memory address is zero and the offset is zero, then reading starts with the first byte of the EPROM. For example, to read only the last byte of the 2K EPROM and load that byte into memory at location $10FF, the command would be:

.£ 10FF 10FF 07

During memory load, a compare of EPROM to memory is done for each data byte just after it is written to memory. Any mismatch because of failure to write the memory with data from the EPROM results in output to the screen of the mismatched memory location. The STOP key can be used to terminate the command early.

Compare EPROM

. = 3000 37FF 00

Compare memory starting at location $3000 and ending at location $37FF with data read from the EPROM via the EPROM programmer on the VIC User I/O port. The last input parameter specifies in hex

Appendix F

the starting 256 byte page offset on the EPROM. If the low order byte of the starting memory address is zero and the offset is zero, then the reading starts with the first byte of the EPROM. For example, to read only the last byte of the 2K EPROM and compare that with the data byte in memory at location $37FF, the command would be: . = 37FF 37FF 07

Any mismatch between the EPROM and corresponding memory data results in output to the screen of the mismatched memory location. The STOP key can be used to terminate the command early.

Commands for VIC Micromon

~ ~

Appendix F

Of the set of commands available on the PET version of Micromon, only two were removed in the conversion to the VIC. These were the K (Kill Micromon) and Z (change character sets) commands. The K command is not necessary since the VIC doesn't have the TIM monitor. The function of the Z command, which is to change character sets, is already provided for on the VIC by pressing the VIC shift and Commodore keys at the same time. The rest of the commands described for the PET Micromon (see elsewhere in this appendix) all apply identically to the commands for VIC Micromon, with the exception of the LOAD and SAVE commands, which have different formats.

VIC Micromon is always entered from VIC BASIC by a SYS 16384 when it resides at $4000 to $4FFF. Either the E (Exit VIC Micromon) or the X (Exit to BASIC) command would be used to exit VIC Micromon and return to the BASIC environment. The difference between these two commands is that the X command leaves the VIC Micromon vectors in the IRQ and BRK interrupt vector locations while in the BASIC environment. Also, the tape buffer is left defined as beginning at $375. Thus, certain IRQ interrupt conditions such as the moving of the cursor to the top or bottom of the screen with output from a D, M, or $ command displayed will cause scrolling and reentry into VIC Micromon. Also, if a BRK instruction is executed, VIC Micromon will be reentered via its BRK interrupt handler.

The E command restores the IRQ and BRK interrupt vectors and resets the tape buffer pointer to a value of $33C prior to exit to the VIC BASIC environment. Thus all active linkages and vectors to VIC Micromon are removed, and the VIC behaves as if VIC Micromon never existed. In particular, the E command should be used to exit VIC Micromon when the normal VIC cassette tape LOAD, SAVE, and VERIFY commands are to be used in the BASIC environment. Otherwise, invalid results are likely to occur with some tape operations.

Both the E and X commands expect the stack pointer value (as shown for SP by the Register display command) to be the same as when VIC Micromon was first entered via the BASIC SYS command. If the value of SP or the part of the stack pointed to by SP is overwritten, such as by the execution of faulty code, a clean exit to BASIC by the E and X commands is unlikely. However, both the E and X commands do check if BASIC has been initialized, and if not, exit to BASIC is via an indirect jump to the address given at location $C000. The address given in location $C000 is $E378, which is the entry to initialize BASIC. In this case, the value of SP and the contents of the stack aren't important. Once in BASIC and regardless of how the exit from VIC Micromon was made, any subsequent access to VIC Micromon at $4000 is always by a SYS16384.

Appendix F

VIC Micromon as given here is located from $4000 to $4FFF. It can be relocated to any 256 byte page boundary by making the changes, as shown in the following example, which relocate VIC Micromon from $4000 to $6000.

The example begins with VIC Micromon at $4000 and ends with a relocated VIC Micromon in RAM at $6000 as well as the original at $4000.

.T 4000 4FFF 6000 .N 6000 6003 2000 4000 4FFF .N 6012 6E6D 2000 4000 4FFF .N 6FB5 6FFE 2000 4000 4FFF W

In order to access the relocated VIC Micromon at $6000, exit using the E command and then from BASIC use SYS24576.

Cartridge And Checksum

The VIC-20 treats cartridge programs located at $A000 in a special way. On power-up, a test is made for the existence of the $A000 cartridge program, and if one exists, an indirect jump is made to the address specified at location $A000. This jump is made after the stack pointer is initialized, but before anything else is done. Because kernal initialization has not occurred, any cartridge program using kernal I/O routines must do kernal initialization before using those routines.

VIC Micromon as presented here has the kernal initialization calls built in so that it can easily be relocated and used as a cartridge program at $A000. Besides making the changes to relocate it to $A000, the only additional changes are to the first four bytes of VIC Micromon.

~ ~

Location

A000 A001 A002 A003

Contents

09 A0 C7 FE

Appendix F

Power-up with VIC Micromon installed as a cartridge at $A000 will result in immediate entry into VIC Micromon. Because BASIC is not initialized when the E or X command is used after power-up, the exit to BASIC will be via an indirect jump to the address given in location $C000, which is the entry to initialization of BASIC. Once in BASIC, subsequent access of VIC Micromon at $A000 must be made to location $A012, which is done via a SYS40978.

There is one last point, or rather one last byte, in VIC Micromon which is not used for anything other than to make the 4K byte checksum of VIC Micromon come out to a rounded up page value. For example, the VIC Micromon from $4000 to $4FFF has a data byte value of $E6 at location $4FFF that results in a checksum of $BF00. This provides an easy way to verify the integrity of VIC Micromon without having to memorize or look up a checksum.

Three Notes On VIC Micromon

Using the VIC Micromon tape commands L, S, and V on a VIC-20 with 3K of RAM installed at $400 to $FFF will result in overwrite of $400 to $438 with file header characters (blanks). This is due to the tape buffer being relocated to $375 while in VIC Micromon from the normal $33C. The normal VIC cassette commands will work properly and not overwrite this area when you EXIT from VIC Micromon. This is because VIC Micromon restores the tape buffer pointer value to $33C when an EXIT is done. This problem does not occur if the 3K RAM at $400 to $FFF is not installed.

If the I (Initialize memory and screen pointers) command was used in VIC Micromon and you EXIT, then the RUN/STOP plus RESTORE should be used in addition to the NEW command to clean up the BASIC environment.

Any binary image saved on cassette tape with the VIC Micromon "S" command can be loaded in the normal VIC-20 BASIC environment by using the command: LOAD"",1,1 which looks for the next program on tape and LOADs it into the same part of memory that it came from (see page 9 of VIC-20 Programmer's Reference Guide).

Checksum

There's a good amount of typing to do to enter the VIC Micromon program. Use the following BASIC program (after you've SAVEd a copy of your efforts) to locate any errors you might have made.

Program I. Micromon Checksum.

1 IFPEEK(20478)=6 7ANDPEEK(20479)=73THENRUN10

2 PRINT"VIC20 MICROMON LOAD Sc":PRINT"VERIFIC

ATION PROGRAM.":PRINT

Appendix F

PRINT:PRINT:PRINT M AT LEAST 4K BYTES OF":PR

INT"RAM MUST BE INSTALLED" PRINT"AT 16384 ($4000) ELSE":PRINT"LOAD WI

LL FAIL.":PRINT PRINT"IF LOADED & VERIFIED":PRINT n OK, MICR

OMON WILL BE":PRINT"ENTERED AUTOMATIC

ALLY." LOAD"'

DATA

40

100

110

120

130

140

150

160

15061,13 73 2,14507,13829,1

6 LOAD"",1,1

10 DATA 13328,16867

3 267,12747,16288,13920 20 DATA 14355,11977,11877,13583,11338,15173,1

2 337,14852,14051,15713 30 DATA 13442,15242,14746,15059,13134,15848,1 5858,17856,13327,8601 DATA 12171,10074

Q=16384

FOR BL0CK=1T032

FOR BYTE=0TO127

X=PEEK(Q+BYTE):CK=CK+X

NEXT BYTE

READ SUM

IF SUM <> CK THEN PRINT"ERROR IN BLOCK #"B

LOCK:ERR=1:GOTO170 165 PRINT"BLOCK #"BLOCK" OK" 170 CK=0:Q=Q+128 180 NEXT BLOCK

190 IFERR=1THENPRINT"L0AD FAILED":END 200 SYS16384

Program 2. VIC Micromon.

40F8 53 03 98 E5 FC A8 0D 53

4100 03 60 A9 00 F0 02 A9 01

4108 3D 57 03 20 CB 47 20 AE

4110 45 20 F0 40 20 21 48 90

4118 18 20 E7 40 90 7F 20 59

4120 41 E6 FD D0 02 E6 FE 20

4128 IF 49 AC 56 03 D0 6E F0

4130 E8 20 E7 40 18 AD 53 03

4138 65 FD 85 FD 98 65 FE 85

4140 FE 20 D5 40 20 59 41 20

4148 E7 40 B0 51 20 AE 40 20

4150 B2 40 AC 56 03 D0 46 F0

4158 EB A2 00 Al FB AC 57 03

4160 F0 02 81 FD CI FD F0 0B

4168 20 F8 47 20 38 49 20 El

4170 FF F0 2A 60 20 E6 47 20

4178 Al 49 F0 IE AE 56 03 D0

Appendix F

~

-

4388 03 20 68 41 20 IF 49 D0

4390 B7 A9 4C 48 A9 77 48 08

4398 48 48 48 6C 60 03 8D 4B

43A0 03 48 20 8C 48 20 00 49

43A8 D0 F8 68 49 FF 4C 72 42

43B0 20 2B 44 AE 56 03 D0 0D

43B8 20 F0 40 90 08 20 C8 43

43C0 20 El FF D0 EE 4C 0E 42

43C8 20 AE 45 A2 2E A9 3A 20

43D0 0E 48 20 38 49 20 F8 47

43D8 A9 08 20 EA 48 A9 08 20

43E0 AB 43 20 38 49 20 38 49

43E8 A9 12 20 D2 FF A0 08 A2

307

Appendix F

43F0 00 Al FB 29 7F C9 20 B0 43F8 02 A9 2E 20 D2 FF A9 00

4400 85 D4 EAEA EA EA EA EA 4408 EA EA EAEA 20 IF 49 88

~ -

~ ~

4578 03 F0 0D 68 68 EE 56 03

4600 03 68 8D 3C 03 AD 14 03 4608 8D 44 03 AD 15 03 8D 43 4610 03 BA 8E 42 03 58 AD 3E

Appendix F

4648 03 90 D2 AE 42 03 9A A9

4650 45 48 A9 BA 48 4C 06 47

4658 20 AE 45 20 14 49 8D 4B

4660 03 A0 00 20 F2 48 AD 3D

4668 03 AE 3C 03 85 FB 86 FC

4670 20 38 49 A9 24 8D 4E 03

4678 20 16 42 20 E4 FF F0 FB

47F0 20 42 48 85 FD 86 FE 60 47F8 A5 FC 20 FF 47 A5 FB 48

4800 4A 4A 4A 4A 20 17 48 AA

4808 68 29 0F 20 17 48 48 8A

4810 20 D2 FF 68 4C D2 FF 18

4818 69 F6 90 02 69 06 69 3A

4820 60 A2 02 B5 FA 48 B5 FC

4828 95 FA 68 95 FC CA D0 F3

4830 60 A9 00 8D 59 03 20 8C

4838 48 C9 20 F0 F9 20 6C 48

4840 B0 08 20 8C 48 20 57 48

4848 90 07 AA 20 57 48 90 01

4850 60 4C 60 40 20 74 41 A9

4858 00 8D 59 03 20 8C 48 C9

4860 20 D0 09 20 8C 48 C9 20

4868 D0 0F 18 60 20 81 48 0A

4870 0A 0A 0A 8D 59 03 20 8C

4878 48 20 81 48 0D 59 03 38

4880 60 C9 3A 08 29 0F 28 90

4888 02 69 08 60 20 A4 49 D0

4890 FA 4C 65 40 A9 91 A2 43

4898 8D 14 03 8E 15 03 60 20

48A0 A4 49 F0 37 20 E6 47 A5

48A8 FB 05 FC F0 22 A5 9A C9

48B0 03 D0 9E A5 FB 8D 93 02

4908 FB CI FB D0 69 20 IF 49

4910 CE 4B 03 60 A9 3E 85 FB

4918 A9 03 85 FC A9 05 60 E6

4920 FB D0 09 E6 FF E6 FC D0

4928 03 EE 56 03 60 98 48 20

4930 AE 45 68 A2 2E 20 0E 48

4938 A9 20 4C D2 FF 20 0E 48

4940 A2 00 BD 76 4F 20 D2 FF

4948 E8 E0 1C D0 F5 A0 3B 20

4950 2D 49 AD 3C 03 20 FF 47

4958 AD 3D 03 20 FF 47 20 38

4960 49 AD 43 03 20 FF 47 AD

4968 44 03 20 FF 47 20 14 49

4970 20 EA 48 4C 68 40 4C 60

4978 40 20 31 48 20 E3 48 20

4980 42 48 8D 44 03 8E 43 03

4988 20 14 49 8D 4B 03 20 8C

4990 48 20 00 49 D0 F8 F0 DB

4998 20 CF FF C9 20 F0 F9 D0

49A0 06 20 F0 47 20 CF FF C9

49A8 0D 60 A0 01 84 BA A9 00

49B0 A2 65 A0 03 20 BD FF A8

49B8 20 E6 47 AD 49 03 C9 53

49C0 D0 08 20 A4 49 F0 AF 20

49C8 EB 47 20 98 49 F0 29 C9

49D0 22 D0 A3 20 CF FF C9 22

49D8 F0 0B 91 BB E6 B7 C8 C0

49E0 51 90 F0 B0 91 20 A4 49

312

Appendix F

49E8 F0 0E 20 57 48 29 IF F0 49F0 85 85 BA 20 98 49 D0 D9 49F8 A9 00 85 B9 AD 49 03 C9

4A00 53 D0 0C A9 FB A6 FD A4 4A08 FE 20 D8 FF 4C 68 40 49 4A10 4C F0 02 A9 01 A6 FB A4 4A18 FC 20 D5 FF A5 90 29 10 4A20 F0 EA A9 69 A0 C3 20 IE 4A28 CB 4C 60 40 20 E6 47 20 4A30 A5 40 4C 68 40 20 E6 47 4A38 20 IF 49 20 IF 49 20 F0 4A40 47 20 38 49 20 F0 40 90 4A48 0A 98 D0 15 AD 53 03 30 4A50 10 10 08 C8 D0 0B AD 53 4A58 03 10 06 20 FF 47 4C 68 4A60 40 4C 60 40 20 E6 47 20 4A68 7A 4A 4C 68 40 20 AE 45 4A70 A2 2E A9 24 20 0E 48 20 4A78 F8 47 20 EA 4A 20 A0 4A

4A80 20 38 49 20 86 4A 20 89

4A88 4A 20 38 49 A2 04 A9 30

4A90 18 0E 54 03 2E 55 03 69

4A98 00 20 D2 FF CA D0 EF 60

4AA0 A5 FC A6 FB 8D 55 03 8E

4AA8 54 03 20 38 49 A5 FC 20

4AB0 B4 4A A5 FB AA 20 38 49

4AB8 8A 29 7F C9 20 08 B0 0A

4AC0 A9 12 20 D2 FF 8A 18 69

4AC8 40 AA 8A 20 D2 FF A9 00

4AD0 8 5 D4 EA EA EA EA EA EA

4AD8 EA EA EA EA 28 B0 C0 A9

4AE0 92 2C A9 14 2C A9 22 4C

4AE8 D2 FF 20 38 49 A6 FB A5

4AF0 FC 4C CD DD 20 05 4B B0

4AF8 41 20 38 49 20 F8 47 20

4B00 7D 4A 4C 68 40 A2 04 A9 4B08 00 85 FC 20 C2 4B 20 2B

Appendix F

4B10 4B 85 FB 20 22 4B 20 3D 4B18 4B CA D0 F7 08 20 38 49 4B20 28 60 20 A4 49 F0 0F C9 4B28 20 F0 0B C9 30 90 0B C9 4B30 3A B0 07 29 0F 60 68 68 4B38 18 60 4C 60 40 85 FE A5 4B40 FC 48 A5 FB 48 06 FB 26 4B48 FC 06 FB 26 FC 68 65 FB 4B50 85 FB 68 65 FC 85 FC 06 4B58 FB 26 FC A5 FE 65 FB 85 4B60 FB A9 00 65 FC 85 FC 60 4B68 20 C2 4B 8D 55 03 48 48 4B70 20 38 49 20 38 49 68 20 4B78 FF 47 20 38 49 68 AA A9

4B80 00 20 Fl 4A 20 38 49 20 4B88 86 4A 4C 68 40 20 9F 4B 4B90 20 38 49 20 F8 47 20 EA 4B98 4A 20 A0 4A 4C 68 40 A2 4BA0 0F A9 00 85 FB 85 FC 20 4BA8 C2 4B 20 2B 4B 20 BC 4B 4BB0 20 22 4B 20 BC 4B CA D0 4BB8 F7 4C 38 49 4A 26 FB 26 4BC0 FC 60 20 8C 48 C9 20 F0 4BC8 F9 60 20 54 48 8D 88 02 4BD0 A6 FB A4 FC 20 8A FE A6 4BD8 FD A4 FE 20 7B FE 20 18 4BE0 E5 20 A4 E3 4C 68 40 20 4BE8 F0 47 4C DB 47 20 E7 4B 4BF0 18 A5 FB 65 FD 85 FB A5 4BF8 FC 65 FE 85 FC 4C 0D 4C

4C00 20 E7 4B 20 F0 40 84 FC 4C08 AD 53 03 85 FB 20 38 49 4C10 20 F8 47 4C 68 40 A9 F0 4C18 2C A9 00 8D 0B 90 4C 65 4C20 40 78 20 52 FD 58 A9 3C 4C28 85 B2 AE 42 03 9A A5 73 4C30 C9 E6 F0 95 6C 00 C0 20 4C38 E7 4B 20 21 48 20 38 49 4C40 A0 00 8C 54 03 8C 55 03

Appendix F

4C48 20 F0 40 90 IB AC 56 03

4C50 D0 16 18 Bl FB 6D 54 03

4C58 8D 54 03 98 6D 55 03 8D

4C60 55 03 20 IF 49 4C 48 4C

4C68 AD 55 03 20 FF 47 AD 54

4C70 03 20 FF 47 4C 68 40 AD

4C78 64 03 D0 04 A5 C6 D0 03

4C80 4C 56 FF AD 77 02 C9 11

4C88 D0 7D A5 D6 C9 16 D0 F0

4C90 A5 Dl 85 FD A5 D2 85 FE

4C98 A9 17 8D 5E 03 A0 01 20

4CA0 51 4E C9 3A F0 1A C9 2C

4CA8 F0 16 C9 24 F0 12 CE 5E

4CB0 03 F0 CD 38 A5 FD E9 16

4CB8 85 FD B0 El C6 FE D0 DD

4CC0 8D 49 03 20 0A 4E B0 B8

4CC8 AD 49 03 C9 3A D0 11 18

4CD0 A5 FB 69 08 85 FB 90 02

4CD8 E6 FC 20 C8 43 4C F4 4C

4CE0 C9 24 F0 1A 20 C9 4D 20

4CE8 6F 42 A9 00 8D 4E 03 A0

4CF0 2C 20 13 42 A9 00 85 C6

4CF8 4C 0E 42 4C 56 FF 20 IF

4D00 49 20 6D 4A 4C F4 4C C9

4D08 91 D0 F0 A5 D6 D0 EC A5

4D10 Dl 85 FD A5 D2 85 FE A9

4D18 17 8D 5E 03 A0 01 20 51

4D20 4E C9 3A F0 1A C9 2C F0

4D28 16 C9 24 F0 12 CE 5E 03

4D30 F0 15 18 A5 FD 69 16 85

4D38 FD 90 El E6 FE D0 DD 8D

4D40 49 03 20 0A 4E 90 03 4C

4D48 56 FF AD 49 03 C9 3A F0

4D50 06 C9 24 F0 ID D0 27 20

4D58 D0 4D 38 A5 FB E9 08 85

4D60 FB B0 02 C6 FC 20 CB 43

4D68 A9 00 85 C6 20 05 4E 4C

4D70 70 40 20 D0 4D 20 B2 40

4D78 20 70 4A 4C 68 4D 20 D0

Appendix F

4D80 4D A5 FB A6 FC 85 FD 86

4D88 FE A9 10 8D 5E 03 38 A5

4D90 FD ED 5E 03 85 FB A5 FE

4D98 E9 00 85 FC 20 C9 4D 20

4DA0 6F 42 20 F0 40 F0 07 B0

4DA8 F3 CE 5E 03 D0 E0 EE 4D

4DB0 03 AD 4D 03 20 AB 43 A2

4DB8 00 Al FB 8E 4E 03 A9 2C

4DC0 20 33 49 20 16 42 4C 68

4DC8 4D A2 00 Al FB 4C 87 42

4DD0 A6 D2 20 D7 4D A6 F4 E8

4DD8 86 AD 86 FE A2 00 86 AC

4DE0 A9 2C 85 FD A0 CE E8 88

4DE8 Bl AC 91 FD 98 D0 F8 C6

4DF0 AD C6 FE CA 10 Fl A9 20

4DF8 A6 D2 86 FE 84 FD A0 2B

4E00 91 FD 88 10 FB A9 13 4C

4E08 D2 FF C0 16 D0 02 38 60

4E10 20 51 4E C9 20 F0 F3 88

4E18 20 3A 4E AA 20 3A 4E 85

4E20 FB 86 FC A9 FF 8D 64 03

4E28 85 CC A5 CF F0 0A A5 CE

4E30 A4 D3 91 Dl A9 00 85 CF

4E38 18 60 20 51 4E 20 81 48

4E40 0A 0A 0A 0A 8D 59 03 20

4E48 51 4E 20 81 48 0D 59 03

4E50 60 Bl FD C8 29 7F C9 20

4E58 B0 02 09 40 60 BD 98 4D

4E60 20 D2 FF E8 D0 F7 60 00

4E68 00 00 00 00 00 00 0D 56

4E70 49 43 32 30 20 4D 49 43

4E78 52 4F 4D 4F 4E 20 56 31

4E80 2E 32 20 20 20 42 49 4C 4E88 4C 20 59 45 45 20 32 32 4E90 20 4A 414E 20 20 38 33 4E98 40 02 45 03 D0 08 40 09 4EA0 30 22 45 33 D0 08 40 09 4EA8 40 02 45 33 D0 08 40 09 4EB0 40 02 45 B3 D0 08 40 09

Appendix F

4EF8 1C 23 5D 8B IB Al 9D 8A

4F00 ID 23 9D 8B ID Al 00 29

4FC0 41 87 41 A4 46 A0 41 AA

Appendix F

4FC8 49 B0 43 3C 47 A8 46 40 4FD0 49 16 4C 06 41 B8 46 2A 4FD8 4C 0C 43 15 44 79 49 64 4FE0 4A F4 4A 68 4B ED 4B 00 4FE8 4C 35 4A CA 4B 2C 4A 8D 4FF0 4B 37 4C 21 4C AA 49 19 4FF8 4C 40 43 40 43 40 43 49

~

Appendix F

Supermon64

Supermon64 is your gateway to machine language programming on the Commodore 64. Supermon, in several versions, has been popular over the years as a major programming tool for Commodore users. Supermon64 itself is in machine language, but you can type it in without knowing what it means. Using the Tiny PEEKer/POKEr (Program 1), or via the built-in monitor of a PET, type it in and SAVE it. The fastest way to check for errors is to type in Program 3 on a regular PET. Then load Supermon64 into the PET. It will come in above your BASIC. Then RUN the checksum and it will report the location of any errors. Type POKE 8192,0 and hit RETURN. Then type POKE 44,32 followed by NEW. Enter the following:

Program I. Tiny PEEKer/POKEr.

100 PRINT "TINY PEEKER/POKER"

110 X$="*":INPUT X$:IF X$="*" THEN END

120 GOSUB 500

130 IF E GOTO 280

140 A=V

150 IF J>LEN(X$) GOTO 300

160 FOR 1=0 TO 7

170 P=J:GOSUB 550

180 C(l)=V

190 IF E GOTO 280

2 00 NEXT I

210 T=0

220 FOR 1=0 TO 7

2 30 POKE A+I,C(l)

2 40 T=T+C(I)

2 50 NEXT I

2 60 PRINT "CHECKSUM=";T 270 GOTO 110

280 PRINT MID$(X$,1,J);"??":GOTO 110

300 T=0

310 FOR 1=0 TO 7

320 V=PEEK(A+I)

3 30 T=T+V 340 V=V/16

3 50 PRINT " "; 360 FOR J=l TO 2 370 V%=V

Appendix F

380 V=(V-V%)*16

390 IF V%>9 THEN V%=V%+7

400 PRINT CHR$(V%+48) ;

410 NEXT J

420 NEXT I

430 PRINT "/";T

440 GOTO 110

500 P=l

510 L=4

5 20 GOTO 600

5 50 P=J 560 L=2 600 E=0 610 V=0

620 FOR J=P TO LEN(X$) 630 X=ASC(MID$(X$,J) ) 640 IF X=32 THEN NEXT J 650 IF J>LEN(X$) GOTO 790 660 P=J

670 FOR J=P TO LEN(X$) 680 X=ASC(MID$(X$,J))

6 90 IF X<>32 THEN NEXT J 700 IF J-P<>L GOTO 790 710 FOR K=P TO J-l

720 X=ASC(MID$(X$,K)) 730 IF X<58 THEN X=X-48 740 IF X>64 THEN X=X-55 750 IF X<0 OR X>15 GOTO 790 760 V=V*16+X

7 70 NEXT K 7 80 RETURN 7 90 E=-l 800 RETURN

This program is a very tiny monitor. It will allow you to enter information into memory, eight bytes at a time. To do this: wait for the question mark, and then type in monitor-format the address and contents:

7 0800 00 1A 08 64 00 99 22 93

The program will return a checksum value to you, which you can use to insure that you have entered the information correctly. To view memory, type in only the address: the contents will be displayed.

Appendix F

Completing The Job

When you have finished entering all that data, you can make Supermon64 happen quite easily. Three last POKE commands and a CLR:

POKE 44,8 POKE 45, 235 POKE 46,17 CLR

You have Supermon64. Save it with a conventional BASIC SAVE before you do anything else.

Now you may RUN it — and learn how to use it.

NOTE: Before entering the hex numbers with Tiny PEEKer/POKEr, type in the memory partitioning POKES: POKE 8192,0 and POKE 44,32, and then type NEW. When you've finished entering all the hex numbers, type: POKE 44,8: POKE 46,17: CLR. You can then SAVE Supermon64 in the ordinary, BASIC waij, to tape or disk. It's ready now to LOAD or RUN. Note also that the checksum program on page 333 checks 129 bytes at a time. This can have the effect of attributing a typing error to the wrong block if the error occurs near the beginning or the end of a block.

• Simple assembler

.A 2000 LDA #$12

.A 2002 STA $8000,X

•A 2005 (RETURN)

In the above example the user started assembly at 2000 hex. The first instruction was load a register with immediate 12 hex. In the second line the user did not need to type the A and address. The simple assembler prompts with the next address. To exit the assembler type a return after the address prompt. Syntax is the same as the disassembler output.

• Disassembler

.D 2000

(SCREEN CLEARS)

2000 A9 12 LDA #$12

Appendix F

2002 9D 00 80 STA $8000,X

2005 AA TAX

2006 AA TAX

(Full page of Instructions)

Disassembles 22 instructions starting at 2000 hex. The three bytes following the address may be modified. Use the CRSR keys to move to and modify the bytes. Hit return and the bytes in memory will be changed. Supermon64 will then disassemble that page again.

• Printing disassembler

.P 2000,2040

2000 A9 12 LDA #$12

2002 9D 00 80 STA $8000,X

2005 AA TAX

203F A2 00 LDX #$00

To engage printer, set up beforehand: OPEN4,4:CMD4

• Fill memory

.F 1000 1100 FF

Fills the memory from 1000 hex to 1100 hex with the byte FF hex.

• Go run

.G

Go to the address in the PC register display and begin RUN code. All the registers will be replaced with the displayed values.

.G 1000

Go to address 1000 hex and begin running code.

• Hunt memory

.H C000 D000 "READ

Hunt through memory from C000 hex to D000 hex for the ASCII string read and print the address where it is found. A maximum of 32 characters may be used.

~

Appendix F

.H C000 D000 20 D2 FF

Hunt through memory from C000 hex to D000 hex for the sequence of bytes 20 D2 FF and print the address. A maximum of 32 bytes may be used.

• Load

.L

Load any program from cassette #1.

.L "RAM TEST"

Load from cassette #1 the program named RAM TEST.

•L "RAM TEST",08

Load from disk (device 8) the program named RAM TEST. This command leaves BASIC pointers unchanged.

• Memory display

.M 0000 0080

..* 0000 00 01 02 03 04 05 06 07

.: 0008 08 09 0A 0B 0C 0D 0E 0F

Display memory from 0000 hex to 0080 hex. The bytes following the .: can be altered by typing over them, then typing a return.

• Register display

.R

PC IRQ SR AC XR YR SP 0000 E62E 01 02 03 04 05

Displays the register values saved when Supermon64 was entered. The values may be changed with the edit followed by a return.

• Save

.S "PROGRAM NAME" ,01,0800, 0C80

SAVE to cassette #1 memory from 0800 hex up to but not including 0C80 hex and name it PROGRAM NAME.

Appendix F

.S "0:PROGRAM NAME",08,1200,1F50

SAVE to disk drive #0 memory from 1200 hex up to but not including 1F50 hex and name it PROGRAM NAME.

• Transfer memory

,T 1000 1100 5000

Transfer memory in the range 1000 hex to 1100 hex and start storing it at address 5000 hex.

• Exit to BASIC

• X

Return to BASIC ready mode. The stack value SAVEd when entered will be restored. Care should be taken that this value is the same as when the monitor was entered. A CLR in BASIC will fix any stack problems.

Program 2. Supermon64.

Appendix F

0880 A5 2D 85 22 A5 2E 85 23

0888 A5 37 85 24 A5 38 85 25

0890 A0 00 A5 22 D0 02 C6 23

0898 C6 22 Bl 22 D0 3C A5 22

08A0 D0 02 C6 23 C6 22 Bl 22

08A8 F0 21 85 26 A5 22 D0 02

08B0 C6 23 C6 22 Bl 22 18 65

08B8 24 AA A5 26 65 25 48 A5

08C0 37 D0 02 C6 38 C6 37 68

08C8 91 37 8A 48 A5 37 D0 02

08D0 C6 38 C6 37 68 91 37 18

08D8 90 B6 C9 4F D0 ED A5 37

08E0 85 33 A5 38 85 34 6C 37

08E8 00 4F 4F 4F 4F AD E6 FF

08F0 00 8D 16 03 AD E7 FF 00

08F8 8D 17 03 A9 80 20 90 FF

1978 8A 0A AA BD C7 FF 00 48

09F8 A0 3B 20 C2 F8 00 AD 39

0A80 D0 F8 4C 47 F8 00 20 CF 0A88 FF C9 0D F0 0C C9 20 D0 0A90 Dl 20 79 FA 00 90 03 20

Appendix F

0A98 80 F8 00 A9 90 20 D2 FF

0AA0 AE 3F 02 9A 78 AD 39 02

0AA8 48 AD 3A 02 48 AD 3B 02

0AB0 48 AD 3C 02 AE 3D 02 AC

0AB8 3E 02 40 A9 90 20 D2 FF

0AC0 AE 3F 02 9A 6C 02 A0 A0

0AC8 01 84 BA 84 B9 88 84 B7

0AD0 84 90 84 93 A9 40 85 BB

0AD8 A9 02 85 BC 20 CF FF C9

0AE0 20 F0 F9 C9 0D F0 38 C9

0AE8 22 D0 14 20 CF FF C9 22

0AF0 F0 10 C9 0D F0 29 91 BB

0AF8 E6 B7 C8 C0 10 D0 EC 4C

IB00 ED FA 00 20 CF FF C9 0D

0BA8 06 60 A2 02 B5 C0 48 B5

Appendix F

Appendix F

iF70 03 68 38 E9 3F A0 05 4A 'F78 6E 11 02 6E 10 02 88 D0

0F80 F6 CA D0 ED A2 02 20 CF

0F88 FF C9 0D F0 IE C9 20 F0

0F90 F5 20 D0 FE 00 B0 0F 20

0F98 9C FA 00 A4 CI 84 C2 85

0FA0 CI A9 30 9D 10 02 E8 9D

0FD8 A2 06 E0 03 D0 19 A4 IF

0FE0 F0 15 A5 2A C9 E8 A9 30

0FE8 B0 21 20 BF FE 00 D0 CC

0FF0 20 CI FE 00 D0 C7 88 D0

0FF8 EB 06 2A 90 0B BC 30 FF

1000 00 BD 2A FF 00 20 B9 FE 1008 00 D0 B5 CA D0 Dl Fb 0A

1068 20 C2 F8 00 20 54 FD 00

Appendix F

Program 3. Supermon64 Checksum.

100 REM SUPERMON64 CHECKSUM PROGRAM 110 DATA 10170,13676,15404,14997,15136,

16221,16696,12816,16228,14554 120 DATA14677,15039,14551,15104,15522,

16414,15914,8958,11945 :S=2048 130 F0RB=1T019:READX:FORI=STOS+128:M=P

EEK(l) :Y=Y+N 140 NEXTI:IFY<>XTHENPRINT"ERROR IN

BLOCK #"B:GOTO160 150 PRINT"BLOCK #"B" IS CORRECT" 160 S=I:Y=0:NEXTB:REM CHECK LAST SHORT

BLOCK BY HAND

Appendix G

The Wedge

One of the best reasons to learn machine language is that it can improve your BASIC programming significantly. There are two main ways that machine language can assist BASIC programming: adding commands to BASIC itself and replacing parts of a BASIC program with a high-velocity machine language subroutine. To add an ML subroutine to a BASIC program, you SYS, USR, or CALL (from Microsoft, Atari, or Apple BASICs respectively). That's fairly straightforward. To make changes to the BASIC language itself, however, we need to wedge into BASIC somehow.

You can make BASIC a customized language with a wedge. Do you want auto-numbering when writing a program in BASIC? Add it. Does your BASIC lack a RENUMBER facility? You can give it one. Do you want all your BASIC programs to contain a REM line with your name in it? This could be automatically put into each of your programs if you know machine language. Using a wedge to a machine language program, you can communicate directly to your machine, bypass BASIC'S limitations, and do pretty much what you want to do.

How To Wedge In

Adding commands to BASIC is a matter of interrupting a loop. This is often referred to as adding a wedge into BASIC. Under the control of the BASIC language, the computer is looking to see if a BASIC word has been typed in, followed by a hit on the RETURN key. Or, during a RUN, the computer examines the program in memory to see what you want accomplished.

These, then, are the two contexts in which the computer analyzes a BASIC word: in a program or in "direct mode." In direct mode, you can type the word "LIST" onto the screen and hit the RETURN key. The computer looks up the meaning of "LIST" in a table of words which includes the addresses of the appropriate ML subroutines. It then JSR's (Jumps to a SubRoutine) somewhere in the vast ML of your computer's BASIC. This subroutine performs the actions necessary to provide you with a listing of the program in your computer's memory. If you could add some additional words to this table, you could add to BASIC. You could customize it.

Here's how. When you first turn on a computer which uses Microsoft BASIC, one of the first things that happens is that the operating system puts some important ML instructions into a zone in

Appendix G

the first 256 memory locations (this area of RAM is called zero page).

These instructions are put into zero page to handle the loop — often

called the CHRGET loop (which means "character get") — where the

operating system will forever after jump while power is on. This

location is of great importance to BASIC; it is the "did they type any

BASIC into the computer?" subroutine. It's where BASIC analyzes

what it finds on screen or in a program, looking at something

character by character to see what it adds up to. •^

If you type "LIST," this little zero page ML subroutine looks at the "L" then the "I" and so on. The exact location of CHRGET differs on the various computers:

PET (Original BASIC): decimal address 194-217

PET/CBM (Upgrade & 4.0): 112-135

VIC: 115-138 ^

64: 115-138

Apple: 177-200 ^

The CHRGET ML program looks like this:

0070 E6 77 INC $77

0072 DO 02 BNE $0076 0074 E6 78 INC $78 0076 AD 03 02 LDA $0203 0079 C9 3A CMP #$3A 007B B0 0A BCS $0087

007D C9 20 CMP #$20 a

007F F0 EF BEQ $0070

0081 38 SEC —>.

0082 E9 30 SBC #$30

0084 38 SEC -"

0085 E9 DO SBC #$D0 0087 60 RTS

This is put into your zero page RAM within the first few seconds after you turn on the computer. You can change it (RAM memory can be changed) to jump (JMP) to your own ML program by replacing the first three bytes of code. In our example above, we will replace the three bytes at hexadecimal location 0070 (the exact address will vary according to the CHRGET location as listed above for the different computers). Here is how the replacement looks in the example CHRGET routine:

0070 4C 00 75 JMP $7500

0073 02 ???

Appendix G

The effect that this has is dramatic. Whenever the computer looks for a character in BASIC mode, it will jump first (because you forced it to) to your personal ML "wedged" routine located at $7500. The subroutine at $7500 could be anything you wanted it to be, anything you've put at address $7500. For an example, we've caused an "A" to appear on the PET/CBM screen:

Notice that we had to first perform the actions that the CHRGET would have performed. Before we can start our LDA #$41 to put an "A" on screen, we had to replace the early part of CHRGET that we wrote over (see 7500 to 7505 in Example 3). And, after we're done with our custom routine, we jump back into CHRGET at 750B.

Adding a wedge to Atari BASIC is somewhat more involved. A clear and complete exposition of the techniques involved appears in an article by my colleague Charles Brannon, "The Atari Wedge" (COMPUTE! Magazine, November 1982).

A

A or AC register (see Accumulator)

Absolute addressing 25, 40-42, 45, 46, 48, 51, 56, 68, 69, 75, 81

Absolute, X and Absoute, Y addressing 48, 51, 68, 69, 75, 81

Accumulator 19, 26, 31, 33, 39, 56, 66

Accumulator mode 51

ADC 20, 56, 58, 68, 149

Addresses 1, 2, 19, 20, 47, 54, 77, 85, 99, 124, 127, 128, 130, 139, 140,

146

get a character address 1

last key pressed 77

safe place address 1, 2

start of RAM 1,99

start print address 1

which key is pressed? 1, 54, 127, 128 Addressing 18^ 22, 40 Addressing modes 12, 33-34, 37-51, 68, 69, 75, 81, 149-166, 223, 224

Absolute 25, 40-42, 45, 46, 48, 51, 56, 68, 69, 75, 81

Absolute, X and Absolute, Y 48, 51, 68, 69, 75, 81

Accumulator mode 51

Immediate 25, 33, 34, 43, 51, 66, 68, 69

Implied 43-45, 55, 81

Indirect Indexed 74, 125, 141

Indirect X 51, 68,69

Indirect Y 42, 49, 51, 57, 58, 69, 70, 74, 77, 85

Relative 25, 45-47, 69

Zero Page 33, 34, 42-43, 51, 55, 65, 68, 69, 75

Zero Page, X 48, 68, 69, 75

Zero Page, Y 51 "Alphabetic" mode 54 AND 39, 88, 89, 149 Arcade game programming in ML vi Argument viii, 40, 55, 69, 70, 77, 81, 223, 224 ASCII code 3, 9, 53, 70, 78, 131, 144 ASL 51, 59, 68, 89, 149 ASM mode (Atari monitor) 27, 28, 110 Assembler vii, 2, 35, 45, 46, 61, 140, 223

assembler program 18

traditional conventions, list of 224

two-pass assemblers 72, 223, 225

339

Assembler Editor (Atari) 23, 26, 28,110,130, 143

Assembly language vii (see machine language)

Assignment of value (see LET)

Atari monitor (see Assembler Editor; DEBUG)

Atari source code 143

ATASCII3, 144

Attract mode 124

Auto-booting 125

B ~

BASIC v-vi, vii-xii, 1-4, 7, 19, et passim advantages of xii commands vii, 63, 121-147

ASC 144

CHR$ 144 ~

CLR 121-22

CONT 86, 122 —

DATA xii, 122-23, 140

DIM 123 ~

END 63,124-25

FOR-NEXT 125-26

FOR-NEXT-STEP 126-27

GET 40, 93, 127-28, 131 —

GOSUB 81, 128-29, 141, 142

GOTO 18, 84, 85, 129-30 <~>

IF-THEN 69, 71, 131

INPUT 131-32, 133 ~

LEFTS 144,145

LEN145 —

LET 132-34

LIST xi, 134 —

LOAD 30, 92, 134-35

MID$ 145 ^

NEW 121, 135-36

ON GOSUB 71, 136. 137 _

ON GOTO 69, 71, 74, 137

PRINT x, 40, 137-40

READ 140

REM 140

RETURN 5, 131, 141

RIGHTS 145-46

RUN 141-43 ~,

SAVE 30, 110, 143

SPC 146-47

STOP 122, 124, 143

340

TAB 146, 147

loaders 19

Microsoft BASIC vii, 2, 4, 17, 91, 93, 105, 135, 141, 144, 224, 335

words xi BCC45, 59, 61, 69, 71, 74, 75, 131, 150 BCS 45, 59, 61, 69, 71, 74, 75, 150 BEQ 25, 45, 47, 59, 69, 71, 77, 131, 151 Binary numbers 7, 8, 9, 15, 243-50

program for printing table of 16 BIT 89, 151

Bits and bytes 8, 9,10, 12-15 BMI 45, 59, 61, 68, 71, 74, 75, 151 BNE 24, 45, 59, 61, 69, 71, 72, 74, 75, 77, 131, 151 BPL 24, 45, 59, 61, 68, 71, 74, 75, 152 Branch address 47 Branches:

ON-GOTO 74

forward 78 Branching instructions 25, 45, 46, 47, 59, 67, 68, 69, 71-72, 73, 88 BRANCHTARGET 72, 74 Breakpoints 86, 87,143

BRK 29, 30, 34, 37, 45, 61, 67, 86-87, 90, 122, 124, 134, 143, 152 Buffer 42, 98 BUG 28, 142 Bugs 31, 33-34 BVC45, 63, 68, 71, 152 BVS 45, 68, 71, 152

CALL instruction viii, x, xi, 23, 65, 141

Carriage return 54, 144

Carry flag 37, 39, 45, 56, 58, 68, 69

Cassette buffer 1

CHRGET loop 336

CHRGET ML program 336

Circumflex 26, 223

CLC 43, 56, 58, 68, 109, 153

CLD 43, 56, 68, 153

CLI89, 153

CLV 63, 153

CMP instruction 8, 33, 34, 61, 69, 70, 71, 75, 77, 89, 127, 128, 131, 136,

145, 154 Code 53 "Cold start" address 124

341

Comma, use of 79, 223-24 Commands:

BASIC 63, 121-47

machine language 63, 64-90,149-66 Commodore character codes 144 Commodore Monitor Extension 26 Comparisons 70 Compiled code 92 Compilers 92-93 Conditional branch 129,131 Control characters 273, 276 Counter variable 125 Counters 125-26

CPU (central processing unit) 8, 18, 37, 39 CPX 70,154 CPY 70,154 Cursor address 77 Cursor controls (PET) 30 Cursor management 77 Cursor position 140, 147

Data table 31,121, 225

Debug xii

DEBUG (Atari monitor mode) 26, 28, 29,142, 143

commands in 28-30 Debugger 23, 36 Debugging 86

methods 87-88 DEC 75,155 Decimal address 19 Decimal flag 56

Decimal numbers 8, 9, 10, 14, 16, 243-50 Default 92

Delay loop 83-84, 125-26,129 Delimiter 78,138,145,146 DEX 45, 75, 84,155 DEY 45, 75,155 Dimensioned memory 123 Direct mode 4, 335 Disassembler viii, 20, 134 Disassembly viii, ix, 20,140 Disassembly listings 25 Dollar sign ($) 10,12, 28, 223 Do-nothing loop (see Delay loop)

342

DOS (disk operating system) xi, 135 Double compare 59

Echo 131

EDIT mode (Atari monitor) 27, 28, 30,143

Effective address 85

END 4 (see Pseudo-ops)

Endless loop 33, 54, 74, 76, 124,125,145

EOR 39, 88, 156

Equates 72

Error messages 26, 48, 77

Fields 25, 27

Filename 143

FILL instruction 67

"Fill memory" option 135

FIND command (Atari) 110-118

Flags 8, 12, 31, 37, 39, 45.. 56, 61, 66-67, 68, 69, 77, 131

B flag 68, 86

C or Carry flag 37, 39, 45, 56, 58, 68, 69

D flag 68

I flag 68, 86

interrupt flag 89

N or Negative flag 64, 66, 68, 69, 75, 89

status register flags 68, 131

V or overthrow flag 45, 68, 89

Z or Zero flag 39, 64, 66, 67, 68, 69, 71, 75, 77, 78, 89, 130, 131,138, 145

Floating point accumulator 141

Floating point arithmetic 59, 142

G

GET* 93

H

Hexadecimal numbers viii, 1-2, 7, 8, 9, 10, 11-12, 16-17, 45, 243-50

conventions of 12 Hex dump 19, 20, 24

343

I

Immediate addressing 25, 33, 34, 43, 51, 66, 68, 69

Immediate mode 141

Implied addressing 43-45, 55, 81

INC 75, 156

Indirect-indexed addressing 74,125,141

Indirect jump 85, 224

Indirect X addressing 51, 68, 69

Indirect Y addressing 42, 49, 51, 57, 68, 69, 70, 74, 77, 85

Initialization routine 121

"Instant action" keys 54

Instruction field (see Fields)

INT mode 25

Interactivity 34-35

Interpreter 125, 133

Interrupt request 31, 86

Interrupts:

maskable 89

non-maskable 90 INX 45, 55, 75, 156 INY 45, 75, 157 IRQ 31, 37, 39, 86

JMP instruction 18, 24, 34, 81, 82, 84-85, 91,128, 129, 130, 157 JSR 24, 25, 45, 67, 71, 72, 80, 81, 82, 91, 92, 124, 129, 130, 136,141,143, 157

K

Kernal 91

Kernal jump table 91, 92, 94, 128

Label table 72 Languages vii, xi

FORTH 82

(see also BASIC; Machine language) LDA 20, 25, 26, 29, 33, 39, 40, 43, 45, 48, 55, 61, 63, 64, 66, 69, 71, 158 LDX 51, 64, 66, 158 LDY 33, 34, 64, 66, 159 Loaders ix-x

344

Loops 67, 75-84, 125-28, 335

delay 83-84, 125-26,129

endless 33, 54, 74, 76, 124, 125,145

FOR-NEXT 46, 47, 75, 125-26

indexed 48

nested 76, 127

timing 76 LSB (Least Significant Byte) 49, 51, 58, 70, 85, 126, 139,141, 243-50 LSR 51, 59, 68, 89, 159

M

Machine language (ML)

advantages of viii, xi

equivalents of BASIC commands 121-47

INPUT subroutine 131-32

instruction groups 64-90 arithmetic 39, 68-69 debuggers 86-90 decision-makers 69-75 loop 75-81

subroutine and jump 81-86 transporters 64-68

instructions vii, 121-47, 149-66

monitor 253, 269-333

strings 77-80, 144-47

subroutines 31, 91-96 Maps 42

Atari Memory Map 205

Commodore 64 Memory Map 193-204

PET/CBM 4.0 BASIC ROM Routines 175-80

PET/CBM 4.0 BASIC. Zero Page 172-75

PET Original and Upgrade BASIC 167-69

Upgrade PET/CBM 169-72

VIC Zero Page and BASIC ROMs 181-92 Masking 88-89 Mass-move 80 Memory addresses 1, 2, 20 Memory dump, 24, 28-29, 30, 275 Memory map 1 (see also Maps) Memory mapped video 70 Memory zones 133 Message table 138 Message zone 77 Micromon 23, 31, 130, 269-333

VIC Micromon 296-318

345

Mnemonics 18, 20, 149-66 «^

Modes:

BASIC mode 142 —

monitor mode 19, 26,142, 143

(see also Addressing modes) -^

Monitor 18, 22, 23-37

Apple II monitor 23-26, 143 a

Atari monitor 26-28 (see also ASM; Assembler Editor; Debugger)

interactive monitors 34-35 ~

monitor extensions 253-334

PET, VIC, and Commodore 64 monitor 30 ,^

"resident" monitor 30, 253

(see also Micromon; Supermon) -,

"Move it" routine 130 MSB (Most Significant Byte) 49, 51, 58, 70, 85, 99, 126,138, 139, 141,

243-50 Multiple branch test 136

N

Natural numbers 7 NOP 45, 86,169

uses of 87-88 Number tables (hex, binary, decimal) 243-50

Object code 18, 22, 28, 47, 225, 226 Opcode 18, 20, 55, 66, 223, 224 Operand 55 (see Argument) ORA 160 OS (operating system) 42

Page 33

page one 42

page six 68, 110

page zero 33, 42, 51, 56, 57, 98,139,140, 336 Parameters 67, 83,128,141 PET ASCII 3 PHA 45, 67, 81, 82,160 PHP 45, 67,160 PLA 3, 4, 5, 45, 67, 82, 161 PLP 45, 67,161

346

Pointers 49, 51, 57, 98, 109

zero page 77 Pound sign (#) 25, 43, 55 Powers of a number 7-9 PRINT routines 140 Program counter 37, 39, 55, 65, 141 Programs:

Adding the Conversion Pseudo-op 226

Apple Version (of Search BASIC Loader) 119-20

Atari Disassembler 240-42

Atari Hex-Decimal Converter 11

BASIC Loader 19

Binary Quiz for All Computers 15-16

CHRGET ML program 336

Decimal to Hex, Microsoft BASIC 17

Disassembler 237-40

Double Compare 60

FIND Utility for Atari BASIC 112-18

for printing out table of binary numbers 16

Full Assembly Listing 21

Labelled Assembly 21

Micromon 269-333

Microsoft Hex-Decimal Converter 10-11

Microsoft Table Printer 250-51

PET Search (4.0 BASIC Version) 100-104

Simple Assembler 227-36 Atari Version 231-36 VIC, PET, Apple, 64 Version 227-30

64 Search BASIC Loader 119

The Source Code by Itself 22

Supermon 253-68

Supermon64 319-33

VIC Micromon 303-18

VIC-20 Search BASIC Loader 120 Prompts 31

Pseudo-ops 4, 27, 28, 29, 47, 224-26 PUT#6 93

RAM (Random Access Memory) viii, xi, 1, 2, 4, 9, 12, 19, 31, 33, 37,

42, 80, 97, 98, 225 Reference sources 221 Registers 26, 28, 30-31, 57, 66, 70, 82 Relative addressing 25, 45-47, 69 REM statements xii, 20 "Resolving" labels 225

347

ROL51, 89, 161

ROM (Read Only Memory) xi, 1, 12, 23, 25, 26,128, 253

ROR 51, 63, 89,162

RTI 63, 89, 90, 162

RTS 20. 25, 45, 67, 81, 124,129, 136,141, 143, 162

Safe areas 2-3, 42, 68, 97-98, 99, 105

SBC 61, 68, 163

Screen address 139,140

Screen position (see STA)

Search bloader 119-20

Search routine 88

SEC 43, 58, 61, 68, 163

SED 43, 56, 63,163

SEI 89-90,164

SGN command 63

Simple Assembler 1, 2, 3, 4, 5, 10, 17, 22, 25, 26, 27, 28, 35, 43, 47, 57,

73, 74, 79, 223-36 -*

Single-stepping 87

6502 machine language (sec Machine language) ■"*

Softkey 132

Source code 18,19, 22, 28, 49, 72, 225 —>

Source program 140, 225

Spaces, important 224 -»

STA 20, 40, 49, 51, 55, 56, 57, 63, 64, 65, 67, 164

Stack 42, 67-68, 81-83,141 a

Stack pointers 26, 28, 37, 39

Status Register 8, 26, 28, 31, 39, 56, 66, 68, 82 ^

Step 26, 29-30, 31, 126-27 >

String handling 77-80,144-47 ^

Structured programming 85

STX 51, 64, 67, 164 m

STY 64, 67, 165

Subroutines 31, 91-96 ^

Supermon 23, 31, 130, 253-68

Supermon64 319-333 ^

Symbols 53 SYS instruction v, viii, x, xi, 19, 25, 30, 65, 124, 141

TAN command 63

Target address 130,139,146

TAX 64, 66, 165

348

TAY 64, 66, 165

TIM (terminal interface monitor) 142, 270, 271, 272, 275

Toggle 88-89

Trace 26, 29, 31

TRACE 87

Transfer 130

"Truth tables" 89

TSX67, 165

Two-pass assemblers 72, 223, 225

TXA 43, 45, 55, 64, 66, 166

TXS 67, 166

TYA 39, 43, 64, 65, 66, 166

u

Unconditional branch 129, 130

Unmatched RTS 141, 143

Upward arrow 223 (see also Circumflex)

USR instruction v, viii, x, xi, 3, 4, 19, 63, 64, 67, 110, 111, 124, 141, 142

V

Variable x-xi, 132-34, 140

storing 57 Vector 86

w

"Warm start" address 124 Wedge 335-37

X register 46, 51, 67, 75, 125, 126

X and Y registers 26, 31, 39, 45, 48, 66, 75, 93, 94

Y

Y register 26, 34, 39, 57, 70, 147

z

Zero address 47

Zero page 33, 42, 51, 55, 56, 57, 98, 139,140, 336

349

Zero page addressing 33, 34, 42-43, 51, 55, 65, 68, 69, 75

Zero page locations 49, 99

Zero page snow 68

Zero page, X addressing 48, 68, 69, 75

Zero page, Y addressing 51

Zone of variables 133, 134

~ ~

350

If you've enjoyed the articles in this book, you'll find the same style and quality in every monthly issue of COMPUTE! Magazine. Use this form to order your subscription to COMPUTE!

For Fastest Service, Call Our Toil-Free US Order Line

800334-0868

In NC call 919-275-9809

COMPUTE!

P.O. Box 5406 Greensboro, NC 27403

My Computer Is:

□ PET □ Apple □ Atari QVIC □ Other.

. □ Don't yet have one..

□ $20.00 One Year US Subscription

□ $36.00 Two Year US Subscription

□ $54.00 Three Year US Subscription

Subscription rates outside the US:

□ $25.00 Canada

□ $38.00 Europe, Australia, New Zealand/Air Delivery

□ $48.00 Middle East, North Africa, Central America/Air Mail

□ $68.00 Elsewhere/Air Mail

"2 $25.00 International Surface Mail [lengthy, unreliable delivery)

Name

Address

City

State

Zip

Country

Payment must be in US Funds drawn on a US Bank; International Money Order, or charge card.

□ Payment Enclosed □ VISA

□ MasterCard □ American Express

Acet, No. Expires /

--

-~

"

-

If you've enjoyed the articles in this book, you'll find the same style and quality in every monthly issue of COMPUTERS Gazette for Commodore.

For Fastest Service Call Our Toil-Free US Order Line

800-334-0868

In NC call 919-275-9809

COMPUTE!^ Gazette

PO. Box 5406 Greensboro. NC 27403

My computer is:

□ VIC-20 □ Commodore 64 □ Other.

□ $20 One Year US Subscription

□ $36 Two Year US Subscription ~J $54 Three Year US Subscription

Subscription rates outside the US:

□ $25 Canada

□ $45 Air Mail Delivery

7J $25 International Surface Mail

Name

Address

City State Zip

Country

Payment must be in US Funds drawn on a US Bank, International Money Order, or charge card.

□ Payment Enclosed □ VISA

□ MasterCard □ American Express

Acct. No. Expires /

COMPUTE! Books

P.O. Box 5406 Greensboro. NC 27403

Ask your retailer for these COMPUTE! Books. If he or she has sold out, order directly from COMPUTE!

For Fastest Service Call Our TOLL FREE US Order Line

800-334-0868

In NC call 919-275-9809

Quantity Title Price

Beainner's Guide to Buvina A Personal

$ 3.95**

$12.95*

$19.95*

$12.95*

$24.95**

$ 4.95**

$12.95*

$12.95*

$12.95*

$14.95*

$14.95*

$12.95*

• Add $2 shipping and handling. Outside US add $4 air mail. S2

surlace mail ■■ Add $1 shipping and handling Outside US acid $4 air mail: $2

surface mail """ Add $3 shipping and handling Outside US add S9 air mail; $3 surface mail

Please add shipping and handling for each book ordered.

Total enclosed or to be charged.

Total

The Beginner's Guide to Buying A Personal Computer

COMPUTED First Book of Atari

Inside Atari DOS

COMPUTED First Book of PET/CBM

Programming the PET/CBM

Every Kid's First Book of Robots and Computers

COMPUTEI's Second Bqok of Atari

COMPUTEI's First Book of VIC

COMPUTEI's First Book of Atari Graphics

Mapping the Atari

Home Energy Applications On Your Personal Computer

Machine Language for Beginners

All orders must be prepaid (money order, check, or charge). All payments must be in US funds. NC residents add 4% sales tax.

□ Payment enclosed Please charge my: □ VISA Q MasterCard

□ American Express Acc't. No. Expires /

Name

Address

City

State

7^

Country

Allow 4-5 weeks for delivery.

-->

r

r r

r-r r-r r-r

r

r

r

r r r

r-r r r

r-

r

Machine Language For Beginners

"Most books about machine language assume a considerable familiarity with both the details of microprocessor chips and with programming techniques. This book only assumes a working knowledge of BASIC. It was designed to speak directly to the amateur programmer, the part-time computerist. It should help you make the transition from BASIC to machine language with relative ease."

— From The Introduction

Contains everything you need to learn 6502 machine language including:

• A dictionary of all major BASIC words and their machine language equivalents. This section contains many sample programs and illustrations of how all the familiar BASIC programming techniques are accomplished in machine language.

• A complete Assembler program which supports pseudo-ops, forward branches, two number systems, and number conversions. It can easily be customized following the step-by-step instructions to make it perform any functions you want to add.

• A Disassembler program with graphic illustrations of jumps and subroutine boundaries.

• An easy-to-use number chart for quick conversions.

• Memory maps, monitor extensions, and all 6502 commands arranged for easy reference.

• Many clear, understandable examples and comparisons to already familiar BASIC programming methods.

ISBN 0-942386