Machine Language For The Commodore Revised and Expanded Edition PDF
Machine Language For The Commodore Revised and Expanded Edition PDF
JIM BUTTERFIELD
MACHINE LANGUAGE
FOR THE COMMODORE 64.128, AND
OTHER COMMODORE COMPUTERS
REVISED & EXPANDED EDITION
STX $0381
■J
MACHINE
LANGUAGE
FOR THE
COMMODORE 64,128,
and Other
Commodore
Computers
Jim Butterfield
A Brady Book
Published by Prentice Hall Press
New York, New York 10023
Machine Language for the Commodore 64,128, and Other
Commodore Computers
A Brady Book
Published by Prentice Hall Press
A Division of Simon & Schuster, Inc.
Gulf + Western Building
One Gulf + Western Plaza
New York, New York 10023
123456789 10
Butterfield, Jim
Machine language for the Commodore 64, 128, and
other Commodore computers
Includes index.
1. Commodore 64 (Computer)—Programming. 2. Commodore
computers—Programming. 3. Programming languages
(Electronic computers) I. Title.
QA76.8.C64B88 1986 001.64'2 84-6351
ISBN Q-
Contents
Note to Readers vii
Preface ix
Introduction xiii
1 First Concepts 1
The Inner Workings of Microcomputers
Memory Elements
Microprocessor Registers
Instruction Execution
First Program Project
Monitors: What They Are
The Machine Language Monitor
MLM Commands
Changing Memory Contents
Changing Registers
Entering the Program
Things You Have Learned
Detail: Program Execution
Questions and Projects
2 Controlling Output 23
Calling Machine Language Subroutines
CHROUT—The Output Subroutine
Why Not POKE?
A Print Project
Monitor Extensions
Checking: The Disassembler
Running the Program
Linking with BASIC
Loops
Things You Have Learned
Questions and Projects
iii
3 Flags, Logic, and Input 39
Flags
A Brief Diversion: Signed Numbers
A Brief Diversion: Overflow
Flag Summary
The Status Register
Instructions: A Review
Logical Operators
Why Logical Operations?
Input: The GETIN Subroutine
STOP
Programming Project
things You Have Learned
Questions and Projects
5 Address Modes 71
Addressing Modes
No Address: Implied Mode
No Address: Accumulator Mode
Not Quite an Address: Immediate Mode
A Single Address: Absolute Mode
Zero-Page Mode
A Range of 256 Addresses: Absolute, Indexed Mode
All of Zero Page: Zero-Page, Indexed
Branching: Relative Address Mode
IV
Data From Anywhere: Indirect, Indexed
A Rarity: Indexed, Indirect
Project: Screen Manipulation
Comment for VIC-20 and Commodore 64
Things You Have Learned
Questions and Projects
vi
Note to Readers
This book introduces beginners to the principles of machine language: what it
is, how it works, and how to program with it.
It is based on an intensive two-day course on machine language that has been
presented many times over the past five years.
Readers of this book should have a computer on hand: students will learn by
doing, not just by reading. Upon completing the tutorial material in this book, the
reader will have a good idea of the fundamentals of machine language. There will
be more to be learned; but by this time, students should understand how to adapt
other material from books and magazines to their own particular computers.
The author and publisher of this book have used their best efforts in preparing
this book and the programs contained in it. These efforts include the development,
research, and testing of the programs to determine their effectiveness. The author
and the publisher make no warranty of any kind, expressed or implied, with regard
to these programs, the text, or the documentation contained in this book. The
author and the publisher shall not be liable in any event for claims of incidental
or consequential damages in connection with, or arising out of, the furnishing,
performance, or use of the text or the programs.
vii
A Commodore 128 owner can read each chapter of this book twice, if desired.
The first time, the exercises for the Commodore 64 can be worked through; the
second time, those for the 128 can be used. The principles are the same; the
code is similar; but the 128 often calls for a little more detailed work.
If you wish to learn machine language for the Commodore 128, please read the
Introduction in Appendix E, under Exercises for the Commodore 128. It will give
you some starting facts about your machine. There is more information on the
128 in the latter section of Appendix B and elsewhere, but don't try to read it all
at the start. It will be there when you need it.
viii
Preface
This book is primarily tutorial in nature. It contains, however, extensive reference
material, which the reader will want to continue to use.
No previous machine language experience is required. It is useful if the reader
has had some background in programming in other languages, so that concepts
such as loops and decisions are understood.
Beginners will find that the material in this book moves at a fast pace. Stay with
it; if necessary, skip ahead to the examples and then come back to reread a difficult
area.
Readers with some machine language experience may find some of the material
too easy; for example, they are probably quite familiar with hexadecimal notation
and don't need to read that part. If this is the case, skip ahead. But do enter all
the programming projects; if you have missed a point, you may spot it while doing
an exercise.
Programming students learn by doing. The beginner needs to learn simple things
about his or her machine in order to feel in control. The elements that are needed
may be itemized as:
• Machine language. This is the objective, but you can't get there without the
next two items.
• Machine architecture. All the machine language theory in the world will have
little meaning unless the student knows such things as where a program may
be placed in memory, how to print to the screen, or how to input from the
keyboard.
• Machine language tools. The use of a simple machine language monitor to
read and change memory is vital to the objective of making the computer do
something in machine language. Use of a simple assembler and elements of
debugging are easy once you know them; but until you know them, it's hard
to make the machine do anything.
Principles of sound coding are important. They are seldom discussed explicitly,
but run as an undercurrent through the material. The objective is this: it's easy to
do things the right way, and more difficult to do them the wrong way. By introducing
examples of good coding practices early, the student will not be motivated to look
for a harder (and inferior) way of coding.
It should be pointed out that this book deals primarily with machine language,
not assembly language. Assembler programs are marvellous things, but they are
ix
too advanced for the beginner. I prefer to see the student forming an idea of how
the bytes of the program lie within memory. After this concept is firmly fixed in
mind, he or she can then look to the greater power and flexibility offered by an
assembler.
Acknowledgements
Thanks go to Elizabeth Deal for acting as resource person in the preparation
of this book. When I was hard to find, the publisher could call upon Elizabeth for
technical clarification.
XI
■J
Introduction
Why learn machine language? There are three reasons. First, for speed; ma
chine language programs are fast. Second, for versatility; all other languages are
limited in some way, but not machine language. Third, for comprehension; since
the computer really works in machine language only, the key to understanding
how the machine operates is machine language.
Is it hard? Not really. It's finicky, but not difficult. Individual machine language
instructions don't do much, so we need many of them to do a job. But each
instruction is simple, and anyone can understand it if he or she has the patience.
Some programmers who started their careers in machine language find "higher
level" languages such as BASIC quite difficult by comparison. To them, machine
language instructions are simple and precise, whereas BASIC statements seem
vague and poorly defined by comparison.
Where will this book take you? You will end up with a good understanding of
what machine language is, and the principles of how to program in it. You won't
be an expert, but you'll have a good start and will no longer be frightened by this
seemingly mysterious language.
Will the skills you learn be transportable to other machines? Certainly. Once
you understand the principles of programming, you'll be able to adapt. If you were
to change to a non-Commodore machine that used the 6502 chip (such as Apple
or Atari), you'd need to learn about the architecture of these machines and about
their machine language monitors. They would be different, but the same principles
would apply on all of them.
Even if you change to a computer that doesn't use a chip from the 6502 family,
you will be able to adapt. As you pick through the instructions and bits of the
Commodore machine, you will have learned about the principles of all binary
computers. You will need to learn the new microprocessor's instruction set, but it
will be much easier the second time around.
Do you need to be a BASIC expert before tackling machine language? Not at
all. This book assumes you know a little about programming fundamentals: loops,
branching, subroutines, and decision making. But you don't need to be an ad
vanced programmer to learn machine language.
XII!
1
First
Concepts
This chapter discusses:
Technicians will tell you that "on" usually means full voltage on the circuit
concerned, and "off" means no voltage. There's no need for volume control
adjustments within a digital computer: each circuit is either fully on or fully
off.
The word "binary" means "based on two," and everything that happens
within the computer is based on the two possibilities of each circuit: on or
off. We can identify these two conditions in any of several ways:
ON or OFF
TRUE or FALSE
YES or NO
1 orO
11DDD111
This would signify that the two leftmost wires were on, the next three off,
and the remaining three on. The value 11000111 looks like a number; in
fact, it is a binary number in which each digit is 0 or 1. It should not be
confused with the equivalent decimal value of slightly over 11 million; the
digits would look the same, but in decimal each digit could have a value
from 0 to 9. To avoid confusion with decimal numbers, binary numbers
are often preceded by a percent sign, so that the number might be shown
asZllDDDlill.
Each digit of a binary number is called a bit, which is short for "binary
digit." The number shown above has eight bits; a group of eight bits is a
byte. Bits are often numbered from the right, starting at zero. The right-
hand bit of the above number would be called "bit 0," and the left-hand
bit would be called "bit 7." This may seem odd, but there's a good math
ematical reason for using such a numbering scheme.
FIRST CONCEPTS 3
The Bus
It's fairly common for a group of circuits to be used together. The wires
run from one microchip to another, and then on to the next. Where a group
of wires are used together and connect to several different points, the
group is called a bus (sometimes spelled "buss").
The PET, CBM, and VIC-20 use a microprocessor chip called the 6502.
The Commodore 64 uses a 6510. The Commodore B series uses a 6509
chip, and the Commodore PLUS/4 uses a chip called 7501. All these chips
are similar, and there are other chips in the same family with numbers like
6504; every one works on the same principles, and we'll refer to all of
them by the family name 650x.
Let's take an example of a bus used on any 650x chip. A 650x chip has
little built-in storage. To get an instruction or perform a computation, the
650x must call up information from "memory"—data stored within other
chips.
The 650x sends out a "call" to all memory chips, asking for information.
It does this by sending out voltages on a group of sixteen wires called the
"address bus." Each of the sixteen wires may carry either voltage or no
voltage; this combination of signals is called an address.
Every memory chip is connected to the address bus. Each chip reads the
address, the combination of voltages sent by the processor. One and only
one chip says, "That's me!" In other words, the specific address causes
—*~ *~ —^ —^
650x —
I I I I I I I I I
\ \ \ in
MEMORY MEMORY
CHIP
OlDllOll
The data might flow either way. That is, the 650x might read from the
memory chip, in which case the selected memory chip places information
onto the data bus which is read by the microprocessor. Alternatively, the
650x might wish to write to the memory chip. In this case, the 650x places
information onto the data bus, and the selected memory chip receives the
data and stores it.
-ADDRESS BUS-
650x
DATA BUS
in in
MEMORY MEMORY
MEMORY
CHIP CHIP
CHIP
(NOT (NOT
("SELECTED"]
SELECTED) SELECTED)
All other chips are still connected to the data bus, but they have not been
selected, so they ignore the information.
the control bus) that control such things as data timing and the direction
in which the data should flow: read or write.
Number Ranges
The address bus has sixteen bits, each of which might be on or off. The
possible combinations number 65536 (two raised to the sixteenth power).
We then have 65536 different possibilities of voltages, or 65536 different
addresses.
The data bus has eight bits, which allows for 256 possibilities of voltages.
Each memory location can store only 256 distinct values.
DDD1DD1D1D1D11DD
128 64 32 16 8 4 2 1
EIGHT BITS
XXX
SIXTEEN BITS
Figure 1.3
Hexadecimal Notation
Binary is an excellent system for the computer, but it is inconvenient for
most programmers. If one programmer asks another, "What address should
MACHINE LANGUAGE FOR COMMODORE MACHINES
The same type of weighting is applied to each bit of the group of four as
was described before. In other words, the rightmost bit (bit zero) has a
weight of 1, the next left a weight of 2, the next a weight of 4, and the
leftmost bit (bit three) a weight of 8. If the total of the weighted bits exceeds
nine, an alphabetic letter is used as a digit: A represents ten; B, eleven;
C, twelve; and F, fifteen.
Hexadecimal to Decimal
As we have seen, hexadecimal and binary numbers are easily inter
changeable. Although we will usually write values in "hex," occasionally
we will need to examine them in their true binary state to see a particular
information bit.
Hexadecimal isn't hard to translate into decimal. You may recall that in
early arithmetic we were taught that the number 24 meant, "two tens and
four units." Similarly, hexadecimal 24 means "two sixteens and four units,"
or a decimal value of 36. By the way, it's better to say hex numbers as
FIRST CONCEPTS
"two four" rather than "twenty-four," to avoid confusion with decimal val
ues.
Using the above steps, let's convert the hexadecimal number $15 AC.
Step 1: The leftmost digit is 1.
Step 2: There are more digits, so we'll continue.
Step 3. 1 times Ifc is It, plus E gives Ifl.
Step 2: More digits to come.
Step 3: Ifl times It is Eflfl, plus ID (for A) gives E^fl.
Step 2: More digits to come.
Step 3: ERfl x It is 47tfl, plus IE (for C) gives 47flD.
Step 2: No more digits: 47flD is the decimal value.
Decimal to Hexadecimal
The most straightforward method to convert from decimal to hexadecimal
is to divide repeatedly by 16; after each division, the remainder is the next
hexadecimal digit, working from right to left. This method is not too well
suited to small calculators, which usually don't give remainders. The fol
lowing fraction table may offer some help:
Do not get fixed on the idea of numbers. Memory locations can always
be described as binary numbers, and thus may be converted to decimal
or hexadecimal at will. But they may not mean anything numeric: the
memory location may contain an ASCII coded character, an instruction,
or any of several other things.
Memory Elements
There are generally three types of devices attached to the memory busses
(address, data, and control busses):
• RAM: Random access memory. This is the read and write memory, where
we will store the programs we write, along with values used by the program.
We may store information into RAM, and may recall the information at any
time.
• ROM: Read only memory. This is where the fixed routines are kept within the
computer. We may not store information into ROM; its contents were fixed
ADDRESS BUS TO
OTHER
CHIPS
650x
MEMORY BUS
£ c
RAM
ROM
(READ IA
(READ
AND (SPECIAL)
ONLY)
WRITE)
CONNECTIONS
TO "OUTSIDE WORLD"
Figure 1.4
FIRST CONCEPTS
when the ROM was made. We will use program units (subroutines) stored in
ROM to do special tasks for us, such as input and output.
• I A: Interface adaptor chips. These are not memory in the usual sense; but,
these chips are assigned addresses on the address bus, so we call them
"memory-mapped" devices. Information may be passed to and from these
devices, but the information is generally not stored in the conventional sense.
IA chips contain such functions as: input/output (I/O) interfaces that serve
as connections to the "outside world"; timing devices; interrupt control sys
tems; and sometimes specialized functions, such as video control or sound
generation. IA chips come in a wide variety of designs, including the PI A
(peripheral interface adaptor), the VIA (versatile interface adaptor), the CIA
(complex interface adaptor), the VIC (video interface chip), and the SID
(sound interface device).
Within a given computer, some addresses may not be used at all. Some
devices may respond to more than one address, so that they seem to be
in two places in memory.
An address may be thought of as split in two parts. One part, usually the
high part of the address, selects the specific chip. The other part of the
address selects a particular part of memory within the chip. For example,
in the Commodore 64, the hex address $DD2D (decimal 53EflD) sets
the border color of the video screen. The first part of the address (roughly,
$DD ...) selects the video chip; the last part of the address (... EU)
selects the part of the chip that controls border color.
Microprocessor Registers
Within the 650x chip are several storage areas called registers. Even
though they hold information, they are not considered "memory" since
they don't have an address. Six of the registers are important to us. Briefly,
they are:
PC: (16 bits) The program counter tells where the next
instruction will come from.
A, X and Y (8 bits each) These registers hold data.
SR The status register, sometimes called PSW
(processor status word), tells about the re
sults of recent tests, data handling, and so
on.
| PC i
ADDRESS BUS
ra i
L_2lJ
rY I
L SR |
c SP | DATA BUS
65Ox CHIP
Figure 1.5
Instruction Execution
Suppose that the 650x is stopped (not an easy trick), and that there is a
certain address, say $1234, in the PC. The moment we start the micro
computer, that address will be put out to the address bus as a read address,
and the processor will add one to the value in the PC.
Thus, the contents of address $1534 will be called for, and the PC will
change to $1535. Whatever information comes in on the data bus will
be taken to be an instruction.
DATA BUS
Figure 1.6
FIRST CONCEPTS
You can see that the processor works in the same way that most computer
languages do: an instruction is executed, and then the computer proceeds
to the next instruction, and then the next, and so on. We can change the
sequence of execution by means of a "jump" or "branch" to a new location,
but normally, it's one instruction after another.
Both "load" and "store" are copying actions. If I load A (LDA) from
address $234 5,1 make a copy of the contents of hex S3 AS into A; but
E3AS still contains its previous value. Similarly, if I store Y into $345b,
I make a copy of the contents of Y into that address; Y does not change.
The 650x has no way of moving information directly from one memory
address to another. Thus, this information must pass via A, X, or Y; we
load it from the old address, and store it to the new address.
Later, the three registers will take on individual identities. For example,
the A register is sometimes called the accumulator, since we perform
addition and subtraction there. For the moment, they are interchangeable:
we may load to any of the three, and we may store from any of them.
Here's our plan. We may load one value into A (say, the contents of
$ D 3 a D), and load the other value into X (the contents of $ D 3 a 1). Then
we could store A and X back, the other way around.
We could have chosen a different pair of registers for our plan, of course:
A and Y, or X and Y. But let's stay with the original plan. We can code
our plan in a more formal way:
You will notice that we have coded »load A" as LDA, »load X11 as
LDX, "store A11 as STA, and "store X" as STX. Every command
has a standard three-letter abbreviation called a mnemonic. Had we used
the Y register, we might have needed to use LDY and STY.
One more command is needed. We must tell the computer to stop when
it has finished the four instructions. In fact, we can't stop the computer;
but if we use the command BRK (break), the computer will go to the
machine language monitor (MLM) and wait for further instructions. We'll
talk about the MLM in a few moments.
AD BD D3 LDA $D3aD
It's traditional to write the machine code on the left, and the source code
on the right. Let's look closely at what has happened.
LDA has been translated into $AD. This is the operation code, or op
code, which says what to do. It will occupy one byte of memory. But we
need to follow the instruction with the address from which we want the
load to take place. That's address $D3aO; it's sixteen bits long, and so
it will take two bytes to hold the address. We place the address of the
instruction, called the operand, in memory immediately behind the instruc
tion. But there's a twist. The last byte comes first, so that address $ D 3 a D
is stored as two bytes: BD first and then D3.
FIRST CONCEPTS 73
Here are some machine language op codes for the instructions we may
use. You do not need to memorize them.
On the right, we have our plan. On the left, we have the actual program
that will be stored in the computer. We may call the right side assembly
code and the left side machine code, to distinguish between them. Some
users call the right-hand information source code, since that's where we
start to plan the program, and the left-hand program object code, since
that's the object of the exercise—to get code into the computer. The job
of translating from source code to object code is called assembly. We
performed this translation by looking up the op codes and translating by
hand; this is called hand assembly.
The code must be placed into the computer. It will consist of 13 bytes:
AD flO D3 AE fll D3 fiD fll D3 flE SO D3 00. That's the
whole program. But we have a new question: where do we put it?
Choosing a Location
We must find a suitable location for our program. It must be placed into
RAM memory, of course, but where?
For the moment, we'll place our program into the^cassette buffer, starting
at address $D33C (decimal fl^fl). That's a goodplace to pufshort test
2 programs, which is what we will be writing for a while.
J3.15 — *- $0600 d<tsjinzl 3-814
Now that we've made that decision, we face a new hurdle: how do we get
the program in there? To do that, we need to use a machine language
monitor.
•v
14 MACHINE LANGUAGE FOR COMMODORE MACHINES
Most machine language monitors work in a similar way, and have about
the same commands. To proceed, you'll need an MLM in your computer.
Use the built-in one, plug it in, load it in, or load and run ... whatever the
instructions tell you. On a PET/CBM machine, typing the command SYS
4 will usually switch you to the built-in monifoh After an MLM has been
added to a VIC or Commodore 64, the command SYS fi will usually get
you there. On the Commodore PLUS/4, the BASIC command MONITOR
will bring the monitor into play.
FIRST CONCEPTS 75
C128 note: When the Commodore 128 is in C64 mode, it needs to have
a monitor program loaded, as does the Commodore 64. When in the C128
mode, however, the command MONITOR will bring the monitor into play.
There will be slight differences in the screen display of this monitor. <Apy
pendix H contains information on the various monitor commands ana
formats.
Monitor Display
The moment you enter the MLM, you'll see a display that looks something
like this:
B*
PC SR AC XR YR SP
. ; DDD5 5D 54 E3 bA Ffl
The cursor will be flashing to the right of the period on the bottom line.
The exact appearance of the screen information may vary according to
the particular monitor you are using. Other material may be displayed—
in particular, a value called IRQ—which we will ignore for the time being.
The information you see may be interpreted as follows:
B*—we have reached the MLM by means of a "break." More about that later.
PC—The value shown below this title is the contents of the program counter.
This indicates where the program "stopped." In other words, if the value shown
is address DDD5, the program stopped at address DDD4, since the PC is
ready to continue at the following address. The exact value (DDD4 versus
DDD5) may vary depending on the particular MLM.
S R—The value shown below shows the status register, which tells us the results
of recent tests and data operations. We'd need to split apart the eight bits and
look at them individually to establish all the information here; we will do this at
a later time.
76 MACHINE LANGUAGE FOR COMMODORE MACHINES
AC, XR, and YR—The values shown below these three titles are the contents
of our three data registers: A, X, and Y.
SP—The value shown below is that of the stack pointer, which indicates a
temporary storage area that the program might use. A value of Ffl, for example,
tells us that the next item to be dropped into the stack area would go to address
$DlFfl in memory. More on this later.
You will notice that the display printed by the monitor (called the register
display) shows the internal registers within the 650x chip. Sometimes there
is another item of information, titled IRQ, in this display. It doesn't belong,
since it does not represent a microprocessor register. IRQ tells us to what
address the computer will go if an interrupt occurs; this information is
stored in memory, not within the 650x.
M L M Commands
The machine language monitor is now waiting for you to enter a command.
The old BASIC commands don't work any more; LIST or NEW or SYS
are not known to the MLM. We'll list some popular commands in a moment.
First, let's discuss the command that takes us back to BASIC.
X exits the MLM and returns to the BASIC monitor. Try it. Remember
to press RETURN after you've typed the X, of course. You will return to
the BASIC system, and the BASIC monitor will type READY. You're back
in familiar territory. Now go back to the monitor with SYS^orSYSflor
MONITOR as the case may be. BASIC ignores spaces: it doesn't matter
if you type SYSfl or SYS fi; just use the right number for your machine
(A for PET/CBM, fl for VIC/64).
There are two other fundamental instructions that we won't use yet: they
are S for save and L for load. These are tricky. Until you learn about
BASIC pointers (Chapter 6), leave them alone.
M 1DDD 1D1D
Be careful that you have exactly one space before each address. You
might get a display that looks something like this:
.:1DDD 11 3A E4 DD El 35 04 AA
.rlDDfl ED 4A 4R 4D 5D 4E 55 54
.:1D1D 54 45 SE 4b 4R 45 4C 44
C128 note: The above display will differ slightly if you are using C128.
The section Exercises for the Commodore 128, in Appendix E, gives
details.
The four-digit number at the start of each line represents the address in
memory being displayed. The two-digit numbers to the right represent the
contents of memory. Keep in mind that all numbers used by the machine
language monitor are hexadecimal.
This is quite similar to the way BASIC programs may be changed; you
may type over on the screen, and when you press RETURN, the new line
replaces the old. The general technique is called screen editing.
Changing Registers
We may also change the contents of registers by typing over and pressing
RETURN. You may take a register display with command R, and then
change the contents of PC, AC, XR, and YR. Leave the contents of SR
and SP unchanged—tricky things could happen unexpectedly if you ex
periment with these two.
We might rewrite our program one last time, marking in the addresses
that each instruction will occupy. You will recall that we have decided to
put our program into memory starting at address $D33C (part of the
cassette buffer).
Remember that most of the above listing is cosmetic. The business end
of the program is the set of two-digit hex numbers shown to the left. At
the extreme left, we have addresses—that's information, but not the pro
gram. At the right, we have the "source code"—our notes on what the
program means.
M D33C D34fl
We might have anything in that part of memory, but we'll get a display
that looks something like
.:D33C xx xx xx xx xx xx xx xx
xx xx xx xx xx xx xx xx
You won't see "xx," of course; there will be some hexadecimal value
printed for each location. Let's move the cursor back and change this
display so that it looks like this:
Don't type in the "xx"—just leave whatever was there before. And be
sure to press RETURN to activate each line; if you move the cursor down
to get to the next line without pressing RETURN, the memory change
would not happen.
Display memory again (M D33C D34fl) and make sure that the
program is in place correctly. Check the memory display against the pro
gram listing, and be sure you understand how the program is being tran
scribed into memory.
If everything looks in order, you're ready to run your first machine language
program.
Preparation
There's one more thing that we need to do. If we want to swap the contents
of addresses $D3flDand$D3fil, we'd better put something into those
two locations so that we'll know that the swap has taken place correctly.
Display memory with M D3flD D3fil and set the resulting display
so that the values are
.:D3flD 11 Rq xx xx xx xx xx xx
20 MACHINE LANGUAGE FOR COMMODORE MACHINES
GO33C
The program runs so quickly that it seems instantaneous (the run time is
less than one fifty thousandth of a second). The last instruction in our
program was BRK for break, and that sends us straight to the MLM with
a display of *B (for break, of course) plus all the registers.
Nothing seems to have changed. But wait. Look carefully at the register
display. Can you explain the values you see in the &C and XR registers?
Can you explain the PC value?
Now you may display the data values we planned to exchange. Give the
memory display command M D3fiD D3fil—have the contents of
the two locations changed?
They'd better have changed. Because that's what the writing of our pro
gram was all about.
2. It asks for the contents of $D33D, and then $D33E. As it receives the
values of $flD and $D3 it gathers them into an "instruction address."
3. The microprocessor now has the whole instruction. The PC has moved along
to $D33F. The 650x now executes the instruction. It sends address $D3flD
to the address bus; when it gets the contents (perhaps $11), it delivers this
to the A register. The A register now contains $11.
4. The 650x is ready to take on the next instruction; the address $D33F goes
from the PC out to the address bus; and the program continues.
The screen on the PET/CBM is at SflDDD and up; on the VIC, it's often
(but not always) at $1EDD and up; on the Commodore 64, it's usually at
$D4DD; and on the PLUS/4, it may be found at $DCDD. With the C128,
the 40-column screen is at $04 DD, but if you are in the 80-column mode,
the screen is not mapped directly to memory.
Two pitfalls may arise. First, you might write a perfect program that places
information near the top of the screen; then, when the program finishes,
the screen might scroll, and the results would disappear. Second, the VIC
and Commodore 64 use color, and you might inadvertently produce white-
on-white characters; these are hard to see.
23
24 MACHINE LANGUAGE FOR COMMODORE MACHINES
Prewritten Subroutines
A number of useful subroutines are permanently stored in the ROM mem
ory of the computer. All Commodore machines have a standard set of
subroutines that may be called up by your programs. They are always at the
same addresses, and perform in about the same way regardless of which
Commodore machine is used: PET, CBM, Commodore 64, PLUS/4, Com
modore 128, or VIC-20. These routines are called the kernal subroutines.
Details on them can be found in the appropriate Commodore reference
manuals, but we'll give usage information here.
The original meaning of the term kernal seems to be lost in legend. It was
originally an acronym, standing for something like "Keyboard Entry Read,
Network and Link." Today, it's just the label we apply to the operating
system that makes screen, keyboard, other input/output and control mech
anisms work together. To describe this central control system, we might
choose to correct the spelling so as to get the English word, "kernel." For
now, we'll use Commodore's word.
CONTROLLING OUTPUT 25
The three major kernal subroutines that we will deal with in the next few
chapters are shown here:
With the first two subroutines, we can input and output data easily. The
third allows us to honor the RON/STOP key, to guard against certain types
of programming error. In this chapter, we'll use CHROUT to print infor
mation to the screen.
Subroutine: CHROUT
Address: $FFDE
Action: Sends a copy of the character in the A register to the
output channel. The output channel is the computer screen
unless arrangements have been made to switch it.
The character sent is usually ASCII (or PET ASCII). When sent to the
screen, all special characters—graphics, color codes, cursor move
ments—will be honored in the usual way.
The screen memory of the VIC-20 in particular may move around a good
deal, depending on how much additional RAM memory has been fitted.
Occasionally, screen POKEs are the best way to do the job. But most of
the time we'll use the CHROUT, $FFD2 subroutine. Here are some of
the reasons why:
• As with PRINT, we won't need to worry about where to place the next
character; it will be positioned automatically at the cursor point.
• If the screen is filled, scrolling will take place automatically.
• Screen memory needs special characters. For example, the character X has
a standard ASCII code of $5fi, but to POKE it to the screen we'd need to
use the code $lfl. The CHROUT subroutine uses $5fl.
• Screen memory may move around, depending on the system and the pro
gram. The POKE address would need to change; but CHROUT keeps
working.
• Special control characters are honored: $DD for RETURN, to start a new
line; cursor movements; color changes. We can even clear the screen by
loading the screen-clear character ($^3) and calling $FFDE.
• To POKE the screen of the Commodore machines with color, the corre
sponding color nibble memory must also be POKEd (see the appropriate
memory map in Appendex C). With the subroutine at $FFD2, color is set
automatically.
A Print Project
Let's write some code to print the letter H on the screen. Once again, we'll
use address $D33C, the cassette buffer, to hold our program. Reminder:
be sure to have your monitor loaded and ready before you start this project.
CONTROLLING OUTPUT 27
LD&
JSR $FFDE
The previous instruction brought the letter H into the A register; this one
prints it to the screen. Now all we need to do is quit. BRK takes us to the
machine language monitor.
Monitor Extensions
We could repeat the steps of the previous chapter: hand-assembling the
source code into machine language, and then placing it into memory. We
would need to know the instruction codes, and then do a careful translation.
But there's an easier way.
Most monitors contain the assemble (A) command. The notable excep
tion is the built-in monitors within the PET/CBM; these, however, can be
extended by loading in a "monitor extension" program such as Supermon.
The Commodore PLUS/4 series contains an extended monitor, which
includes the A command.
Load your monitor or monitor extension. Do any setup that may be needed.
Then type the following monitor command:
1. It may do nothing except print a question mark somewhere on the line. The
question mark indicates an error in your coding. If the question mark appears
directly after the letter A, your monitor does not understand the A assemble
instruction; get another monitor or properly set up the one you have.
2. Or, it will correctly translate your instruction, and put the object code into
memory starting at the address specified. In this case, that would happen
to be $AR at address $D33C and $4fl at address $D33D. It would then
help you by printing part of the next expected instruction. The computer
expects that you will type a line starting with
A D33E
It places the first part of this line on the screen to save you typing. The
screen should now look like this:
You may now complete the instruction by typing in JSR $FFDE and
pressing RETURN. Again, the computer will anticipate your next line by
printing A D341, which allows you to type in the final command, BRK.
The screen now looks like this:
At this point, our program is stored in memory. The instructions have been
assembled directly into place, and the object code is hopefully ready to
go.
If you like, you can display memory and look at the object program with
theM D33C D 3 41. You'll see the bytes of your program in memory:
. :D33C AR A& 5D DE FF DD xx xx
The first six bytes are your program. The last two bytes don't matter: they
were whatever was in that part of memory before. We don't care what is
there, since the program will stop when it reaches the BRK ($DD) at
address $D341; it won't be concerned with the contents of memory at
$D342 or $D343.
Now we've written a program and it's safely stored in memory. We have
inspected memory and have seen the bytes there; but they are hard to
read. It would be convenient if we could perform an inverse assembly,
that is, take the contents of memory and translate it into source code. The
monitor has this capability, called a disassembler.
Give the command D D33C and press RETURN. D stands for disas
semble, of course, and the address must follow.
The computer will now show a full screen of code. On the left is the address
followed by the bytes making up the instruction. On the right is the re
constructed source code. The screen shows much more memory than our
program needs. Again, we ignore all lines beyond address $D341, which
is the last instruction of our program. Anything following is "junk" left in
memory that the program does not use.
You recognize that the 5fl should be A fi; you may move the cursor up—
use cursor home if you wish—and type over the value on the left-hand
side. In this case, you place the cursor over the 5, type A to change the
display to A fl, and press RETURN. You will see from the display that the
problem has been fixed.
Project for enthusiasts: Can you add to the program and print HI? The
ASCII code for the letter I is $4R. Can you add again and print HI on
a separate line? The ASCII code for a RETURN is $DD. Remember that
you can find all ASCII codes in Appendix D; look in the column marked
ASCII.
We can link to a machine language program from BASIC and when the
program is finished, it can return to BASIC and allow the BASIC program
to continue to run. The commands we need are
Let's change our machine language program first. We must change the
BRK at the end to RTS (return from subroutine) so that when the program
is finished it will return to BASIC. If you like, you may change it directly
on the disassembly listing: disassemble and then type over the DD byte
that represents BRK with a value of bD. Press RETURN and you'll see
that the instruction has now changed to RTS. Alternatively, you may re
assemble with
We're not finished. Any machine language subroutine may be called from
a BASIC program. Type NEW, which clears out the BASIC work area; our
machine language program is left untouched, since NEW is a BASIC com
mand. Now enter the following program:
1DD FOR J = l TO ID
11D SYS fiEfl
1SQ NEXT J
How many times will our program at fl E fl ($ D 3 3 C) be called? How many
times will the letter H be printed? Will they be on the same line or separate
lines? Type RUN and see.
Loops
We know how to send characters to the screen, one at a time. But long
messages, such as THE QUICK BROWN CAT . . ., might lead to te
dious coding if we had to write an instruction for each letter to be sent.
We need to set up a program loop to repeat the printing activity.
How can we do this? It seems that we must write one address into the
LDA instruction, and that address can't change. But there is a way.
We can ask the computer to take the address we supply, and add the
contents of X or Y to this address before we use it. The computed address
is called an effective address.
Let's look at our position. The first time around the loop, X is counting the
characters and has a value of zero. If we specify our address as D 3 A A + X,
the effective address will be 03 A A. That's where we will have stored the
letter H.
When we come back around the loop—we haven't written that part yet—
X should now equal one. An address of D 3 A A + X would give an effective
address of 03 AB] the computer would go there and get the letter E. As
CONTROLLING OUTPUT 33
A D33E LDA$D34A,X
A D341 JSR $FFD5
The first time, the computer loads the contents of address $D34A (the
letter H of HELLO) and prints it. When the loop comes back here, with
X equal to one, this instruction will load the contents of $034B and print
the letter E.
The X register counts the number of letters printed, so we must add one
to the contents of X. There's a special command that will add one to the
contents of X: I NX, for increment X. A similar code, I NY, allows Y to
be incremented; and DEX (decrement X) and DEY (decrement Y) allow
X or Y to be decremented, or reduced, by one. At the moment, I NX is
the one we need for counting:
A Q3AA INX
Now we can test X to see if it is equal to six yet. The first time around, it
won't be since X started at zero and was incremented to a value of 1. If
X is not equal to six, we'll go back to $D33E and print another letter.
Here's how we code it:
A D345 CPX#$Db
A Q3A? BNE $D33E
CPX stands for compare X; note that we are testing for an immediate
value of six, so we use the # symbol. BNE means branch not equal; if X
is not equal to six, back we go to address $D33E.
A little careful thought will reveal that the program will go back five times
for a total of six times around the loop. It's exactly what we want.
We may now put the characters for HELLO into memory. These are data,
not instructions, so we must not try to assemble them. Instead, we change
memory in the usual way, by displaying and then typing over. We give
the command M D 3 4 A D 3 4 F, and type over the display to show
:D34ft A& AS AC AC AT DD xx xx
When all looks well, return to BASIC (with . X) and try SYS flEfl. The
computer should say HELLO.
A Comment on SAVE
If you wished to save the program to cassette tape, you'd have a problem
on the VIC or Commodore 64. The machine language program is in the
cassette buffer; a save-to-tape command would cause the contents of that
buffer to be destroyed before the program could be written to tape. Even
disk commands would not be completely safe: 4.0 BASIC disk commands
use the cassette buffer area as a work area; using these commands would
probably destroy our machine language program.
But saving the program is not the main problem. A correctly saved program
can give trouble when you try to bring it back and run it safely. The difficulty
is related to BASIC pointers, especially the start-of-variables pointer. The
problem, and how to solve it, will be discussed in some detail in Chapter
6.
A Stopgap SAVE
We can preserve short programs by making them part of DfiTR state
ments. The procedure is not difficult if screen editing is used intelligently.
for the Commodore 128, will give you the correct addresses and values
for doing this on the C128. Enter the following BASIC line:
Study the above line. You will see that it asks BASIC to go through the
part of memory containing your machine language program, and display
the contents (in decimal notation, of course). You'll see a result that looks
something like this:
Its 0 Iflq 74 3 35 51D 555 535 EE4 t 50fi 545 qt
75 tq 7t 7t 7q 13
These are indeed the bytes that make up your program. With a little study,
you could reconstruct the lbE-D combination to be LDX #$DD, or the
7 E-bR-7 b-7 b-7 q at the end to be the word HELLO in ASCII. It looks
different when it's in decimal, but it's still the same numbers.
You may try a little skill and artistry, using screen editing to perform the
next activity, or you may just retype the numbers into data lines a shown.
Either way, arrange the numbers as follows:
5D DATA lt2,D,Iflq,74,3,35,BID,355,535,254,b
bD DATA EDfl.E^^t^E^q^t^t,?^!]
We now have a copy of our program, exactly the way it appears in memory,
but stored within DATA statements. The DATA statements are part of a
normal BASIC program, of course, and will SAVE and LOAD with no
trouble at all.
Now our program is safe and sound—it handles like BASIC, but it will do
a machine language task for us as desired. Let's display the entire BASIC
program
You may have noticed that in our example, we had register X counting
up from zero to the desired value. What would happen if you started X at
5 and counted down? Try it if you like.
Remember that you can also include cursor movements, color codes (if
your machine has color), and other special ASCII characters. Could you
lay out the coding to draw a box? (Try it in BASIC first). Draw a box with
the word HELLO inside it.
■J
3
Flags, Logic,
and Input
This chapter discusses:
39
40 MACHINE LANGUAGE FOR COMMODORE MACHINES
Flags
Near the end of Chapter 2, we coded a program that had the seemingly
natural sequence
CPX #$0b
BNE $
It made sense: compare X for a value of b, and if not equal, branch back.
Yet it implies something extraordinary; the two instructions are somehow
linked.
Let's flash forward for a moment. Even when you have a machine language
program running, the computer "freezes" sixty times a second. The com
puter undertakes a special activity, called interrupt processing. It stops
whatever it was doing, and switches to a new set of programs that do
several tasks: flashing the cursor, checking the keyboard, keeping the
clock up to date, and checking to see whether the cassette motor needs
power. When it's finished, it "unfreezes" the main program and lets it
continue where it left off.
This interrupt might take place between the two instructions shown above,
that is, after the CPX and before the BNE. Hundreds of interrupt instruc
tions might be executed between the two, yet nothing is harmed. The two
instructions work together perfectly to achieve the desired effect. How can
the computer do this?
The two instructions are linked by means of a flag—a part of the 650x
that records that something has happened. The CPX instruction tests X
and turns a special flag on or off to signal how the comparison turned out:
equal or unequal. The BNE instruction tests that flag. If it's on (meaning
equal), no branch will take place and the program will continue with the
next instruction; if it's off (meaning not equal), a branch will take place.
ZF/ag
The Z (zero) flag is probably misnamed, and should have been called the
E flag (for "equals"). After any comparison (CPX to compare X, CPY to
compare Y, or CMP to compare A), the Z flag will be set to "on" if the
compared values are equal; otherwise it will be reset to "off."
FLAGS, LOGIC, AND INPUT 41
Sometimes the Z flag checks for equal to zero, hence its name, Z for
zero. This happens for every activity that may change one of the three
data registers. Thus, any load command will affect the Z flag status. The
same is true of increment and decrement instructions, which obviously
change registers. And later, when we meet other operations such as ad
dition and subtraction, they too will affect the Z flag.
There are many instructions that don't affect the Z flag (or any flag, for
that matter). Store instructions (STA, STX, STY), never change a flag.
Branch instructions test flags but don't change them.
An example will help illustrate the way that some instructions change flags
and others do not. Examine the following coding:
LDA#$53 (Load 53 to A)
LDX #$DD (Load zero to X)
STA $1234 (store 53 to address $1234)
BEQ$
Will the branch (BEQ) be taken, or will the 650x continue with the next
instruction? Let's analyze the Z flag's activity step by step. The first in
struction (LDA #$53) resets the Z flag, since 53 is not equal to zero.
The second instruction (LDX #$QD) sets the Z flag because of the zero
value. The third instruction (STA $1534) does not affect the Z flag; in
fact, store instructions do not affect any flags. Thus, by the time we reach
the BEQ instruction, the Z flag is set "on" and the branch will be taken.
650x reference manuals show the specific flags that are affected by each
instruction. In case of doubt, they are easy to check.
The Z flag is quite busy—it clicks on and off very often since many in
structions affect it. It's an important flag.
If the Z flag is set "on," the BEQ (branch equals) instruction will branch
to the specified address; otherwise it will be ignored and the next instruction
in sequence will be executed. If the Z flag is reset "off," the BNE (branch
not equals) instruction will branch.
We can see in more detail how our program from Chapter 2 worked.
CPX #$Dt causes the Z flag to be set "on" if X contains the value t;
otherwise it causes the Z flag to be reset "off." BNE tests this flag, and
branches back to the loop only if the Z flag is off—in other words, only if
the contents of X is not equal to six.
42 MACHINE LANGUAGE FOR COMMODORE-MACHINES
CFlag
The C (carry) flag is probably misnamed, too. It should have been called
theGE (greater/equal) flag, since after a comparison (CPX, CPY.orCMP),
the C flag is set "on" if the register (X, Y, or A) is greater than or equal
to the value compared. If the register concerned is smaller, the C flag will
be reset "off."
The C flag is not as busy as the Z flag. The C flag is affected only by
comparison instructions and by arithmetic activities (add, subtract, and a
type of multiplication and division called rotate or shift). When used in
arithmetic, the C flag is properly named, since it acts as a "carry" bit
between various columns as they are calculated. For example, an LDA
instruction always affects the Z flag since a register is being changed, but
never affects the C flag since no arithmetic or comparison is being per
formed.
If the C flag is set "on," the BCS (branch carry set) instruction will branch
to the specified address; otherwise it will be ignored and the next instruction
in sequence will be executed. If the C flag is reset "off," the BCC (branch
carry clear) instruction will branch.
The C flag may be directly set or reset by means of the instructions SEC
(set carry) and CLC (clear carry). We will use these instructions when we
begin to deal with addition and subtraction.
If you examine the last program of Chapter 2, you will see that the BNE
instruction could be replaced by BCC. Instead of "branch back if not equal
to 6," we could code "branch back if less than 6." The operation would
be the same in either case.
NF/ag
The N (negative) flag is also probably misnamed. It should have been
called the HB (high bit) flag, since numbers are positive or negative only
if they are used in a certain way. The N flag is set to indicate that a register
has been given a value whose high bit is set.
The N flag is as busy as the Z flag; it changes with every instruction that
affects a register. The N flag is affected by comparisons, but in this case
its condition is not usually meaningful to the programmer.
To sort out the operation of the N flag, it's important to become familiar
with hexadecimal-to-binary conversion. For example, will LDft #$t5 set
the N flag? Rewrite it into binary: $ 15 equals %U 11D D1D1. We can see
FLAGS, LOGIC, AND INPUT 43
that the high bit is not set, meaning that the N flag will be off after loading
this value. As another example, suppose we LDX #$DA. Hex DA is
11D11D1D binary. We see that the high bit is on and thus the N flag is
set.
If the N flag is set "on," the BMI (branch minus) instruction will branch
to the specified address; otherwise it will be ignored and the next instruction
in sequence will be executed. If the N flag is reset "off," the BPL (branch
plus) instruction will branch.
We may need more intuitive help, however. If the computer loads the
decimal value EDD into the A register with LDA #$Cfl, the N flag will be
set and will seemingly indicate that E D D is a negative number. It may be
more comfortable to simply think of EDD as a number with the high bit
set. But in a sense, EDD could be a negative number if we wanted it to
be. Let's examine the situation by means of examples.
If I were asked to count down in hexadecimal from ID, I'd start out $ ID,
IDF, $DE, and $DD, continuing down to $DE,$D 1, and $DD. If I needed
to keep going, I'd continue past $ D D with $FF; in this case, hex FF would
clearly represent negative one. Continuing, FE, FD, and FC would rep
resent - E, - 3, and - A. And the high bit is set on all these "negative"
numbers.
the mileage by 1,501 miles, you'd see a reading of 99999, which would
mean -1. (The author does not know this from personal experience, but
is assured by many machine language students that it is so.) In these
cases, based on the decimal system, the negative numbers are called
"ten's complement."
V Flag
As with the other flags, the V (overflow) flag is probably misnamed. It
should have been called the SAO (signed arithmetic overflow) flag, since
it is affected only by addition and subtraction commands, and is meaningful
only if the numbers concerned are considered to be signed.
The V flag is used only occasionally in typical 650x coding. Many machine
language programs don't use signed numbers at all. The most typical use
of the V flag is in conjunction with a rather specialized command, BIT
(bit test). For this instruction, the V flag signals the condition of bit t of
the memory location being tested. In this case, V and N work in a similar
way: N reflects the high bit, bit 7, and V represents the "next bit down,"
bit t. The BIT command is used primarily for testing input/output ports
on IA (interface adaptor) chips.
If the V flag is set "on," the BVS (branch overflow set) instruction will
branch to the specified address; otherwise it will be ignored and the next
instruction in sequence will be executed. If the V flag is reset "off," the
BVC (branch overflow clear) instruction will branch.
The V flag may be directly reset by means of the CLV (clear overflow)
instruction. Oddly, there is no equivalent instruction to set the flag.
One special feature of the V flag: on some 650x chips, the V flag can be
set by hardware. There is a pin on the chip that can be used so that an
external logic signal will trigger the V flag.
Flag Summary
A brief table may help review the four testable flags.
Each flag is a bit within the status register. Again, it's useful to be able to
easily translate the hexadecimal display, so as to view the individual flags.
Here's a chart of the flags within the status register:
7bS43ElD
NV-BDIZC
I—Interrupt disable. More exactly, this bit disables the IRQ (interrupt request)
pin activity. More on this control bit much later. This flag may be turned on with
the SEI (set interrupt disable) instruction, and turned off with the CLI (clear
interrupt disable) instruction.
Z—the Z flag, as above.
Flags B, D, and I are not testable flags in that there are no branch instructions
that test them directly. D, the decimal mode flag, and I, the interrupt lockout
flag, may be considered "control" flags. Instead of reporting conditions found
as the program runs, they control how the program operates.
A Note on Comparison
If we wish to compare two bytes with each other, we must perform a
comparison. One value must be in a register (A, X, or Y); the other must
either be stored in memory, or must be an immediate value we use in the
instruction.
We can use more than one branch instruction after a comparison. Suppose
our program wanted to test the Y register for a value equal to or less than
5. We might code
CPY #$D5
BEQ . .somewhere
BCC ..somewhere
We can see that our code will branch if the value is equal to 5 (using the
BEQ) or less than 5 (using the BCC); otherwise it will continue without
branching. In this case, we could make the coding more efficient by chang
ing it to read
FLAGS, LOGIC, AND INPUT 47
CPY #$Dt
BCC ..somewhere
A little common sense will tell us that testing a number to see if it is less
than b is the same as testing it to see if it is less than or equal to 5.
Common sense is a valuable programming tool.
Instructions: A Review
We have looked at the three data registers—A, X, and Y—and have seen
three types of operation we can perform with them:
Load: LDA, LDX, LDY
Store: STA, STX, STY
Compare: CMP, CPX, CPY
Up to this point, the registers have identical functions, and we can use
any of them for any of these functions. But new instructions are creeping
in that give a different personality to each of the three.
We have noted that INX, INY, DEX, and DEY for increment and dec
rement are restricted to X and Y only; and we've also mentioned that X
and Y can be used for indexing. Soon, we'll start to examine some of the
functions of the A register, which is often called the accumulator because
of its ability to do arithmetic.
There are eight branch instructions. They have already been discussed,
but there is one additional piece of information that is important to keep
in mind. All branches are good only for short hops of up to a hundred
memory locations or so. So long as we write short programs, that won't
be a limitation; but we'll look at this more closely in Chapter 5.
Logical Operators
Three instructions perform what are called logical operations. They are:
AND (Logical AND); ORA (Logical OR); and EOR (Exclusive OR). These
instructions work on the A register only.
48 MACHINE LANGUAGE FOR COMMODORE MACHINES
Logical operators work in such a way that each bit within a byte is treated
independently of all the other bits. This makes these instructions ideal for
extracting bits, or manipulating certain bits while leaving others alone.
We'll look at formal definitions, but the following intuitive concepts are
useful to programmers:
AND—Logical AND to A
For each bit in the A register, AND performs the following action:
Examine the upper half of this table. When the mask is zero, the original
bit in A is changed to zero. Examine the lower half. When the mask is
one, the original bit is left unchanged. Hence, AND can selectively turn
bits off.
Result 1DDDD111
xxx
Note that the bits marked have been forced to "off," while all other bits
remain unchanged.
FLAGS, LOGIC, AND INPUT 49
OR A—Logical OR to A
For each bit in the A register, OR A performs the following action:
Original A Bit Mask Resulting A Bit
D D D
1 D 1
D 1 1
111
Examine the upper half of this table. When the mask is zero, the original
bit in A is left unchanged. Examine the lower half. When the mask is one,
the original bit is forced to "on." Hence, OR A can selectively turn bits on.
Result 1111D111
xxx
Note that the bits marked have been forced to "on," while all other bits
remain unchanged.
EOR—Exclusive OR to A
For each bit in the A register, EOR performs the following action:
Original A Bit Mask Resulting A Bit
ODD
1 D 1
D 1 1
1 1 D
Examine the upper half of this table. When the mask is zero, the original
bit in A is left unchanged. Examine the lower half. When the mask is one,
the original bit is inverted; zero becomes one and one becomes zero.
Hence, EOR can selectively flip bits over.
Result 1D11D111
xxx
50 MACHINE LANGUAGE FOR COMMODORE MACHINES
Note that the bits marked have been flipped to the opposite value, while
all other bits remain unchanged.
Why would we turn individual bits on or off? There are several possible
reasons. For example, we might wish to control external devices through
the I A's (interface adaptors). Within the I&'s input and output ports each
of the eight bits might control a different signal; we might want to switch
one control line on or off without affecting other lines.
When we're looking at input from an IA port, we often read several input
lines mixed together within a byte. If we want to test a specific bit to see
if it is on or off, we might mask out all other bits with the AND instruction
(changing unwanted bits to zero); if the remaining bit is zero, the whole
byte will now be zero and the Z flag will be set.
ASCII SDDllDlDl
Binary
We must use the ASCII value for input or output. We must use the binary
value for arithmetic, particularly addition and subtraction. How could we
get from one to the other? By taking bits out (AND) or putting bits in (OR A).
Alternatively, we could use addition or subtraction; the logical operators,
however, are simplier.
You may be familiar with the GET statement in BASIC. If so, you'll find
the same characteristics in GET IN:
To call for a key from the keyboard, code JSR $FFE4. Values in X and
Y are not guaranteed to be preserved, so if you have important information
in either register, put it away into memory.
Subroutine: GETIN
Address: $FFE4
Action: Takes a character from the input channel and places it
into the A register. The input channel is the keyboard input
buffer unless arrangements have been made to switch it.
The character received is usually ASCII (or PET ASCII). When read
from the keyboard, the action is similar to a BASIC GET statement:
one character will be taken from the buffer; it will not be shown on the
screen. If no character is available from the keyboard input buffer, a
value of binary zero will be put into the ft register. The subroutine will
not wait for a key to be pressed but will always return immediately.
STOP
Machine language programs will ignore the RUN/STOP key ... unless
the program checks this key itself. It may do so with a call to STOP,
address $FFE1. This checks the RUN/STOP key at that moment. To
make the key operational, $FFE1 must be called frequently.
52 MACHINE LANGUAGE FOR COMMODORE MACHINES
The RON/STOP key is often brought into play while programs are being
tested, so that unexpected "hangups" can still allow the program to be
terminated. Coding to test the RUN/STOP key is often removed once
testing is complete, on the assumption that no one will want to stop a
perfect program. Incidentally, if you plan to write nothing but 100 percent
perfect programs, you will not need to use this subroutine.
Subroutine: STOP
Address $FFE1
Action: Check the RUN/STOP key. If RUN/STOP is being pressed
at that instant, the Z flag will be set when the subroutine
returns.
In PET/CBM, the system will exit to BASIC and say READY if the
RUN/STOP key is being pressed. In this case, it will not return to the
calling machine language program.
Programming Project
Here's our task: we wish to write a subroutine that will wait for a numeric
key to be pressed. All other keys (except RUN/STOP) will be ignored.
C128 note: Remember to check Appendix E, under Exercises for the
Commodore 128, for the appropriate coding.
When a numeric key is pressed, it will be echoed to the screen, and then
the subroutine will be finished. One more thing. The numeric character
will arrive in ASCII from the keyboard: we wish to change it to a binary
value before giving the final RTS statement. This last operation has no
useful purpose yet, except as an exercise, but we'll connect it up in the
next chapter.
We will check the RUN/STOP key first. But wait. Where will we go if we
find that the key is pressed? To the RTS, of course; but we don't know
FLAGS, LOGIC, AND INPUT 53
where that is, yet. In these circumstances, we usually make a rough guess
and correct it later. Make a note to check this one ...
Now we've gotten a character; we must check that it's a legitimate numeric.
The ASCII number set D to q has hexadecimal values $3D to $3S. So
if the value is less than $30, it's not a number. How do we say "less
than?" After a compare, it's BCC (branch carry clear). So we code
Did you spot the use of immediate mode at address $D344? Make sure
you follow the logic on this. Another point: what if no key has been pressed?
We're safe. There will be a zero in the A register, which is less than hex
30; this will cause us to go back and try again.
Now for the high side. If the number is greater than hex 3R, we must
reject it since it cannot be an ASCII numeric. Our first instinct is to code
CMP #$3q and BCS. But wait! BCS (branch carry set) means "branch
if greater than or equal to." Our proposed coding would reject the digit q,
since the carry flag would be set when we compared to a value of hex
aq.
We must check against a value that is one higher that $3q. Be careful,
though, for we're in hexadecimal. The next value is $3 A. Code it:
If we get this far, we must have an ASCII character from 0 to q; let's print
it to the screen so that the user gets visual feedback that the right key
has been pressed:
Now for our final task. We are asked to change the ASCII character into
true binary. We may do this by knocking off the high bits. We remember,
of course, that to turn bits off we must use AND:
It's a good thing that we printed the character first, and then converted to
binary; the character must be ASCII to print correctly.
54 MACHINE LANGUAGE FOR COMMODORE MACHINES
One last thing. We had a branch (on the RUN/STOP key) that needed to
connect up with the RTS. Did you make that note about going back and
fixing up the branch? Now is the time to do it, but before you go back,
terminate the assembly with an extra RETURN on the keyboard (the
assembler gets confused if it prompts you for one address and you give
another; get out before you go back).
Check your coding, disassemble, go back to BASIC and run with a SYS
flEfl. Tap a few letter keys and note that nothing happens. Press a num
ber,1 and see it appear on the screen. The program will terminate. SYS it
again and see if the RUN/STOP works. Try a BASIC loop to confirm that
BASIC and machine language work together.
Project for enthusiasts: Try modifying the program so that it checks for
alphabetic characters only. Alphabetic characters run from $41 to $5A,
inclusive.
number is negative. The computer doesn't care. It handles the bits whether
the number is considered signed or not, but we must write our program
keeping in mind the type of number being used.
—There are three logical operator instructions: AND, ORA, and EOR. These
allow us to modify bits selectively within the A register. AND turns bits off;
ORA turns bits on; and EOR inverts bits, or flips them over.
57
58 MACHINE LANGUAGE FOR COMMODORE MACHINES
When signed numbers are held in multiple bytes, the sign is the highest
bit of the highest byte only.
Addition
Principles of addition are similar to those we use in decimal arithmetic;
for decimal "columns," you may substitute "bytes." Let's look at a simple
decimal addition:
NUMBERS, ARITHMETIC, AND SUBROUTINES 59
Rule 3: When we start at the right-hand column, there is no carry for the first
addition. (We must clear the carry with CLC before starting a new addition.)
Rule 4: When we have finished the whole addition, if we have a carry and no
column to put it in, we say the answer "won't fit." (If an addition sequence of
unsigned numbers ends up with the carry flag set, it's an overflow condition.)
START:
OO1O1O11 10111001 N0CAR«Y
4- Q0001010 11100101/
10011110
CARRY
/
00110110'
Figure 4.1
CLC
LDA$D3flD
ADC $D3fll
STA $D3fi2
CLC
LDA $D3AD
ADC $D3BD
STA $D3CD
LDA $D3A1
ADC $D3B1
STA $D3C1
If we had two-byte signed numbers in the same locations, we'd add them
exactly the same way, using the same code as above. In this case, how
ever, we'd check for overflow by adding the instruction BVS, which would
branch to an error routine. The carry flag would have no meaning at the
end of the addition sequence.
Subtraction
Subtraction might be defined as "upside down" addition. The carry flag
again serves to link the parts of a multibyte subtraction, but its role is
reversed. The carry flag is sometimes called an "inverted borrow" when
used in subtraction. Before performing a subtraction, we must set the C
flag with SEC. If we are worried about unsigned overflow, we look to
confirm that the carry is set at the completion of the subtraction operation.
If the carry is clear, there's a problem.
SEC
LDfi $03flD
SBC $D3fil
STft $D3flE
Comparing Numbers
If we have two unsigned numbers and wish to know which one is larger,
we can use the appropriate compare instruction—CMP, CPX, or CPY—
and then check the carry flag. We've done this before. If the numbers are
more than one byte long, however, it's not quite so easy. We must then
use a new technique.
1D0: £D11DD1QD
EDD: 211DD1DDQ
To double the number, each bit has moved one position to the left. This
makes sense, since each bit has twice the numeric "weight" of the bit to
its right.
left one position; and the bit that "fali$ out" of the byte—in this case, a
zero bit—moves into the carry. It can be diagrammed like this:
CARRY 4I|{| T T T
(C FLAG) I I 1 1 1 1 1 1
ASL
IN AN ASL (ARITHMETIC SHIFT LEFT), EACH BIT
MOVES ONE POSITION LEFT. A ZERO MOVES INTO THE
LOW-ORDER BIT.
Figure 4.2
That's good for doubling the value of a single byte. If a "one" bit falls into
the carry flag, we can treat that as an overflow. What about multiple bytes?
It would be ideal if we had another instruction that would work just like
&SL. Instead of pushing a zero bit into the right hand side of the byte,
however, it would push the carry bit, that is, the bit that "fell out" of the
last operation. We have such an instruction: ROL.
ROL (rotate left) works exactly like &SL except that the carry bit is pushed
into the next byte. We can diagram it as follows:
CARRY
CAR
J^ j,_ J^ ^ J, j, j, '« J
T T T T T T T i
RRY
CARRY
IN A ROL (ROTATE LEFT), THE CARRY MOVES INTO
THE LOW ORDER BIT; EACH BIT MOVES LEFT; AND THE
HIGH ORDER BIT BECOMES THE NEW CARRY.
Figure 4.3
Thus, we can hook two or more bytes together. If they hold a single
multibyte number, we can double that number by starting at the low-order
end. We ASL the first value and ROL the remainder. As the bits fall out
of each byte, they will be picked up in the next.
Multiplication
Multiplying by two may not seem too powerful. We can build on this starting
point, however, and arrange to multiply by any number we choose.
NUMBERS, ARITHMETIC, AND SUBROUTINES 63
0
ASL
^U"1
POL f-\ — ~- ~- 4-C LOW ORDER BYTE
Figure 4.4
How can we multiply by four? Multiply by two, twice. How can we multiply
by eight? Multiply by two, three times.
To multiply by ten, you first multiply by two; then multiply by two again.
At this point, we have the original number times four. Now, add the original
number, giving the original number times five. Multiply by two one last
time and you've got it. We'll see an example of this in Chapter 7.
LSR (logical shift right) puts a zero into the left (high-order) bit, moves all
the bits over to the right, and drops the leftover bit into the carry. ROR
(rotate right) puts the carry bit into the left bit, moves everything right, and
64 MACHINE LANGUAGE FOR COMMODORE MACHINES
0 LSR
C FLAG
IN AN LSR, ZERO MOVES INTO THE HIGH BIT, AND ALL
BITS MOVE RIGHT ONE POSITION; THE LOWEST BIT
BECOMES THE CARRY.
9. i i i i i \% i i^
i t~ ~r ~r ~7~ ~r ~r t~
0 LSR
ROR
Figure 4.5
drops the leftover bit into the carry once again. At the end of a right-shifting
sequence, the final carry bit might be considered a remainder after dividing
by two.
For this reason, you'll often see the instructions coded with the identity of
the A register coded in the address part of the instruction. We would code
NUMBERS, ARITHMETIC, AND SUBROUTINES 65
Some programmers wonder about the terms logical and arithmetic, used
as part of the definition. The distinction is related to the way that signed
numbers are treated. "Logical" means that the sign of a number will prob
ably be lost if the number was intended to be signed. "Arithmetic" means
that the sign will probably be preserved. It's purely a terminology question:
the bits themselves move exactly as you would expect them to do.
Subroutines
We have written programs that are subroutines called by BASIC. We have
written subroutine calls to built-in operations such as $FFD5 or $FFE4.
Can we also write our own subroutine and arrange to call it?
Of course we can. RTS (return from subroutine) does not mean "return
to BASIC." It means "return to whoever called this routine." If BASIC
called up the machine language routine, RTS takes you back to BASIC.
If another machine language program called up the subroutine, RTS will
return to the calling point.
We wrote a useful subroutine in the last chapter. Its purpose was to accept
only numeric keys, echo them to the screen, and convert the ASCII value
to binary. Now we'll use this subroutine to build a more powerful program.
Here it is. Be sure it's entered in your computer.
The Project
Here is our mission: using the above subroutine, we wish to build a simple
addition program. Here's how we want it to work. The user will touch a
numeric key, say "3". Immediately, "3 + M will appear on the screen.
Now the user will touch another key, say f' Af', and the program will
complete the addition so that the screen shows fl3 + < = 7M. We will
assume that the total is in the range D to R so that we don't have to worry
about printing a two-digit answer—don't try 5 + 5 or you'll get a wrong
answer.
We call our prewritten subroutine, which waits for a numeric key, echos
it to the screen, and converts the value to binary in the a register.
Our next action is to print the plus sign. We know how to do this, once
we look up the ASCII code for this character. Appendix D tells us that it's
$2B, so we'll need to LDft #$2B and JSR $FFD2. But wait a minute!
Our binary value is in the a register, and we don't want to lose it. Let's
store the value somewhere:
We picked $D3CD, since nobody seems to be using it, and put the binary
number safely away there. Now we print the plus sign, and go back to
ask for another digit.
When the subroutine returns, it has a new binary value in the a register;
the digit has been neatly printed on the screen behind the plus sign. Now
we need to print the equal sign. But again, wait! We must put our binary
value away first.
the moment, so let's slip the value across into one or the other. We have
four "transfer" commands that will move information between a and either
index register:
TAX—Transfer A to X TAY—Transfer A to Y
TX A—Transfer X to A TY A—Transfer Y to A
Like the load series of commands, these instructions make a copy of the
information. Thus, after TAX, whatever information was in A is now also
in X. Again like the load commands, the Z and N status flags are affected
by the information transferred. It doesn't matter whether we use X or Y.
Let's pick X:
AQ3bD TAX
A D3bl LDA #$3D
A D3b3 JSR $FFD5
We have put our second value into X and printed the equal sign ($3D).
Now we can bring the value back and do our addition. The next two
instructions can come in any order:
AD3tb TXA
A D3t7 CLC
A D3tfl ADC $D3CD
We have our total in the A register. It's almost ready to print, except for
one thing: it's in binary. We want it in ASCII.
Are you basically a neat person? Then you'll want to print a RETURN to
start a new line:
Back to BASIC. This time we do not give SYS fl 2 fl—that's the subroutine
and we want the main routine, remember?
Give the SYS fl5D command. Tap a couple of numeric keys that total
nine or less. Watch the results appear instantly on the screen.
If you like, set up a BASIC loop and call the routine several times.
Project for enthusiasts: You couldn't resist, could you? You had to type
in two digits that totaled over 9 and got a silly result. OK, your project is
to try to expand the above code to allow for two-digit results. It's not that
hard, since the highest possible total is q + R or Ifl; so if there are two
digits, the first one must be the digit 1. You'll need to compare for the
result over binary nine, and then arrange for printing the one and sub
tracting ten if necessary. Sounds like fun.
bytes by using the ROR (rotate right) instruction, starting at the high byte of
the number.
—The shift and rotate instructions may be used on the contents of the A register
or directly on memory. The N and Z flags are affected, and the C flag plays
an important role in the shift/rotate action.
—If we wish to multiply by a value other than two, we may need to do more
work but we can get there.
If you've been following the logic, you have developed quite a bit of ca
pability in machine language. You can input, you can output, and you can
do quite a bit of arithmetic in between.
By now, you should have developed skills with the machine language
monitor and feel much more comfortable zipping in and out. These skills
are not difficult, but they are important to the beginner. Without them, you
can never get comfortably into the real meat: how to code machine lan
guage itself.
5
Address
Modes
This chapter discusses:
71
72 MACHINE LANGUAGE FOR COMMODORE MACHINES
Addressing Modes
Computer instructions come in two parts: the instruction itself, or op code,
and the address, or operand. The term "address" is a little misleading,
since sometimes the operand does not refer to any memory address.
The term address mode refers to the way in which the instruction obtains
information. Depending on how you count them, there are up to 13 ad
dress modes used by the 650x microprocessor. They may be summarized
as follows:
5. A location in which the real (two-byte) jump address may be found: indirect.
6. An offset value (e.g., forward R, back 17) used for branch instructions:
relative.
7. Combination of indirect and indexed addresses, useful for reaching data
anywhere in memory: indirect, indexed; indexed, indirect.
We might say that such instructions have "no address." The precise term
is "implied address," which seems to say that there is in fact an address
but we do not need to state it.
D35D LDA#$DD
D35E STA $1Z34
D355 ORA
That's all very well, but when we have finished testing our program and
are satisfied that it runs correctly, we don't want the BRK instructions
there. That's easy to fix. We replace the BRK codes ($DD) with NOP's
($EA), and the program will run through to the end.
If we are writing a program and suspect that we may need to insert one
or two extra instructions within a certain area of the code, we can put a
74 MACHINE LANGUAGE FOR COMMODORE MACHINES
number of NOP instructions there. The space will be available for use
when we need it.
Other than the shift and rotate instructions, there is one other set of in
structions that manipulates memory directly. You may recall I NX, I NY,
DEX, and DEY increment or decrement an index register.
INC (increment memory) adds one to any memory location. DEC (dec
rement memory) subtracts one from any memory location. Both instruc
tions affect the Z and N flags.
There are certain instructions for which immediate addressing is not pos
sible. For example, we can LDA #$DD, that is, bring in the actual value
zero rather than the contents of an address, but we cannot STA imme
diate—we must store the information somewhere in memory.
I MEMORY 1
/
Figure 5.1 Absolute Mode Specifies One Address Anywhere Within Memory.
One-location addressing can be good for any of several jobs. On the PET/
76 MACHINE LANGUAGE FOR COMMODORE MACHINES
Zero-Page Mode
A hexadecimal address such as $D3fll is sixteen bits long and takes up
two bytes of memory. We call the high byte (in this case, $D3), the
"memory page" of the address. We might say (but usually don't) that this
address is in page 3 at position $fll.
I 1 ~1
\
Figure 5.2 Zero-Page Mode Specifies A Single Address from $0D to $FF.
Addresses such as $DD4C and $DDF7 are in page zero; in fact, page
zero consists of all addresses from $DDDD to $DDFF. Page-zero lo
cations are very popular and quite busy. There's an address mode spe
cially designed to quickly get to these locations: zero-page addressing.
We may think of it as a short address, and omit the first two digits. Instead
of coding LDA $DDRD, we may write LDA IRQ, and the resulting code
will occupy less space and run slightly faster.
Zero-page locations are so popular that we'll have a hard time finding
spare locations for our own programs. As a result, we tend to conserve
zero-page Ipcations on Commodore machines. We'll need the few that
are available for a special addressing mode, indirect, indexed, that will
be discussed later.
There are many locations in zero page that are useful to read. For example,
the BASIC system variable ST, which is important in input/output handling,
may be examined there (location $qt in PET/CBM, location $qD in VIC-
20 and Commodore 64). If you need to know whether the user is holding
down a key, there's an address in zero page that will tell you that (location
$q? in PET/CBM, $CB in VIC and 64, $D4 in C128).
only. It's good for a specific value; but for a range of values we need
something more.
4
I
>-- -Index
BASE VALUE
ADDRESS
Figure 5.3
Indexing is used only for data handling: it's available for such activities as
load and store, but not for branch or jump. Many instructions give you a
choice of X or Y as an index register; a few are limited specifically to X
or Y. Instructions that compare or store X and Y (CPX, CPY, STX, and
STY) do not have absolute, indexed addressing; neither does the BIT
instruction.
This mode usually uses the X register; only two instructions, LDX and
STX, use the Yregister for zero-page, indexed addressing. In either case,
the index is added to the zero-page address; if the total goes beyond zero
page, the address "wraps around." As an example, if an instruction is
coded LDA $ED, X and the X register contains 50 at the time of exe
cution, the effective address will be $DD3D. The total ($ED + $50 or
$13 D) will be trimmed back into zero page.
$00 $FF
BASE
ADDRESS
Figure 5.4
Thus, any zero-page address can be indexed to reach any other place in
zero page; the reach of E 5 b locations represents the whole of zero page.
This creates a new possibility: with zero-page, indexed addressing, we
can achieve negative indexing. For this address mode only, we can index
in a downward direction by using index register values such as $FF for
-1, $FE for - E, and so on.
Figure 5.5
A branch instruction with a relative address of $D5 would mean, "if the
branch is taken, skip the next 5 bytes." A branch instruction with a relative
address of $F7 would mean, "if the branch is taken, back up R bytes
from where you would otherwise be." As a signed number, $F7 is equal
to a value of -R.
broken into subroutines, and the subroutines into even smaller subrou
tines; this way, everything is neat and testable. If you should find a branch
that won't reach, ask yourself whether it's time to break your program into
smaller chunks before the logic gets too messy. By the liberal use of
subroutines, you can arrange your code so that all branches are short and
easily within reach. If you do break up the program structure, the branches
will then always reach. It's up to you to choose your coding style, but you
might give the question some thought.
An example will help to make things clear. Suppose that at address $D33C
we have the instruction JMP ($1234). The parentheses tell us that in
direct addressing is involved. The machine code is hex tC 34 12; as
always, the address is "turned around." Now suppose that at addresses
$1234 and $1235 we have stored values $24 and $fcfi. The jump
instruction would behave as follows: it would go to $1234 and $1235,
get the contents, and the program would transfer to address $fcfl24.
INDIRECT
ADDRESS
Figure 5.6
ADDRESS MODES 87
Within ROM, there are a large amount of permanent instructions that the
computer uses to perform its tasks. Since it's in ROM, we can never change
this code. If the various programs were linked only by means of JMP and
JSR statements, they could not be changed, and we would not be able
to modify the behavior of the machine.
Built into the ROM program, there are a series of carefully planned indirect
jumps. Instead of the ROM leaping from one instruction directly to another,
it jumps indirectly via an address stored in RAM. We can change the
contents of RAM; and if we change the address stored in RAM, we can
modify the behavior of the system. The best-known indirect address is
that associated with the interrupt sequence: it's at lOCHD in PET/CBM
and $D314 in VIC, 64, PLUS/4, and C128.
You might not code many indirect jumps, but you'll be glad that they are
there in ROM.
In fact, we get a limitation and a bonus. First, the limitation: for indirect,
indexed instructions the indirect address must be in zero-page—two bytes,
of course, organized low byte first, as always. Next, the bonus: after the
indirect address is obtained, it will be indexed with the Y register to form
the final effective address.
Let's step our way through the mechanism and see how it works. Suppose
I code LDA ($CD). Y with values $11 in address $DDCD and $EE in
address $DDC1. If the Y register contains a value of 3, the instruction
will follow these steps: The address of $DDCD/1 is extracted, giving
-$ 2 211; then the contents of Y are added to give the effective address
of $221 A. If the contents of Y changed, the effective address would
82 MACHINE LANGUAGE FOR COMMODORE MACHINES
change slightly. If the indirect address at $CD and $C1 was changed,
the effective address would change radically.
The combination of indirect and indexing may seem like overkill. If you
can designate any location in memory with an indirect address, why bother
with indexing? After all, anywhere plus one is still anywhere.
00 FF
INDIRECT,
INDEXED
Figure 5.7
-DATA IN MEMORY-
Figure 5.8
$00 / / $FF \
Figure 5.9
could then put the character away with the instruction STA ($bD,X).
Thus, if line zero was involved, its indirect address at address $bD/tl
would be used; for line 1, the address at $fc2/b3 would be used; and
so on. After we had stored the character concerned, we'd need to bump
the indirect pointer so that the next character will go into a new position:
INC $t>D , X would do the trick.
First, look for unused locations. There are only a few of them: on the VIC
and Commodore 64, you'll find four locations at locations $DDFCto$DDFF.
That's enough for two indirect addresses.
If you need more, look through the memory maps for locations designed
as "work areas" or "utility pointers." They can usually be put to work for
a temporary job.
Finally, you can take working parts of zero-page and copy them to some
other parts of memory. You can use these locations, carefully putting back
the original contents before returning to BASIC. Don't try this with any
values that are used by the interrupt routines (involved with screen, key
board, or RS-232); the interrupt can and does strike while your machine
language program is running. And if the interrupt program changes these
zero-page values, your program is going to behave badly.
We'll select a number of lines on the screen; within each line, we'll change
a certain group of characters. In other words, we will write the code so as
to manipulate a window on the screen.
To do this, we'll need to code two steps: setting up the start of a screen
line, and later moving on to the next line when needed. Within each line,
we'll work our way through the range of screen columns that we have
selected. In fact, it's a big loop (for the lines) containing a small loop (for
the columns within that line). We'll use indirect addressing to point to the
start of each line, and indexing (the Y register) to select the portion of that
line to change.
For a 22-column machine, change the above to LDfl #$lb; and for an
80-column PET, code LDfl #$5D.
Have you coded the correct value? Let's proceed with our next decision.
In the PET/CBM, screen memory starts at address $flDDD; in VIC or
Commodore 64, the screen starts at whatever page is designated in ad
dress $DEflfl. Let's code as follows:
The NOP instruction does nothing, but it makes the coding the same length
so that we may continue with address $D341 in either case. The R
register tells us our line length, and the X register tells us the page number
on which the screen starts. Let's put them away. The line length will be
86 MACHINE LANGUAGE FOR COMMODORE MACHINES
needed for addition later, so we may put it anywhere safe; the screen
address will be part of an indirect address, so it must go into zero-page.
It's hard to find a zero-page address that may be used in all Commodore
machines; we'll choose $DDBBand$DDBC. $BB contains the low byte
of the address, of course. Let's code
Note that we are using the zero-page addressing mode for the instruction
at address $D344. That puts the high byte of the address in place. Now
we'll set the low byte to zero:
Our indirect address is now pointing at the start of screen memory. Let's
discuss in more detail what we want to do with the screen. Specifically,
we want to change a number of lines, let's say K, on the screen. We
will step along our indirect address by adding to the indirect address:
maybe 2E, maybe 4U, maybe 60; whatever is in address $D3AD. And
we won't do the whole line; we'll start in column 5 and go to column Ifl.
Let's count the lines in the X register; we'll start X at zero
Now we're ready to do a screen line. Later, we'll adjust the indirect address
and come back here to do another line. We should make a note to our
selves: "Come back to $D34C for the next screen line."
The indirect address is pointing at the start of the line. We want to start
work in column 5. That means that Y should start with an offset of A (the
start of the line plus 4). Let's do it:
We're going to walk Y up, and loop back to this point for the next character
on the line. We might note: "Come back to $ D 3 4E for the next character."
We're ready to go. Let's dig out the character that's currently on the screen:
This is worth a review. Locations $BB and $BC contain the address of
the start of screen memory; on the PET/CBM, for example, this would be
$fl DDD. To this, we add the contents of Y (value A) to create an effective
address of $fiDD4; and from location $flDD4 we get the screen char
acter.
ADDRESS MODES 87
We decide that we will leave spaces alone. The space character shows
on the screen as a value of decimal 32, hex ED. Let's skip the next
operation if it's a space:
We have done one character. Let's move along the line to the next char
acter, and if we have passed column Ifl (Y = 17) we should quit and go
to the next line.
A D35fi INY
A 035^ CPY #$12
a D35B BCC $034E
Y moves along to the next character position: five, then six the next time
around, and so on. So long as Y is less than Ifl (hex 12) we'll go back,
since BCC means "branch less than." If we get past this point, we have
completed the line and must move to the next one.
We move to the next line by adding to the indirect address. We must add
22, or 4 0, or fiD; the value is in address $D3AD (you may remember
that we stored it with the instruction at $0341). We must remember to
clear the carry flag before starting the addition, and to add starting at the
low byte of the address (at $BB).
A 035D CLC
A 035E LDA $BB
A 03b0 ADC $03A0
88 MACHINE LANGUAGE FOR COMMODORE MACHINES
The last three instructions seem odd. Why would we add zero to the
contents of $BC? Surely that changes nothing. The answer is obvious
after a little thought: there might be a carry from the previous addition.
Now we're ready to count the lines: we had decided to use X as a counter.
Let's add one to X, and test to see whether we have done the 14 lines:
A D3tB INX
A Q3tC CPX #$DE
A D3bE BNE $034C
A D37D RTS
Disassemble and check it. Again, you'll find that the code occupies more
than one full screen. Return to BASIC.
This time, we'll write a small BASIC program to exercise the machine
language code. Type NEW to clear out any old BASIC code, and enter
1DD FOR J =1 to ID
11D SYS flEfl
120 FOR K = 1 to 2DD
13D NEXT K,J
The extra loop is to slow things down. Machine language runs so fast that
the effect might not be properly visible if run at full speed.
1. Below the BASIC area, we have the cassette buffer area. This is available
to us, providing we are not engaged in input/output activity.
2. Start-of-BASIC (SOB) is usually a fixed address within the machine. In PET/
CBM, it's at $04Dl (decimal IDE5). In Commodore 64, it's at $DflDl
(decimal ED4R). In the PLUS/4 series, it's at $1DD1 (decimal 4DR7). In
the VIC-20, it may be at one of several places: $UAU1, $lDDlfor$lBDl.
A pointer marks this location. The pointer is located at SEfi/SER (decimal
AU and Al) in PET/CBM, and at $EB/$EC (decimal A3 and AA), in VIC-
20, Commodore 64, and PLUS/4.
You should inspect the pointer and confirm that it contains an appropriate
address. You may notice that it's much easier to do this using the machine
language monitor, since the address is split between the two bytes (low order
first, as always).
3. End-of-BASIC is signaled by three zero bytes somewhere after the SOB. If
you command NEW in BASIC, you'll find the three bytes right at the start of
BASIC; there is no program, so start and end are together. There is no
pointer that indicates end-of-BASIC, just the three zeros; but the next location
(SOV) will often be directly behind the end-of-BASIC
BASIC RAM
A
I I I
SOB SOV SOA EOA BOS TOM
Figure 6.1
LINKING BASIC AND MACHINE LANGUAGE 93
The BASIC program that you type in will occupy memory space from start-
of-BASIC to end-of-BASIC. If you add lines to a program, end-of-BASIC will
move up as extra memory IS taken up by your programs. If you delete lines,
end-of-BASIC will move down.
The SOV pointer is extremely important during BASIC load and save activ
ities. If we give the BASIC command SAVE in direct mode, the computer
will automatically save all memory from SOB to just before the SOV. Thus,
it saves the whole BASIC program, including the end-of-BASIC marker of
three zero bytes, but does not save any variables. If we give the BASIC
command LOAD in direct mode, the computer will automatically load the
program, and thfcn place the SO V pointer to just behind the last byte loaded.
In this way, variables will never be stored over the BASIC program; they will
be written above the end-of-BASIC. More on this later.
6. End-of-arrays (EOA) is set one location beyond the last array location in
BASIC. Above this point is seemingly "free" memory—but it's not really free,
as we'll see soon. A pointer marks this location. The pointer is located at
$EE/$EF (decimal 4h and 47) in PET/CBM, and at $31/$3E (decimal
41 and 5D) in VIC-20, Commodore 64, and PLUS/4.
If the BASIC program is changed, the EOA pointer is set to match the SO A
and SOV. Thus, all BASIC arrays are wiped out the moment a change is
made to the BASIC program.
Let's change direction and start to work our way down from the top of BASIC
memory.
7. Top-of-memory (TOM) is set one location beyond the last byte available to
BASIC. On the PET/CBM and VIC-20, its location depends on the amount
of memory fitted; a 32K PET would locate TOM at $flDDD. On the Com-
94 MACHINE LANGUAGE FOR COMMODORE MACHINES
modore 64, the TOM will normally be located at $ ADD D. A pointer marks
this location. The pointer is located at $34/$3 5 (decimal 55 and
53) in PET/CBM, and at $3?/$3fl (decimal 55 and 5b) in VIC-20, Com
modore 64, and PLUS/4.
If you examine the TOM pointer, you may find that it does not point at the
expected position. That may be because of the machine language monitor,
which has taken up residence at the top of memory and stolen away some
memory.
8. Bottom-of-strings, (BOS) is set to the last "dynamic" string that has been
created. If there are no BASIC strings, the BOS will be set to the same
address as TOM. As new dynamic strings are created, this pointer moves
down from the top-of-memory towards the EOA address. A pointer marks
this location. The pointer is located at $3D/$31 (decimal 4fi and A^) in
PET/CBM, and at $3 3/$3 A (decimal 51 and 55) in VIC-20, Commodore
64, and PLUS/4.
A dynamic string is one that cannot be used directly from the program
where it is defined; you might like to think of it as a manufactured string.
If, within a BASIC program, I type: 1DD X$ = "HAPPY NEW YEAR",
the BASIC interpreter will not need to store the string in upper memory;
it will use the string directly from where it lies within the program. On the
other hand, if I define strings with commands such as R$ = R$ + " *fl or
INPUT N$, the strings must be built into some spare part of memory.
That's where the BOS pointer comes in: the computed string is placed
high in memory, and the BOS moved down to mark the next free place.
If the BASIC program is changed, the BOS pointer is set to match the
TOM. Thus, all strings are wiped out the moment a change is made to
the BASIC program.
Here's the danger. As more and more dynamic strings are created, the
bottom-of-strings location keeps moving down. Even when strings are no
longer needed, they are abandoned and left dead in memory, taking up
space.
The BOS keeps moving down. Only when it touches the EOA will the
dead strings be cleaned up and the good ones repacked, an action called
garbage collection. It's important for BASIC programmers to know about
LINKING BASIC AND MACHINE LANGUAGE 95
It's evident that the space between EO& and BOS is not safe. If you put
a program there, the strings will eventually destroy it. We must look else
where.
sov SOA
OA EOi
EOA BOS TOM
CASSETTE 'A
BASIC VAR ARR STR
BUFFER ^
Figure 6.2
Second, move down the top-of-memory pointer and place the program in
the space that has been freed. Your spdce here is unlimited. Programs
placed here will take up permanent residence until the power is turned
off. Many monitors, such as Supermon, live here.
C.B.
Figure 6.3
f
EOA B< TOM
SOV SOV |
C.B.
00
0
Figure 6.4
Third, move up the start-of-variables pointer, and place the program after
the end of BASIC and before the new start-of-variables. Your space here
96 MACHINE LANGUAGE FOR COMMODORE MACHINES
is unlimited. Programs placed here will tend to "join company" with the
BASIC program; the two will save and load together.
After moving a pointer—as was done in the last two methods—it's a good
idea to return to BASIC and command CLR, so that all other variable
pointers will align correctly with the ones that have moved.
These three areas will be discussed more in a few moments. First, there
are one or two extra locations available to VIC-20 and Commodore 64.
This works, and will make as much space available as is needed. BASIC
programs will relocate as they load. But since the computer needs to be
reconfigured before the main program is loaded, and often needs to be
restored to its original configuration after the program is run, the method
is not popular in most Commodore machines. It's used fairly often in the
VIC-20, however.
The video chip in the VIC-20 can "see" RAM memory only in the memory
space $DDDD to $1FFF (decimal D to fllSl). Whatever variable in
formation appears on the screen must be taken from this memory area.
LINKING BASIC AND MACHINE LANGUAGE 97
The Wicked SO V
The start-of-variables pointer can be the cause of many troubles, if it's not
understood. The rules covering it are as follows:
1. Variables are written starting at the SOV.
2. BASIC S A VEs will save from memory beginning at start-of-BASIC and stop
ping at SOV.
3. Direct command BASIC LOADs will bring a program into memory, relocating
if appropriate, and then set the SO V pointer to the location following the last
byte loaded.
But if the SOV gets the wrong address, we're in trouble. The rules work
against us. Variables may be written into disastrous places. SAVEs will
cause too much or too little to be saved. LOADs may fix things, since
SOV will be changed by the load action. An attempt to change a program
with a bad SOV may cause too little or far too much memory to be moved
around. We must get the SOV right.
98 MACHINE LANGUAGE FOR COMMODORE MACHINES
How can the SO V go bad on us? Let's take three examples, corresponding
to the three major places that we might put machine language programs:
We're in trouble. The program seems to list correctly, but it's sick. If we
RUN, variables will start to be placed in the cassette buffer area; as more
variables are created, they are placed in progressively higher memory
locations. Eventually, the variables start to write over the BASIC program.
Everything stops. The poor programmer says LIST to see what's hap
pened; his BASIC program is gone, and all that's left is gibberish.
We're in still more trouble. Alternatively, the programmer lists the program,
and decides to delete one character from a line of BASIC. BASIC im
mediately starts to move memory, starting at the change point. It won't
stop moving memory until it reaches SOV, but that, again, is below where
we started. It will move everything that can be moved. RAM will be moved
along, which may not hurt anything; then the IA chips will be moved,
which may scramble colors or make the display go crazy; then it will try
to move ROM, which won't work because ROM can't be changed; then it
will wrap around to zero-page and move everything there, which is fatal
to the system. Eventually, it will collapse before reaching SOV since it
destroys its own working pointers.
All this could have been avoided if the programmer had loaded the machine
language program first, and then loaded the BASIC program. The SOV
would be placed behind the BASIC program, which is where it belongs in
this case.
Quiet Interlude
It's easy to see how the problem occurs, once you understand about the
SOV and its role. But if you don't understand the SOV, the results can
LINKING BASIC AND MACHINE LANGUAGE 99
It works this way. The student writes a perfect program into the cassette
buffer and saves it using the machine language monitor. Later, with a
BASIC program in place, the student recalls the program and inadvertently
moves SOV to an impossible location. When BASIC runs, the variables
will start to be written behind the machine language program, ahead of
the BASIC program. As more and more variables come into play, they
creep relentlessly toward the BASIC coding.
What goes through the programmer's mind at this time? "I was so sure
that the program is correct [in fact, it is]; but it's so bad that it's destroyed
memory! I suppose that machine language is much more difficult than I
thought."
And the student loses hope and gives up, not knowing that there's only
one small piece of information needed to fix everything up. This is only
one of the things that might go wrong when the SO V pointer is improperly
placed; even an attempt to change or save a BASIC program can cause
system failure.
Such experiences destroy confidence. They are responsible for the myth
that machine language is hard and only super-clever programmers can
cope with it.
S "PROGRAM",Dl,D33C,D3bl
followed by the end address plus one. In the example, the last location
saved will be $D3fcD. For disk saves, we might want to add the drive
number:
These programs, once saved, may be loaded directly from BASIC, but
watch the SOV carefully. VIC-20 and Commodore 64 BASIC LOAD com
mands should contain the extra field to defeat relocation: LOAD "PRO
GRAM » , fl 11 will insist that the program load back into the same memory
locations from which it was saved.
More on LOAD
There is a machine language L command to do a program load without
changing any pointer (especially SOV). There are a number of different
machine language monitors around, and the L command does not work
the same way on all of them. You might check out the one you are using:
ideally, the L command (format: L "PROGRAM", Dl) should bring back
the program without relocation.
We have been careful to say that the BASIC LOAD command changes
the SOV when given as a direct command. If a LOAD command is given
from within a program, SOV is not changed; but there's a new item to be
taken care of.
1. No pointers are affected. The program will not lose any variables when it
performs a LOAD. That's good: we will not lose any of our computations.
2. Once a LOAD is complete, the BASIC program will resume execution at the
first statement. It will not continue from where it left off; it will go back to the
beginning. For our application, that's bad; we seem to have lost our place
in BASIC.
ML. Then it would go back to the beginning and load ML. Then it would
go back to the beginning ... and so on. This is not satisfactory. Let's use
rule 1 to fix everything:
When we say RUN, the first line is executed. A is not equal to one, so
we continue on line 110. A is set to one, and line 12D causes a load
of the desired program. BASIC goes back to the beginning, but all variables
are preserved, so A is still equal to 1. Line 1DD tests A and goes to line
13 D, the next statement beyond the load. Everything works as required.
If there are multiple LOADs, line 1DD might be changed to IDD ON A
GOTO 130,150,170 . . . as necessary.
The answer is yes, although the problem is not so severe. You can see
that after loading a program to high memory using a direct command,
SOV will be positioned immediately above it. But that's too high—there's
no room for variables and we'll get an OUT OF MEMORY error for almost
anything we do.
loads; you cannot even give the next LOAD command if you're apparently
totally out of memory.
After End-of-BASIC—Harmony
Suppose we place the machine language program behind the end-of-
BASIC—that's the three zeros in memory—and move up the SOV so that
variables won't disturb this program. How will everything work now?
Things will work very well indeed. This time, we need to load our BASIC
program first; the SOV will go immediately behind BASIC. Then we may
load our machine language program, and the SOV moves right behind it.
The SOV is in exactly the right place, assuming we load in the right order.
(If we don't, the variables will destroy our machine language program.)
Once our two programs are together, and we say SAVE, the combination
program—BASIC and machine language together—will be saved. A little
thought will reveal that memory from start-of-BASIC to just before start-
of-variables contains everything we need. A subsequent load will bring
everything back in, and position SOV to exactly the right place. We now
have a "unit" program—BASIC and machine language working together,
loading and saving as one program.
BASIC Variables
There are four types of entry in the BASIC variable table. All variables,
regardless of type, occupy seven bytes; the first two bytes are the name,
LINKING BASIC AND MACHINE LANGUAGE 103
and the remaining five bytes (not always fully used) contain the value or
definition. The variable type is signaled as part of the name: high bits are
set over one or both letters of the name to signal a specific type.
SOV SOA
For example, if a floating point variable had a name AB, the name would
be stored in the two bytes as $41, $4 E—the ASCII codes for A and B.
The same would be true if the variable were named ABACUS, since only
the first two letters of the name are kept. In contrast, if the variable were
named AB£, meaning that it was an integer variable, the name would be
stored as $C1, $C5. The ASCII codes are the same, but the high bit
has been set over them. To complete the picture, a string variable named
AB$ would be coded with the name $41, $CE—the high bit is set over
the second character only.
NAME VALUE
2 BYTES 5 BYTES
Figure 6.6
There's a fourth type of entry that can go into the variable table, but it's
not a variable: it's a function definition. If we give the variable command
DEFFNA ( . . . an entry will be made in this table. It will be distinguished
by the high bit being set over the first character only.
String variables use only three of the five bytes provided; the first byte
signals the length of the string, and the next two bytes give the string's
address. This group of three bytes is called a descriptor.
704 MACHINE LANGUAGE FOR COMMODORE MACHINES
There are two types of numeric variables: floating point and integer. Float
ing point variables use all five bytes; integer variables use the first two
bytes only. It's possible to extract the value from a floating point variable
and put it to work, but it's not a simple procedure. A description of how
to do this is given in Appendix F. In contrast, it's quite easy to take the
value from an integer variable and use it.
41 DD fl3 ED DD DD DD
The first two bytes are the name— $41 is ASCII for A, and the zero
signifies no second letter—but where's the 5? Embedded within the fl3
EDDDDDDO, that's where; and it's a good deal of work to extract the
5 for further processing.
CE flD □□ D5 DD DD DD
Hex CE is the ASCII for the letter B ( $4E ) with the high bit set. $flO is
zero with the high bit set—again, there's no second letter. The value is
in the next two bytes, and it's easy to read. The last three bytes are not
used.
For a small number of variables, there's a short cut. Variables are placed
into the variable table in the order in which they are defined: whichever
variable is defined first in the BASIC program will be first in the variable
table. So if we arrange for our variables to be defined in a certain order,
we can streamline our machine language search to "first variable," "sec
ond variable," and so on, with no need to examine the names.
Let's take this one step further. If we want to use the first variable, all we
need to have is the address of the first variable somewhere in zero-page
so that we may use it as an indirect address. We already have that ad
dress—it's the SO V, the start-of-variables, and it's there pointing helpfully
at the first variable for us. By increasing the value of Y appropriately, we
can reach beyond the first variable and into the second or, for that matter,
the third or the thirty-sixth.
Project: We plan to place the machine language program behind the end-
of-BASIC. This will vary, depending on the machine being used. The
following code shows the correct addresses for the Commodore 64. Refer
to Appendix E for other machines.
First, let's do our BASIC coding to estimate its size. We need to guess at
the location of the end-of-BASIC so as to place our machine language
program. This program will ask machine language to take a value, V%,
and multiply it by ten. Remember to say NEW. We write the BASIC program
as follows:
106 MACHINE LANGUAGE FOR COMMODORE MACHINES
1DD V£ = D
110 FOR J=l TO 5
15D INPUT "VALUE";1%
13D SYS + + + +
14D PRINT "TIMES TEN = " ; 1%
15D NEXT J
It seems likely that our BASIC program will occupy less than 157 bytes.
We may check this later, but it seems safe to plan to start our machine
language program at around ED^n + lET.orElTb (hexadecimal flflD).
On that basis, we may change line 13D to SYS 517 b. Do not\ry to run
the program yet.
At this point, we could save the BASIC program to tape or disk and develop
the machine language program. This would allow us to refine each of the
two parts independently. For the sake of brevity—and because our ex
ample is an easy one and won't need touching up—we'll write the machine
code directly into memory.
Switch into the machine language monitor. Assemble the following code:
We have now extracted two bytes from the first variable, V%. The high
byte has been stored at both $D33C and $D33E; we'll see why in a
moment. The low byte of the value has gone to$D33Dand$D33F.
Project for enthusiasts: You might be able to code the above more com
pactly by more effective use of indexing.
byte. Perhaps we should be checking for overflow; but let's trust the num
ber to be within range for now.
A □ SAD CLC
A DflAl LDA $033D
A 0flA4 ADC $D33F
A DflA? STA $D33D
A DflAA LDA $D33C
A DflAD ADC $D33E
A DflBD STA $D33C
We have multiplied the number by ten. Now let's put it back into the variable
a DflBq LDY #$DE
a DflBB LDa $D33C
a DflBE STa ($ED),Y
a DflCD LDY #$D3
a DflCE LDa $D33D
a DflCE STa ($ED),Y
a DflC? RTS
The numbers go back exactly the same way we drew them out. We must
be careful to keep the high and low bytes correct. Integer variables have
the high-order byte first, followed by the low-order byte; this is exactly the
reverse of the way we use 650x addresses.
Check ... disassemble ... and then back to BASIC. List, and you'll
see your BASIC program again. There's no sign of the machine language
program, of course, but SaVE will now save everything together.
108 MACHINE LANGUAGE FOR COMMODORE MACHINES
RUN the BASIC program. Enter numbers as requested. Confirm that they
are multiplied by ten.
You may recall that our machine language program does not check for
overflow. RUN the program again, and see if you can find the highest
number that can be multiplied by ten without error. What happens at time
of overflow? Is it what you expected?
Project for enthusiasts: Can you add checks for overflow to the above
program? You must decide what to do if overflow occurs: print a message;
set the value to zero; or whatever you decide. But you shouldn't stop the
program or break to the monitor. Such a thing would upset the program
user. Your program will be longer. Don't forget, therefore, to change the
SOV pointer at $ED/$5E so that your program is safe from variables
—BASIC variables are of three major types: integer, real (floating point), and
string. Machine language programs are capable of reading and using any of
them; in particular, integer variables are quite straightforward.
—If we want, we can simplify the task of searching for BASIC variables by
deliberately creating them in a certain sequence.
Bootstrap programs are especially popular with VIC, Commodore 64, and
PLUS/4 for bringing in chunks of data such as sprites, new character sets,
or whole display screens of information. You might like to try your hand
at setting up such a system.
111
112 MACHINE LANGUAGE FOR COMMODORE MACHINES
A Brief Intermission
If you have been following along and performing the various projects, you
should know a great deal about the principles of machine language. You
should be capable of trying your hand at a number of small projects, and
investigating areas that may be of special interest.
This is a good time to stop and take stock. The remaining chapters are
"icing on the cake" ... they give extra detail and fine tuning on aspects
of machine language. If you feel uncertain about any material covered so
far, go back. Fix the fundamentals firmly in focus before you proceed and
plunge into ponderous points of interest.
The important rule to keep in mind about the stack is: "Leave these prem
ises as clean as when you found them." In other words, if you push three
items onto the stack, be sure you pull those three items back off again.
Don't ever branch away and leave the stack littered.
The stack is in memory at page 1. The stack pointer (SP) is one of the
items displayed in the register. To look for the information on the stack,
you must add $ D1D D to the value to get the next available stack position.
As an example, if the SP shows a value of $Ffl, the next item to go on
the stack will go into address $DlFfl; the moment we put an item onto
the stack, the pointer will move down so that it becomes $F7.
As the stack is filled, the stack pointer goes down. As the items are brought
back out of the stack, the stack pointer goes up. A low value in the stack
pointer means a full stack: a value below $40 signals trouble.
The 650x chip itself doesn't give the stack any special treatment. If a
machine language program—probably because of a coding error—wanted
to push one thousand items onto the stack, that would be OK as far as
the microprocessor was concerned. The stack would never leave page 1:
as the stack pointer went down beyond zero, it would wrap around to $FF
and keep going. You'd never get those thousand distinct items back, of
course. Similarly, if a program wanted to pull a thousand items from the
STACK, USR, INTERRUPT, AND WEDGE 113
SP USED 01FF
1 F8 USED 01FE
USED 01FD
USED 01FC
USED 01FB
USED 01 FA
USED 01F9
FREE 01F8
NEXT ITEM
PUSHED WILL GO
TO ADDRESS $01F8
NEXT ITEM
PULLED WILL COME
FROM ADDRESS $01F9
Figure 7.1
Within the BASIC environment, the stack pointer starts around $FA (the
first item will go into the stack at address $D1FA), and goes down from
there. When the stack pointer goes below about $AU, BASIC will signal
OUT OF MEMORY. That's over ibO available locations on the stack,
plenty of room for most applications
stack pointer is at $F3. If the program says PHA, the value 5 is stored
at address $ OIF3, and the stack pointer changes to $F2. Later in the
program, we encounter the instruction PL A: the stack pointer moves back
to $F3 and the value 5 is read from address $D1F3 and placed into
the A register.
PH A and PHP both put exactly one item onto the stack; PL A and PLP
pull one item. There are other commands that handle more than one stack
location.
More precisely, when a JSR occurs, the processor places onto the stack
the return address minus one as two bytes; the high-order part of the
address goes to the stack first. When an RTS is encountered, the pro
cessor takes the two bytes from the stack, adds one, and then proceeds
from the address so formed.
following events occur. The return address would be $D355, the instruc
tion directly behind the JSR; but an address of $D354 is calculated—
the D3 goes to the stack first, and the SA below it. The subroutine at
$D33C now starts to run. Eventually, it encounters an RTS. The values
54 and D3 are pulled from the stack and formed into address $D354;
one is added, and the processor resumes execution at address $D355.
You hardly need to know this. We have been using subroutines for some
time without knowing that all this happened. But sometimes, it's useful to
be able to examine the stack, asking, "Who called this subroutine?" The
answer is there.
The processor then takes its execution address from one of the following
locations:
Note that the address on the stack is the return address. This differs from
JSR/RTS, where the return address minus one is stored.
On all Commodore machines, the IRQ strikes about sixty times a second.
The NMI is unused (but available) on PET/CBM; it isn't available in the
116 MACHINE LANGUAGE FOR COMMODORE MACHINES
The BRK command can be distinguished from the IRQ signal by means
of a bit in the status register. Bit A is the B, or break flag; if it's set, the
last interrupt was caused by a BRK and not by an IRQ.
Later, we will discuss using the interrupt routines for our own programming.
By the time we can "catch" the interrupt, several more things will have
been pushed to the stack: the A, X, and Y registers. This is done by a
ROM program, not the processor; but it will prove handy since we can use
these registers, safe in the knowledge that they will be restored at the end
of the interrupt.
LDA #$24
PHA
LDA #$fcfl
PHA
RTS
This coding is identical to JMP $24 bR. We have placed a "false return
address" onto the stack, and RTS has removed it and used it. This may
not seem very useful, since we could easily have coded the JMP $24 tn
directly. But look at the following code:
LDA TABLE1, X
PHA
LDA TABLE2, X
PHA
RTS
The principle of coding is the same, but now we can "fan out" to any of
several different addresses, depending on the value contained in X.
When the USR function returns control to BASIC, the function value will
be whatever is in the floating accumulator. If we have not modified it, this
will be the same as the argument, so that in many cases PRINT USR(5)
would print a value of 5.
computer when it has a dead keyboard. You could try to program all these
functions yourself; but there's an easier way.
Suppose we use the vector to temporarily divert to our own program, and
at the end of our program we allow the interrupt to continue with whatever
it was going to do anyway. That way, our program would get service sixty
times a second, and the usually interrupted jobs would still get done.
It's not hard to do, and we can achieve many interesting effects by diverting
the interrupt. Remember that the interrupt runs all the time, even when
no BASIC program is running. By playing with the interrupt, we can make
a permanent computer system change that is in effect even when no
programs are in place.
There's a second way of turning off the interrupt—that is, by shutting off
the interrupt source. Something makes an interrupt happen—it might be
a timer, it might be an external signal, or it might even be a screen event.
Whatever it is, we can get to the source of the interrupt and disconnect
it.
Commodore 64: Store $7F into address $DCDD (POKE 5b333 ,12?)
to disable; store $fll into the same address (POKE 5b333,12cl)to
re-enable.
STACK, USR, INTERRUPT, AND WEDGE 119
PET/CBM: Store $3C into address $Efll3 (POKE 5^11, bD) to dis
able; store $3D into the same address (POKE 5^411, bl) to re-enable.
It goes without saying that the above POKEs should not normally be given
as direct commands; the first POKE in each case will disable the keyboard
(among other things), and you won't be able to type the restoring POKE.
An Interrupt Project
The following project is written for the Commodore 64 only. The equivalent
coding for PET/CBM and VIC-20 may be found in Appendix E. Appendix
E, under Exercises for the Commodore 128, also contains appropriate
coding for the C128.
Let's write the coding for the interrupt itself. Sixty times a second, we'd
like to copy the contents of address SHI to the top of the screen. Here
goes:
Why the indirect jump? We want to "pick up" the regular interrupt routine,
but we don't know .where it is yet. When we find the address, we'll put it
into locations $ D 3 a D/$ D 3 a 1 so that the indirect jump will link things up
for us.
Now let's write the routine to enable the above interrupt coding. First, let's
copy the interrupt address from $ D 314 into the indirect address at $ D 3 a D:
Now we are ready to put the address of our own interrupt routine (at
$D33C) into the IRQ vector:
a D35D SEI
120 MACHINE LANGUAGE FOR COMMODORE MACHINES
ft 035D SEI
A D35E LDA $03fi0
A 03bl STA $0314
ft 03b4 LDA $O3A1
A 03fc7 STft $0315
A 03bA CLI
A 03bB RTS
As you can see, we put the original address back, copying it from the
indirect address area where it was saved.
Once this code is in place, disassembled, and checked, you may return
to BASIC. SYS 631 will invoke the new interrupt code; SYS flfcl will
turn it off. Note that the character (a copy of the contents of address $ q 1)
appears at the top left of the screen. The character seems to be affected
by pressing some keys; can you establish how many keys are involved?
Some models of Commodore 64 may print blue-on-blue when screen
memory is POKEd, as we are doing now. If so, the character may not
always appear in the left-hand corner. Project for enthusiasts: Fix this
problem by storing a value into the color nybble table at address $ D fl D D.
. Event latching and interrupt control. We have noted that these chips can be
manipulated to block the interrupt signal. In fact, they do more than "gating"
the signal—allowing it through to the processor's IRQ trigger or alternatively
blocking it. They also often latch a signal into an event flag, sometimes called
an interrupt flag.
ON. .OFF
INTERRUPTING H LATCH 1
EVENT '
| COMPUTER
EVENT ACKNOWLEDGEMENT
FLAG
Figure 7.2
On the other hand, the event might not be particularly time critical. In this
case, you can simply check the appropriate event flag from time to time.
When the event occurs, you may then clear the flag and handle it. No interrupt
is needed. Even when an event flag is not connected to the interrupt, it may
be called an interrupt flag; don't let the terminology confuse you.
Whether or not you handle these events through interrupt sequences, it's
important to know that it's your job to turn the event flag off. The flag will
hold the signal until it's turned off—and it usually won't turn off unless your
program takes some action to do this.
The various flags are triggered by timers or external signals. You can read
a flag's state by checking the interrupt flag register. Several flags will be
packed together in this register; as always, you will use the logical operators—
AND, OR A, or EOR—to extract or modify the particular flags in which you
are interested. You may also use the IFR (interrupt flag register) to clear
the flags.
722 MACHINE LANGUAGE FOR COMMODORE MACHINES
2. Timing. Certain addresses within the IA chip are often assigned as "timers."
These timers count down; in other words, if we place a value of $R? into a
timer and look at the value immediately, we might find that it has gone down
to $^3. Timers come in many shapes and sizes—again, check the chip
reference for details—but most of them toggle an interrupt flag when they
have counted down to zero. As discussed, you may choose whether or not
this flag will really cause an interrupt signal.
3. Input/output. Certain addresses within the IA chip are connected to "ports,"
which extend outside the computer. Thus, the computer can detect external
events or control external devices. Output signals are usually latching in
nature: in other words, a store command might be taken to mean, "turn on
port 5 and leave it on."
Tips on IK Chips
Many addresses within an I a chip have a different meaning, depending
on whether they are being written to (stored) or read (loaded). Watch for
this when you are reading the chip specifications.
Often, the action required to turn an interrupt flag off is odd. It looks like
the kind of thing you should do to turn the flag on. Keep in mind that a
flag may be turned on only by the external activity to which it is linked.
So, although it may seem odd to turn the flag in bit zero off by storing a
value of 1 (which would seem to want to turn bit zero on), don't worry.
You'll get used to it.
the next character from your BASIC program; and CHRGOT, to recheck
the last character. The subroutine is located at$DD7Dto$DDfl?in most
PET/CBM computers, and at $ D D7 3 to $ D D fl A in VIC-20 or Commodore
64. You may disassemble it there if you wish. The coding is described
below.
If the character is a colon ($3 A), we'll leave the subroutine with the Z
flag set. That's one of our objectives. Here's part of another one: if the
character is $3A or higher, it can't possibly be an ASCII numeric-
numerics are in the range of $30 to $3^.
CMP #$ED
BEQ CHRGET
124 MACHINE LANGUAGE FOR COMMODORE MACHINES
SEC
SBC#$3D
SEC
SBC #$DD
After this, the R register is not changed; but the C flag will be set if the
number is less than $3D, which means that it is not an ASCII numeric.
Additionally, the Z flag will bet set if fl contains a binary zero. We have
met all our objectives and may now return:
EXIT RTS
This is a tall order. The last requirement is often helped by two techniques:
use the wedge to implement extra commands in direct mode only; and
make use of a special character to identify our special commands.
VIC-20 and Commodore 64 have made the job much more easy by pro
viding a vector at address SDBDfl/SDBDR that will give us control of the
computer, if we wish, immediately before each BASIC command is exe
cuted. We still need to use due care, but we have much more latitude.
STACK, USR, INTERRUPT, AND WEDGE 725
An important point: the vector will give us control, if we want it, with
TXTPTR positioned immediately before the next instruction. When we
return control to BASIC, we must be sure that TXTPTR is similarly po
sitioned.
Since Y equals one, we'll look just beyond the address to which TXTPTR
is pointing:
We may call CHRGET to move the pointer along. Now TXTPTR points
squarely at the ampersand character. We are ready to print ten asterisks:
A 034A LDY #$00
A Q3AC LDA #$2A
A D3AE JSR $FFDE
A 0351 INY
A 0352 CPY #$0A
A 0354 BCC $034E
A 035b LDA #$0D
A 035fl JSR $FFDB
A 035B JMP $U3AA
The above code prints an asterisk ($EA) ten times and then prints a
RETURN ($DD). It then goes to the regular BASIC interpreter, which will
look behind the ampersand character for a new BASIC command.
Now we! need to set up the link to our program. We'll write the code to do
this starting at $03 5E, so that SYS flbE will put the new command
(ampersand) into effect:
When you have completed and checked the code (remember this is for
VIC and Commodore 64 only), return to BASIC. Type NEW and write the
following program:
If you type RUN, you will get a SYNTAX ERROR in line 1DD. We have
not yet implemented our "ampersand" command. Type the command SYS
fit2. Now type RUN again. The ampersand command obediently prints
ten asterisks each time it is invoked.
You'll get a fascinating screen. There will be timers going, and as you
type on the keyboard you'll see various inner values changing around.
Enjoy the view.
There are some utility programs which, when placed in the computer,
allow a listing to be "scrolled." In other words, if the screen shows BASIC
lines 5 5 D to A b D, the user can take the cursor to the bottom of the screen
and continue to press the cursor-down key. New BASIC lines (following
4bD) will then appear. This is not an easy thing to code, but here's the
question: do you think that this feature is done with a SYS command, a
wedge, or an interrupt technique? Why?
• Symbolic assemblers
• Where to go from here
131
132 MACHINE LANGUAGE FOR COMMODORE MACHINES
Timing
For many applications, machine language programs seem to run instan
taneously. The speed of the 650x is much greater than that of other
devices, including the human user. The machine language program usually
ends up waiting for something: waiting for the keyboard, waiting for the
printer, waiting for the disk, or waiting for the human to read and react to
information presented on the screen.
Let's take a simple routine and estimate its timing. The following program
logically ANDs the contents of 1DD locations from $17ED to $1&AA :
D345 CPX
D3A? BCC $D34D
RTS
RTS—6 cycles: 6
Where timing is critical, the interrupt could be locked out with SEI. Be
careful: it's seldom necessary, and is potentially dangerous.
We have seen that we may obtain input from the keyboard buffer into the
a register by calling kernal routine GETIN at $FFE4. We may also
redirect the input so that we draw information from any logical file.
The same commands— $FFD2 and $FFE4—still perform the input and
output. But we "switch" either of them to connect to a chosen device—or
more accurately, a chosen logical file. The file must be open; we may
switch to the file, and then switch back to normal I/O as we wish.
Switching Output
We use subroutine CHKOUT at address SFFC^ to switch output to a
logical file. When we want to restore output to the screen, we call sub
routine CLRCHN at $FFCC. This is not the same as an OPEN and
134 MACHINE LANGUAGE FOR COMMODORE MACHINES
KEYBOARD
SCREEN
INPUT OUTPUT
DEVICES DEVICES
CLRCHN ($FFCC)
RESTORES BOTH
SWITCHES TO "NORMAL"
Figure 8.1
Subroutine: CHKOUT
Address: $FFCR
Action: Switches the output path (used by CHROUT, $FFDE)
so that output is directed to the logical file specified in the
X register. The logical file must previously have been
opened.
Subroutine: CLRCHN
Address: $FFCC
Action: Disconnects input and output from any logical files and
restores them to the "default" input and output channels,
keyboard and screen. The logical files are not closed, and
may be reconnected at a later time.
This simplifies the machine language programmer's job. It's a simple task
to send the characters to some logical channel; the programmer does not
need to take special coding action depending on which device is involved.
Output Example
If we wanted to print the message HI on the printer, we might code as
follows.
First, we'll open the printer channel in BASIC. Let's use logical file num
ber 1:
If you don't have a printer, you may open the file to cassette (OPEN
136 MACHINE LANGUAGE FOR COMMODORE MACHINES
Don't forget to send the RETURN—the printer needs it. After the machine
language program says HI, the program will return to BASIC and close
the file. Notice that the machine language program doesn't care what it's
saying HI to ... it sends the data to logical file 1.
Switching Input
We use subroutine CHKIN at address $FFCb to switch input so as to
draw data from a logical file. When we want to restore input from the
keyboard, we call subroutine CLRCHN at $FFCC. Again, this is not the
same as an OPEN and CLOSE—we simply connect to the file and dis
connect, and we can do this as many times as we want.
Subroutine: CHKIN
Address: $FFCk
Action: Switches the input path (used by GET, $FFE4) so that input is
taken from the logical file specified in the X register. The logical
file must previously have been opened.
Input Example
We can write a program to read an input file from disk or cassette. First,
let's write the file. We open the file according to its type:
This may be done with a direct statement. Now let's write a few things to
the file:
This more closely approximates the logic flow of our machine language
program, since it will get the characters one at a time. If you are unsure
738 MACHINE LANGUAGE FOR COMMODORE MACHINES
about the role of ST, read up on it. We will use the same variable (at its
address of $ R D or $ q fc) to do exactly the same thing in machine language.
1ED CLOSE 1
We will read the file and copy it to the screen entirely in machine language.
Let's start coding at $D33C :
a D34R BEQ
a D34B JSR $FFCC
a D34E RTS
Check it and try it. The file is delivered to the screen quickly.
It's not a good idea to switch input and output at the same time—in other
words, to call both $FFCb and SFFCH without canceling either via $FFCC.
The kernal doesn't mind, but it confuses the peripheral devices, which
expect to have exclusive occupancy of the data bus to the computer. So
we'll follow the pattern: switching on, sending or receiving, switching off,
and then going to the other device.
One more thing. S T tells us the status of the last device handled. Consider:
if we input a character, then output a character, and then check the value
of ST, we have a problem. ST will not tell us about the input, since the
last device handled was output; thus, we won't know if we are at the end
of the file or not. In machine language, as in BASIC, we must code carefully
to solve this problem.
We'll work this out for the Commodore 64 computer; you can adjust it for
PET/CBM or VIC-20. The above BASIC program should not take up more
than 511 bytes; on a standard Commodore 64, that means that we'll have
clear space for our machine language program starting at $DADD (dec
imal E5tiD). We'll move the start-of-variables along, of course, so that
our machine language program won't be disturbed by them.
When we first type line E4D, we won't know what SYS address to use.
After the program is typed in (with SYS xxxx at line E4D), we can easily
confirm that the machine language can start at $DADD by checking the
140 MACHINE LANGUAGE FOR COMMODORE MACHINES
By this time, we have a character in the A register from the input source.
We also have a value in ST, telling us if this is the last character. Let's
examine the ST problem: we must check its value now, since ST will be
changed after we do the output. But we don't want to take any action
based on ST yet; we must first send the character we have received. Let's
check ST, and put the results of the check onto the stack:
If ST is zero, the Z flag will be set; we'll preserve this flag along with the
others until we call it back from the stack. If you are adapting this program
for the PET/CBM, don't forget that S T is at address $ q fc for your machine.
A DADB PHA
We are now free to disconnect from the input channel and connect to the
output. Here we go:
The A register gets back the last thing saved to the stack, and that, of
course, is our input character. We're ready to send it to the output device:
A DA1B PLP
TIMING, INPUT/OUTPUT, AND CONCLUSION 141
If the Z flag is set, we want to go back and get another character. If not,
we're finished and can return to BASIC, allowing BASIC to close the files
for us:
The instructions are almost identical in action, although only the A register
has indirect, indexed addressing modes. We continued with the logical
and arithmetic routines that apply only to A:
Arithmetic also includes the shift and rotate instructions, which may be
used on the A register or directly upon memory:
INC DEC
INX DEX
INY DEY
The branch instructions can make only short "hops"; the jump instruction
is unconditional:
JMP
142 MACHINE LANGUAGE FOR COMMODORE MACHINES
Subroutines are called with the jump-subroutine, and returned with return-
from-subroutine; we may also return from interrupts:
We may modify any of several flags with the appropriate set or clear
command. Some of the flags control internal processor operation: for ex
ample, the I (interrupt disable) flag locks out the interrupt; the D (decimal
mode) affects the way the ADC and SBC work with numbers.
PHA PHP
PLA PLP
BIT
The BIT test is used only for specific locations: no indexing is allowed.
The high bit from the location being tested is transferred straight to the N
flag. The next highest bit (bit b) goes straight to the V flag. Finally, the Z
flag is set according to whether the location has any bits set that match
bits set in the A register. Thus, we can check a location with BIT $ ....
followed by BMI to test the high bit, or BVS to test bit fc, or BNE to test
any selected bit or group of bits. It's a rather specialized instruction, but
useful for testing input/output ports.
Finally, the instruction that does nothing, and the BRK instruction that
causes a "false interrupt," usually taking us to the monitor:
NOP BRK
That's the whole set. With these instructions, you can write programs to
make the computer do whatever you choose.
TIMING, INPUT/OUTPUT, AND CONCLUSION 143
Debugging
When a program has been written, the next step is to look for any possible
errors, or bugs. The process of searching for and systematically elimi
nating these bugs is called debugging.
Most programs are made up of sections, each of which has a clear task
to perform. When a program misbehaves, it may be easy to go to the area
of the bug, since you can see which parts of the program are working and
where things start to go wrong.
In case of doubt, you may insert breakpoints into your program. Replace
selected instructions with the instruction BRK; this may be done by re
placing the instructions' op codes with the value DD. Run the program;
when it reaches the first breakpoint, it will stop and the machine language
monitor will become active. Examine the registers carefully to see whether
they contain the values expected. Display memory locations that the pro
gram should have written; the contents will tell you whether the program
has been doing its job correctly.
When you have confirmed that the program is behaving correctly up to
the breakpoint, replace the BRK command at that point with the original
op code. Command . G to that address, and the program will continue to
the next breakpoint. If it helps your investigation, you may even change
memory or registers before continuing program execution.
If you carried this procedure to the extreme, you might stop your program
after every instruction. It would take time, but you would certainly track
down everything the program did.
The best debugging takes place at the time you write the program. Write
sensibly, not "super cleverly." If you fear getting caught in an endless
loop, insert a stop key test (JSR $FFE1) so that you'll still have control
of the computer.
Get to know your machine language monitor. The monitor uses a number
of locations in memory; you'll have trouble debugging a program if it uses
the same storage addresses as does your program. Every time you try to
check the contents of a memory location to see what your program has
done, you'll see the monitor working values instead—and that would be
misleading and annoying.
Symbolic Assemblers
Throughout these exercises, we have used small, "nonsymbolic" assem
blers such as would be found within a machine language monitor. These
144 MACHINE LANGUAGE FOR COMMODORE MACHINES
are good for beginners; they parallel the machine code quite closely and
allow you to keep the working machine clearly in focus.
As you write bigger and better programs, these small assemblers will be
less convenient. Forward, branches and subroutines we have not yet writ
ten make it necessary for us to "guess" at the address and fix up our
guess later. There is the possible danger that an address will be typed in
wrongly ($D34 5 instead of $D354), causing the program to fail.
It saves work and helps guard against errors. But symbolic assemblers
allow a more powerful capability: they help documentation and allow pro
gram updating.
Your assembly may be listed to the printer. This allows you to examine
and annotate the program, and file the details away for later reference.
The assembler allows you to include comments, which improve the read
ability of the listing but don't affect the machine language program.
The source program you have written may be saved and used again later.
If you find it is necessary to change the program, bring back the source
code from cassette or disk, make the changes, and reassemble. In this
way, programs can be easily corrected or updated.
With the elementary concepts we have introduced here, you will be able
to go deeper into more advanced texts. Many programming books deal
with the abstract 650x chip. That's hard for the beginner; it's difficult to
see how the instructions fit within the architecture of a real machine, or
how the programs can actually be placed within the computer. By now,
you should be able to take a piece of abstract coding and fit it into your
system.
Many things start to happen at once when you take your first steps in
machine language programming. You must learn how to use the monitor.
You must learn a good deal about how your machine is designed. And
you must learn how to fit the pieces together. It takes a while to adapt to
the "information shock"—but things start to fit together. Eventually, you'll
have a stronger and sounder view of the whole computer: hardware, soft
ware, languages, and usage.
-We can handle input from devices other than the keyboard by switching the
identity of the designated input device. If an input channel has been opened
as a file, we may connect to it with JSR $FFCb and disconnect with JSR
$FFCC.
-We can handle output to devices other than the screen by switching the
identity of the designated output device. If an output channel has been opened
as a file, we may connect to it with JSR SFFCR and disconnect with JSR
$FFCC.
-Once input or output has been switched, we may receive in the usual way
with the subroutine at $FFE4, or send in the usual way with the subroutine
at $FFD2.
-You have met all the instructions of the 650x microprocessor. There are
enough for versatility, but not so many that you can't keep track of them all.
746 MACHINE LANGUAGE FOR COMMODORE MACHINES
You have made a worthwhile start in the art and science of machine language
programming.
Write a program to print HAPPY NEW YEAR to the printer ten times.
If you own a disk system, you know that you can scratch a program named
JUNK by using the sequence:
Write a "typewriter" program to read a line of text from the keyboard and
then transfer it to the printer. It will be a more useful program if you show
what is being typed on the screen and if you write extra code to honor
the DELETE key.
A
The 6502/
6510/6509/
7501/8500
Instruction
Set
The four chips differ only in their use of addresses 0 and 1:
147
148 MACHINE LANGUAGE FOR COMMODORE MACHINES
Addressing Modes
Accumulator Addressing—This form of addressing is represented with a
one byte instruction, implying an operation on the accumulator.
Zero Page Addressing—The zero page instructions allow for shorter code
and execution times by only fetching the second byte of the instruction
and assuming a zero high address byte. Careful use of the zero page can
result in significant increase in code efficiency.
when the counter is set at the next instruction. The range of the offset is
-lEfl to +127 bytes from the next instruction.
NOP No Operation
Programming Model
ACCUMULATOR A
INDEX REGISTER Y
INDEX REGISTER X
15 7 0
PCH PCL PROGRAM COUNTER "PC"
8 7
L CARRY
-► ZERO
1 = TRUE
1 = RESULT ZERO
-► IRQ DISABLE 1 = DISABLE
-► DECIMAL MODE 1 = TRUE
-► BRK COMMAND
-► OVERFLOW 1 = TRUE
-► NEGATIVE 1 = NEG
Figure A.1
752 MACHINE LANGUAGE FOR COMMODORE MACHINES
CJ
CO CD 2
CO
-CO
LLJ LLJ LU LU LU LU
COCO uuuuu
coior
r-co QLL CM CO < O LU
CO
coco
UJLLJLLJLLJLJJUJLUUJ
O CM ^t CD 00 < O LU
><CM o)ca o
Q
N
< O
CO CD
CD DO CM00<OUJ
N
CD CD CD CD CD CD
I- 31
t-CO ID l1^ QLL ooo
CO
CDCDCDCDCDCDCOCD
se§5g
"D
O CM ^t CD 00 < O UJ c
CD
CM
■8
O
< (J
O)
a
Ql-
(IND) |
Eu
CO
O)C7)O)O)C7)O)O)O>
co<
ABS
CD
x
S88RQqQQ 8
<
CO
COCO
QQQQQQQ
QQQ
O CM ^t-CO 00 < O UJ
si i
(75
CO
><CM O CM <* CD 00 < O UJ
oooo
corcou
LOLOLOLOLOLOLOlO
r- CO LO (^ O) 00 Q LL
o = c/)c/)CJ
ldldloldldlololo S>Oiu
OCM<frCOCO<OLU 00 00 00 00
ooo
t-LOO) 00 0.
0)0(7)0) G> 0) C7>
•CM
OCMtCO <OLU o
a
ooo
O
q!>uz I I
O < lu < en z! o co 00 00 00 OQ
il
CONDITION
NSTRUCTIONS 1 Immediate 1 Absolute 1 Zero Page I Accum. llmplied |z,Page.X | Abs.X 1 Relative 1 Indirect |z,Page.Y CODES
1 Mnemonic Operation
NZCIOV
ADC
AND
BIT AAM
CLC
CM
CM
f t
CLD
CM CCMM
CLI
CCCMM CCMMM ft
CLV
CMP A - M (1)
s
SO
CPX
CO
CPY
DEC
DEX
DEY
CM
CM §
> - M i
EOR AVM-A (1) »
CM
Y + 1—Y
| LDA
CO
CCCMM L5O to M ss
MUJo18LC
iZ
£
<
CO
O)
753
g
)P
OP M # OP M # OP
BE 3 B6 2
0 4 A6
LDX M-»X (1) A? AF 3
0 0 B4 BC 3
LDY M-»Y (1) AO 2 AC 4 3 A4 3
2 7
48 0 4A 1 58 5E 3
LSR 0-»[7 0]-C 4F 6 3
NOP NO OPERATION FA 1
j>
0 0 0 01 fi 2 11 «> 15 4 1D ^ 3 19 3
ORA AVM-A 09 0D 4 3 05 3
(RESTORED)
?fl 4 1
PLP S + 1—S Ms-*P
2 1 36 > 2 3E 7 3
0 2A
ROL ?F 6 3 ?8 5
7
6A 2 1 76 6 2 7E 3
0
ROR UTCUTt—o\J fiF 6 3 68
(RESTORED)
(See Figure A-1) 40 6 1
SEI 1-*l 7R 2 1
0 R1 6 ? 91 6 ? 95 4 2 9D 3 99
STA A-M 80 4 3 R5 3
96
STX X—M RF 3 R8 3 2
0 94 4 2
STY Y—M RC R4 3
? 1
TAX A->X AA
\* *
AR 9 1
TAY A->Y
//
RA 9 1
TSX S-X
RA 9 1
TXA
9A 9 1
TXS X-»S
W*
? 1
TYA Y^A 9R
Figure A.4
B
Some
Characteristics
of Commodore
IVIachines
155
156 MACHINE LANGUAGE FOR COMMODORE MACHINES
PET—Original ROM
The first PET. It can be recognized by the message seen at power up:
using asterisks but with no identifying number after the word BASIC.
The original machine may be upgraded to Upgrade ROM by fitting a new
set of ROM chips. This is a good idea, since the original logic cannot
handle disk, does a poor job on cassette data files, has no built-in machine
language monitor, and has a zero page architecture that differs significantly
from all later PET/CBM's. The BASIC language on this unit is also limited;
arrays may not contain over 256 elements, for example.
PET/CBM—Upgrade ROM
The first PET that can handle disk. It can be recognized by the message
seen at power up:
VIC-20
The VIC-20 was a new design concept for Commodore. Color, graphics,
and sound were built into the computer. The memory architecture changed
radically. Zero-page locations were shifted significantly as compared to
previous PET/CBM computers.
The VIC comes with no machine language monitor; it's necessary to load
one. The S YS command has a new attractive feature that allows registers
A, X, and Y to be "preloaded" by POKE ing values into addresses 780,
781, and 782. Location 783 could also be used to set the status register,
but that's dangerous; unless it's done carefully, the decimal mode or in
terrupt disable flags might be set inadvertently.
Commodore 64
The Commodore 64 has much in common with the VIC-20. In particular,
its zero page organization is almost identical to that of VIC. The Com-
758 MACHINE LANGUAGE FOR COMMODORE MACHINES
The Commodore 64 has a more stable architecture than the VIC. BASIC
starts in a consistent place, and the screen is always at hex DA DD unless
you move it. There's a bank of memory at $CDDD to $CFFF that is not
used by the computer system; it's useful for staging machine language
coding.
Commodore PLUS/4
Similar to the Commodore 64 in many ways. The processor is a 7501,
which has the same instruction set as the 6502. Screen memory and
BASIC RAM have been moved a little higher. BASIC itself is greatly ex
panded.
B Series
The B-128, B-256, CBM-128, and CBM-256 were designed as successors
to the 80-column PET/CBM units. Architecture has been radically changed:
the processor is a 6509, memory is bank switched, and zero page is
significantly different from that of other models.
A machine language monitor is built into this line of machines. A few new
commands have been made available: . V to switch banks, . @ to test
disk status.
160 MACHINE LANGUAGE FOR COMMODORE MACHINES
Commodore 128
The Commodore 128 is three machines in one.
Introduction (128)
The Commodore 128 may be used as if it were a Commodore 64 or in
CP/M mode. The following material deals with its use in C128 mode.
BASIC is rich with extra commands, and there's a good built-in machine
language monitor, which will be useful for us. The SYS command allows
preloading of registers ft, X, and Y if desired, and reading the contents
of these registers after a return to BASIC.
The Commodore 128 has a large amount of memory, and this calls for
an elaborate architecture. There is 128K of RAM, 44K of ROM, the input/
output chips, and the potential for much more ROM and RftM to be added
internally or through a cartridge. The processor can reach only 64K of
memory at a time, so that a sophisticated system of "memory banking"
must be used to get access to everything.
In this book, we will be using Bank 15 almost exclusively. That will allow
us to put our own programs into RAM at a low address in memory, and
call upon the built-in programs that are stored in ROM at high memory
addresses.
Since the Commodore 128 contains a "built-in" Commodore 64, it will not
be surprising to learn that many of the interface chips—for video, sound,
and other purposes—are almost identical to those of the Commodore 64.
Do not worry if all this sounds technical. You will learn about many of
these features as you go.
Here's the important thing to remember: when you reach the exercises
that are found in each chapter, check Appendix E, under Exercises for
the Commodore 128, to get the C128 version. The principles are the
same—we're doing the same thing using the same techniques—but small
adjustments are needed for the special characteristics of the Commodore
128.
If you have not read the main part of the book, stop here and return to
Chapter 1. When you're ready to dig for more technical information, you
will find it here, and in Appendix E and Appendix C and Appendix H. But
first: read, learn, and enjoy.
FEFF
"Bank 0"—Almost 64K of RAM. This is where BASIC programs are stored. RAM exists
above $FF04, but is not normally used.
$0400i
"Bank 1 "—Addresses from $0400 up are RAM 1, where BASIC variables, arrays, and strings
are stored. Below $0400, RAM 0 is used.
$D0OO SE000
$8000
K—Kernal
K(l/O)—Kernal (Input/Output)
"Bank 13"—Below $8000, addresses RAM 0. Cartridge ROM (if present) occupies addresses
$8000 to $BFFF. From $000 to $FFFF, we have Kernal ROM, except for the area from
$D000 to $DFFF, which holds input/output chips.
APPENDIX B — COMMODORE 128 763
SDOOO $E000
K-«-4-K— Kernal
"Bank 14"—Memory below $4000 is RAM 0. From $4000 up, we have ROM for BASIC and
Kernal, exceptfor a slot from $D000 to $DFFF, which contains the character generator ROM.
K—Kernal
I/O—Input/Output
"Bank 15"—Memory below $4000 is RAM 0. From $4000 up, we have ROM for BASIC and
Kernal except for a slot from $D000 to $DFFF, which contains input/output chips.
764 MACHINE LANGUAGE FOR COMMODORE MACHINES
for the addition of extra RAM. None of these are good configurations for
programs—you will always want to do input and output—but they are
often called in briefly to get or store data. Bank 0 uses the RAM that
normally holds BASIC programs; Bank 1 uses the RAM that holds BASIC
variables, arrays, and strings.
Banks 8, 9, 10, and 11 are similar to Banks 0-3 below address IflODD.
A set of ROM lies over the RAM at addresses $BDDD to $FFFF, except
for addresses $DDDD to $DFFF which contain I/O chip registers. This
ROM is external, which means that it is plugged into the cartridge port.
Again, stay away; using these configurations calls for you to supply the
entire logic of the machine.
On rare occasions you may find a need to tuck a program into high RAM.
That makes the job harder. You will certainly be located beneath ROM,
and that means you need to call to make bank transfers as your program
calls the Kernal and returns. It can be done. But it is messier, and if you
can relocate your program to eliminate the problem, do so.
APPENDIX B — COMMODORE 128 765
All these use indirect, indexed addressing to reference the desired data.
Thus, you must set up the indirect address in zero page as usual and load
Y with the index value desired. You must give these subroutines two extra
pieces of information: where the indirect address is located, and what data
bank is desired.
Indicate the desired bank (0 to 15) by loading its value into register X.
It is wise to lock out interrupts with SEI before starting any of the above
calling sequences; do not forget to release the interrupt with CLI after
the call. Chapter 6 has an example of these routines.
You must place the address of your desired destination into addresses E,
3, and A. Oddly, the address is not "backwards" like most 650x addresses.
The bank number goes into address E, the high address byte into 3, and
the low byte into address A. Address 5 is a "status register" image, if you
want it; usually it is best to leave this value as zero. If you want to pass
information via the processor's registers, the values must be stored in
766 MACHINE LANGUAGE FOR COMMODORE MACHINES
This system is not all bad. For one thing, blocks of "private" memory can
be moved internally to provide for fast scrolling. For another, the 80-column
controller has no need to dip into main memory to keep its screen alive;
with the result that the 80-column machine can be much faster than the
40-column one, which needs to reference memory almost continuously.
Memory
Maps
A word about memory maps: they are always too big or too small for the
use you have in mind.
The beginner may feel swamped by the wealth of detail. There's no threat,
however. The information is there when you're ready for it. Browse through
the information; it may be thought-provoking. Try reading or changing
locations to see what happens.
The advanced programmer may want more: lengthy details on how each
location is used, which parts of the system use these locations, and so
on. Time and space don't permit such detail.
The maps are intended to be fairly complete. Those who want more detail
may find them cryptic; but at least each location will be associated with a
type of activity. Different machines may be compared by checking their
respective maps. In some cases, programs may be converted with their
use, since they will help to find the corresponding memory location in the
target machine.
767
768 MACHINE LANGUAGE FOR COMMODORE MACHINES
Memory Map
Hex Decimal Description
PI A and VIA charts are the same as shown for Upgrade/4.0 units.
Memory Map
Where Upgrade ROM differs from 4.0, an asterisk (*) is shown and the 4.0
value is given. There are some differences in usage between the 40- and
80-column machines.
6520
Figure C.1
PIA1 chart
6520
DDRA
E821 ATN Int NDAC Out ATN Int Control 59425
Access
DDRB
E823 SRQInt DAV Out SQR Int Control 59427
Access
Figure C.2
PIA 2 chart
APPENDIX C — CBM 8032 AND FAT-40 179
6522
E845 59461
E847 59463
E849 59465
Latch Controls
E84B T1 Control T2 Cont Shift Register Control 59467
PB PA
CA1
CB1 Cntl CA2 Control
E84C CB2 (PUP) Control (PUP) 59468
Tape#2 Graphics/Text Mode
Control
Figure C.3
VIA chart
0 HORIZONTAL TOTAL 49 49
2 H. SYNC POSITION 41 41
3 v SYNC WIDTH H 15 15
4 XI VERTICAL TOTAL 32 40
6 x VERTICAL DISPLAYED 25 25
8 ^><3 MODE 0 0
9 SCAN LINES 9 7
10 0 0
:URSOR START (UNUSED) —
11 0 0
12 XI c R DISPLAY 16 16
13 ADDRESS 0 0
VIC-20
The Great Zero-Page Hunt
Locations $FC to $FF are available. Locations $22 to $EA, $4E to
$53, and $57 to $bD are work areas available for temporary use.
Memory Map
Hex Decimal Description
Figure C.5
APPENDIX C — VIC 6522 789
I3S-232 Interfa ce
$9110 37136
or, Parallej| User Port
$911C CB2: RS-232 Send CB1 C CA2: Tape motor Ctrl CA1 Ctl 37148
Figure C.6
190 MACHINE LANGUAGE FOR COMMODORE MACHINES
Figure C.7
APPENDIX C — COMMODORE 64 191
Commodore 64:
The Great Zero-Page Hunt:
Locations $FC to $FF are available. Locations $22 to $2 ft, $<E to
$53, and $57 to $LD are work areas available for temporary use.
Memory Map
Hex Decimal Description
A . B R . L . D . U
$DC00 PRA 56320
Keyboard Row Select (inverted)
Joystick 1
$DC01 PRB 56321
Keyboard Column Read
Figure C.8
APPENDIX C — COMMODORE 64 799
Timer
$DD0F CRB 56591
, B Start
Figure C.9
200 MACHINE LANGUAGE FOR COMMODORE MACHINES
7 6 5 4 3 2 10
D013 53267
Light Pen Input
DOM 53268
D018
vm13
Screen (Video Matrix)
vm12 vm11 vm10 cb13
Character Base
. cb12 . cb11 I x 53272
Light Collision
D01A IRQ Enable Pen, Sprt , Back , Rastr 53274
COLOUR REGISTERS
Figure C.10
APPENDIX C — COMMODORE PLUS/4 201
V1 V2 V3 V1 V2 V3
D400 D407 D40E L_ 54272 54279 54286
— Frequency
D401 D408 D40F H 54273 54280 54287
Voice Type
KEY
D404 D40B D412 NSE . PUL , SAW, TRI , 54276 54283 54290
Attack Decay
Time Time
D405 D40C D413 2ms-8sec 6ms-24sec 54277 54284 54291
Sustain Release time
D406 D40D D414 level I
l
6ms-24sec
i i i
54278 54285 54292
Voices
(write only)
D415 0 0 0 0 0 L 54293
Passband Master
D418 OFF, H, , BO, 10 I y^™, 54296
Sense
(read only)
Figure C.11
202 MACHINE LANGUAGE FOR COMMODORE MACHINES
On the prototype units, much of zero-page is the same as for VIC and
Commodore 64; in particular, the Basic pointers (SOB, SOV, etc.) are the
same.
(Most other vectors are similar to the C64, but are two locations lower)
FFOO 65280
T1
FFO1 65281
FFO2 65282
TIMERS T2
FFO3 65283
FF04 65284
T3
FFO5 65285
FFOC 65292
FF1O 2 HI 65296
FF15 65301
FF16 65302
FF17 65303
FF18 65304
FF19 65305
FF1A 65306
FF1C 65308
Figure C.12
204 MACHINE LANGUAGE FOR COMMODORE MACHINES
If you need space in bank 15 zero page, you'll need to do some looking
around. Addresses $Eb to $FF are not used by the system. Locations
$ED to $EB and $t4 to $bE are work areas available for temporary
use.
Memory Map
The following information applies to B systems released after April 1983,
which contain a revised machine language monitor. (If POKE t, D : S YS
t doesn't bring in a monitor display complete with a "period" prompt, you
have an incompatible version.)
ALL BANKS:
DDDD D execution register
DDD1 1 indirection register
BANK 0: Unused.
BANK 1:
DDDE-FDDD E BASIC program (text) RAM
FA5E-FBDD Input buffer area
BANK 2:
B256:
DDDS-FFFF E-fc5535 BASIC arrays in RAM
BlEfi:
DDDE-FFFF E-b5535 BASIC variables, arrays and
strings
Key definitions
BANK 3: (B256 only)
DDDE-7FFF E-3E7t7 Unused RAM.
flDDD-FFFF 3E7bfl-b553S BASIC variables in RAM
BftNK A: (BESb only)
DDDE-FBFF E-W511 BASIC strings (top down) in
RAM
FCDD-FCFF Unused RAM (descriptors?)
FDDD-FFFF b47fcfl-bSS35 Current KEY definitions
BANKS 5 to 14: Unused.
BANK 15:
DDDE-QDD4 S-A DSR jump
DDD5-DDDfl 5-fl TI$ output elements:
H,M,S,T
0DDR-D00B Print Using format pointer
DDDC IE Search character
DDDD 13 Scan-between-quotes Flag
DDDE 14 Input point; number of
subscripts
DDDF 15 Catalog line counter
DD1D It Default DIM flag
DD11 17 Type: E55= string, 0 = integer
206 MACHINE LANGUAGE FOR COMMODORE MACHINES
The above table shows contents for the link and vector addresses at $0280
to $0295; these are taken from a recent B-128.
212 MACHINE LANGUAGE FOR COMMODORE MACHINES
4 Vertical Total 25 or 31 or 38
6 Vertical Displayed 25
8 Mode 0
9 Scan Lines 13 or 7
11 Cursor End 13 or 7
0
— Display Address —
0
14 H Varies
15 L Varies
LJ
16 0
Liaht Pe*n In
17 0
Most Registers are Write Only 14/15 are Read/Write
16/17 are Read Only
Registers 10, 14 and 15 change as the cursor moves
Figure C.13
APPENDIX C — COMMODORE 128 213
Figure C.14
214 MACHINE LANGUAGE FOR COMMODORE MACHINES
COMMODORE 128:
Memory Maps
These maps apply to the machine when used in the 128K mode. When
used in the 64 mode, the machine's map is identical to that of the Com
modore 64. Since the RAM work area is 7K in size—as compared to the
Commodore 64 with 1K—the map can be huge; it is somewhat abridged
here.
Memory Map
ALL BANKS:
0001 xxxxx Caps Tape Tape Tape HiRes LoRes Color 00001
Key Motor Sense Outpt Acces
Figure C.15
224 MACHINE LANGUAGE FOR COMMODORE MACHINES
7 6 5 4 3 2 1 0
Figure C.16
APPENDIX C — COMMODORE 128 225
D013 X 53267
— Light Pen Input
D014 Y 53268
COLOR REGISTERS
D030 xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx Test Fast 53296
Clock
V1 V2 V3 , V1 V2 V3
D400 D407 D40E L_ 54272 54279 54286
— Frequency
D401 D408 D40F H 54273 54280 54287
Voice Type
KEY
D404 D40B D412 NSE , PUL .SAW, TRI , 54276 54283 54290
Attack Decay
Time Time
2ms-8sec 6ms-24sec 54277 54284 54291
D405 D40C D413
Sustain Release time
D406 D40D D414 level I
i
6ms-24sec
i i i
54278 54285 54292
Voices
(write only)
D415 0 0 0 0 0 L 54293
Passband Master
D418 off, h. .bd.loI Volume, 54296
Sense
(read only)
Figure C.17
APPENDIX C — COMMODORE 128 227
Figure C.18
228 MACHINE LANGUAGE FOR COMMODORE MACHINES
13 $0D Address L 0
20 $14 Color H 8
21 $15 Address L 0
Figured 9
APPENDIX C — COMMODORE 128 229
Joystick 1
$DC01 PRB 56321
Keyboard Column Read
Figure C.20
230 MACHINE LANGUAGE FOR COMMODORE MACHINES
Timer
$DD0F CRB 56591
B Start
Figure C.21
DMA Controller
DF00 Busy Fault xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 57088
DF01 Exec Sum xxxxxxxxxxx IRQ | Inc Mode 57089
DF02 Host L 57090
DF03 Address H 57091
DF04 Expansion L 57092
DF05 Address H 57093
DF06 xxxxxxxxxxxxxxxxxxxxxxxxxxxxx | Expansion Bank 57094
DF07 Transfer L 57095
DF08 Length H 57096
DF09 Checksum 57097
DFOA Version, Maximum-Memory 57098
xxxx = unused
(blank) = not of interest
Figure C.22
APPENDIX C — COMMODORE 128 231
B7F7: Float-fixed
BflDD: Evaluate [PEEK]
BflE<: Perform [POKE]
BflED: Perform [WRIT]
Bfi^R: Add 0.5
BQ50: Subtract-from
BflS3: Evaluate [subtract]
BflbR: Evaluate [add]
Complement FRC (floating accumulator)#1
BRTE: Print "overflow"
Bqfl3: Multiply by zero byte
B^ER: Evaluate [LOG]
BRSB: Evaluate [multiply]
BRSR: Multiply-a-bit
BRflC: Memory to FRC#E
BRB7: Adjust FRC#1 and FRC#E
BAD4: Underflow/overflow
BAES: Multiply by ID
BRFR: +1D in floating point
BRFE: Divide by ID
BB1S: Evaluate [divide]
BBR5: Memory to FRC#1
BBC7: FRC#1 to memory
BBFC: FRC#E to FRC#1
BCDC: FRC#1 to FRC#S
BC1B: Round FRC#1
BCEB: Get sign
BC3q: Evaluate [SGN]
BC5fi: Evaluate [ABS]
BC5B: Compare FRC#1 to memory
BCRB: Float-fixed
BCCC: Evaluate [INT]
BCF3: String to FRC
BD7E: Get ASCII digit
BDCE: Print "IN. ."
BDCD: Print line number
BDDD: Float to ASCII
BFlt: Decimal constants
BF3R: TI constants
BF71: Evaluate [SQB]
BF7B: Evaluate [power]
BFB4: Evaluate [negative]
APPENDIX C -- COMMODORE 128 235
247
242 MACHINE LANGUAGE FOR COMMODORE MACHINES
Superchart
The "superchart" shows the PET character sets. A byte may have any of
several meanings, depending on how it is used. The chart is constructed
to reflect this. "ASCII" is PET ASCII; these are the characters as they
would be input or printed. "Screen" is the Commodore screen codes, as
they would be used in screen memory—POKEing to or PEEKing from
the screen would yield these codes. Notice that the numeric character set
is the same for both screen and PET ASCII.
Within a program, the code changes again. "BASIC" shows these codes;
they are similar to ASCII in the range $ED to $5F.
Machine language op codes are included for the sake of convenience and
completeness.
1 01 A ORA(I,X) 1
02 B 2
2
03 C 3
3
4 04 D 4
5 05 white E ORAZ 5
06 F ASLZ 6
6
07 bell G 7
7
8 08 lock H PHP 8
09 unlock I ORA# 9
9
10 OA J ASLA 10
11 OB K 11
12 OC L 12
14 OE text N ASL 14
15 OF top 0 15
16 10 P BPL 16
17 11 cur down Q ORA(I),Y 17
18 12 reverse R 18
19 13 cur home S 19
20 14 delete T 20
21 15 del. line U ORA Z,X 21
22 16 ers.begin V ASL Z,X 22
23 17 w 23
24 18 X CLC 24
25 19 scr. up Y ORAY 25
26 1A z 26
27 1B [ 27
28 1C red \ 28
29 1D cur right ] ORAX 29
APPENDIX D — SUPERCHART 243
ROL 46
47 2F / 1 / 47
48 30 0 0 0 BMI 48
49 31 1 1 1 AND(I),Y 49
50 32 2 2 2 50
51 33 3 3 3 51
52 34 4 4 4 52
53 35 5 5 5 AND Z,X 53
54 36 6 6 6 ROL Z,X 54
55 37 7 7 7 55
56 38 8 8 8 SEC 56
57 39 9 9 9 ANDY 57
58 3A :
CLI 58
59 3B j j
i 59
60 3C < < < 60
61 30 = = ANDX 61
62 3E > > > ROLX 62
63 3F 9 ? ? 63
64 40 @ H @ RTI 64
65 41 A i,a A EOR(I,X) 65
66 42 B (B, b B 66
67 43 C B.c C 67
68 44 D B,d D 68
69 45 E H.e E EORZ 69
70 46 F H,f F LSRZ 70
71 47 G (Eg G 71
72 48 H a, n H PHA 72
73 49 I ffl, i 1 EOR# 73
J LSRA 74
74 4A J H.J
75 4B K H.k K 75
76 4C L D,l L JMP 76
77 4D M S,m M EOR 77
78 4E N IZ, n N LSR 78
244 MACHINE LANGUAGE FOR COMMODORE MACHINES
Figure D.1
ASCII
ASCII is the American Standard Code for Information Interchange. It is
the standard for communications, and is often used with non-Commodore
printers.
0 1 2 3 4 5 6 7
»
0 NUL DLE SP 0 @ P P
1 SOH DCI ! 1 A Q a q
STX DC2 B R b
>>
2 2 r
3 ETX DC3 # 3 C S c s
4 EOT DC4 $ 4 D T d t
5 ENQ NAK % 5 E U e u
ETB G w
i
7 BEL 7 g w
8 BS CAN ( 8 H X h X
9 HT EM ) 9 I Y i y
SUB J z j
*
A LF * z
B VT ESC 4- > K [ k {
C FF FS »
< L \ I !
D CR GS - =
M ] m }
-
E SO RS > N n
F SI US / ? 0 0 DEL
251
252 MACHINE LANGUAGE FOR COMMODORE MACHINES
From Chapter 6:
VIC-20 (Unexpanded) Version
We write the BASIC program as follows:
1DD V£=D
110 FORJ = 1TO5
1ED INPUT "VALUE";V£
13D SYS + + + +
14D PRINT "TIMES TEN = " ; V&
15D NEXT J
Plan to start the machine language program at around 40^7 + 127, or
4E24 (hexadecimal IDflD). On that basis, we may change line 130 to
SYS A224. Do not try to run the program yet.
A IDfiD LDY #$D5
A loaa LDA ($SD), Y
A 10fi4 STA $D33C
A 1D67 STA $D33E
A IDflA LDY #$D3
A IDflC LDA ($5D), Y
A IDflE STA $D33D
A ioqi STA $D33F
A 1U^4 ASL $D33D
A inq? ROL $D33C
A 1DRA ASL $D33D
A 1DRD ROL $033C
A 1DAD CLC
A 1DA1 LDA $D33D
A 1QA4 ADC $D33F
A 1DA7 STA $D33D
A 1DAA LDA $D33C
A 1DAD ADC $D33E
A 1DBD STA $D33C
A 1DB3 ASL $D33D
A IDBb ROL $D33C
A IDBR LDY #$DE
A 1DBB LDA $D33C
A 1DBE STA ($5D), Y
A 1DCD LDY #$D3
A 1DCE LDA $D33D
A 1DC5 STA ($5D), Y
A 1DC7 RTS
APPENDIX E — PET/CBM VERSION 253
002D Cfl ID
PET/CBM Version
We write the BASIC program as follows:
1DD V£=D
110 F0RJ = lT0 5
120 INPUT "VALUE"; V£
130 SYS + + + +
UD PRINT "TIMES TEN =»;V%
150 NEXTJ
:002A Cfl 04
From Chapter 7:
An Interrupt Project
VIC-20 (Unexpanded) Version
The only difference with the VIC-20 is that the screen is located at $ IE 0 D:
ft 0350 SEI
ft 0351 LDft #$3C
ft 0353 STfi $0314
ft 035b LDft #$D3
ft 035fl STft $0315
ft 035B CLI
ft 035C RTS
ft 035D SEI
ft 035E LDft $03ft0
ft 03fcl STA $0314
ft 03t4 LDft $D3fil
ft 03t7 STA $D315
APPENDIX E — PET/CBM VERSION 255
R D3bfi CLI
a D3tB RTS
SYS fl3b will invoke the new interrupt code; SYS fifcl will turn it off. As
with the Commodore 64, there is a possibility of the character printing
white-on-white, so that it cannot be seen.
PET/CBM Version
This version is not for original ROM machines, which have the IRQ vector
located at address $051cl/fl:
fi 033C LDR
fi 033E STfi $fiDDD
R 0341 JMP ($D3flO)
R 0350 SEI
R 0351 LDR #$3C
R 0353 STR $00c10
fi 035k LDfl #$03
R 0356 STR $0091
R 035B CLI
fi 035C RTS
R 035D SEI
R 035E LDR $03R0
R 03tl STR $00R0
R 03k4 LDR $D3R1
R 03b? STR $00Rl
R 03tfl CLI
fi 03bB RTS
SYS fl3t will invoke the new interrupt code; SYS flbl will turn it off.
Since the PET/CBM does not have colors, the characters will always show.
256 MACHINE LANGUAGE FOR COMMODORE MACHINES
The equivalent program for PET/CBM won't be given here. It would involve
writing over part of the CHRGET program (at$DD7Dto$D0fl7), sup
plying replacement code for the part we have destroyed, and then adding
the new features.
APPENDIX E — EXERCISES FOR THE C128 257
On the Commodore 128, the BASIC command MONITOR will bring the
machine language monitor into action. You'll immediately get the register
display:
MONITOR
PC SR AC XR YR SP
; FBDDD DD DD DD DD FR
The cursor will be flashing at the start of the next line.
For the moment, we can consider that "bank 15" means "situated in
ROM—Read Only Memory." We know that the Machine Language Monitor
is built into the Commodore 128; so of course it's in ROM.
When we write programs, we will put them in RAM. The Commodore 128
has a great deal of RAM fitted to it. We could choose RAM from bankO,
where BASIC programs are stored, or from bank 7, where BASIC vari
ables, arrays and strings are kept. There's plenty of memory in either
bank. We will choose bank 0, mostly because it's easier—if the first digit
is zero (for bank 0) we don't have to type it.
Display memory with M BflD Bfll and set the resulting display so that
the values are
>DDBfiD 11 RR xx xx xx xx xx xx
Remember to press RETURN. Now we may run our program; we start it
with
GBDD
Display the data values to confirm that they have been exchanged with
M 03fl0 D3fll.
From Chapter 2:
Print projects:
The first exercise uses the same code, but is placed in address BOD:
A DBOD LDA #4fl
Note that the use of the dollar sign for hexadecimal is optional in this
monitor. It's probably better to use it, but if you don't the computer will
assume hex numbers are intended. Using the plus sign to signal a decimal
number, you could type . . .LDA # + 7E and the decimal value of
7 E—the same value as hex A fi—will be accepted. You could even type
. . .LDA#£DlD01D0Dto enter the same number in binary. Either
way, it's still the ASCII letter H, and you'll find that hexadecimal is more
convenient and compact.
If you have correctly typed the line, it will be assembled. The object code
will be placed into memory starting at the address specified and you'll also
260 MACHINE LANGUAGE FOR COMMODORE MACHINES
see it on the screen as part of the line that you originally typed. You'll also
get a prompt for the next instruction. The screen will now show:
A DDBDD Rq 4& LDA #$4fl
A DDBDE
The cursor will be flashing to the right of the "£." Continue by typing in
JSR $FFDE and pressing RETURN. Again, the computer will rework
what you have typed and anticipate your next line by printing A DDBD5,
which allows you to type in the final command, BRK. The screen now
looks like this:
A DDBDD AR A& LDA #$4fl
A DDBOE ED DE FF JSR $FFDE
A 0DBD5 DD BRK
A OOBDb
Press RETURN to signal that you're finished. If you wish, you may display
memory with M BDD BDb. You'll see your program in memory:
>DDBDD AR A& ED DE FF DD xx xx
There's less need to check your work with a disassembler since you can
see the code as it is being written. But it never hurts to be safe; so we
may inspect our program again with:
D BDD BD5
Note that we give two addresses, the starting and the ending address. If
you give the starting address only, you'll get about 20 bytes of code, which
is more than we need.
If you wish to disassemble more code than the screen will hold, you may
"continue" a disassembly by typing the letter D by itself. You'll get about
20 more bytes from wherever your previous disassembly left off. Inciden
tally, you can use the same technique for memory display: an M command
without an address will continue a memory display.
To run the program type the command G FDBDD. What? Our program
is in bank 0; how can we call it by naming bank 15? And why would we
do so?
APPENDIX E — EXERCISES FOR THE C128 261
We can successfully call the program by naming bank 15 for this reason:
in bank 15, all addresses below hexadecimal 4DDD (decimal It3fl4)
are taken from RAM, bank 0. It would be more accurate to call bank 15,
"configuration 15." You can read more on this, when you're ready, in
Appendix B-1. That explains how we get there; but why bother?
When you get into advanced programming in the Commodore 128, you
will be able to call subroutines across banks. At that time, you'll be able
to have a program running in bank zero that will call a subroutine in ROM,
bank 15, with provision for a successful return to bank zero. When you're
ready for it, you'll find that a subroutine called JSF AR, located at address
$D2CD in all banks, will do the job of getting you across and back. You'll
need to learn how to carefully set up the contents of addresses 5 to fl
before calling JSFAR.
For the moment, we can skip the advanced techniques and get our pro
gram running with G FDBDD. The H will be printed on the same line, to
the right of the last zero.
Just before returning to BASIC, let's ask for the decimal equivalent to
hexadecimal DBDD. Type $BDD and press return. You'll see the equiv
alent representations in hexadecimal (the same number), in decimal
( + Efllb) and also in octal and binary. The decimal value Eflit is what
we need.
Return to BASIC (using the X command). The computer will say READY;
you may now call your program with a SYS command.
1DD BANK 15
11D FOR J = l TO ID
150 SYS 5S1E,
130 NEXT J
If you prefer, you're allowed to say SYS DEC( "BDD") in line 150.
BASIC commands know only decimal numbers, so we must translate from
hex one way or another.
Loops project:
A DBDD LDX#D
A DBD5 LDA $BDE,X
A DB05 JSR $FFD5
A DBOfl INX
A OBOR CPX #t
A DBDB BNE $B05
A DBDD RTS
After entry, the program looks like this:
A DBDD A5 DD LDX #$DD
A 0B05 BD OE OB LDA$DB0E,X
A DBD5 5D D5 FF JSR $FFD5
ADBDaEa INX
ADBDREDDt CPX #$0b
A DBDB DD F5 BNE $0B05
A DBDD bO RTS
We guessed (or planned) that the address $DBDE would be available for
our message HELLO. Now we must store these characters in memory.
Command M BDE B13, and type over the display to show
>DDBDE A& AS AC AC AT DD XX XX
Return to BASIC (with X) and try BANK IS: SYS 5aib. The computer
should say HELLO.
From Chapter 3:
Input exercise:
A 0DB00 JSR $FFE1
A 00B03 BEQ $B15
A OOB05 JSR $FFE<
A DOBOfl CMP #30
A DOBOA BCC $0B00
A 00B0C CMP #3A
A 00B0E BCS $B00
A O0B1O JSR $FFD2
A 00B13 AND#$0F
A 00B15 RTS
The forward branch to $ 0 B15 was a guess, but it turns out to be correct.
The final assembly looks like this:
A 00B00 ED El FF JSR $FFE1
A 00B03 FO ID BEQ $0B15
A 00B05 20 E4 FF JSR $FFE4
A □ DBOfl CR 30 CMP #$30
A 00B0A SO TA BCC $0B00
A 00B0C cq 3A CMP #$3A
A 00B0E BO FO BCS $0B00
A 00B10 20 D2 FF JSR $FFD2
A 00B13 sq OF AND #$0F
A 00B15 to RTS
Call the subroutine for testing with BANK 15:SYS 2816.
From Chapter 4:
Addition program:
Be sure that the above code from the previous chapter has been entered
before continuing with the main program:
A DOBlt JSR $BDD
264 MACHINE LANGUAGE FOR COMMODORE MACHINES
From Chapter 5:
Project: Screen Manipulation
It's not possible to do a simple POKE to the screen when you are in the
Commodore 128's 80-column mode, so this example must be for the 40-
column configuration only. The first instruction uses a decimal value of 40
rather than hex 28.
A ODBDD LDA # + 40
A 00B02 STA $BfiO
The 40-column screen is usually at $04 00.
A 00B05 LDX #$04
A 00BD7 STX $BC
A OOBOq LDA #$00
A DOBOB STA $BB
A 00B0D LDX #0
Here's where we start on a new line
A ODBOF LDY #$04
And this is where we handle the next column.
A 00B11 LDA ($BB),Y
A 00B13 CMP #$E0
A 0DB15 BEQ $Biq
A 00B1? EOR #$ao
A ODBIR STA ($BB),Y
A 0DB1B INY
A D0B1C CPY # + lfl
A 00B1E BCC $B11
A 00B20 CLC
A 0DB21 LDA $BB
A 00B23 ADC $BflD
A OOBSb STA $BB
A 00B5B LDA $BC
A OOBSA ADC #$00
A 00B2C STA $BC
A 00B2E INX
A D0B2F CPX # + 14
A 00B31 BNE $B0F
A 00B33 RTS
The assembled code will look like the following. There's not enough room
on the screen to see it all at once.
A OOBOD AS 5fl LDA #$5fl
A 00B05 flD flO OB STA $OBflO
A OOBOS AE 04 LDX #$04
266 MACHINE LANGUAGE FOR COMMODORE MACHINES
From Chapter 6:
We can do the exercise, but there are new rules that we must learn.
The Commodore 128 has more than one bank of memory, and different
things are found in different banks. The role of pointers in keeping various
types of data separate is not the same as before. An earlier Commodore
machine (the B-128 or 700 series) had this same kind of multi-bank ar
chitecture, and some of the following comments will also apply to that
machine.
APPENDIX E — EXERCISES FOR THE C128 267
About the pointers: Our BASIC program is in bank 0, but the variables,
arrays and strings are in bank 1. This means that the start-of-BASIC pointer
works with a different memory bank than the others. When we place a
machine language program directly behind a BASIC program we are in
no danger of bumping into variables. There is a pointer, saying where the
empty space starts in bank 0—the pointer is at hex 121D, decimal A124—
but it mustn't be confused with start-of-variables.
To LOAD from any memory bank, we must call subroutine INDFET (indirect
fetch), located at address $FF74. Set things up as if you are about to
do a LDA (..),Y command. That calls for first setting up an indirect address
somewhere in page zero. Tell the subroutine where the indirect address
is located by loading its address into the A register. Then load the bank
number into register X, load Y with an appropriate value for indirect use,
and call JSR $FF?4. The data from the appropriate bank will be re
turned in the A register.
268 MACHINE LANGUAGE FOR COMMODORE MACHINES
Project
Switch into the machine language monitor. Assemble the following code,
but don't type the comments in parentheses:
A OlCflO LDY #$05
A 01Cfl5 SEI
A 01Cfl3 LDA #$5F (the indirect address)
A OlCflS LDX #$01 (bank 1)
A 01Cfl7 JSR $FF74
A OlCflA STA $0B00
A OlCfiD STA $0B02
A OlCqO LDY #$03
A 01Cq5 LDA #$5F
A Q1CT4 LDX #$01
A DlCRt JSR $FF7<
A OICRR CLI
A OICRA STA $0B01
A OICRD STA $0B03
A 01CA0 ASL $0B01
A 01CA3 R0L $0B00
APPENDIX E — EXERCISES FOR THE C128 269
A D1CAC ia CLC
A D1CAD AD Dl DB LDA $OBD1
A D1CBD bD D3 DB ADC $0BD3
A D1CB3 flD Dl DB STA $OBD1
A OlCBb AD DD DB LDA $DB00
A OICBR bD DS DB ADC $DB0E
A D1CBC flD DD DB STA $DBDD
A D1CBF DE Dl DB ASL $DBD1
A D1CCE EE DD DB ROL $0BDD
A D1CC5 7fi SEI
A DICCb AD DE LDY #$0E
A OlCCfl k<=\ SF LDA #$SF
A 01CCA flD B^ DE STA $DEBq
A D1CCD AE Dl LDX #$01
A D1CCF AD DD DB LDA $0BDD
A D1CDE ED 77 FF JSR $FF77
A 01CD5 AD D3 LDY #$03
A D1CD7 AD Dl OB LDA $DB01
A 01CDA AE Dl LDX #$D1
A 01CDC ED 77 FF JSR $FF77
A D1CDF 5fl CLI
A DICED bD RTS
We must change the End-of-BASIC pointer to a location above the ma
chine) language programi. That would be $1CE1, and so we display the
EOB pointer with M 1S1D 1E11 and change the pointer to
>1E1O El
Check ... disassemble ... and then back to BASIC. List, and you'll see
your BASIC program again. There's no sign of the machine language
program, of course, but*SAVE will now save everything together. You may
now RUN.
From Chapter 7:
Interrupt Exercise
The interrupt:
A OOBDD LDA
A DOBOE STA
A ODBDS JMP ($0B50)
The enable:
A ODBDfl LDA $0314
A DOBOB STA $DBSD
A DOBOE LDA $0315
A D0B11 STA $DBS1
APPENDIX E — EXERCISES FOR THE C128 271
A 00B14 SEI
A 00B15 LDA #$DD
A DDB1? STA $0314
A DDB1A LDA #$DB
A DDB1C STA $0315
A 0DB1F CLI
A DDBED RTS
The disable:
A 00B51 SEI
A DDBEE LDA $0B50
A 00B55 STA $0314
A DDBEfl LDA $DB51
A 00B5B STA $0315
A 00B2E CLI
A DDB5F RTS
The completed program should look like this:
A DOBDD A5 Rl LDA IRl
A 00B02 flD DD 04 STA $040D
A 00B05 tC 50 QB JMP ($DB5D)
A DDBDfl AD 14 D3 LDA $031^5
A DDBDB flD 5D DB STA $DB5D
A DDBDE AD 15 D3 LDA $0315
A 00B11 flD 51 OB STA $DB51
A 00B14 7fl SEI
A 00B15 AR 00 LDA #$00
A 00B17 flD 14 03 STA $0314
A 00B1A AH OB LDA #$0B
A 00B1C flD 15 03 STA $0315
A 00B1F 5fl CLI
A 00B50 tO RTS
A 00BE1 7fi SEI
A 00BE5 AD 50 OB LDA $0B50
A 00B55 flD 1A 03 STA $0314
A OOBSfl AD 51 OB LDA $0B51
A 0DB5B flD 15 03 STA $D315
A OOBEE 5fl CLI
A 00B5F tO RTS
Enable the new interrupt procedure by a SYS to $0B0fl, above (SYS
2824). Return to the "standard" interrupt with a call to $ 0 B 21 (SYS 2849).
From Chapter 8:
Output Example
A ODBDA LDA
A DDBDC ED DE FF JSR $FFDE
A DDBDF DD LDA #$DD
A DDB11 50 DE FF JSR $FFDE
A 0DB14 ED CC FF JSR $FFCC
A DDB17 tO RTS
Input Example
277
278 MACHINE LANGUAGE FOR COMMODORE MACHINES
ZF/ Sign
Exponent (High Or
Mantissa—4 Bytes
der
Bit only)
fl5 5fl DD DD DD
G
Uncrashing
It's best to write a program that doesn't fail (or "crash"). Not all of us
succeed in doing this.
279
280 MACHINE LANGUAGE FOR COMMODORE MACHINES
PET/CBM
Original ROM PETs cannot be uncrashed.
VICICommodore 64
You might try holding down the RON/STOP key and tapping the
RESTORE key to see if that will bring the machine to its senses. Oth
erwise, you must do a more serious reset.
You must depend on the fact that the computer does a nondestructive
memory test during reset. There are various commercially available in
terfaces for the cartridge port—usually "mother boards" that are fitted with
reset switches.
When the reset switch is pressed, the computer starts from the beginning;
but memory is not disturbed. If you have logged the entry location of the
machine language monitor, you can bring it back with the appropriate S YS
command.
APPENDIX G — COMMODORE PLUS/4 287
Commodore PLUS/4
There's a reset button next to the power switch. Before you press it, hold
down the RON/STOP and CTRL keys. Now press the reset button and
you'll find yourself in the machine language monitor.
■J
H
Supermon
Instructions
Commodore 128 users should turn to page 290, which also includes a
summary of instructions to SUPERMON+ for C64 users who also want a
better monitor.
Load Supermon and say RUN. It will write an MLM for you, and call it up.
Now, exit back to BASIC and command NEW. You do not want the MLM
builder any more (it's done the job) and you do not want the danger of
building two—or more—MLM's. Get rid of the generator program. Any
time you need to use the MLM, give SYS4orSYSfl,as appropriate.
a—to assemble
D—to disassemble
283
284 MACHINE LANGUAGE FOR COMMODORE MACHINES
A Do-lt-Yourself Supermon
If you do not have access to Supermon from friends, dealers, clubs, or
disk, you may find the following program useful for the Commodore 64
only.
Enter this program (it will take you hours). Be sure that lines 3 00 and
above are correct; the lower numbered DATA lines will be checked for
accuracy by the program.
When you say RUN, the program will run in two phases. Part 1 takes over
two minutes to run: it will check all DATA statements for missing lines
and errors and report any problems to you. Part 2 will run only if part 1
shows no errors: it will cause the program to "collapse" into itself, resulting
in Supermon. The moment the program has completed running, save
Supermon to disk or tape.
E3 DRTa E37,lb5,55,133,51,lb5,5t,,133,5E,10B,-17
24 DaTa 55,D,7cl,7c!,7q,7cl,173,E3D,E55,D,-EE
E5 DaTa 141,EE,3,173,E31,E55,D,Kl,E3,3,-fc4
Eb DBTa lfcq,lEa,3E,144,E55,D,D,Elfc,lD^,l<l,-3D
E7 DRTa bE,E,lD4,:Ul,bl,E,lD4,141,bD,E,-41
iD4,i4i,5q,E,iD^,i7 0,iD<,ita,5b,i3a, -17
E33,E,141,5a,E,152,E33,0,Q,141,-lE
3D DaTa 57,E,iat,14E,t3,E,3E,147,E53,D,-57
31 DaTa lfcE,bfc,ltq,4E,3E,ED5,E51,D,lbq,aE,-bE
3E DaTa EDa,4E,E3D,iq3,E0a,fc,E3D,iq4,E0a,E,-5E
33 DBTa E3D,3a,qt,3E,ED7,S55,EDl,13,EDa,E4a,-24
34 DaTa 104,lD4,lfcq,D,D,133,3a,lfcE,13,lbq,-ll
35 DaTa 4b,3E,E05,E51,D,3E,EE0,E4q,D,EDl,-3E
37 DaTaiq5
3B DaTa E55
sq Daia it
41 DaTa Eq,ltD,D,D,3E,l^i3,S53,D,177,iq3,-31
4? DaTa 3E,iqo,E5i,o,3E,EDq,E<q,D,iqa,Eq,-bi
43 DaTa EDa,E^l,qfc,3E,E54,E51,D,144,ll,lfcE,-53
D,o,iEq,iq3,iq3,iq3,E4o,3,7b,aD,-5a
E5E,D,3E,EDq,E^q,D,iqa,Eq,qb,ibq,-5b
5q,i33,iq3,ibq,E,i33,iq/;,it,q,5,qt,-ED
47 DaTa 15E,7E,3E,147,E53,D,lD4,lbE,<fc,7b,-^
4& DaTa ED5,E51,D,lfcE,D,D,iaq,E34,E55,D,-31
<q DaTa 3E,SlD,S55,E3E,SE4,EE,EDa,E4 5,ltD,5q,-
5D DaTa 3E,afc,E5D,0,173,57,E,3£,iq0,E51,-4
51 DaTa D,173,5a,E,3E,iqD,E51,D,3E,7 5,-31
5E DaTa E5D,D,3E,33,E5D,D,E4D,a7,3S,EED,-13
53 Daia E4q,D,3E,E3q,E51,D,144,4t,3E,EE3,-4D
54 DaTa E51,0,3E,£E0,E4q,0,3E,E3q,E51, 0,-51
55 DaTa 144,35,3£,EE3,E51,0,3E,EE5,E55,E40,-33
286 MACHINE LANGUAGE FOR COMMODORE MACHINES
5b DATA t0,ltt,3Q,200,5t,lt5,iq5,iq7,iq3,lt5,-22
5? DATA iqt, EEq,iq4,144,4b,lLD,5fi,3E,ab,£50,-El
5fl DATA 0,32,103,251,0,32,31,550,0,540,-tO
5R DATA 224,71,00,252,0,32,23q,251,0,144,-42
bD DATA 3,3E,ED,E50,D,3E,75,E5D,D,EDa,-43
tl DATA 7,32,23R,251,0,144,235,Itq,0,133,-20
fcE DATA 2q,32,220,24R,0,32,53,250,0,200,-10
b3 DATA 240,7t,25R,54^,0,35,507,555,501,13,-55
b4 DATA 540,15,201,35,500,50q,35,53R,551,0,-57
b5 DATA 144,3,3E,E0,E5D,D,174,b3,E,154,-4b
bb DATA lE0,173,57,E,7E,17 3,5a,E,7E,173,-35
b7 DATA 5q,5,75,173,10,5,174,11,5,175, -55
bfl DATA tS,5,t4,174,t3,2,154,10Q,S,lt0,-5t
tq DATA 110,1,135,Iflt,135,105,131,135,103,135,-57
7D DATA144,135,147,ltq,t4,133,107,ltq,B,133,-iq
71 DATA lflfl,3E,ED7,E55,E01/3E,E4D,E4q/E0:L,13,-4E
7E DATA 240,5t,2Dl,34,200,20,32, 207,255,201,-35
73 DATA 34,240,It,201,13,240,41,145,107,230,-3q
74 DATA lfl3,EDD,iqE,lt.,EDfl,E3b/7t,aD/E5E,0,-lfl
75 DATA 32,207,255,201,13,240,22,201,44,200,-51
7b DATA 220,32,254,251,0,41,15,540,533, 501,-41
77 DATA 3,540,22q,133,Iflt,32,207,255,501,13,-45
7fl DATA qfc,lDfl,4fl,3,lDfl,5D,3,3E,EE,E51/-bD
7q DATA 0,500,212,Itq,0,0,32,111,551, 0,-37
00 DATAlt5,144,41,lt,20fl,201,7t,22q,24q,0,-22
■01 DATA 32, 22, 551, 0,201, 44, 200, iqi, 32, 23q,-40
flE DATA E51,D/3E,EE3/S51/D,3E,ED7,E55,ED1/-E5
S3 DATA 44,200,170,32,53q,551,0,It5,iq3,133,-7
&A DATA 174,It5,iq4,133,175,32,223,251, 0,32,-34
fi5 DATA 207,255,201,13,200,157,32,114, 251, 0,-3t
fib DATA 7t,22q,24q,0,lt5,iq4,32,iq0,251,0,-3q
fl7 DATA It5,iq3,75,74,74,74,74,35,514, 551,-13
fifi DATA 0,170,104,41,15,35,514,551,0,75,-It
flq DATA 13a,3E,E10,E55,lD4,7b,ElD,E55,q,4a,-q
qO DATA 501,50,144,5,105,t,qt,It5,5,101, -3D
qi data iqa,7S,iQi,iq4,i4q,iqs,iO4,i4q,iq4,so2,-25
qE DATA EDa,E4 3,qb,3E,E54,E51,D,144,E/133, -30
q3 DATA iq4,32,254,251,0,144,2,133,iq3,qt,-43
q4 DATA ltq,0,0,133,4 2,32,220,24q,0,201,-3q
q5 DATA 32,200,q,32,220,24q,0,201, 32, 200,-25
qb DATA 14,E4,qb,3E,37,E5E,D,lD,lD/10, -t2
q7 DATA lD,133,4E,3E,SED/E4q,D,3E,37,E5E,-3q
qa DATA 0,5,42,5t,qt,201,50,144,2,105, -2t
APPENDIX H — A DO-IT-YOURSELF SUPERMON 287
qq DfiTfi 6,41,15,qt,qt,35,S30,54q,0,S01,-tS
1D0 DfiTfi 3S,S4 0,34q,qt,ltq,D,0,141,0,0,-5S
1D1 DfiTfi 1,35,47,555,0,35,5,353,0,35,-Sq
IDE DfiTfi 545,551,0,144,q,qt,35,550,S4q,0,-56
1D3 DfiTfi 33,33q,351,0,l?t,33S,174,t3,S,154,-35
104 DfiTfi lbq,b3,3E,£10,ES5,7b,EEq,£4q,0,3E,-4a
105 DfiTfi 143,553,0,SOS,SOQ,550,Rk,lb5,iq5,lk4,-IE
10b DfiTfi iqt,5t,S33,5,17t,l,13t,5t,S5q,iq3,-tl
107 DfiTfi 133,3 0,15E,EEq,iq4,lba,5,30,qb,3E, -41
lOfl DfiTfi 55,E5E,0,133,3S,lt5,1^4,133, 33 ,ltS,-33
10q DfiTfi 0,0,134,4 0,lbq,147,3E,E10,E55,lbq,-3E
110 DfiTfi EE,133,Eq,3E,lb5,E5E,0,3E,5,E53,-E
111 DfiTfi 0,133,iq3,13E,iq4,ISfl,E^,EOfl,E4E,Ib^,-It
HE DfiTfi 14 5,3E,E10,E55,7fc,EEcl,E4q,0,lt0,44, -41
113 DfiTfi 3E,fib,E50,0,3E,143,E53,0,3E,lfl3,-S3
114 DfiTfi 551,D,33,143,553,0,Its,0,0,ltl, -35
115 DfiTfi iq3,3E,E0,E53,0,7E,3E,q0,E53,0,-E5
lib DftTfi 104,3E,llE,E53,0,lbE,b,EE4,3,E0fl,-43
117 DfiTfi Ifl,lt4,31,340,14,It5,45,501,333,177,-10
llfi DfiTfi iq3,17b,Sfi,3E,E53,E5E,0,13b,EDfl,E4E,-15
liq DfiTfi b,4E,144,14,lflcl,54,S55,0,3E,lfl7,-3
1E0 DfiTfi E53,0,lfiq,b0,E55,0,E4 0,3,3E,lfl7,-E4
1E1 DfiTft E53,0,E0E,E0fl,E13,qb,3E,fl,E53,0,-4 5
1ES DfiTfi 17 0,333,300,1,300,155,35,353, 353, 0,-3q
1E3 DfiTfi 13fl,134,3fl,3S,iq0,351,0,ltt,3fl,qt,-47
1E4 DfiTft lt5,31,5t,lt4,iq4,17 0,lt,l,13t,101,-53
1E5 DftTfi iq3,144,1,300,qt,Itfl,74,144,11,74,-51
131 DfiTfi 171,S3,301,34,340,iq,41,7, q, 155,-t3
1E7 DfiTfi 74,170,lflq,EEq,E54,0,17b,4,74,74,-5E
156 DfiTfi 74,74,41,15,506,4,1tO,136,Itq,0,-30
13q DflTfi 0,170,16q,41,355,0,133,45,41,3,-t5
130 DATfi 133,31,155,41,143,170,155,It0,3, 334,-3t
131 DfiTfi 136,340,11,74,144,6,74,74,q,33,-b
13E DfiTfi 13b,E0a,E50,EDO,13b,E0B,E4E,qb, 177, iq3,-Eq
133 DfiTfi 35,553,555,0,It5,1,35,q3,355,0,-It
134 DfiTfi iqt,31,500,144,541,It3,3,iq3,4,144,-16
135 DfiTfi E4E,qb,lbfi,ia5,b7,E55,0,133,40,165,-13
13 b DfiTfi 131,555,0,133,41,Itq,0,0,It0,5,-3
137 DfiTfi t,41,36,40,45,131,306,346,105,t3,-57
-136 DfiTfi 33, 510, 555, 503, 306, 531, Itq, 35, 506,11,-It
13q DfiTfi lbq,13,3b,iq,lb,5,3E,E10,E55,lbq,-30
140 DfiTfi 10,7b,E10,E55,3E,55,E5E,0,lbq,3,-El
141 DfiTfi 133,Eq,3E,EE0,E4q,0,3E,53,E50,0,-4E
288 MACHINE LANGUAGE FOR COMMODORE MACHINES
—Syntax is somewhat easier. Leading zeros need not be typed on any number.
Spacing between addresses is non-critical.
—Conversion between number systems is built-in. The user may employ the
following prefixes: $ for hexadecimal; + for decimal; & for octal (rarely used
with present day microcomputers); and % for binary. Any value or address
may be entered in any number system. If a number is typed in alone, with
its prefix, it will be shown converted to all other number systems.
—When the A(assemble) command is given, the object code immediately ap
pears on the line just typed.
—The disk may be controlled, interrogated or cataloged by means of the disk
"@" command.
—Memory displays contain information on the ASCII equivalents of the bytes
displayed.
—Commands such as M (memory display) and D allow: two addresses, to
display a specified range; one address, to display a fixed range; or no ad
dresses, to continue the display from that shown previously.
If you are using the Commodore 128 in its C128 mode, there's no need
to load a monitor. Just command MONITOR and you are there.
APPENDIX H — C128 MONITOR AND SUPERMON + 29t
G—to go to an ML program
The above commands are the only ones used within the text of the book.
Other commands which are available are:
293
294 MACHINE LANGUAGE FOR COMMODORE MACHINES
CONTROL
8 BIT \ 8 BIT
DATA ) DATA PERIPHERAL
MICRO BUS / PORT
DEVICES-
6520 PRINTERS,
PROCESSORS
650 x 8 BIT DISPLAYS,
ETC.
(control) . DATA
\ PORT
CONTROL)
Figure 1.1
The various bits in the control registers will be accessed many times during
a program to allow the processor to enable or disable interrupts, change
operating modes, etc. as required by the peripheral device being con
trolled.
The Data Direction Registers are normally programmed only during the
system initialization routine which is performed in response to a Reset
signal. However, the contents of these registers can be altered during
system operation. This allows very convenient control of some peripheral
devices such as keyboards.
Reset (RES)
The active low Reset line resets the contents of all 6520 registers to a
logic zero. This line can be used as a power-on reset or as a master reset
during system operation.
Each Interrupt Request line has two interrupt flag bits which can cause
the Interrupt Request line to go low. These flags are bits fc and 7 in the
two Control Registers. These flags act as the link between the peripheral
interrupt signals and the microprocessor interrupt inputs. Each flag has a
corresponding interrupt disable bit which allows the processor to enable
or disable the interrupt from each of the four interrupt inputs (CA1, CAE,
CB1, CBE).
The four interrupt flags are set by active transitions of the signal on the
interrupt input (CA1, CAE, CB1, CBE). Controlling this active transition
is discussed in the next section.
Control of IRQA
Control Register A bit 7 is always set by an active transition of the CA1
interrupt input signal. Interrupting from this flag can be disabled by setting
bit D in the Control Register A (CRA) to a logic D. Likewise, Control
Register A bit fc can be set by an active transition of the CAE interrupt
input signal. Interrupting from this flag can be disabled by setting bit 3 in
the Control Register to a logic D.
Both bit b and bit 7 in CRA are reset by a "Read Peripheral Output
Register A" operation. This is defined as an operation in which the pro
cessor reads the Peripheral A I/O port.
APPENDIX I 297
Control of IRQB
Control of IRQB is performed in exactly the same manner as that de
scribed above for IRQ A. Bit 7 in CRB is set by an active transition on
CB1; interrupting from this flag is controlled by CRB bit 0. Likewise, bit
b in CRB is set by an active transition on CB2; interrupting from this flag
is controlled by CRB bit 3.
Also, both bit t and bit 7 are reset by a "Read Peripheral B Output
Register" operation.
Summary
IRQA goes low when CRA-7 = lanc/CRA-D = 1 or when CRA-b
= 1 and CRA-3 = 1.
It should be stressed at this point that the flags act as the link between
the peripheral interrupt signal and the processor interrupt inputs. The in
terrupt disable bits allow the processor to control the interrupt function.
Setting the interrupt flag will interrupt the processor through IRQA if bit
D of CR A is a 1 as described previously.
These control register bits and interrupt inputs serve the same basic func
tion as that described above for CA1. The input signal sets the interrupt
flag which serves as the link between the peripheral device and the pro
cessor interrupt structure. The interrupt disable bit allows the processor
to exercise control over the system interrupts.
In the Output mode (CRA, bit 5 = 1), CAE can operate independently
to generate a simple pulse each time the microprocessor reads the data
on the Peripheral A I/O port. This mode is selected by setting CRA, bit 4
to a "D11 and CRA, bit 3 to a "1". This pulse output can be used to
control the counters, shift registers, etc. which make sequential data avail
able on the Peripheral input lines.
7 6 5 4 3 2 1 0
CRA IRQA1 IRQA2 CA2 CONTROL DDRA CA1 CONTROL
ACCESS r
r
7 6 5 4 3 2 1 0
Figure I.2
7 6 5 4 3 2 1 0
8 4 2 1
*IF BITS 4-7 ARE ALL "0"; THEN VSYNC WILL BE 16 SCAN LINES WIDE.
■v
Reg. Register bit rn
No. Register Name Stored Info RD WR 7 6 5 4 3 2 10 g
X
RO Horiz. total # Charac y
R1 Horiz. Displayed # Charac. y
R2 Horiz. Sync Position # Charac. y
R3 VSYNC, HSYNC Widths # Scan Lines & y V3 V2 V! Vo H3 H2 Ht Ho
# Char. Times
-CURSOR SKEW
"0" FOR NEW DELAY
"1" TO DELAY CURSOR ONE
CHARACTER TIME
NOT USED
Figure 1.3
BIT
CURSOR MODE
6 5
0 0 No Blinking
0 1 No Cursor
1 0 Blink at 1/16 field rate
1 1 Blink at 1/32 field rate
Note that the ability to program both the start and end scan line for the
cursor enables either block cursor or underline to be accommodated.
Registers R14 and R15 are used to control the character position of the
cursor over the entire 16K address field.
the strobe occurred. When the LPEN input changes from low to high, then,
on the next negative-going edge of CCLK, the contents of the internal
scan counter is stored in registers R16 and R17.
Features
• Fully expandable system with a 16K byte address space
• System uses industry standard 8 bit wide ROMS and 4 bit wide RAMS
• Mask programmable sync generation, NTSC-6560, PAL-6561
• On-chip color generation (16 colors)
• Up to 600 independently programmable and movable background locations
on a standard TV
E: Number of video rows: The number of rows may range from 0 to 23.
A larger number of rows causes garbage to appear on the bottom of the
screen.
F: Character size: This bit determined the size of the matrix used for
each character. A 0 here sets normal mode, in which characters are 8 by
8 dots. A 1 sets 8 by 16 mode, where each character is now twice as tall.
8 by 16 mode is normally used for high resolution graphics, where it is
likely to have many unique characters on the screen.
G: Raster value: This number is used to synchronize the light pen with
the TV picture.
Note that bit 7 of location 36866 also determines the location of color
memory. If this bit is a 0, color memory starts at location 37888. If this bit
is a 1, color memory begins at 38400. Here is a formula for this:
J: Light pen horizontal: This contains the latched number of the dot
under the light pen, from the left of the screen.
K: Light pen vertical: The latched number of the dot under the pen,
counted from the top of the screen.
Location
X uoniems
Value HEX Decimal
The actual frequency of the sound in cycles per second (hertz) is deter
mined by the following formula:
Clock
Frequency =
(127-X)
X is the number from 0 to 127 that is put into the frequency register. If X
is 127, then use -1 for X in the formula. The value of Clock comes from
the following table:
V: Loudness of sounds: This is the volume control for all the sounds
playing. 0 is off and 15 is the loudest sound.
W: Auxiliary color: This register holds the color number of the auxiliary
color. The value can be from 0 to 15.
APPENDIX I 309
These eight bits are connected to the eight pins which make up port A.
Each pin can be set for either input or output.
Input latching is available on this port. When latch mode is enabled the
data in the register freezes when the CB1 interrupt flag is set. The register
stays latched until the interrupt flag is cleared.
Handshaking is available for output from this port. CB2 will act as a DATA
READY SIGNAL. This must be controlled by the user program. CB1 acts
as the DATA ACCEPTED signal, and must be controlled by the device
connected to the port. When DATA ACCEPTED is sent to the 6522, the
DATA READY line is cleared, and the interrupt flag is set.
These eight bits are connected to the eight pins which make up port B.
Each pin can be set for either input or output. Handshaking is available
for both read and write operations. Write handshaking is similar to that on
PORT B. Read handshaking is automatic. The CA1 input pin acts as a
DATA READY signal. The CA2 pin (used for output) is used for a DATA
ACCEPTED signal. When a DATA READY signal is received a flag is set.
The chip can be set to generate an interrupt or the flag can be polled
under program control. The DATA ACCEPTED signal can either be a
pulse or a DC level. It is set low by the CPU and cleared by the DATA
READY signal.
This is similar to the DDR for port B, except that it works on PORT A.
There are two timers on the 6522 chip. The timers can be set to count
down automatically or count pulses received by the VIA. The mode of
operation is selected by the Auxiliary Control register.
TIMER T1 on the 6522 consists of two 8-bit latches and a 16-bit counter.
The various modes of the TIMER are selected by setting the AUXILIARY
CONTROL REGISTER (ACR). The latches are used to store a 16-bit data
word to load into the counter. Loading a number into the latches does not
affect the count in progress.
After it is set, the counter will begin decrementing at 1 MHz. When the
counter reaches zero, an interrupt flag will be set, and the IRQ will go low.
Depending on how the TIMER is set, either further interrupts will be dis
abled, or it will automatically load the two latches into the counter and
continue counting. The TIMER can also be set to invert the output signal
on a peripheral pin each time it reaches zero and resets.
E: Write into the low order latch. This latch can be loaded into the low
byte of the 16-bit counter.
F: Write into the high order latch, write into the high order counter, trans
fer low order latch into the low order counter, and reset the TIMER T1
interrupt flag. In other words, when this location is set the counter is loaded.
G: Same as E.
H: Write into the high order latch and reset the TIMER T1 interrupt flag.
READ TIMER T1
E: Read the TIMER T1 low order counter and reset the TIMER T1 in
terrupt flag.
TIMER T2
WRITING TO TIMER T2
READING TIMER T2
I: Read TIMER T2 low order counter byte, and clear TIMER T2 interrupt
flag.
K: SHIFT REGISTER
A shift register is a register which will rotate itself through the CB2 pin.
The shift register can be loaded with any 8-bit pattern which can be shifted
out through the CB1 pin, or input to the CB1 pin can be shifted into the
shift register and then read. This makes it highly useful for serial to parallel
and parallel to serial conversions.
The shift register is controlled by bits 2-4 of the Auxiliary Control register.
L: TIMER 1 CONTROL
BIT# 7 6
0 0 One-shot mode (output to PB7 disabled)
0 1 Free running mode (output to PB7 disabled)
1 0 One-shot mode (output to PB7 enabled)
1 1 Free running mode (output to PB7 enabled)
M: TIMER 2 CONTROL
BIT# 4 3 2
0 0 0 SHIFT REGISTER DISABLED
0 0 1 SHIFT IN (FROM CB1) UNDER CONTROL OF
TIMER 2
0 1 0 SHIFT IN UNDER CONTROL OF SYSTEM CLOCK
PULSES
0 1 1 SHIFT IN UNDER CONTROL OF EXTERNAL
CLOCK PULSES
As long as this bit is 0, the PORT B register will directly reflect the data
on the pins.
If this bit is set to one, the data present on the input pins of PORT A will
be latched within the chip when the CB1 INTERRUPT FLAG is set. As
long as the CB1 INTERRUPT FLAG is set, the data on the pins can change
without affecting the contents of the PORT B register. Note that the CPU
always reads the register (the latches) rather than the pins.
Input latching can be used with any of the input or output modes available
for CB2.
As long as this bit is 0, the PORT A register will directly reflect the data
on the pins.
If this bit is set to one, the data present on the input pins of PORT A will
be latched within the chip when the CA1 INTERRUPT FLAG is set. As
long as the CA1 INTERRUPT FLAG is set, the data on the pins can change
without affecting the contents of the PORT A register. Note that the CPU
always reads the register (the latches) rather than the pins.
Input latching can be used with any of the input or output modes available
for CA2.
314 MACHINE LANGUAGE FOR COMMODORE MACHINES
Q: CB2 CONTROL
Q Q Q
BIT# 7 6 5 DESCRIPTION
0 0 0 Interrupt Input Mode
0 0 1 Independent Interrupt Input Mode
0 1 0 Input Mode
0 1 1 Independent Input Mode
1 0 0 Handshake Output Mode
1 0 1 Pulse Output Mode
1 1 0 Manual Output Mode (CB2 is held LOW)
1 1 1 Manual Output Mode (CB2 is held HIGH)
The CB2 interrupt flag (IFR bit 3) will be set on a negative (high-to-low)
transition on the CB2 input line. The CB2 interrupt bit will be cleared on
a read or write to PORT B.
INPUT MODE:
The CB2 interrupt flag (IFR bit 3) will be set on a positive (low-to-high)
transition of the CB2 line. The CB2 flag will be cleared on a read or write
of PORT B.
As above, the CB2 interrupt flag will be set on a positive transition on the
CB2 line. However, reading or writing PORT B does not affect the flag.
The CB2 line will be set low on a write to PORT B. It will be reset high
again when there is an active transition on the CB1 line.
The CB2 line is set low for one cycle after a write to PORT B.
APPENDIX I 3t5
R: CB1 CONTROL
This bit selects the active transition of the input signal applied to the CB1
pin. If this bit is 0, the CB1 interrupt flag will be set on a negative transition
(high-to-low). If this bit is a 1, the CB1 interrupt flag will be set on a positive
(low-to-high) transition.
S:CA2 CONTROL
S s s
BIT# 3 2 1 DESCRIPTION
0 0 0 interrupt Input Mode
0 0 1 Independent Interrupt Input Mode
0 1 0 Input Mode
0 1 1 Independent Input Mode
1 0 0 Handshake Output Mode
1 0 1 Pulse Output Mode
1 1 0 Manual Output Mode (CA2 is held LOW)
1 1 1 Manual Output Mode (CA2 is held HIGH)
The CA2 interrupt flag (IFR bit 0) will be set on a negative (high-to-low)
transition on the CA2 input line. The CA2 interrupt bit will be cleared on
a read or write to PORT A.
INPUT MODE:
The CA2 interrupt flag (IFR bit 0) will be set on a positive (low-to-high)
transition of the CA2 line. The CA2 flag will be cleared on a read or write
of PORT A.
316 MACHINE LANGUAGE FOR COMMODORE MACHINES
As above, the CA2 interrupt flag will be set on a positive transition on the
CA2 line. However, reading or writing PORT A does not affect the flag.
The CA2 line will be set low on a read or write to PORT A. It will be reset
high again when there is an active transition on the CA1 line.
The CA2 line is set low for one cycle after a read or write to PORT A.
T: CA1 CONTROL
This bit of the PCR selects the active transition of the input signal applied
to the CA1 input pin. If this bit is 0, the CA1 interrupt flag (Bit) will be set
by a negative transition (high-to-low) on the CA1 pin. If this bit is 1, the
CA1 interrupt flag will be set by a positive transition (low-to-high).
There are two registers associated with interrupts: The INTERRUPT FLAG
REGISTER (IFR) and the INTERRUPT ENABLE REGISTER (IER). The
IFR has eight bits, each one connected to a register in the 6522. Each bit
in the IFR has an associated bit in the IER. The flag is set when a register
wants to interrupt. However, no interrupt will take place unless the cor
responding bit in the IER is set.
When the flag is set, the pin associated with that flag is attempting to
interrupt the 6502. Bit U is not a normal flag. It goes high if both the flag
and the corresponding bit in the INTERRUPT ENABLE REGISTER are
set. It can be cleared only by clearing all the flags in the IFR or disabling
all active interrupts in the IER.
APPENDIX 1 377
SET BY CLEARED BY
U IRQ STATUS
V TIMER 1 time-out Reading TIMER 1 low order
counter and writing TIMER 1
high order latch
w TIMER 2 time-out Reading TIMER 2 low order
counter and writing TIMER 2
high order counter
X CB1 pin active transition Reading or writing PORT B
Y CB2 pin active transition Reading or writing PORT B
z Completion of 8 shifts Reading or writing the shift
register
a CA1 pin active transition Reading or writing PORT A
(BBBBBBBB in above chart)
b CA2 pin active transition Reading or writing PORT A
(BBBBBBBB in above chart)
c: ENABLE CONTROL
If this bit is a 0 during a write to this register, each 1 in bits 0-6 clears the
corresponding bit in the IER. If this bit is a 1 during this register, each 1
in bits 0-6 will set the corresponding IER bit.
d TIMER 1 time-out enable
e TIMER 2 time-out enable
f CB1 interrupt enable
g CB2 interrupt enable
h Shift interrupt enable
i CA1 interrupt enable
j CA2 interrupt enable
PORT A
This is similar to BBBBBBBB, except that the handshaking lines (CA1 and
CA2) are unaffected by operations on this port.
318 MACHINE LANGUAGE FOR COMMODORE MACHINES
Ports A and B each consist of an 8-bit Peripheral Data Register (PR) and
an 8-bit Data Direction Register (DDR). If a bit in the DDR is set to a one,
the corresponding bit in the PR is an output; if a DDR bit is set to a zero,
the corresponding PR bit is defined as an input. On a READ, the PR
reflects the information present on the actual port pins (PA0-PA7, PBO-
PB7) for both input and output bits. Port A and Port B have passive pull-
up devices as well as active pull-ups, providing both CMOS and TTL
compatibility. Both ports have two TTL load drive capability. In addition to
normal I/O operation, PB6 and PB7 also provide timer output functions.
Handshaking
REG NAME D7 D6 D5 D4 D3 D2 Di Do
Each interval timer consists of a 16-bit read-only Timer Counter and a 16-
bit write-only Timer Latch. Data written to the timer are latched in the Timer
Latch, while data read from the timer are the present contents of the Time
Counter. The timers can be used independently or linked for extended
operations. The various timer modes allow generation of long time delays,
variable width pulses, pulse trains and variable frequency waveforms.
Utilizing the CNT input, the timers can count external pulses or measure
frequency, pulse width and delay times of external signals. Each timer has
an associated control register, providing independent control of the fol
lowing functions:
Start/Stop
PB On/Off:
A control bit allows the timer output to appear on a PORT B output line
(PB6 for TIMER A and PB7 for TIMER B). This function overrides the
DDRB control bit and forces the appropriate PB line to an output.
Toggle/Pulse
One-Shot/Continuous
A control bit selects either timer mode. In one-shot mode, the timer will
count down from the latched value to zero, generate an interrupt, reload
the latched value, then stop. In continuous mode, the timer will count from
the latched value to zero, generate an interrupt, reload the latched value
and repeat the procedure continuously.
Force Load
A strobe bit allows the timer latch to be loaded into the timer counter at
any time, whether the timer is running or not.
Input Mode:
Control bits allow selection of the clock used to decrement the timer.
TIMER A can count §2 clock pulses or external pulses applied to the CNT
pin. TIMER B can count $2 pulses, external CNT pulses, TIMER A un
derflow pulses or TIMER A underflow pulses while the CNT pin is held
high.
The timer latch is loaded into the timer on any timer underflow, on a force
load or following a write to the high byte of the prescaler while the timer
is stopped. If the timer is running, a write to the high byte will load the
timer latch, but not reload the counter.
READ (TIMER)
REG NAME
WRITE (PRESCALER)
REG NAME
The TOD clock is a special purpose timer for real-time applications. TOD
consists of a 24-hour (AM/PM) clock with 1/1 Oth second resolution. It is
organized into 4 registers: 10ths of seconds, Seconds, Minutes and Hours.
The AM/PM flag is in the MSB of the Hours register for easy bit testing.
Each register reads out in BCD format to simplify conversion for driving
displays, etc. The clock requires an external 60 Hz or 50 Hz (programm
able) TTL level input on the TOD pin for accurate timekeeping. In addition
to time-keeping, a programmable ALARM is provided for generating an
interrupt at a desired time. The ALARM registers are located at the same
addresses as the corresponding TOD registers. Access to the ALARM is
governed by a Control Register bit. The ALARM is write-only; any read of
a TOD address will read time regardless of the state of the ALARM access
bit.
READ
REG NAME
8 TOD 0 0 0 0 T8 T4 T2 Ti
10THS
WRITE
CRB7 = 0 TOD
CRB7 = 1 ALARM
(SAME FORMAT AS READ)
The bidirectional capability of the Serial Port and CNT clock allows many
6526 devices to be connected to a common serial communication bus on
which one 6526 acts as a master, sourcing data and shift clock, while all
other 6526 chips act as slaves. Both CNT and SP outputs are open drain
to allow such a common bus. Protocol for master/slave selection can be
transmitted over the serial bus, or via dedicated handshaking lines.
REG NAME
C | SDR | S7 s6 s5 s« s3 s2 s, | s0
APPENDIX 323
There are five sources of interrupts on the 6526: underflow from TIMER
A, underflow from TIMER B, TOD ALARM, Serial Port full/empty and
FLAG. A single register provides masking and interrupt information. The
interrupt Control Register consists of a write-only MASK register and a
read-only DATA register. Any interrupt will set the corresponding bit in the
DATA register. Any interrupt which is enabled by the MASK register will
set the IR bit (MSB) of the DATA register and bring the IRQ pin low. In a
multi-chip system, the IR bit can be polled to detect which chip has gen
erated an interrupt request. The interrupt DATA register is cleared and
the IRQ line returns high following a read of the DATA register. Since each
interrupt sets an interrupt bit regardless of the MASK, and each interrupt
bit can be selectively masked to prevent the generation of a processor
interrupt, it is possible to intermix polled interrupts with true interrupts.
However, polling the IR bit will cause the DATA register to clear, therefore,
it is up to the user to preserve the information contained in the DATA
register if any polled interrupts were present.
REG NAME
Control Registers
There are two control registers in the 6526, CRA and CRB. CRA is as
sociated with TIMER A and CRB is associated with TIMER B. The register
format is as follows:
324 MACHINE LANGUAGE FOR COMMODORE MACHINES
CRA:
.TA
RUN OUT
REG NAME ALARM IN MODE LOAD MODE MODE PBON START
.TB. I
All unused register bits are unaffected by a write and are forced to zero
on a read.
address. The lower order 10 bits are provided by an internal counter (VC3-
VCO) which steps through the 1000 character locations. Note that the
6566/6567 provides 14 address outputs; therefore, additional system hard
ware may be required for complete system memory decodes.
A13 A12 A11 A10 A09 A08 A07 A06 A05 A04 A03 A02 A01 A00
VM13 VM12 VM11 VM10 VC9 VC8 VC7 VC6 VC5 VC4 VC3 VC2 VC1 VCO
A13 A12 A11 A10 A09 A08 A07 A06 A05 A04 A03 A02 A01 A00
CHARACTER
FUNCTION BIT COLOR DISPLAYED
Therefore, each character has a unique color determined by the 4-bit color
nybble (1 of 16) and all characters share the common background color.
CHARACTER
FUNCTION BIT PAIR COLOR DISPLAYED
Since two bits are required to specify one dot color, the character is now
displayed as a 4 x 8 matrix with each dot twice the horizontal size as in
standard mode. Note, however, that each character region can now con
tain 4 different colors, two as foreground and two as background (see
MOB priority).
328 MACHINE LANGUAGE FOR COMMODORE MACHINES
CHAR. POINTER
MS BIT PAIR BACKGROUND COLOR DISPLAYED FOR 0 BIT
Since the two MSB of the character pointers are used for color information,
only 64 different character definitions are available. The 6566/6567 will
force CB10 and CB9 to "0" regardless of the original pointer values, so
that only the first 64 character definitions will be accessed. With extended
color mode each character has one of sixteen individually defined fore
ground colors and one of the four available background colors.
NOTE: Extended color mode and multi-color mode should not be enabled simulta
neously.
A13 A12 A11 A10 A09 A08 A07 A06 A05 A04 A03 A02 AO1 AOO
CB13 VC9 VC8 VC7 VC6 VC5 VC4 VC3 VC2 VC1 VCO RC2 RC1 RCO
VCx denotes the video matrix counter outputs, RCx denotes the 3-bit raster
line counter and CB13 is from register 24 ($18). The video matrix counter
steps through the same 40 locations for eight raster lines, continuing to
the next 40 locations every eighth line, while the raster counter increments
once for each horizontal video line (raster line). This addressing results
in each eight sequential memory locations being formatted as an 8 x 8
dot block on the video display.
Multi-colored bit map mode is selected by setting the MCM bit in register
22 ($16) to a "1" in conjunction with the BMM bit. Multi-color mode uses
the same memory access sequences as standard bit map mode, but
interprets the dot data as follows:
Note that the color nybble (DB11-DB8) IS used for the multi-color bit map
mode, again, as two bits are used to select one dot color, the horizontal
dot size is doubled, resulting in a screen resolution of 160H x 200V.
Utilizing multi-color bit map mode, three independently selected colors can
be displayed in each 8x8 block in addition to the background color.
00 01 02
03 04 05
57 58 59
60 61 62
Enable
Each MOB can be selectively enabled for display by setting its corre
sponding enable bit (MnE) to "1" in register 21 ($15). If the MnE bit is
"0," no MOB operations will occur involving the disabled MOB.
Position
Each MOB is positioned via its X and Y position register (see register
map) with a resolution of 512 horizontal and 256 vertical positions. The
position of a MOB is determined by the upper-left corner of the array. X
locations 23 to 347 ($17-$157) and Y locations 50 to 249 ($32-$F9) are
visible. Since not all available MOB positions are entirely visible on the
screen, MOBs may be moved smoothly on and off the display screen.
APPENDIX I 331
Color
Each MOB has a separate 4-bit register to determine the MOB color. The
two MOB color modes are:
In the standard mode, a "0" bit of MOB data allows any background data
to show through (transparent) and a "1" bit is displayed as the MOB color
determined by the corresponding MOB Color register.
Since two bits of data are required for each color, the resolution of the
MOB is reduced to 12x21, with each horizontal dot expanded to twice
standard size so that the overall MOB size does not change. Note that up
to 3 colors can be displayed in each MOB (in addition to transparent) but
that two of the colors are shared among all the MOBs in the multi-color
mode.
Magnification
Each MOB can be selectively expanded (2 x) in both the horizontal and
vertical directions. Two registers contain the control bits (MnXE,MnYE)
for the magnification control:
REGISTER FUNCTION
23 ($17) Horizontal expand MnXE—"1" = expand;
"0" = normal
29($1D) Vertical expand MnYE—"1" = expand; "0" = normal
332 MACHINE LANGUAGE FOR COMMODORE MACHINES
Priority
The priority of each MOB may be individually controlled with respect to
the other displayed information from character or bit map modes. The
priority of each MOB is set by the corresponding bit (MnDP) of register
27 ($1B) as follows:
MnDP = 1 MnDP = 0
MOBn Foreground
Foreground MOBn
Background Background
MOB data bits of "0" ("00" in multi-color mode) are transparent, always
permitting any other information to be displayed.
The MOBs have a fixed priority with respect to each other, with MOB 0
having the highest priority and MOB 7 the lowest. When MOB data (except
transparent data) of two MOBs are coincident, the data from the lower
number MOB will be displayed. MOB vs. MOB data is prioritized before
priority resolution with character or bit map data.
Collision Detection
Two types of MOB collision (coincidence) are detected, MOB to MOB
collision and MOB to display data collision:
MOB-MOB COLLISION register 30 ($1E) will be set to "1" for both colliding
MOBs. As a collision between two (or more) MOBs occurs, the MOB-MOB
collision bit for each collided MOB will be set. The collision bits remain set
until a read of the collision register, when all bits are automatically cleared.
MOBs collisions are detected even if positioned off-screen.
2) The second type of collision is a MOB-DATA collision between a MOB and
foreground display data from the character or bit map modes. The MOB-
DATA COLLISION register 31 ($1F) has a bit (MnD) for each MOB which
is set to "1" when both the MOB and non-background display data are
coincident. Again, the coincidence of only transparent data does not generate
a collision. For special applications, the display data from the 0-1 multicolor
bit pair also does not cause a collision. This feature permits their use as
background display data without interfering with true MOB collisions. A MOB-
DATA collision can occur off-screen in the horizontal direction if actual display
data has been scrolled to an off-screen position (see scrolling). The MOB-
DATA COLLISION register also automatically clears when read.
The collision interrupt latches are set whenever the first bit of either register
is set to "1." Once any collision bit within a register is set high, subsequent
collisions will not set the interrupt latch until that collision register has been
cleared to all "Os" by a read.
A13 A12 A11 A10 A09 A08 A07 A06 A05 A04 A03 A02 A01 A00
MP7 MP6 MP5 MP4 MP3 MP2 MP1 MP0 MC5 MC4 MC3 MC2 MC1 MC0
Where MPx are the MOB pointer bits from the video matrix and MCx are
the internally generated MOB counter bits. The MOB pointers are read
from the video matrix at the end of every raster line. When the Y position
register of a MOB matches the current raster line count, the actual fetches
of MOB data begin. Internal counters automatically step through the 63
bytes of MOB data, displaying three bytes on each raster line.
334 MACHINE LANGUAGE FOR COMMODORE MACHINES
Other Features
Screen Blanking
The display screen may be blanked by setting the DEN bit in register 17
($11) to a "0." When the screen is blanked, the entire screen will be filled
with the exterior color as in register 32 ($20). When blanking is active,
only transparent (Phase 1) memory accesses are required, permitting full
processor utilization of the system bus. MOB data, however, will be ac
cessed if the MOBs are not also disabled. The DEN bit must be set to "1"
fornormal video display.
Row/Column Select
The normal display consists of 25 rows of 40 characters (or character
regions) per row. For special display purposes, the display window may
be reduced to 24 rows and 38 characters. There is no change in the format
of the displayed information, except that characters (bits) adjacent to the
exterior border area will now be covered by the border. The select bits
operate as follows:
NUMBER OF NUMBER OF
RSEL ROWS CSEL COLUMNS
0 24 rows 0 38 columns
1 25 rows 1 40 columns
The RSEL bit is in register 17 ($11) and the CSEL bit is in register 22
($16). For standard display the larger display window is normally used,
while the smaller display window is normally used in conjunction with
scrolling.
Scrolling
The display data may be scrolled up to one entire character space in both
the horizontal and vertical direction. When used in conjunction with the
smaller display window (above), scrolling can be used to create a smooth
panning motion of display data while updating the system memory only
when a new character row (or column) is required. Scrolling is also used
to center a fixed display within the display window.
Light Pen
The light pen input latches the current screen position into a pair of reg
isters (LPX,LPY) on a low-going edge. The X position register 19 ($13)
will contain the 8 MSB of the X position at the time of transition. Since the
X position is defined by a 512-state counter (9 bits) resolution to 2 hori
zontal dots is provided. Similarly, the Y position is latched to its register
20 ($14) but here 8 bits provide single raster resolution within the visible
display. The light pen latch may be triggered only once per frame, and
subsequent triggers within the same frame will have no effect. Therefore,
you must take several samples before turning the light pen to the screen
(3 or more samples, average), depending upon the characteristics of your
light pen.
Raster Register
The raster register is a dual-function register. A read of the raster register
18 ($12) returns the lower 8 bits of the current raster position (the MSB-
RC8 is located in register 17 ($11)). The raster register can be interrogated
to implement display changes outside the visible area to prevent display
flicker. The visible display window is from raster 51 through raster 251
($033-$0FB). A write to the raster bits (including RC8) is latched for use
in an internal raster compare. When the current raster matches the written
value, the raster interrupt latch is set.
Interrupt Register
The interrupt register shows the status of the four sources of interrupt. An
interrupt latch in register 25 ($19) is set to "1" when an interrupt source
has generated an interrupt request. The four sources of interrupt are:
LATCH ENABLE
BIT BIT WHEN SET
To enable an interrupt request to set the IRQ/ output to "0," the corre
sponding interrupt enable bit in register 26 ($1 A) must be set to "1." Once
an interrupt latch has been set, the latch may be cleared only by writing
a "1" to the desired latch in the interrupt register. This feature allows
selective handling of video interrupts without software required to "re
member" active interrupts.
Reset
The reset bit (RES) in register 22 ($16) is not used for normal operation.
Therefore it should be set to "0" when initializing the video chip. When
set to a "1," the entire operation of the video chip is suspended, including
video outputs and sync, memory refresh, and system bus access.
Theory of Operation
System Interface
The 6566/6567 video controller devices interact with the system data bus
in a special way. A 65XX system requires the system buses only during
the Phase 2 (clock high) portion of the cycle. The 6566/6567 devices take
advantage of this feature by normally accessing system memory during
the Phase 1 (clock low) portion of the clock cycle. Therefore, operations
such as character data fetches and memory refresh are totally transparent
to the processor and do not reduce the processor throughput. The video
chips provide the interface control signals required to maintain this bus
sharing.
The video devices provide the signal AEC (address enable control) which
is used to disable the processor address bus drivers allowing the video
device to access the address bus. AEC is active low which permits direct
APPENDIX I 337
connection to the AEC input of the 65XX family. The AEC signal is normally
activated during Phase 1 so that processor operation is not affected. Be
cause of this bus "sharing," all memory accesses must be completed in
1/2 cycle. Since the video chips provide a 1-MHz clock (which must be
used as system Phase 2), a memory cycle is 500 ns including address
setup, data access and, data setup to the reading device.
The MOB pointers are fetched every other Phase 1 at the end of each
raster line. As required, the additional cycles are used for MOB data
fetches. Again, all necessary bus control is provided by the 6566/6567
devices.
Memory Interface
The two versions of the video interface chip, 6566 and 6567, differ in
address output configurations. The 6566 has thirteen fully decoded ad
dresses for direct connection to the system address bus. The 6567 has
multiplexed addresses for direct connection to 64K dynamic RAMs. The
least significant address bits, A06-A00, are present on A06-A00 while RAS/
is brought low, while the most significant bits, A13-A08, are present on
A05-A00 while CAS/ is brought low. The pins A11-A07 on the 6567 are
REGISTER MAP
ADDRESS DB7 DB6 DB5 DB4 DB3 DB2 DB1 DBO DESCRIPTION
00 ($00) M0X7 M0X6 M0X5 M0X4 M0X3 M0X2 M0X1 MOXO MOB 0 X-position
01 ($01) M0Y7 M0Y6 M0Y5 M0Y4 M0Y3 M0Y2 M0Y1 MOYO MOB 0 Y-position
02 ($02) M1X7 M1X6 M1X5 M1X4 M1X3 M1X2 M1X1 M1X0 MOB 1 X-position
03 ($03) M1Y7 M1Y6 M1Y5 M1Y4 M1Y3 M1Y2 M1Y1 M1Y0 MOB 1 Y-position
04 ($04) M2X7 M2X6 M2X5 M2X4 M2X3 M2X2 M2X1 M2X0 MOB 2 X-position
05 ($05) M2Y7 M2Y6 M2Y5 M2Y4 M2Y3 M2Y2 M2Y1 M2Y0 MOB 2 Y-position
06 ($06) M3X7 M3X6 M3X5 M3X4 M3X3 M3X2 M3X1 M3X0 MOB 3 X-position
07 ($07) M3Y7 M3Y6 M3Y5 M3Y4 M3Y3 M3Y2 M3Y1 M3Y0 MOB 3 Y-position
08 ($08) M4X7 M4X6 M4X5 M4X4 M4X3 M4X2 M4X1 M4X0 MOB 4 X-position
09 ($09) M4Y7 M4Y6 M4Y5 M4Y4 M4Y3 M4Y2 M4Y1 M4Y0 MOB 4 Y-position
10 ($0A) M5X7 M5X6 M5X5 M5X4 M5X3 M5X2 M5X1 M5X0 MOB 5 X-position
11 ($0B) M5Y7 M5Y6 M5Y5 M5Y4 M5Y3 M5Y2 M5Y1 M5Y0 MOB 5 Y-position
12 ($0C) M6X7 M6X6 M6X5 M6X4 M6X3 M6X2 M6X1 M6X0 MOB 6 X-position
13 ($0D) M6Y7 M6Y6 M6Y5 M6Y4 M6Y3 M6Y2 M6Y1 M6Y0 MOB 6 Y-position
14 ($0E) M7X7 M7X6 M7X5 M7X4 M7X3 M7X2 M7X1 M7X0 MOB 7 X-position
15 ($0F) M7Y7 M7Y6 M7Y5 M7Y4 M7Y3 M7Y2 M7Y1 M7Y0 MOB 7 Y-position
16 ($10) M7X8 M6X8 M5X8 M4X8 M3X8 M2X8 M1X8 M0X8 MSB of X-position
17 (S11) RC8 ECM BMM DEN RSEL Y2 Y1 YO See text
18 ($12) RC7 RC6 RC5 RC4 RC3 RC2 RC1 RCO Raster register
19 ($13) LPX8 LPX7 LPX6 LPX5 LPX4 LPX3 LPX2 LPX1 Light Pen X
20 ($14) LPY7 LPY6 LPY5 LPY4 LPY3 LPY2 LPY1 LPYO Light Pen Y
21 ($15) M7E M6E M5E M4E M3E M2E M1E MOE MOB Enable
— —
22 ($16) RES MCM CSEL X2 X1 XO See text
23 ($17) M7YE M6YE M5YE M4YE M3YE M2YE M1YE MOYE MOB Y-expand
24 ($18) VM13 VM12 VM11 VM10 CB13 CB12 CB11 —
Memory Pointers
25 ($19) IRQ — — — ILP IMMC IMBC IRST Interrupt Register
26 ($1A) — — — — ELP EMMC EMBC ERST Enable Interrupt
>
"0
m
z
27 ($1B) M7DP M6DP M5DP M4DP M3DP M2DP M1DP MODP MOB-DATA Priority g
x
28 ($1C) M7MC M6MC M5MC M4MC M3MC M2MC M1MC MOMC MOB Multicolor Sel
29 ($1D) M7XE M6XE M5XE M4XE M3XE M2XE M1XE MOXE MOB X-expand
30 ($1E) M7M M6M M5M M4M M3M M2M M1M MOM MOB-MOB Collision
31 ($1F) M7D M6D M5D M4D M3D M2D M1D MOD MOB-DATA Collision
— — — —
32 ($20) EC3 EC2 EC1 ECO Exterior Color
— — — —
33 ($21) BOC3 BOC2 BOC1 BOCO Bkgd #0 Color
— — — —
34 ($22) B1C3 B1C2 B1C1 B1C0 Bkgd #1 Color
— — — —
35 ($23) B2C3 B2C2 B2C1 B2C0 Bkgd #2 Color
— — — — B3C2 Bkgd #3 Color
36 ($24) B3C3 B3C1 B3C0
— — — —
37 ($25) MM03 MM02 MM01 MM00 MOB Multicolor #0
— — — —
38 ($26) MM13 MM12 MM11 MM10 MOB Multicolor #1
— — — —
39 ($27) M0C3 M0C2 M0C1 MOCO MOB 0 Color
— — — —
40 ($28) M1C3 M1C2 M1C1 M1C0 MOB 1 Color
— — — —
41 ($29) M2C3 M2C2 M2C1 M2C0 MOB 2 Color
— — — —
42 ($2A) M3C3 M3C2 M3C1 M3C0 MOB 3 Color
— — — —
43 ($2B) M4C3 M4C2 M4C1 M4C0 MOB 4 Color
— — — —
44 ($2C) M5C3 M5C2 M5C1 M5C0 MOB 5 Color
— — — —
45 ($2D) M6C3 M6C2 M6C1 M6C0 MOB 6 Color
46 ($2E) — — — — M7C3 M7C2 M7C1 M7C0 MOB 7 Color
CO
CO
340 MACHINE LANGUAGE FOR COMMODORE MACHINES
COLOR CODES
0 0 0 0 0 0 BLACK
0 0 0 1 1 1 WHITE
0 0 1 0 2 2 RED
0 0 1 1 3 3 CYAN
0 1 0 0 4 4 PURPLE
0 1 0 1 5 5 GREEN
0 1 1 0 6 6 BLUE
0 1 1 1 7 7 YELLOW
1 0 0 0 8 8 ORANGE
1 0 0 1 9 9 BROWN
1 0 1 0 A 10 LTRED
1 0 1 1 B 11 DARK GRAY
1 1 0 0 C 12 MED GRAY
1 1 0 1 D 13 LT GREEN
1 1 1 0 E 14 LT BLUE
1 1 1 1 F 15 LT GRAY
Features
• 3 TONE OSCILLATORS
Range: 0-4 kHz
APPENDIX I 341
• OSCILLATOR SYNCHRONIZATION
• RING MODULATION
Description
The 6581 consists of three synthesizer "voices" which can be used in
dependently or in conjunction with each other (or external audio sources)
to create complex sounds. Each voice consists of a Tone Oscillator/Wave
form Generator, an Envelope Generator and an Amplitude Modulator. The
Tone Oscillator controls the pitch of the voice over a wide range. The
Oscillator produces four waveforms at the selected frequency, with the
unique harmonic content of each waveform providing simple control of
tone color. The volume dynamics of the oscillator are controlled by the
Amplitude Modulator under the direction of the Envelope Generator. When
triggered, the Envelope Generator creates an amplitude envelope with
programmable rates of increasing and decreasing volume. In addition to
the three voices, a programmable Filter is provided for generating complex,
dynamic tone colors via subtractive synthesis.
SIS allows the microprocessor to read the changing output of the third
Oscillator and third Envelope Generator. These outputs can be used as
a source of modulation information for creating vibrator, frequency/filter
sweeps and similar effects. The third oscillator can also act as a random
number generator for games. Two A/D converters are provided for inter
facing SID with potentiometers. These can be used for "paddles" in a
game environment or as front panel controls in a music synthesizer. SID
can process external audio signals, allowing multiple SID chips to be daisy-
chained or mixed in complex polyphonic systems.
342 MACHINE LANGUAGE FOR COMMODORE MACHINES
Together these registers form a 16-bit number which linearly controls the
frequency of Oscillator 1. The frequency is determined by the following
equation:
Where Fn is the 16-bit number in the Frequency registers and Fclk is the
system clock applied to the $2 input (pin 6). For a standard 1.0-MHz clock,
the frequency is given by:
Together these registers form a 12-bit number (bits 4-7 of PW HI are not
used) which linearly controls the Pulse Width (duty cycle) of the Pulse
waveform on Oscillator 1. The pulse width is determined by the following
equation:
PW0Ut = (PWn/40.95)%
The pulse width resolution allows the width to be smoothly swept with no
discernable stepping. Note that the Pulse waveform on Oscillator 1 must
be selected in order for the Pulse Width registers to have any audible
ADDRESS REG# REG NAME REG
A4 A3 A2 i*1 Ao (HEX) D7 D6 D5 D4 D3 D2 Di Do Voice 1 TYPE
0 0 0 0 0 0 00 F7 F6 F5 F4 F3 F2 F1 Fo FREQ LO WRITE-ONLY
2 0 0 0 1 0 02 PW7 PW6 PW5 PW4 PW3 PW2 PW1 PW0 PW-LO WRITE-ONLY
5 0 0 1 0 1 05 ATK3 ATK2 ATT^ ATKo DCY3 DCY2 DCY1 DCY0 ATTACK/DECAY WRITE-ONLY
6 0 0 1 1 0 06 STN3 STN2 STN1 STNq RLS3 RLS2 RLS1 RLSo SUSTAIN/RELEASE WRITE-ONLY
Voice 2
7 0 0 1 1 1 07 F7 F6 F5 F4 F3 F2 F1 Fo FREQ LO WRITE-ONLY
9 0 1 0 0 1 09 PW7 PW6 PW5 PW4 PW3 PW2 PWi PW0 PWLO WRITE-ONLY
12 0 1 1 0 0 OC ATK3 ATK2 ATK1 ATKo DCY3 DCY2 DCY1 DCY0 ATTACK/DECAY WRITE-ONLY
13 0 1 1 0 1 OD STN3 STN2 STN1 STNq RLS3 RLS2 RLS1 RLSo SUSTAIN/RELEASE WRITE-ONLY
Voice 3
14 0 1 1 1 0 OE F7 F6 F5 F4 F3 F2 F1 Fo FREQ LO WRITE-ONLY
16 1 0 0 0 0 10 PW7 PW6 PW5 PW4 PW3 PW2 PW! PW0 PWLO WRITE-ONLY
19 1 0 0 1 1 13 ATK3 atk2 ATK1 ATKo DCY3 DCY2 DCY1 DCY0 ATTACK/DECAY WRITE-ONLY
20 1 0 1 0 0 14 STN3 STN2 STN1 STN0 RLS3 RLS2 RLS1 RLSo SUSTAIN/RELEASE WRITE-ONLY
Filter
>
21 1 0 1 0 1 15 — — — — — O
FC2 FCi FCo FCLO WRITE-ONLY
22 1 0 1 1 0 16
I
FC10 FCg FC8 FC7 FC6 FC5 FC4 FC3 FCHI WRITE-ONLY
23 1 0 1 1 1 17
z
RES3 RES2 RES1 RESo FILTEX FILT3 FILT2 FILT1 RES/FILT WRITE-ONLY m
24 1 1 0 0 0 18 3 OFF HP BP LP VOL3 VOL2 VOM VOLo MODE/VOL WRITE-ONLY
Misc.
25 1 1 0 0 1 19 Q
PX7 PXe PX5 PX4 PX3 PX2 PX1 PXo POTX READONLY
26 1 1 0 1 0 1A PY7 PY6 PY5 PY4 PY3 PY2 PY1 PYo POTY READ-ONLY
27 1 1 0 1 1 1B Oy O6 05 04 03 02 O1 Oo aOSC3/RANDOM READ-ONLY
i
28 1 1 1
m
0 0 1C E7 E6 E5 E4 E3 E2 E1 Eo ENV3 READ-ONLY
Figure I.4 O
O
O
D
O
m
m
CO
APPENDIX I 345
effect. A value of 0 or 4095 ($FFF) in the Pulse Width registers will produce
a constant DC output, while a value of 2048 ($800) will produce a square
wave.
This register contains eight control bits which select various options on
Oscillator 1.
Gate (Bit 0): The GATE bit controls the Envelope Generator for Voice 1.
When this bit is set to a one, the Envelope Generator is Gated (triggered)
and the ATTACK/DECAY/SUSTAIN cycle is initiated. When the bit is reset
to a zero, the RELEASE cycle begins. The Envelope Generator controls
the amplitude of Oscillator 1 appearing at the audio output, therefore, the
GATE bit must be set (along with suitable envelope parameters) for the
selected output of Oscillator 1 to be audible. A detailed discussion of the
Envelope Generator can be found at the end of this Appendix.
SYNC (Bit 1): The SYNC bit, when set to a one, synchronizes the fun
damental frequency of Oscillator 1 with the fundamental frequency of
Oscillator 3, producing "Hard Sync" effects.
RING MOD (Bit 2): The RING MOD bit, when set to a one, replaces the
Triangle waveform output of Oscillator 1 with a "Ring Modulated" com
bination of Oscillators 1 and 3. Varying the frequency of Oscillator 1 with
respect to Oscillator 3 produces a wide range of non-harmonic overtone
structures for creating bell or gong sounds and for special effects. In order
for ring modulation to be audible, the Triangle waveform of Oscillator 1
must be selected and Oscillator 3 must be set to some frequency other
than zero. No other parameters of Voice 3 have any effect on ring mod
ulation.
TEST (Bit 3): The TEST bit, when set to a one, resets and locks Oscillator 1
at zero until the TEST bit is cleared. The Noise waveform output of Oscillator 1
is also reset and the Pulse waveform output is held at a DC level. Normally
this bit is used for testing purposes, however, it can be used to synchronize
Oscillator 1 to external events, allowing the generation of highly complex
waveforms under real-time software control.
346 MACHINE LANGUAGE FOR COMMODORE MACHINES
(Bit 4): When set to a one, the Triangle waveform output of Oscillator 1
is selected. The Triangle waveform is low in harmonics and has a mellow,
flute-like quality.
(Bit 5): When set to a one, the Pulse waveform output of Oscillator 1 is
selected. The Sawtooth waveform is rich in even and odd harmonics and
has a bright, brassy quality.
(Bit 6): When set to a one, the Pulse waveform of Oscillator 1 is selected.
The harmonic content of this waveform can be adjusted by the Pulse
Width registers, producing tone qualities ranging from a bright, hollow
square wave to a nasal, reedy pulse. Sweeping the pulse width in real
time produces a dynamic "phasing" effect which adds a sense of motion
to the sound. Rapidly jumping between different pulse widths can produce
interesting harmonic sequences.
NOISE (Bit 7): When set to a one, the Noise output waveform of Oscillator 1
is selected. This output is a random signal which changes at the frequency
of Oscillator 1. The sound quality can be varied from a low rumbling to
hissing white noise via the Oscillator 1 Frequency registers. Noise is useful
in creating explosions, gunshots, jet engines, wind, surf and other un-
pitched sounds, as well as snare drums and cymbals. Sweeping the os
cillator frequency with Noise selected produces a dramatic rushing effect.
0 (0) 2 ms 6 ms
1 (1) 8 ms 24 ms
2 (2) 16 ms 48 ms
3 (3) 24 ms 72 ms
4 (4) 38 ms 114 ms
5 (5) 56 ms 168 ms
6 (6) 68 ms 204 ms
7 (7) 80 ms 240 ms
8 (8) 100 ms 300 ms
9 (9) 250 ms 750 ms
10 (A) 500 ms 1.5 s
11 (B) 800 ms 2.4 s
12 (C) 1 s 3s
13 (D) 3s 9s
14 (E) 5s 15s
15 (F) 8s 24 s
NOTE: Envelope rates are based on a 1.0-MHz c|>2 clock. For other
<|>2 frequencies, multiply the given rate by 1 MHz/<}>2. The rates refer
to the amount of time per cycle. For example, given an ATTACK
value of 2, the ATTACK cycle would take 16 ms to rise from zero to
peak amplitude. The DECAY/RELEASE rates refer to the amount of
time these cycles would take to fall from peak amplitude to zero.
348 MACHINE LANGUAGE FOR COMMODORE MACHINES
Voice 2
Registers 07-$0D control Voice 2 and are functionally identical to registers
00-06 with these exceptions:
Voice 3
Registers $0E-$14 control Voice 3 and are functionally identical to reg
isters 00-06 with these exceptions:
Filter
FC LO/FC HI (Registers $15,$ 16)
Bits 4-7 of this register (RES0-RES3) control the resonance of the filter.
Resonance is a peaking effect which emphasizes frequency components
at the Cutoff Frequency of the Filter, causing a sharper sound. There are
16 resonance settings ranging linearly from no resonance (0) to maximum
resonance (15 or $F). Bits 0-3 determine which signals will be routed
through the Filter:
FILT 1 (Bit 0): When set to a zero, Voice 1 appears directly at the audio
output and the Filter has no effect on it. When set to a one, Voice 1 will
be processed through the Filter and the harmonic content of Voice 1 will
be altered according to the selected Filter parameters.
FILTEX (Bit 3): Same as bit 0 for External audio input (pin 26).
Bits 4-7 of this register select various Filter mode and output options:
LP (Bit 4): When set to a one, the Low-Pass output of the Filter is selected
and sent to the audio output. For a given Filter input signal, all frequency
components below the Filter Cutoff Frequency are passed unaltered, while
all frequency components above the Cutoff are attenuated at a rate of 12
dB/Octave. The Low-Pass mode produces full-bodied sounds.
BP (Bit 5): Same as bit 4 for the Bandpass output. All frequency com
ponents above and below the Cutoff are attenuated at a rate of 6 dB/
Octave. The Bandpass mode produces thin, open sounds.
350 MACHINE LANGUAGE FOR COMMODORE MACHINES
HP (Bit 6): Same as bit 4 for the High-Pass output. All frequency com
ponents above the Cutoff are passed unaltered, while all frequency com
ponents below the Cutoff are attenuated at a rate of 12 dB/Octave. The
High-Pass mode produces tinny, buzzy sounds.
3 OFF (Bit 7): When set to a one, the output of voice 3 is disconnected
from the direct audio path. Setting Voice 3 to bypass the Filter (FILT 3 =
0) and setting 3 OFF to a one prevents Voice 3 from reaching the audio
output. This allows Voice 3 to be used for modulation purposes without
any undesirable output.
NOTE: The Filter output modes ARE additive and multiple Filter
modes may be selected simultaneously. For example, both LP and
HP modes can be selected to produce a Notch (or Band Reject)
Filter response. In order for the Filter to have any audible effect, at
least one Filter output must be selected and at least one Voice must
be routed through the Filter. The Filter is, perhaps, the most important
element in SID as it allows the generation of complex tone colors
via subtractive synthesis (the Filter is used to eliminate specific fre
quency components from a harmonically rich input signal). The best
results are achieved by varying the Cutoff Frequency in real-time.
Bits 0-3 (VOL0-VOL3) select 1 of 16 overall Volume levels for the final
composite audio output. The output volume levels range from no output
(0) to maximum volume (15 or $F) in 16 linear steps. This control can be
used as a static volume control for balancing levels in multi-chip systems
or for creating dynamic volume effects, such as Tremolo. Some Volume
level other than zero must be selected in order for SID to produce any
sound.
Miscellaneous
POTX (Register $19)
This register allows the microprocessor to read the position of the poten
tiometer tied to POTX (pin 24), with values ranging from 0 at minimum
resistance, to 255 ($FF) at maximum resistance. The value is always valid
and is updated every 512 c}>2 clock cycles. See the Pin Description section
for information on pot and capacitor values.
POTY (Register $1 A)
This register allows the microprocessor to read the upper 8 output bits of
Oscillator 3. The character of the numbers generated is directly related to
the waveform selected. If the Sawtooth waveform of Oscillator 3 is se
lected, this register will present a series of numbers incrementing from 0
to 255 ($FF) at a rate determined by the frequency of Oscillator 3. If the
Triangle waveform is selected, the output will increment from 0 up to 255,
then decrement down to 0. If the Pulse waveform is selected, the output
will jump between 0 and 255. Selecting the Noise waveform will produce
a series of random numbers, therefore, this register can be used as a
random number generator for games. There are numerous timing and
sequencing applications for the OSC 3 register, however, the chief function
is probably that of a modulation generator. The numbers generated by
this register can be added, via software, to the Oscillator or Filter Fre
quency registers or the Pulse Width registers in real-time. Many dynamic
effects can be generated in this manner. Siren-like sounds can be created
by adding the OSC 3 Sawtooth output to the frequency control of another
oscillator. Synthesizer "Sample and Hold" effects can be produced by
adding the OSC 3 Noise output to the Filter Frequency control registers.
Vibrato can be produced by setting Oscillator 3 to a frequency around
7 Hz and adding the OSC 3 Triangle output (with proper scaling) to the
Frequency controj of another oscillator. An unlimited range of effects are
available by altering the frequency of Oscillator 3 and scaling the OSC 3
output. NormaNy, when Oscillator 3 is used for modulation, the audio output
of Voice 3 should be eliminated (3 OFF = 1).
Same as OSC 3, but this register allows the microprocessor to read the
output of the Voice 3 Envelope Generator. This output can be added to
the Filter Frequency to produce harmonic envelopes, WAH-WAH, and
similar effects. "Phaser" sounds can be created by adding this output to
the frequency control registers of an oscillator. The Voice 3 Envelope
Generator must be Gated in order to produce any output from this register.
The OSC 3 register, however, always reflects the changing output of the
oscillator and is not affected in any way by the Envelope Generator.
352 MACHINE LANGUAGE FOR COMMODORE MACHINES
6525 Addressing
AIR A4 A3 A2 A, Ao
DDRC M4 M3 M2 M, Mo
When MC = 1
PRC CB CA IRQ u I3 ■2 li lo
When MC = 1
CA Output Modes
CB Output Modes
MODE DESCRIPTION
When the Interrupt Mode is selected (MC = 1), the Data Direction Register
for Port C (DDRC) is used to enable or disable a corresponding interrupt
input. For example: If Mo = 0 then l0 is disabled and any l0 interrupt latched
in the interrupt latch register will not be transferred to the AIR and will not
cause IRQ to.go low. The interrupt latch can be cleared by writing a zero
to the appropriate I bit in PRC.
354 MACHINE LANGUAGE FOR COMMODORE MACHINES
Port Register C (PRC) can operate in two modes. The mode is controlled
by bit MC in register CR. When MC = 0, PRC is a standard I/O port,
operating identically to PRA & PRB. If MC = 1, then port register C is
used for handshaking and priority interrupt input and output.
PRC When MC = 0:
PRC When MC = 1
CB CA IRQ U i3 l2 l1 lo
Bits IE4 and IE3 in the control register (CR) are used to determine the
active edge which will be recognized by the interrupt latch.
If IE4 (IE3) = 1 then l4 (l3) latch will be set on a positive transition of the
l4 (l3) input.
All other interrupt latches (l2, li, l0) are set on a negative transition of the
corresponding interrupt input.
IP = 1 Interrupts Prioritized
APPENDIX I 355
FUNCTIONAL DESCRIPTION
1. IP = 0 No Priority
2. IP = 1 Interrupts Prioritized
In this mode the Interrupt Inputs are prioritized in the following order l4 >
l3 > l2 > li > lo
In this mode only one bit of the AIR can be set at any one time. If an
interrupt occurs it is latched into the interrupt latch register, the IRQ line
is pulled low and the appropriate bit of the AIR is set. To understand fully
the operation of the priority interrupts it is easiest to consider the following
examples.
A. The first case is the simplest. A single interrupt occurs and the pro
cessor can service it completely before another interrupt request is
received.
1. Interrupt 1j is received.
2. BitJ1 is set high in Interrupt Latch Register.
3. IRQ is pulled low.
4. Ai is set high.
5. Processor recognizes IRQ and reads AIR to determine which in
terrupt occurred.
6. Bit I, is reset and IRQ is reset to high.
7. Processor Services Interrupt and signals completion of Service
routine by writing to AIR.
8. Ai is reset low and interrupt sequence is complete.
B. The second case occurs when an interrupt has been received and a
higher priority interrupt occurs. (See Note)
1. Interrupt I, is received.
2. Bitj! is set high on the Interrupt Latch Register.
3. IRQ is pulled low and AA is set high.
356 MACHINE LANGUAGE FOR COMMODORE MACHINES
C. The third case occurs when an interrupt has been received and a
lower priority interrupt occurs.
1. Interrupt \A is received and latched.
2. IRQ is pulled low and A1 is set high.
3. Processor recognizes IRQ and reads AIR to determine that h in
terrupt occurred.
4. Processor logic servicing I, interrupt during which l0 interrupt oc
curs and is latched.
5. Upon completion of ^ interrupt routine the processor writes AIR
to clear A, to signal 6525 that interrupt service is complete.
6. Latch l0 interrupt is transferred to AIR and IRQ is pulled low to
begin new interrupt sequence.
NOTE: It was indicated that the 6525 will maintain Priority Interrupt
information from previously serviced interrupts.
The only time a write of AIR should occur is to signal the 6525 that
the interrupt service is complete.
J
Disk User's
Guide
The optional disk holds programs supplementary to the book. The pro
grams are as follows:
These programs are public domain, and may be obtained from user groups.
They are available here for user convenience.
Remember that the programs on disk are "monitor generators," that is,
they build the monitor for you. After the monitor has been built, you should
remove the builder program so that you don't end up with two copies. In
other words, after RUN type .X to return to BASIC, NEW to scrap the
builder, and then SYS^orSYSflto return to the monitor whenever de
sired.
The monitor is always built near the top of memory. Its entry address can
be determined by checking the TOM (top-of-memory) pointer. Monitors
are complex, but feel free to ask the monitor to disassemble itself for your
information.
After Supermon is placed, you may load BASIC programs and use the
computer normally. Supermon will remain until you shut off the power.
APPENDIX J 359
UHICOPYt*
A utility for copying files from one disk to another, on a single drive; or
copying from one disk to cassette tape. The program is written entirely in
machine language, apart from the SYS that starts it up.
Information is copied from the selected files into RAM memory. When the
output phase begins, the data is then written to disk or tape.
0NIC0PY INST
UNICOPY LIST
JUNICOPY ASSY
For cassette tape output, direct calls to the ROM routines are made; that's
usually not good practice, but there's little choice here.
The program is written in machine language so that the BASIC ROM can
be flippped out, allowing for more memory space in which to copy pro
grams.
A utility for copying files from one disk drive to another. You will find two
S Y S commands in the BASIC part of the program: one to get the directory,
and the other to do the actual copying.
Information is copied from the selected file into a BASIC string that has
been set aside for the purpose. A similar technique may be found in the
simpler STRING THING.
The program uses two table lookup techniques that may be confusing to
the beginning machine language program reader. First, it classifies all
characters received from BASIC in terms of "type"; this is done with a
table of 256 elements, one for each possible character. Second, it uses
a "state transition table" to record the nature of the job in progress; for
example, after meeting a GOSUB "token," it will expect to receive a line
number.
The second S YS in the BASIC program is used to print the line numbers
of the cross-reference. It employs an efficient binary-to-decimal conversion
technique, which uses decimal mode.
The program does not try all divisors. After trying a few initial values (2,
3, and 5), it switches to a "30-counter" technique, trying only multiples of
30 plus 1,7, 11, 17, 19, 23, and 29.
was originally loaded. This was originally done to allow for the VIC-20's
variable start-of-BASIC, which rambles according to the amount of extra
memory fitted. It turns out to be useful for study to have the program in a
fixed location; so the PET/CMB version was also set up in this way.
C-128 note: The above locations are sensitive in the C128; the above
variables have been relocated to page B. Thus, instead oi $Q3A^ given
above, address $UBAC\ will be used.
C128 note: Basic is located at quite a high address in this machine; the
start address of the program has been moved up to $1DDD to allow for
this. The above table is correct if the extra offset is allowed; thus $1415
above becomes $ IE 15 in the C128.
Even at machine language speeds, this program can take a long time to
analyze large factors and prime numbers. The RUN/STOP key is active
to allow the user to stop the run.
This is a puzzle solving problem. Pieces are fitted into a selected rectan
gular shape "visibly"—in other words, they may be seen on the screen
as they are tried.
The machine language programs follow the logic of the BASIC program
precisely. The "shape tables" have been rearranged for greater machine
language convenience (each piece is reached by indexing; the index range
of 0 to 255 dictates the piece being selected and its rotation).
This program makes use of tables, and is worth studying for that reason.
It is also useful to examine the close relationship between the BASIC
program and its machine language equivalent, especially starting at line
2000 in BASIC.
C128 note: The above locations are sensitive in the C128; the above
variables have been relocated to page B. Thus, instead of $D3t5 given
above, address $DBb2 will be used.
C128 note: Basic is located at quite a high address in this machine; the
start address of the program has been moved up to $lFbD to allow for
this. The above table is correct if the extra offset is allowed; thus $lbDD
above becomes $2DDD in the C128.
The B128 version does not align to the above addresses. It is written to
illustrate the "boot" loading system needed for that computer. Programs
whose names begin with a + symbol are loaded by the bootstrap program;
do not try to load them directly.
364 MACHINE LANGUAGE FOR COMMODORE MACHINES
INPUT# has several limitations that sometimes make it awkward for use
with files:
The 128 machine language program is brief and makes good study ma
terial. Since the program is in bank 0 but the variable table is in bank 1,
it is necessary to call special Kernal routine INDFET ($FF7 A ) to get
the information. Later, when the program wishes to place a character into
the string (which also resides in bank 1), it must call special Kernal routine
INDSTA ($FF77 ) to get it there. The manner in which the calls are set
up is instructive.
Glossary
The numbers in parentheses indicate the chapter in which the word or
phrase is first used.
Absolute address: (5) An address that can indicate any location in
memory.
365
366 MACHINE LANGUAGE FOR COMMODORE MACHINES
371
372 MACHINE LANGUAGE FOR COMMODORE MACHINES
BVS, Branch on overflow set, 150 Color codes of the 6566/6567, 340
Bytes, multiple, 58 Commodore computers,
characteristics of, 156-166
C flag, 42, 45, 46 Compare, 141
Character display mode of the Comparing numbers, 61-62
6566/6567, 325-327 Complex interface adaptor 6526,
Character sets, 242-250 318-325
Chip information, 293-356 Control bus, 5
6520 (PIA) Peripheral interface CPX, Compare memory and index
adaptor, 294-298 X, 150
6522 (VIA) Versatile interface CPY, Compare memory and index
adaptor, 309-318 Y, 150
6525 Tri-port interface, 352-356
6526 (CIA) Complex interface Data bus, 4-5
adaptor, 318-325 see also Bus
6545-1 (CRTC) CRT controller, Data exchange, BASIC machine
299-304 language, 104-108
6560 (VIC) video interface chip, Debugging, 143
304-309 DEC, Decrement memory by one,
6566/6567 (VIC II) chip 150
specifications, 325-340 Decimal notation to hexadecimal,
6581 (SID) Sound interface 7-8
device, chip specifications, DEX, Decrement index X by one,
340-351 150
CHKIN subroutine, 136 DEY, Decrement index Y by one,
CHKOUT subroutine, 133, 134 150
CHRGET subroutine, 122-123, Disassembler, checking the,
124 29-30
CHRGOT subroutine, 123, Disk user's guide, 357-364
124-125 Division by two, 63-64
CHROUT subroutine, 25, 133 Do nothing insruction, 72-74
CIA chip, 120 Dynamic string, 94
CLC, Clear carry flag, 150
CLD, Clear decimal mode, 150 Effective address, 32
CLI, clear interrupt disable bit, 118 End of BASIC, 92-93
Clock speed, 132 Envelope rates of the 6581, 347
CLOSE, 134 EOA, end of arrays, 93
CLRCHN subroutine, 133, 135, EOR, exclusive or, 47, 48, 49, 121,
136, 137 150
CLV, Clear overflow flag, 150 EOR instruction, 87
CMP, Compare memory and Exercises, 11-13, 52-54,
accumulator, 150 84-88, 252-278
INDEX 373
TABLE OF CONTENTS
First Concepts / Controlling Output / Flags, Logic, and Input / Numbers,
Arithmetic, and Subroutines / Address Modes / Linking BASIC and
Machine Language / Stack, USR, Interrupt, and Wedge / Timing, Input/
Output, and Conclusion / Appendix A: The 6502/6510/6509/7501 Instruc
tion Set / Appendix B: Some Characteristics of Commodore Machines /
Appendix C: Memory Maps / Appendix D: Character Sets / Appendix E:
Exercises for Alternative Commodore Machines / Appendix F: Floating
Point Formats / Appendix G: Uncrashing / Appendix H: A Do-It-Yourself
Supermon / Appendix I: IA Chip Information / Appendix J: Disk User's
Guide / Index